copypasta_ext/
x11_fork.rs

1//! Like [`x11_clipboard`][x11_clipboard], but forks to set contents.
2//!
3//! This provider ensures the clipboard contents you set remain available even after your
4//! application exists, unlike [`X11ClipboardContext`][X11ClipboardContext].
5//!
6//! When setting the clipboard, the process is forked in which the clipboard is set. The fork is
7//! kept alive until the clipboard content changes, and may outlive your application.
8//!
9//! Use the provided `ClipboardContext` type alias to use this clipboard context on supported
10//! platforms, but fall back to the standard clipboard on others.
11//!
12//! ## Benefits
13//!
14//! - Keeps contents in clipboard even after your application exists.
15//!
16//! ## Drawbacks
17//!
18//! - Set contents may not be immediately available, because they are set in a fork.
19//! - Errors when setting the clipboard contents are not catched, the fork will panic
20//!   `set_contents` will return no error.
21//! - The fork might cause weird behaviour for some applications.
22//!
23//! # Examples
24//!
25//! ```rust,no_run
26//! use copypasta_ext::prelude::*;
27//! use copypasta_ext::x11_fork::X11ForkClipboardContext;
28//!
29//! let mut ctx: X11ForkClipboardContext = X11ForkClipboardContext::new().unwrap();
30//! println!("{:?}", ctx.get_contents());
31//! ctx.set_contents("some string".into()).unwrap();
32//! ```
33//!
34//! Use `ClipboardContext` alias for better platform compatability:
35//!
36//! ```rust,no_run
37//! use copypasta_ext::prelude::*;
38//! use copypasta_ext::x11_fork::ClipboardContext;
39//!
40//! let mut ctx = ClipboardContext::new().unwrap();
41//! println!("{:?}", ctx.get_contents());
42//! ctx.set_contents("some string".into()).unwrap();
43//! ```
44//!
45//! [copypasta]: https://docs.rs/copypasta/*/copypasta/x11_clipboard/index.html
46//! [X11ClipboardContext]: https://docs.rs/copypasta/*/copypasta/x11_clipboard/struct.X11ClipboardContext.html
47
48use std::error::Error as StdError;
49use std::fmt;
50
51use copypasta::x11_clipboard::{Clipboard, Selection, X11ClipboardContext};
52use libc::fork;
53use x11_clipboard::Clipboard as X11Clipboard;
54
55use crate::display::DisplayServer;
56use crate::prelude::*;
57
58/// Platform specific context.
59///
60/// Alias for `X11ForkClipboardContext` on supported platforms, aliases to standard
61/// `ClipboardContext` provided by `rust-clipboard` on other platforms.
62pub type ClipboardContext = X11ForkClipboardContext;
63
64/// Like [`X11ClipboardContext`][X11ClipboardContext], but forks to set contents.
65///
66/// `set_contents` forks the process, `get_contents` is an alias for
67/// [`X11ClipboardContext::get_contents`][X11ClipboardContext].
68///
69/// See module documentation for more information.
70///
71/// [X11ClipboardContext]: https://docs.rs/copypasta/*/copypasta/x11_clipboard/struct.X11ClipboardContext.html
72pub struct X11ForkClipboardContext<S = Clipboard>(X11ClipboardContext<S>)
73where
74    S: Selection;
75
76impl X11ForkClipboardContext {
77    pub fn new() -> crate::ClipResult<Self> {
78        Ok(Self(X11ClipboardContext::new()?))
79    }
80}
81
82impl<S> ClipboardProvider for X11ForkClipboardContext<S>
83where
84    S: Selection,
85{
86    fn get_contents(&mut self) -> crate::ClipResult<String> {
87        self.0.get_contents()
88    }
89
90    fn set_contents(&mut self, contents: String) -> crate::ClipResult<()> {
91        match unsafe { fork() } {
92            -1 => Err(Error::Fork.into()),
93            0 => {
94                // Obtain new X11 clipboard context, set clipboard contents
95                let clip = X11Clipboard::new().expect("failed to obtain X11 clipboard context");
96                clip.store(
97                    S::atom(&clip.setter.atoms),
98                    clip.setter.atoms.utf8_string,
99                    contents,
100                )
101                .expect("failed to set clipboard contents through forked process");
102
103                // Wait for clipboard to change, then kill fork
104                clip.load_wait(
105                    S::atom(&clip.getter.atoms),
106                    clip.getter.atoms.utf8_string,
107                    clip.getter.atoms.property,
108                )
109                .expect("failed to wait on new clipboard value in forked process");
110
111                std::process::exit(0)
112            }
113            _pid => Ok(()),
114        }
115    }
116}
117
118impl<S> ClipboardProviderExt for X11ForkClipboardContext<S>
119where
120    S: Selection,
121{
122    fn display_server(&self) -> Option<DisplayServer> {
123        Some(DisplayServer::X11)
124    }
125
126    fn has_bin_lifetime(&self) -> bool {
127        false
128    }
129}
130
131/// Represents X11 fork related error.
132#[derive(Debug)]
133#[non_exhaustive]
134pub enum Error {
135    /// Failed to fork process, to set clipboard in.
136    Fork,
137}
138
139impl fmt::Display for Error {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        match self {
142            Error::Fork => write!(f, "Failed to fork process to set clipboard"),
143        }
144    }
145}
146
147impl StdError for Error {
148    fn source(&self) -> Option<&(dyn StdError + 'static)> {
149        match self {
150            Error::Fork => None,
151        }
152    }
153}