mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 23:19:02 +08:00
fix: keep clipboard alive
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
parent
075a877284
commit
9976fc9723
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5257,9 +5257,11 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
"objc",
|
"objc",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
|
"once_cell",
|
||||||
"os-version",
|
"os-version",
|
||||||
"pam",
|
"pam",
|
||||||
"parity-tokio-ipc",
|
"parity-tokio-ipc",
|
||||||
|
"percent-encoding",
|
||||||
"rdev",
|
"rdev",
|
||||||
"repng",
|
"repng",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -134,6 +134,8 @@ pam = { git="https://github.com/fufesou/pam", optional = true }
|
|||||||
users = { version = "0.11" }
|
users = { version = "0.11" }
|
||||||
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
|
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
|
||||||
x11rb = {version = "0.12", features = ["all-extensions"]}
|
x11rb = {version = "0.12", features = ["all-extensions"]}
|
||||||
|
percent-encoding = "2.3"
|
||||||
|
once_cell = "1.18"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.13"
|
android_logger = "0.13"
|
||||||
|
@ -67,9 +67,12 @@ impl X11Clipboard {
|
|||||||
// NOTE:
|
// NOTE:
|
||||||
// # why not use `load_wait`
|
// # why not use `load_wait`
|
||||||
// load_wait is likely to wait forever, which is not what we want
|
// load_wait is likely to wait forever, which is not what we want
|
||||||
get_clip()?
|
let res = get_clip()?.load(clip, target, prop, X11_CLIPBOARD_TIMEOUT);
|
||||||
.load(clip, target, prop, X11_CLIPBOARD_TIMEOUT)
|
match res {
|
||||||
.map_err(|_| CliprdrError::ConversionFailure)
|
Ok(res) => Ok(res),
|
||||||
|
Err(x11_clipboard::error::Error::UnexpectedType(_)) => Ok(vec![]),
|
||||||
|
Err(_) => Err(CliprdrError::ClipboardInternalError),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_batch(&self, batch: Vec<(Atom, Vec<u8>)>) -> Result<(), CliprdrError> {
|
fn store_batch(&self, batch: Vec<(Atom, Vec<u8>)>) -> Result<(), CliprdrError> {
|
||||||
@ -85,7 +88,6 @@ impl X11Clipboard {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let v = self.load(self.text_uri_list)?;
|
let v = self.load(self.text_uri_list)?;
|
||||||
// loading 'text/uri-list' should be enough?
|
|
||||||
let p = parse_plain_uri_list(v)?;
|
let p = parse_plain_uri_list(v)?;
|
||||||
Ok(Some(p))
|
Ok(Some(p))
|
||||||
}
|
}
|
||||||
|
@ -18,70 +18,108 @@ pub enum GrabState {
|
|||||||
)))]
|
)))]
|
||||||
pub use arboard::Clipboard as ClipboardContext;
|
pub use arboard::Clipboard as ClipboardContext;
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
||||||
|
static X11_CLIPBOARD: once_cell::sync::OnceCell<x11_clipboard::Clipboard> =
|
||||||
|
once_cell::sync::OnceCell::new();
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
||||||
|
fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> {
|
||||||
|
X11_CLIPBOARD
|
||||||
|
.get_or_try_init(|| x11_clipboard::Clipboard::new())
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
||||||
pub struct ClipboardContext {
|
pub struct ClipboardContext {
|
||||||
string_setter: x11rb::protocol::xproto::Atom,
|
string_setter: x11rb::protocol::xproto::Atom,
|
||||||
string_getter: x11rb::protocol::xproto::Atom,
|
string_getter: x11rb::protocol::xproto::Atom,
|
||||||
text_uri_list: x11rb::protocol::xproto::Atom,
|
text_uri_list: x11rb::protocol::xproto::Atom,
|
||||||
clip: x11_clipboard::Clipboard,
|
|
||||||
|
clip: x11rb::protocol::xproto::Atom,
|
||||||
|
prop: x11rb::protocol::xproto::Atom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
||||||
|
fn parse_plain_uri_list(v: Vec<u8>) -> Result<String, String> {
|
||||||
|
let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?;
|
||||||
|
let mut list = String::new();
|
||||||
|
for line in text.lines() {
|
||||||
|
if !line.starts_with("file://") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let decoded = percent_encoding::percent_decode_str(line)
|
||||||
|
.decode_utf8()
|
||||||
|
.map_err(|_| "ConversionFailure".to_owned())?;
|
||||||
|
list = list + "\n" + decoded.trim_start_matches("file://");
|
||||||
|
}
|
||||||
|
list = list.trim().to_owned();
|
||||||
|
Ok(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
||||||
impl ClipboardContext {
|
impl ClipboardContext {
|
||||||
pub fn new() -> Result<Self, String> {
|
pub fn new() -> Result<Self, String> {
|
||||||
log::debug!("create clipboard context");
|
let clipboard = get_clipboard()?;
|
||||||
let clip = x11_clipboard::Clipboard::new().map_err(|e| e.to_string())?;
|
let string_getter = clipboard
|
||||||
let string_getter = clip
|
|
||||||
.getter
|
.getter
|
||||||
.get_atom("UTF8_STRING")
|
.get_atom("UTF8_STRING")
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let string_setter = clip
|
let string_setter = clipboard
|
||||||
.setter
|
.setter
|
||||||
.get_atom("UTF8_STRING")
|
.get_atom("UTF8_STRING")
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let text_uri_list = clip
|
let text_uri_list = clipboard
|
||||||
.getter
|
.getter
|
||||||
.get_atom("text/uri-list")
|
.get_atom("text/uri-list")
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
log::debug!("clipboard context created");
|
let prop = clipboard.getter.atoms.property;
|
||||||
|
let clip = clipboard.getter.atoms.clipboard;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
clip,
|
|
||||||
text_uri_list,
|
text_uri_list,
|
||||||
string_setter,
|
string_setter,
|
||||||
string_getter,
|
string_getter,
|
||||||
|
clip,
|
||||||
|
prop,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_text(&mut self) -> Result<String, String> {
|
pub fn get_text(&mut self) -> Result<String, String> {
|
||||||
let clip = self.clip.getter.atoms.clipboard;
|
let clip = self.clip;
|
||||||
let prop = self.clip.getter.atoms.property;
|
let prop = self.prop;
|
||||||
log::trace!("clipboard get text");
|
|
||||||
|
|
||||||
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
|
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
|
||||||
|
|
||||||
let text_content = self
|
let text_content = get_clipboard()?
|
||||||
.clip
|
|
||||||
.load(clip, self.string_getter, prop, TIMEOUT)
|
.load(clip, self.string_getter, prop, TIMEOUT)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let file_urls = self.clip.load(clip, self.text_uri_list, prop, TIMEOUT);
|
let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT);
|
||||||
|
|
||||||
if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() {
|
if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() {
|
||||||
log::trace!("clipboard get text, no file urls");
|
log::debug!("clipboard get text, no file urls");
|
||||||
String::from_utf8(text_content).map_err(|e| e.to_string())
|
return String::from_utf8(text_content).map_err(|e| e.to_string());
|
||||||
} else {
|
|
||||||
log::trace!("clipboard get text, file urls found");
|
|
||||||
Err("clipboard polluted".to_owned())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let file_urls = parse_plain_uri_list(file_urls.unwrap())?;
|
||||||
|
|
||||||
|
let text_content = String::from_utf8(text_content).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
if text_content.trim() == file_urls.trim() {
|
||||||
|
log::debug!("clipboard got text but polluted");
|
||||||
|
return Err(String::from("polluted text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(text_content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text(&mut self, content: String) -> Result<(), String> {
|
pub fn set_text(&mut self, content: String) -> Result<(), String> {
|
||||||
let clip = self.clip.setter.atoms.clipboard;
|
let clip = self.clip;
|
||||||
|
|
||||||
let value = content.into_bytes();
|
let value = content.clone().into_bytes();
|
||||||
self.clip
|
get_clipboard()?
|
||||||
.store(clip, self.string_setter, value)
|
.store(clip, self.string_setter, value)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user