From 50fc7ef8f27ebb94cae7d4e152986c7ca30e9fd0 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 31 Oct 2023 14:46:32 -0400 Subject: [PATCH] cleanup(iced-sctk): add implementation for the iced clipboard actions --- runtime/src/clipboard.rs | 4 +- .../platform_specific/wayland/data_device.rs | 10 +++ sctk/src/application.rs | 18 +++-- sctk/src/clipboard.rs | 2 +- sctk/src/event_loop/mod.rs | 81 +++++++++++++++++++ 5 files changed, 106 insertions(+), 9 deletions(-) diff --git a/runtime/src/clipboard.rs b/runtime/src/clipboard.rs index bc45091266..f49dda82c2 100644 --- a/runtime/src/clipboard.rs +++ b/runtime/src/clipboard.rs @@ -9,7 +9,7 @@ use std::fmt; /// [`Command`]: crate::Command pub enum Action { /// Read the clipboard and produce `T` with the result. - Read(Box) -> T>), + Read(Box) -> T + Send>), /// Write the given contents to the clipboard. Write(String), @@ -42,7 +42,7 @@ impl fmt::Debug for Action { /// Read the current contents of the clipboard. pub fn read( - f: impl Fn(Option) -> Message + 'static, + f: impl Fn(Option) -> Message + 'static + std::marker::Send, ) -> Command { Command::single(command::Action::Clipboard(Action::Read(Box::new(f)))) } diff --git a/runtime/src/command/platform_specific/wayland/data_device.rs b/runtime/src/command/platform_specific/wayland/data_device.rs index 3bf76c3283..3c843e54ee 100644 --- a/runtime/src/command/platform_specific/wayland/data_device.rs +++ b/runtime/src/command/platform_specific/wayland/data_device.rs @@ -32,6 +32,16 @@ pub trait DataFromMimeType { fn from_mime_type(&self, mime_type: &str) -> Option>; } +impl DataFromMimeType for String { + fn from_mime_type(&self, mime_type: &str) -> Option> { + if mime_type == "text/plain;charset=utf-8" { + Some(self.as_bytes().to_vec()) + } else { + None + } + } +} + /// DataDevice Action pub enum ActionInner { /// Indicate that you are setting the selection and will respond to events diff --git a/sctk/src/application.rs b/sctk/src/application.rs index 796f773da8..e49957d529 100644 --- a/sctk/src/application.rs +++ b/sctk/src/application.rs @@ -43,8 +43,7 @@ use sctk::{ seat::{keyboard::Modifiers, pointer::PointerEventKind}, }; use std::{ - collections::HashMap, ffi::c_void, hash::Hash, marker::PhantomData, - time::Duration, + collections::HashMap, hash::Hash, marker::PhantomData, time::Duration, }; use wayland_backend::client::ObjectId; use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport; @@ -96,6 +95,13 @@ pub enum Event { SetCursor(Interaction), /// Application Message Message(Message), + /// Miscellaneous messages + Misc(MiscEvent), +} + +pub enum MiscEvent { + IcedReadSelection(Box) -> T + Send>), + IcedWriteSelection(String), } pub struct IcedSctkState; @@ -1910,11 +1916,11 @@ where }))); } command::Action::Clipboard(action) => match action { - clipboard::Action::Read(..) => { - todo!(); + clipboard::Action::Read(m) => { + proxy.send_event(Event::Misc(MiscEvent::IcedReadSelection(m))); } - clipboard::Action::Write(..) => { - todo!(); + clipboard::Action::Write(s) => { + proxy.send_event(Event::Misc(MiscEvent::IcedWriteSelection(s))); } }, command::Action::Window(..) => { diff --git a/sctk/src/clipboard.rs b/sctk/src/clipboard.rs index 74ab0c6c94..fa1277a0be 100644 --- a/sctk/src/clipboard.rs +++ b/sctk/src/clipboard.rs @@ -70,7 +70,7 @@ impl iced_runtime::core::clipboard::Clipboard for Clipboard { /// Read the current contents of the clipboard. pub fn read( - f: impl Fn(Option) -> Message + 'static, + f: impl Fn(Option) -> Message + 'static + std::marker::Send, ) -> Command { Command::single(command::Action::Clipboard(Action::Read(Box::new(f)))) } diff --git a/sctk/src/event_loop/mod.rs b/sctk/src/event_loop/mod.rs index 46d1b83228..6db55b7c44 100644 --- a/sctk/src/event_loop/mod.rs +++ b/sctk/src/event_loop/mod.rs @@ -1298,6 +1298,87 @@ where } } } + Event::Misc(a) => { + match a { + crate::application::MiscEvent::IcedReadSelection(to_msg) => { + if let Some(selection_offer) = self.state.selection_offer.as_mut() { + let mime_type = "text/plain;charset=UTF-8".to_string(); + let read_pipe = match selection_offer.offer.receive(mime_type.clone()) { + Ok(p) => p, + Err(_) => continue, // TODO error handling + }; + let loop_handle = self.event_loop.handle(); + match self.event_loop.handle().insert_source(read_pipe, move |_, f, state| { + let selection_offer = match state.selection_offer.as_mut() { + Some(s) => s, + None => return, + }; + let (mime_type, data, token) = match selection_offer.cur_read.take() { + Some(s) => s, + None => return, + }; + let mut reader = BufReader::new(f.as_ref()); + let consumed = match reader.fill_buf() { + Ok(buf) => { + if buf.is_empty() { + loop_handle.remove(token); + let data = String::from_utf8(data).ok(); + state.pending_user_events.push(Event::Message(to_msg(data))); + } else { + let mut data = data; + data.extend_from_slice(buf); + selection_offer.cur_read = Some((mime_type, data, token)); + } + buf.len() + }, + Err(e) if matches!(e.kind(), std::io::ErrorKind::Interrupted) => { + selection_offer.cur_read = Some((mime_type, data, token)); + return; + }, + Err(e) => { + error!("Error reading selection data: {}", e); + loop_handle.remove(token); + return; + }, + }; + reader.consume(consumed); + }) { + Ok(token) => { + selection_offer.cur_read = Some((mime_type.clone(), Vec::new(), token)); + }, + Err(_) => continue, + }; + } + }, + crate::application::MiscEvent::IcedWriteSelection(data) => { + let mime_types = vec!["text/plain;charset=UTF-8"]; + let qh = &self.state.queue_handle.clone(); + let seat = match self.state.seats.get(0) { + Some(s) => s, + None => continue, + }; + let serial = match seat.last_ptr_press { + Some(s) => s.2, + None => continue, + }; + // remove the old selection + self.state.selection_source = None; + // create a new one + let source = self + .state + .data_device_manager_state + .create_copy_paste_source(qh, mime_types); + source.set_selection(&seat.data_device, serial); + self.state.selection_source = Some(SctkCopyPasteSource { + source, + cur_write: None, + accepted_mime_types: Vec::new(), + pipe: None, + data: Box::new(data), + }); + }, + } + }, } }