Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use iced clipboard for interacting with the selection #78

Merged
merged 1 commit into from
Nov 9, 2023
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
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 @@

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 @@
|| compositor.fetch_information(),
&mut auto_size_surfaces,
&mut Vec::new(),
&mut simple_clipboard,
);
}
runtime.track(
Expand Down Expand Up @@ -419,7 +421,6 @@
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 @@
&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 @@
&mut actions,
|| compositor.fetch_information(),
&mut auto_size_surfaces,
&mut simple_clipboard,
);

pure_states.insert(surface_id.inner(), cache);
Expand Down Expand Up @@ -1612,7 +1615,7 @@
self.frame = frame;
}

pub(crate) fn frame(&self) -> Option<&WlSurface> {

Check warning on line 1618 in sctk/src/application.rs

View workflow job for this annotation

GitHub Actions / native (ubuntu-latest, stable)

method `frame` is never used
self.frame.as_ref()
}

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

Expand Down Expand Up @@ -1860,6 +1866,7 @@
(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 @@
debug,
graphics_info,
auto_size_surfaces,
clipboard,
) {
actions.push(a);
}
Expand All @@ -1897,6 +1905,7 @@
SurfaceIdWrapper,
(u32, u32, Limits, bool),
>,
clipboard: &mut Clipboard,
) -> Option<command::Action<A::Message>>
where
A: Application,
Expand All @@ -1911,11 +1920,17 @@
})));
}
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 @@
| 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
Loading