aweinstock314 / rust-clipboard

System Clipboard interfacing library in Rust
Apache License 2.0
368 stars 75 forks source link

Clipboard is cleared when the process exits on Linux #61

Open yaa110 opened 5 years ago

yaa110 commented 5 years ago

Please consider the following code:

fn main() {
    let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
    ctx.set_contents("some string".to_owned()).unwrap();
    // Clipboard is "some string"
} // Clipboard is empty after exit
aweinstock314 commented 5 years ago

This is a known tradeoff/design decision around a limitation of x11. This was documented in a comment back when x11-clipboard lived in-tree (https://github.com/aweinstock314/rust-clipboard/blob/9a13971d023c5c4975d286bea70a7b139c0954b4/src/x11_clipboard.rs#L263-L279). I'll probably address this by copying that comment back in tree (and possibly adding the feature to create a clipboard context that forks, with a warning about the issues around locks/destructors).

brownjohnf commented 5 years ago

I realize this might be out of scope, but is there a way of detecting when the contents of the clipboard have been accessed, from rust-clipboard? I want to write a tool to receive input from stdin and copy that to the clipboard. I'd be fine with the process hanging until the contents had been pasted out (in fact, that could be ideal), but I'm not sure how i'd know when that had occurred.

I guess this might require something like a variation on set_contents that would block until the contents had been read back out?

ghost commented 5 years ago

Thank you for addressing this in detail. As someone who is learning Rust, would you be able to give brief details on the problems involved with using fork?

LordFoom commented 4 years ago

This is a real pity as it renders the library essentially useless for my purposes.

timvisee commented 4 years ago

Assuming this is a wontfix, I've created the clipboard-ext crate to extend upon this. It provides some additional clipboard providers for X11 that do implement the noted workarounds such as forking or invoking xclip/xsel.

For example, these will keep clipboard contents after exit:

use clipboard_ext::prelude::*;

// Fork and keep contents
clipboard_ext::x11_fork::ClipboardContext::new().unwrap()
    .set_contents("some string".into()).unwrap();

// Invoke xclip/xsel and keep contents
clipboard_ext::x11_bin::ClipboardContext::new().unwrap()
    .set_contents("some string".into()).unwrap();

See the README for more information.

brownjohnf commented 4 years ago

I did a bit of digging to see how much work it would be to enable the paste detection functionality that I mention in https://github.com/aweinstock314/rust-clipboard/issues/61#issuecomment-479347425, and it appears pretty nontrivial, since this package uses various lower-level libraries for the actual clipboard bits.

timvisee commented 4 years ago

@brownjohnf I've done exactly this in clipboard-ext which I mentioned in https://github.com/aweinstock314/rust-clipboard/issues/61#issuecomment-585942224.

You'd have to use the x11-clipboard crate for this which provides access to the lower level X11 library. This is alright though, because this is the only provider you'd need to implement this for anyway. It provides the load_wait function which blocks until clipboard content is changed. See my implementation and usage here: https://github.com/timvisee/rust-clipboard-ext/blob/a47cc13807ca19bc2c815b07688a27235ad9b4f7/src/x11_fork.rs#L90-L107

woodruffw commented 4 years ago

Following up here with my own ~hack~ solution, which is to use the nix crate's fork functionality to spawn a temporary child:

match fork() {
    Ok(ForkResult::Child) => {
        let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
        ctx.set_contents(password.value.to_owned()).unwrap();

        std::thread::sleep(std::time::Duration::from_secs(10));

        // optionally clear the clipboard here
    }
    Err(_) => return Err("clipboard fork failed".into()),
    _ => {}
}

This only works on platforms that have a fork, of course.

Edit: This also shouldn't be done in library code. It's safe-ish in the context I intend to use it in, but not necessarily others.