Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/clipboard-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
clipboard-manager: patch
clipboard-manager-js: patch
---

Explicitly drop `arboard::Clipboard` on exit. Add recommendation to not use read methods on the mainthread.
38 changes: 32 additions & 6 deletions plugins/clipboard-manager/src/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
) -> crate::Result<Clipboard<R>> {
Ok(Clipboard {
app: app.clone(),
clipboard: arboard::Clipboard::new().map(Mutex::new),
clipboard: arboard::Clipboard::new().map(|c| Mutex::new(Some(c))),
})
}

/// Access to the clipboard APIs.
pub struct Clipboard<R: Runtime> {
#[allow(dead_code)]
app: AppHandle<R>,
clipboard: Result<Mutex<arboard::Clipboard>, arboard::Error>,
// According to arboard docs the clipboard must be dropped before exit.
// Since tauri doesn't call drop on exit we'll use an Option to take() on RunEvent::Exit.
clipboard: Result<Mutex<Option<arboard::Clipboard>>, arboard::Error>,
}

impl<R: Runtime> Clipboard<R> {
pub fn write_text<'a, T: Into<Cow<'a, str>>>(&self, text: T) -> crate::Result<()> {
match &self.clipboard {
Ok(clipboard) => clipboard.lock().unwrap().set_text(text).map_err(Into::into),
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_text(text)
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
Expand All @@ -38,6 +46,8 @@ impl<R: Runtime> Clipboard<R> {
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_image(ImageData {
bytes: Cow::Borrowed(image.rgba()),
width: image.width() as usize,
Expand All @@ -48,10 +58,11 @@ impl<R: Runtime> Clipboard<R> {
}
}

/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_text(&self) -> crate::Result<String> {
match &self.clipboard {
Ok(clipboard) => {
let text = clipboard.lock().unwrap().get_text()?;
let text = clipboard.lock().unwrap().as_mut().unwrap().get_text()?;
Ok(text)
}
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
Expand All @@ -67,6 +78,8 @@ impl<R: Runtime> Clipboard<R> {
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_html(html, alt_text)
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
Expand All @@ -75,15 +88,22 @@ impl<R: Runtime> Clipboard<R> {

pub fn clear(&self) -> crate::Result<()> {
match &self.clipboard {
Ok(clipboard) => clipboard.lock().unwrap().clear().map_err(Into::into),
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.clear()
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}

/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_image(&self) -> crate::Result<Image<'_>> {
match &self.clipboard {
Ok(clipboard) => {
let image = clipboard.lock().unwrap().get_image()?;
let image = clipboard.lock().unwrap().as_mut().unwrap().get_image()?;
let image = Image::new_owned(
image.bytes.to_vec(),
image.width as u32,
Expand All @@ -94,4 +114,10 @@ impl<R: Runtime> Clipboard<R> {
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}

pub(crate) fn cleanup(&self) {
if let Ok(clipboard) = &self.clipboard {
clipboard.lock().unwrap().take();
}
}
}
8 changes: 7 additions & 1 deletion plugins/clipboard-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
Manager, RunEvent, Runtime,
};

#[cfg(desktop)]
Expand Down Expand Up @@ -59,5 +59,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
app.manage(clipboard);
Ok(())
})
.on_event(|_app, _event| {
#[cfg(desktop)]
if let RunEvent::Exit = _event {
_app.clipboard().cleanup();
}
})
.build()
}
Loading