Skip to content

Commit

Permalink
refactor: use iced clipboard for interacting with the selection
Browse files Browse the repository at this point in the history
  • Loading branch information
wash2 committed Nov 9, 2023
1 parent 8656300 commit b98c0f2
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 349 deletions.
22 changes: 0 additions & 22 deletions runtime/src/command/platform_specific/wayland/data_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,6 @@ pub trait DataFromMimeType {

/// DataDevice Action
pub enum ActionInner {
/// Indicate that you are setting the selection and will respond to events
/// with data of the advertised mime types.
SetSelection {
/// The mime types that the selection can be converted to.
mime_types: Vec<String>,
/// The data to send.
data: Box<dyn DataFromMimeType + Send + Sync>,
},
/// Unset the selection.
UnsetSelection,
/// Request the selection data from the clipboard.
RequestSelectionData {
/// The mime type that the selection should be converted to.
mime_type: String,
},
/// Start a drag and drop operation. When a client asks for the selection, an event will be delivered
/// This is used for internal drags, where the client is the source of the drag.
/// The client will be resposible for data transfer.
Expand Down Expand Up @@ -116,13 +101,6 @@ impl fmt::Debug for ActionInner {
Self::Accept(mime_type) => {
f.debug_tuple("Accept").field(mime_type).finish()
}
Self::SetSelection { mime_types, .. } => {
f.debug_tuple("SetSelection").field(mime_types).finish()
}
Self::UnsetSelection => f.debug_tuple("UnsetSelection").finish(),
Self::RequestSelectionData { mime_type } => {
f.debug_tuple("RequestSelection").field(mime_type).finish()
}
Self::StartInternalDnd { origin_id, icon_id } => f
.debug_tuple("StartInternalDnd")
.field(origin_id)
Expand Down
26 changes: 20 additions & 6 deletions sctk/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ where

let mut states: HashMap<SurfaceId, State<A>> = HashMap::new();
let mut interfaces = ManuallyDrop::new(HashMap::new());
let mut simple_clipboard = Clipboard::unconnected();

{
run_command(
Expand All @@ -382,6 +383,7 @@ where
|| compositor.fetch_information(),
&mut auto_size_surfaces,
&mut Vec::new(),
&mut simple_clipboard,
);
}
runtime.track(
Expand Down Expand Up @@ -419,7 +421,6 @@ where
let mut mods: Modifiers = Modifiers::default();
let mut destroyed_surface_ids: HashMap<ObjectId, SurfaceIdWrapper> =
Default::default();
let mut simple_clipboard = Clipboard::unconnected();

'main: while let Some(event) = receiver.next().await {
match event {
Expand Down Expand Up @@ -937,6 +938,7 @@ where
&mut Vec::new(),
|| compositor.fetch_information(),
&mut auto_size_surfaces,
&mut simple_clipboard,
);

interfaces = ManuallyDrop::new(build_user_interfaces(
Expand Down Expand Up @@ -1101,6 +1103,7 @@ where
&mut actions,
|| compositor.fetch_information(),
&mut auto_size_surfaces,
&mut simple_clipboard,
);

pure_states.insert(surface_id.inner(), cache);
Expand Down Expand Up @@ -1789,6 +1792,7 @@ pub(crate) fn update<A, E, C>(
SurfaceIdWrapper,
(u32, u32, Limits, bool),
>,
clipboard: &mut Clipboard,
) where
A: Application + 'static,
E: Executor + 'static,
Expand All @@ -1808,6 +1812,7 @@ pub(crate) fn update<A, E, C>(
debug,
graphics_info,
auto_size_surfaces,
clipboard,
) {
actions.push(a);
}
Expand All @@ -1831,6 +1836,7 @@ pub(crate) fn update<A, E, C>(
graphics_info,
auto_size_surfaces,
actions,
clipboard,
)
}

Expand Down Expand Up @@ -1860,6 +1866,7 @@ fn run_command<A, E>(
(u32, u32, Limits, bool),
>,
actions: &mut Vec<command::Action<A::Message>>,
clipboard: &mut Clipboard,
) where
A: Application,
E: Executor,
Expand All @@ -1877,6 +1884,7 @@ fn run_command<A, E>(
debug,
graphics_info,
auto_size_surfaces,
clipboard,
) {
actions.push(a);
}
Expand All @@ -1897,6 +1905,7 @@ fn handle_actions<A, E>(
SurfaceIdWrapper,
(u32, u32, Limits, bool),
>,
clipboard: &mut Clipboard,
) -> Option<command::Action<A::Message>>
where
A: Application,
Expand All @@ -1911,11 +1920,17 @@ where
})));
}
command::Action::Clipboard(action) => match action {
clipboard::Action::Read(..) => {
todo!();
clipboard::Action::Read(s_to_msg) => {
if matches!(clipboard.state, crate::clipboard::State::Connected(_)) {
let contents = clipboard.read();
let message = s_to_msg(contents);
proxy.send_event(Event::Message(message));
}
}
clipboard::Action::Write(..) => {
todo!();
clipboard::Action::Write(contents) => {
if matches!(clipboard.state, crate::clipboard::State::Connected(_)) {
clipboard.write(contents)
}
}
},
command::Action::Window(..) => {
Expand Down Expand Up @@ -2142,7 +2157,6 @@ fn event_is_for_surface(
| SctkEvent::RemovedOutput(_) => false,
SctkEvent::ScaleFactorChanged { id, .. } => &id.id() == object_id,
SctkEvent::DndOffer { surface, .. } => &surface.id() == object_id,
SctkEvent::SelectionOffer(_) => true,
SctkEvent::DataSource(_) => true,
}
}
39 changes: 0 additions & 39 deletions sctk/src/commands/data_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,6 @@ use iced_runtime::{
};
use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;

/// Set the selection. When a client asks for the selection, an event will be delivered to the
/// client with the fd to write the data to.
pub fn set_selection<Message>(
mime_types: Vec<String>,
data: Box<dyn DataFromMimeType + Send + Sync>,
) -> Command<Message> {
Command::single(command::Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::DataDevice(
wayland::data_device::ActionInner::SetSelection {
mime_types,
data,
}
.into(),
)),
))
}

/// unset the selection
pub fn unset_selection<Message>() -> Command<Message> {
Command::single(command::Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::DataDevice(
wayland::data_device::ActionInner::UnsetSelection.into(),
)),
))
}

/// request the selection
/// This will trigger an event with a read pipe to read the data from.
pub fn request_selection<Message>(mime_type: String) -> Command<Message> {
Command::single(command::Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::DataDevice(
wayland::data_device::ActionInner::RequestSelectionData {
mime_type,
}
.into(),
)),
))
}

/// start an internal drag and drop operation. Events will only be delivered to the same client.
/// The client is responsible for data transfer.
pub fn start_internal_drag<Message>(
Expand Down
93 changes: 2 additions & 91 deletions sctk/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use crate::{
},
sctk_event::{
DndOfferEvent, IcedSctkEvent, LayerSurfaceEventVariant,
PopupEventVariant, SctkEvent, SelectionOfferEvent, StartCause,
WindowEventVariant,
PopupEventVariant, SctkEvent, StartCause, WindowEventVariant,
},
settings,
};
Expand Down Expand Up @@ -65,7 +64,7 @@ use wayland_backend::client::WaylandError;

use self::{
control_flow::ControlFlow,
state::{Dnd, LayerSurfaceCreationError, SctkCopyPasteSource, SctkState},
state::{Dnd, LayerSurfaceCreationError, SctkState},
};

#[derive(Debug, Default, Clone, Copy)]
Expand Down Expand Up @@ -199,10 +198,8 @@ where
frame_events: Vec::new(),
pending_user_events: Vec::new(),
token_ctr: 0,
selection_source: None,
_accept_counter: 0,
dnd_offer: None,
selection_offer: None,
fractional_scaling_manager,
viewporter_state,
compositor_updates: Default::default(),
Expand Down Expand Up @@ -1208,92 +1205,6 @@ where
};
}
}
platform_specific::wayland::data_device::ActionInner::RequestSelectionData { mime_type } => {
if let Some(selection_offer) = self.state.selection_offer.as_mut() {
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 PostAction::Continue,
};
let (mime_type, data, token) = match selection_offer.cur_read.take() {
Some(s) => s,
None => return PostAction::Continue,
};
let mut reader = BufReader::new(f.as_ref());
let consumed = match reader.fill_buf() {
Ok(buf) => {
if buf.is_empty() {
loop_handle.remove(token);
state.sctk_events.push(SctkEvent::SelectionOffer(SelectionOfferEvent::Data {mime_type, 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 PostAction::Continue;
},
Err(e) => {
error!("Error reading selection data: {}", e);
return PostAction::Continue;
},
};
reader.consume(consumed);
PostAction::Continue
}) {
Ok(token) => {
selection_offer.cur_read = Some((mime_type.clone(), Vec::new(), token));
},
Err(_) => continue,
};
}
}
platform_specific::wayland::data_device::ActionInner::SetSelection { mime_types, data } => {
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.iter().map(|s| s.as_str()).collect::<Vec<_>>());
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,
});
}
platform_specific::wayland::data_device::ActionInner::UnsetSelection => {
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,
};
self.state.selection_source = None;
seat.data_device.unset_selection(serial);
}
platform_specific::wayland::data_device::ActionInner::SetActions { preferred, accepted } => {
if let Some(offer) = self.state.dnd_offer.as_ref() {
offer.offer.set_actions(accepted, preferred);
Expand Down
33 changes: 2 additions & 31 deletions sctk/src/event_loop/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,8 @@ use sctk::{
activation::ActivationState,
compositor::CompositorState,
data_device_manager::{
data_device::DataDevice,
data_offer::{DragOffer, SelectionOffer},
data_source::{CopyPasteSource, DragSource},
DataDeviceManagerState, WritePipe,
data_device::DataDevice, data_offer::DragOffer,
data_source::DragSource, DataDeviceManagerState, WritePipe,
},
error::GlobalError,
output::OutputState,
Expand Down Expand Up @@ -233,12 +231,6 @@ impl<T> Debug for Dnd<T> {
}
}

#[derive(Debug)]
pub struct SctkSelectionOffer {
pub(crate) offer: SelectionOffer,
pub(crate) cur_read: Option<(String, Vec<u8>, RegistrationToken)>,
}

#[derive(Debug)]
pub struct SctkDragOffer {
pub(crate) dropped: bool,
Expand All @@ -254,25 +246,6 @@ pub struct SctkPopupData {
pub(crate) positioner: XdgPositioner,
}

pub struct SctkCopyPasteSource {
pub accepted_mime_types: Vec<String>,
pub source: CopyPasteSource,
pub data: Box<dyn DataFromMimeType>,
pub(crate) pipe: Option<WritePipe>,
pub(crate) cur_write: Option<(Vec<u8>, usize, RegistrationToken)>,
}

impl Debug for SctkCopyPasteSource {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SctkCopyPasteSource")
.field(&self.accepted_mime_types)
.field(&self.source)
.field(&self.pipe)
.field(&self.cur_write)
.finish()
}
}

/// Wrapper to carry sctk state.
pub struct SctkState<T> {
/// the cursor wl_surface
Expand Down Expand Up @@ -302,9 +275,7 @@ pub struct SctkState<T> {
pub compositor_updates: Vec<SctkEvent>,

/// data data_device
pub(crate) selection_source: Option<SctkCopyPasteSource>,
pub(crate) dnd_offer: Option<SctkDragOffer>,
pub(crate) selection_offer: Option<SctkSelectionOffer>,
pub(crate) _accept_counter: u32,
/// A sink for window and device events that is being filled during dispatching
/// event loop and forwarded downstream afterwards.
Expand Down
Loading

0 comments on commit b98c0f2

Please sign in to comment.