This commit entirely reworks the internal structure of the entire crate, as well as some of its APIs. This crate only accepts a C pointer to a Wayland display object, since the target audience of this crate are libraries without a wayland-client types. Also since seat information is not presented in such clients most of the time, the clipboard entirely relies on its seat tracking.
131 lines
4.4 KiB
Rust
131 lines
4.4 KiB
Rust
#![macro_use]
|
|
|
|
use std::io::Result;
|
|
use std::sync::mpsc::Sender;
|
|
|
|
use sctk::reexports::client::protocol::wl_keyboard::Event as KeyboardEvent;
|
|
use sctk::reexports::client::protocol::wl_pointer::Event as PointerEvent;
|
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
|
use sctk::reexports::client::DispatchData;
|
|
|
|
use super::dispatch_data::ClipboardDispatchData;
|
|
|
|
/// Macro to handle load for selection and primary clipboards.
|
|
macro_rules! handle_load {
|
|
($env:ident, $sel_ty:ident, $seat:ident, $queue:ident, $tx:ident ) => {
|
|
let result = $env.$sel_ty(&$seat, |device| {
|
|
let (mut reader, mime_type) = match device.with_selection(|offer| {
|
|
// Check that we have an offer.
|
|
let offer = match offer {
|
|
Some(offer) => offer,
|
|
None => return None,
|
|
};
|
|
|
|
// Check that we can work with advertised mime type and pick the one
|
|
// that suits us more.
|
|
let mime_type = match offer.with_mime_types(MimeType::find_allowed) {
|
|
Some(mime_type) => mime_type,
|
|
None => return None,
|
|
};
|
|
|
|
// Request given the mime type.
|
|
let reader = offer.receive(mime_type.to_string()).ok()?;
|
|
Some((reader, mime_type))
|
|
}) {
|
|
Some((reader, mime_type)) => (reader, mime_type),
|
|
None => {
|
|
handlers::reply_error(&$tx, "offer receive failed.");
|
|
return ();
|
|
}
|
|
};
|
|
|
|
$queue
|
|
.sync_roundtrip(&mut (), |_, _, _| unreachable!())
|
|
.unwrap();
|
|
|
|
let mut contents = String::new();
|
|
let result = reader.read_to_string(&mut contents).map(|_| {
|
|
if mime_type == MimeType::Utf8String {
|
|
mime::normilize_to_lf(contents)
|
|
} else {
|
|
contents
|
|
}
|
|
});
|
|
|
|
$tx.send(result).unwrap();
|
|
});
|
|
|
|
// Send back that we've failed to load data from the clipboard.
|
|
if result.is_err() {
|
|
handlers::reply_error(&$tx, "failed to access clipboard.");
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Macro to handle store for selection and primary clipboards.
|
|
macro_rules! handle_store {
|
|
($env:ident,
|
|
$sel_source:ident, $sel_device:ident, $event_ty:ident,
|
|
$seat:ident, $serial:ident, $queue:ident, $contents:ident) => {
|
|
let data_source = $env.$sel_source(
|
|
vec![MimeType::TextPlainUtf8.to_string()],
|
|
move |event, _| {
|
|
if let $event_ty::Send { mut pipe, .. } = event {
|
|
write!(pipe, "{}", $contents).unwrap();
|
|
}
|
|
},
|
|
);
|
|
|
|
let _ = $env.$sel_device(&$seat, |device| {
|
|
device.set_selection(&Some(data_source), $serial);
|
|
|
|
let _ = $queue.sync_roundtrip(&mut (), |_, _, _| unreachable!());
|
|
});
|
|
};
|
|
}
|
|
|
|
/// Reply an error to a clipboard master.
|
|
pub fn reply_error(tx: &Sender<Result<String>>, description: &str) {
|
|
tx.send(Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
description,
|
|
)))
|
|
.unwrap();
|
|
}
|
|
|
|
/// Update seat and serial on pointer events.
|
|
pub fn pointer_handler(seat: WlSeat, event: PointerEvent, mut dispatch_data: DispatchData) {
|
|
let dispatch_data = match dispatch_data.get::<ClipboardDispatchData>() {
|
|
Some(dispatch_data) => dispatch_data,
|
|
None => return,
|
|
};
|
|
match event {
|
|
PointerEvent::Enter { serial, .. } => {
|
|
dispatch_data.set_last_seat(seat, serial);
|
|
}
|
|
PointerEvent::Button { serial, .. } => {
|
|
dispatch_data.set_last_seat(seat, serial);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
/// Update seat and serial on keyboard events.
|
|
pub fn keyboard_handler(seat: WlSeat, event: KeyboardEvent, mut dispatch_data: DispatchData) {
|
|
let dispatch_data = match dispatch_data.get::<ClipboardDispatchData>() {
|
|
Some(dispatch_data) => dispatch_data,
|
|
None => return,
|
|
};
|
|
match event {
|
|
KeyboardEvent::Enter { serial, .. } => {
|
|
dispatch_data.set_last_seat(seat, serial);
|
|
}
|
|
KeyboardEvent::Key { serial, .. } => {
|
|
dispatch_data.set_last_seat(seat, serial);
|
|
}
|
|
KeyboardEvent::Leave { .. } => {
|
|
dispatch_data.remove_seat(seat);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|