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}