atuinsh / atuin

✨ Magical shell history

Home Page:https://atuin.sh

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug]: Copy to clipboard unsuccessful

ovv opened this issue · comments

What did you expect to happen?

Pressing ctrl-y in the search view should copy the selected line to the clipboard

What happened?

Pressing ctrl-y closes atuin but the clipboard content is not set

Atuin doctor output

atuin:
  version: 18.2.0
  sync:
    cloud: false
    records: true
    auto_sync: true
    last_sync: 2024-05-17 11:10:19.493442155 +00:00:00
shell:
  name: fish
  default: unknown
  plugins:
  - atuin
system:
  os: Fedora Linux
  arch: x86_64
  version: '39'
  disks:
  - name: /dev/mapper/luks-a71d0523-7b8b-4cc2-9d60-a73e65041027
    filesystem: btrfs
  - name: /dev/mapper/luks-a71d0523-7b8b-4cc2-9d60-a73e65041027
    filesystem: btrfs
  - name: /dev/nvme0n1p2
    filesystem: ext4
  - name: /dev/nvme0n1p1
    filesystem: vfat

Code of Conduct

  • I agree to follow this project's Code of Conduct

Interestingly using this patch make it work. I'm quite confused how/why

diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs
index 9185bc0f..5caedeb4 100644
--- a/crates/atuin/src/command/client/search/interactive.rs
+++ b/crates/atuin/src/command/client/search/interactive.rs
@@ -1174,7 +1174,8 @@ pub async fn history(
     any(target_os = "windows", target_os = "macos", target_os = "linux")
 ))]
 fn set_clipboard(s: String) {
-    cli_clipboard::set_contents(s).unwrap();
+    cli_clipboard::set_contents(s.to_owned()).unwrap();
+    assert_eq!(cli_clipboard::get_contents().unwrap(), s);
 }
 
 #[cfg(not(all(

I was not able to reproduce this on macOS.

I think this is only an issue on Linux. What window manager/graphical env are you using?

I think this is only an issue on Linux. What window manager/graphical env are you using?

I'm using KDE Plasma, it reproduces on plasma 5 and 6

The cli_clipboard crate documentation mention a different behavior on Linux.

This uses the platform default behavior for setting clipboard contents. Other users of the Wayland or X11 clipboard will only see the contents copied to the clipboard so long as the process copying to the clipboard exists. MacOS and Windows clipboard contents will stick around after your application exits.

I don't fully understand the so long as the process copying to the clipboard exists since it seems to work if the process doesn't exit instantly. This code work, removing the sleep makes it fail.

use cli_clipboard;
use std::time::Duration;
use std::thread::sleep;

fn main() {
    let the_string = "foobar";
    cli_clipboard::set_contents(the_string.to_owned()).unwrap();
    sleep(Duration::new(1, 0));
}

Looks like the bug is not directly related to atuin

Hi @ellie, I am working on packaging atuin for Fedora (now there is only 1 dependency left, guess which), and I've encountered errors when testing cli_clipboard. Upstream appears to encounter similar test failures. They also point to a few alternatives, primarily arboard and copypasta. The former seems to be the most active right now. Both are packaged right now, so it could be that these work better for linux. Will you consider trying either of these out?

I'd be pretty happy to switch to arboard - I'll likely get around to doing so fairly soon, unless anyone fancies making a PR

@LecrisUT #2067 works fine, but it has the same issue. Removing the ctx.get_text().unwrap(); line result in the clipboard not being set.

@LecrisUT #2067 works fine, but it has the same issue. Removing the ctx.get_text().unwrap(); line result in the clipboard not being set.

Indeed, but I am not sure what would be an appropriate fix. From what I've tested the issue is that the clipboard must be consumed somewhere for it to be saved on the system, otherwise even with set_text().unwrap() it doesn't seem to be propagated. Could be an upstream issue or it could be a misunderstanding on the API, not sure. I couldn't figure it out from reading the issues related to it, but maybe someone has better ideas.

But anything that does ctx.get_text().unwrap() should be fine and it could be something like checking that the clipboard is set, otherwise try again n times and give an error message that it failed afterwards. Dunno how to implement the latter part for that though

I can't test right now, but I'm curious to know if we need an explicity get_text or if a sleep does it

From my testing, the sleep also works, but it's finicky and it makes it slugish. get_text is more responsive. In both cases though, there is no guarantee that the clipboard values was set (or even not overwritten) until we actually test for it

Hi there, maintainer/owner of arboard here 👋:

From my testing, the sleep also works...

It might be possible that the clipboard manager is taking longer to respond then the data serving thread is willing to wait currently. If you enable trace level logging for arboard, you should be able to see if that is happening by viewing the steps of clipboard handoff.

I don't know about @ovv, but I am testing on wayland. Is there an equivalent there?

It doesn't look like wl-clipboard-rs has much logging it it, so I think the answer is no :(. Most of what that crate does is a black box to arboard for better or worse.

I've been struggling with this issue for a long time. Even in 18.4.0-beta.3, the problem still persists.
Environment: Ubuntu-2204, X11, Kitty, Zsh.
Calling external commands, while pretty ugly, does the trick. Sharing in case it helps someone.

 crates/atuin/src/command/client/search/interactive.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git crates/atuin/src/command/client/search/interactive.rs crates/atuin/src/command/client/search/interactive.rs
index 777ec257..e0e615b3 100644
--- crates/atuin/src/command/client/search/interactive.rs
+++ crates/atuin/src/command/client/search/interactive.rs
@@ -1,5 +1,6 @@
 use std::{
     io::{stdout, Write},
+    process::Command,
     time::Duration,
 };
 
@@ -1197,6 +1198,9 @@ pub async fn history(
     any(target_os = "windows", target_os = "macos", target_os = "linux")
 ))]
 fn set_clipboard(s: String) {
+    let wtf = format!("copyq write text/plain '{}' && copyq select 0", s.clone());
+    Command::new("sh").arg("-c").arg(&wtf).output().expect("Failed to execute command");
+
     let mut ctx = arboard::Clipboard::new().unwrap();
     ctx.set_text(s).unwrap();
     // Use the clipboard context to make sure it is saved

@s5unty I cannot reliably reproduce on my end, but if you can, could you check what you get when you try to compare ctx.get_text().unwrap() with the input s? My assumption is that doing this test n times should make it more reliable. As for copyq approach, can you elaborate more what is going on there?

IIUC, copyq is a third-party clipboard manager, so it ~sort of makes sense that directly invoking it would provide more reliable results then going through X11, etc.

I would be curious to see if blocking atuin's main thread from exiting for around 2 seconds also "fixes" the issue. That would confirm my hypothesis above that the process is just exiting before a clipboard handoff to the manager can complete. In comparison, the logic above blocks the process until the data is directly handed to copyq and then put back on the clipboard.

I would be curious to see if blocking atuin's main thread from exiting for around 2 seconds also "fixes" the issue

When I tried @ovv approach there with the sleep, it indeed worked. But of course there is a difference between 2 seconds and 0.01 sec.

In comparison, the logic above blocks the process until the data is directly handed to copyq and then put back on the clipboard.

Well it's a bit weird because that command is run before the set_text

Well it's a bit weird because that command is run before the set_text

It likely has the same observable effect IIUC. Once atuin stop's serving its data, the clipboard manager takes over serving data again (since that's how it can keep serving data after arbitrary numbers of processes/apps exit). Even though arboard failed to hand the data over, identical data was already in the clipboard manager so the failure is papered over.

% git di
 crates/atuin/src/command/client/search/interactive.rs | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git crates/atuin/src/command/client/search/interactive.rs crates/atuin/src/command/client/search/interactive.rs
index 777ec257..5bc91777 100644
--- crates/atuin/src/command/client/search/interactive.rs
+++ crates/atuin/src/command/client/search/interactive.rs
@@ -1,5 +1,7 @@
 use std::{
     io::{stdout, Write},
+    fs::write,
+    process::Command,
     time::Duration,
 };
 
@@ -1197,10 +1199,15 @@ pub async fn history(
     any(target_os = "windows", target_os = "macos", target_os = "linux")
 ))]
 fn set_clipboard(s: String) {
+    // let wtf = format!("copyq write text/plain '{}' && copyq select 0", s.clone());
+    // Command::new("sh").arg("-c").arg(&wtf).output().expect("Failed to execute command");
+
+    write("/tmp/debug1.log", s.clone()).expect("Unable to write to file");
     let mut ctx = arboard::Clipboard::new().unwrap();
     ctx.set_text(s).unwrap();
     // Use the clipboard context to make sure it is saved
-    ctx.get_text().unwrap();
+    let ret = ctx.get_text().unwrap();
+    write("/tmp/debug2.log", ret).expect("Unable to write to file");
 }

 #[cfg(not(all(
% cat /tmp/debug1.log; echo "###"; cat /tmp/debug2.log; echo "###"
lsb_release -a###
lsb_release -a###

copyq is a third-party tool that I usually keep running in the background to manage my clipboard.
I tried closing copyq, using Ctrl-Y to copy, and then observing with xclip.

# The "reproduce" here refers to the content that was in the clipboard before using Ctrl-Y.
2210% xclip -o -selection primary   
reproduce%
2211% xclip -o -selection secondary 
Error: target STRING not available
2212% xclip -o -selection clipboard 
Error: target STRING not available
2213% xclip -o -selection buffer-cut
2214%