1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::io::{Error, ErrorKind, Read, Result, Write};
4use std::mem;
5use std::os::unix::io::{AsRawFd, RawFd};
6use std::rc::Rc;
7use std::sync::mpsc::Sender;
8
9use sctk::data_device_manager::data_device::{DataDevice, DataDeviceHandler};
10use sctk::data_device_manager::data_offer::{DataOfferError, DataOfferHandler, DragOffer};
11use sctk::data_device_manager::data_source::{CopyPasteSource, DataSourceHandler};
12use sctk::data_device_manager::{DataDeviceManagerState, WritePipe};
13use sctk::primary_selection::device::{PrimarySelectionDevice, PrimarySelectionDeviceHandler};
14use sctk::primary_selection::selection::{PrimarySelectionSource, PrimarySelectionSourceHandler};
15use sctk::primary_selection::PrimarySelectionManagerState;
16use sctk::registry::{ProvidesRegistryState, RegistryState};
17use sctk::seat::pointer::{PointerData, PointerEvent, PointerEventKind, PointerHandler};
18use sctk::seat::{Capability, SeatHandler, SeatState};
19use sctk::{
20 delegate_data_device, delegate_pointer, delegate_primary_selection, delegate_registry,
21 delegate_seat, registry_handlers,
22};
23
24use sctk::reexports::calloop::{LoopHandle, PostAction};
25use sctk::reexports::client::globals::GlobalList;
26use sctk::reexports::client::protocol::wl_data_device::WlDataDevice;
27use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
28use sctk::reexports::client::protocol::wl_data_source::WlDataSource;
29use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
30use sctk::reexports::client::protocol::wl_pointer::WlPointer;
31use sctk::reexports::client::protocol::wl_seat::WlSeat;
32use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle};
33use sctk::reexports::protocols::wp::primary_selection::zv1::client::{
34 zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
35 zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
36};
37use wayland_backend::client::ObjectId;
38
39use crate::mime::{normalize_to_lf, MimeType, ALLOWED_MIME_TYPES};
40
41pub struct State {
42 pub primary_selection_manager_state: Option<PrimarySelectionManagerState>,
43 pub data_device_manager_state: Option<DataDeviceManagerState>,
44 pub reply_tx: Sender<Result<String>>,
45 pub exit: bool,
46
47 registry_state: RegistryState,
48 seat_state: SeatState,
49
50 seats: HashMap<ObjectId, ClipboardSeatState>,
51 /// The latest seat which got an event.
52 latest_seat: Option<ObjectId>,
53
54 loop_handle: LoopHandle<'static, Self>,
55 queue_handle: QueueHandle<Self>,
56
57 primary_sources: Vec<PrimarySelectionSource>,
58 primary_selection_content: Rc<[u8]>,
59
60 data_sources: Vec<CopyPasteSource>,
61 data_selection_content: Rc<[u8]>,
62}
63
64impl State {
65 #[must_use]
66 pub fn new(
67 globals: &GlobalList,
68 queue_handle: &QueueHandle<Self>,
69 loop_handle: LoopHandle<'static, Self>,
70 reply_tx: Sender<Result<String>>,
71 ) -> Option<Self> {
72 let mut seats = HashMap::new();
73
74 let data_device_manager_state = DataDeviceManagerState::bind(globals, queue_handle).ok();
75 let primary_selection_manager_state =
76 PrimarySelectionManagerState::bind(globals, queue_handle).ok();
77
78 // When both globals are not available nothing could be done.
79 if data_device_manager_state.is_none() && primary_selection_manager_state.is_none() {
80 return None;
81 }
82
83 let seat_state = SeatState::new(globals, queue_handle);
84 for seat in seat_state.seats() {
85 seats.insert(seat.id(), Default::default());
86 }
87
88 Some(Self {
89 registry_state: RegistryState::new(globals),
90 primary_selection_content: Rc::from([]),
91 data_selection_content: Rc::from([]),
92 queue_handle: queue_handle.clone(),
93 primary_selection_manager_state,
94 primary_sources: Vec::new(),
95 data_device_manager_state,
96 data_sources: Vec::new(),
97 latest_seat: None,
98 loop_handle,
99 exit: false,
100 seat_state,
101 reply_tx,
102 seats,
103 })
104 }
105
106 /// Store selection for the given target.
107 ///
108 /// Selection source is only created when `Some(())` is returned.
109 pub fn store_selection(&mut self, ty: SelectionTarget, contents: String) -> Option<()> {
110 let latest = self.latest_seat.as_ref()?;
111 let seat = self.seats.get_mut(latest)?;
112
113 if !seat.has_focus {
114 return None;
115 }
116
117 let contents = Rc::from(contents.into_bytes());
118
119 match ty {
120 SelectionTarget::Clipboard => {
121 let mgr = self.data_device_manager_state.as_ref()?;
122 self.data_selection_content = contents;
123 let source =
124 mgr.create_copy_paste_source(&self.queue_handle, ALLOWED_MIME_TYPES.iter());
125 source.set_selection(seat.data_device.as_ref().unwrap(), seat.latest_serial);
126 self.data_sources.push(source);
127 },
128 SelectionTarget::Primary => {
129 let mgr = self.primary_selection_manager_state.as_ref()?;
130 self.primary_selection_content = contents;
131 let source =
132 mgr.create_selection_source(&self.queue_handle, ALLOWED_MIME_TYPES.iter());
133 source.set_selection(seat.primary_device.as_ref().unwrap(), seat.latest_serial);
134 self.primary_sources.push(source);
135 },
136 }
137
138 Some(())
139 }
140
141 /// Load selection for the given target.
142 pub fn load_selection(&mut self, ty: SelectionTarget) -> Result<()> {
143 let latest = self
144 .latest_seat
145 .as_ref()
146 .ok_or_else(|| Error::new(ErrorKind::Other, "no events received on any seat"))?;
147 let seat = self
148 .seats
149 .get_mut(latest)
150 .ok_or_else(|| Error::new(ErrorKind::Other, "active seat lost"))?;
151
152 if !seat.has_focus {
153 return Err(Error::new(ErrorKind::Other, "client doesn't have focus"));
154 }
155
156 let (read_pipe, mime_type) = match ty {
157 SelectionTarget::Clipboard => {
158 let selection = seat
159 .data_device
160 .as_ref()
161 .and_then(|data| data.data().selection_offer())
162 .ok_or_else(|| Error::new(ErrorKind::Other, "selection is empty"))?;
163
164 let mime_type =
165 selection.with_mime_types(MimeType::find_allowed).ok_or_else(|| {
166 Error::new(ErrorKind::NotFound, "supported mime-type is not found")
167 })?;
168
169 (
170 selection.receive(mime_type.to_string()).map_err(|err| match err {
171 DataOfferError::InvalidReceive => {
172 Error::new(ErrorKind::Other, "offer is not ready yet")
173 },
174 DataOfferError::Io(err) => err,
175 })?,
176 mime_type,
177 )
178 },
179 SelectionTarget::Primary => {
180 let selection = seat
181 .primary_device
182 .as_ref()
183 .and_then(|data| data.data().selection_offer())
184 .ok_or_else(|| Error::new(ErrorKind::Other, "selection is empty"))?;
185
186 let mime_type =
187 selection.with_mime_types(MimeType::find_allowed).ok_or_else(|| {
188 Error::new(ErrorKind::NotFound, "supported mime-type is not found")
189 })?;
190
191 (selection.receive(mime_type.to_string())?, mime_type)
192 },
193 };
194
195 // Mark FD as non-blocking so we won't block ourselves.
196 unsafe {
197 set_non_blocking(read_pipe.as_raw_fd())?;
198 }
199
200 let mut reader_buffer = [0; 4096];
201 let mut content = Vec::new();
202 let _ = self.loop_handle.insert_source(read_pipe, move |_, file, state| {
203 let file = unsafe { file.get_mut() };
204 loop {
205 match file.read(&mut reader_buffer) {
206 Ok(0) => {
207 let utf8 = String::from_utf8_lossy(&content);
208 let content = match utf8 {
209 Cow::Borrowed(_) => {
210 // Don't clone the read data.
211 let mut to_send = Vec::new();
212 mem::swap(&mut content, &mut to_send);
213 String::from_utf8(to_send).unwrap()
214 },
215 Cow::Owned(content) => content,
216 };
217
218 // Post-process the content according to mime type.
219 let content = match mime_type {
220 MimeType::TextPlainUtf8 | MimeType::TextPlain => {
221 normalize_to_lf(content)
222 },
223 MimeType::Utf8String => content,
224 };
225
226 let _ = state.reply_tx.send(Ok(content));
227 break PostAction::Remove;
228 },
229 Ok(n) => content.extend_from_slice(&reader_buffer[..n]),
230 Err(err) if err.kind() == ErrorKind::WouldBlock => break PostAction::Continue,
231 Err(err) => {
232 let _ = state.reply_tx.send(Err(err));
233 break PostAction::Remove;
234 },
235 };
236 }
237 });
238
239 Ok(())
240 }
241
242 fn send_request(&mut self, ty: SelectionTarget, write_pipe: WritePipe, mime: String) {
243 // We can only send strings, so don't do anything with the mime-type.
244 if MimeType::find_allowed(&[mime]).is_none() {
245 return;
246 }
247
248 // Mark FD as non-blocking so we won't block ourselves.
249 unsafe {
250 if set_non_blocking(write_pipe.as_raw_fd()).is_err() {
251 return;
252 }
253 }
254
255 // Don't access the content on the state directly, since it could change during
256 // the send.
257 let contents = match ty {
258 SelectionTarget::Clipboard => self.data_selection_content.clone(),
259 SelectionTarget::Primary => self.primary_selection_content.clone(),
260 };
261
262 let mut written = 0;
263 let _ = self.loop_handle.insert_source(write_pipe, move |_, file, _| {
264 let file = unsafe { file.get_mut() };
265 loop {
266 match file.write(&contents[written..]) {
267 Ok(n) if written + n == contents.len() => {
268 written += n;
269 break PostAction::Remove;
270 },
271 Ok(n) => written += n,
272 Err(err) if err.kind() == ErrorKind::WouldBlock => break PostAction::Continue,
273 Err(_) => break PostAction::Remove,
274 }
275 }
276 });
277 }
278}
279
280impl SeatHandler for State {
281 fn seat_state(&mut self) -> &mut SeatState {
282 &mut self.seat_state
283 }
284
285 fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: WlSeat) {
286 self.seats.insert(seat.id(), Default::default());
287 }
288
289 fn new_capability(
290 &mut self,
291 _: &Connection,
292 qh: &QueueHandle<Self>,
293 seat: WlSeat,
294 capability: Capability,
295 ) {
296 let seat_state = self.seats.get_mut(&seat.id()).unwrap();
297
298 match capability {
299 Capability::Keyboard => {
300 seat_state.keyboard = Some(seat.get_keyboard(qh, seat.id()));
301
302 // Selection sources are tied to the keyboard, so add/remove decives
303 // when we gain/loss capability.
304
305 if seat_state.data_device.is_none() && self.data_device_manager_state.is_some() {
306 seat_state.data_device = self
307 .data_device_manager_state
308 .as_ref()
309 .map(|mgr| mgr.get_data_device(qh, &seat));
310 }
311
312 if seat_state.primary_device.is_none()
313 && self.primary_selection_manager_state.is_some()
314 {
315 seat_state.primary_device = self
316 .primary_selection_manager_state
317 .as_ref()
318 .map(|mgr| mgr.get_selection_device(qh, &seat));
319 }
320 },
321 Capability::Pointer => {
322 seat_state.pointer = self.seat_state.get_pointer(qh, &seat).ok();
323 },
324 _ => (),
325 }
326 }
327
328 fn remove_capability(
329 &mut self,
330 _: &Connection,
331 _: &QueueHandle<Self>,
332 seat: WlSeat,
333 capability: Capability,
334 ) {
335 let seat_state = self.seats.get_mut(&seat.id()).unwrap();
336 match capability {
337 Capability::Keyboard => {
338 seat_state.data_device = None;
339 seat_state.primary_device = None;
340
341 if let Some(keyboard) = seat_state.keyboard.take() {
342 if keyboard.version() >= 3 {
343 keyboard.release()
344 }
345 }
346 },
347 Capability::Pointer => {
348 if let Some(pointer) = seat_state.pointer.take() {
349 if pointer.version() >= 3 {
350 pointer.release()
351 }
352 }
353 },
354 _ => (),
355 }
356 }
357
358 fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: WlSeat) {
359 self.seats.remove(&seat.id());
360 }
361}
362
363impl PointerHandler for State {
364 fn pointer_frame(
365 &mut self,
366 _: &Connection,
367 _: &QueueHandle<Self>,
368 pointer: &WlPointer,
369 events: &[PointerEvent],
370 ) {
371 let seat = pointer.data::<PointerData>().unwrap().seat();
372 let seat_id = seat.id();
373 let seat_state = match self.seats.get_mut(&seat_id) {
374 Some(seat_state) => seat_state,
375 None => return,
376 };
377
378 let mut updated_serial = false;
379 for event in events {
380 match event.kind {
381 PointerEventKind::Press { serial, .. }
382 | PointerEventKind::Release { serial, .. } => {
383 updated_serial = true;
384 seat_state.latest_serial = serial;
385 },
386 _ => (),
387 }
388 }
389
390 // Only update the seat we're using when the serial got updated.
391 if updated_serial {
392 self.latest_seat = Some(seat_id);
393 }
394 }
395}
396
397impl DataDeviceHandler for State {
398 fn enter(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
399
400 fn leave(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
401
402 fn motion(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
403
404 fn drop_performed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
405
406 // The selection is finished and ready to be used.
407 fn selection(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
408}
409
410impl DataSourceHandler for State {
411 fn send_request(
412 &mut self,
413 _: &Connection,
414 _: &QueueHandle<Self>,
415 _: &WlDataSource,
416 mime: String,
417 write_pipe: WritePipe,
418 ) {
419 self.send_request(SelectionTarget::Clipboard, write_pipe, mime)
420 }
421
422 fn cancelled(&mut self, _: &Connection, _: &QueueHandle<Self>, deleted: &WlDataSource) {
423 self.data_sources.retain(|source| source.inner() != deleted)
424 }
425
426 fn accept_mime(
427 &mut self,
428 _: &Connection,
429 _: &QueueHandle<Self>,
430 _: &WlDataSource,
431 _: Option<String>,
432 ) {
433 }
434
435 fn dnd_dropped(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource) {}
436
437 fn action(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource, _: DndAction) {}
438
439 fn dnd_finished(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource) {}
440}
441
442impl DataOfferHandler for State {
443 fn source_actions(
444 &mut self,
445 _: &Connection,
446 _: &QueueHandle<Self>,
447 _: &mut DragOffer,
448 _: DndAction,
449 ) {
450 }
451
452 fn selected_action(
453 &mut self,
454 _: &Connection,
455 _: &QueueHandle<Self>,
456 _: &mut DragOffer,
457 _: DndAction,
458 ) {
459 }
460}
461
462impl ProvidesRegistryState for State {
463 registry_handlers![SeatState];
464
465 fn registry(&mut self) -> &mut RegistryState {
466 &mut self.registry_state
467 }
468}
469
470impl PrimarySelectionDeviceHandler for State {
471 fn selection(
472 &mut self,
473 _: &Connection,
474 _: &QueueHandle<Self>,
475 _: &ZwpPrimarySelectionDeviceV1,
476 ) {
477 }
478}
479
480impl PrimarySelectionSourceHandler for State {
481 fn send_request(
482 &mut self,
483 _: &Connection,
484 _: &QueueHandle<Self>,
485 _: &ZwpPrimarySelectionSourceV1,
486 mime: String,
487 write_pipe: WritePipe,
488 ) {
489 self.send_request(ty:SelectionTarget::Primary, write_pipe, mime);
490 }
491
492 fn cancelled(
493 &mut self,
494 _: &Connection,
495 _: &QueueHandle<Self>,
496 deleted: &ZwpPrimarySelectionSourceV1,
497 ) {
498 self.primary_sources.retain(|source: &PrimarySelectionSource| source.inner() != deleted)
499 }
500}
501
502impl Dispatch<WlKeyboard, ObjectId, State> for State {
503 fn event(
504 state: &mut State,
505 _: &WlKeyboard,
506 event: <WlKeyboard as sctk::reexports::client::Proxy>::Event,
507 data: &ObjectId,
508 _: &Connection,
509 _: &QueueHandle<State>,
510 ) {
511 use sctk::reexports::client::protocol::wl_keyboard::Event as WlKeyboardEvent;
512 let seat_state = match state.seats.get_mut(data) {
513 Some(seat_state) => seat_state,
514 None => return,
515 };
516 match event {
517 WlKeyboardEvent::Key { serial, .. } | WlKeyboardEvent::Modifiers { serial, .. } => {
518 seat_state.latest_serial = serial;
519 state.latest_seat = Some(data.clone());
520 },
521 // NOTE both selections rely on keyboard focus.
522 WlKeyboardEvent::Enter { serial, .. } => {
523 seat_state.latest_serial = serial;
524 seat_state.has_focus = true;
525 },
526 WlKeyboardEvent::Leave { .. } => {
527 seat_state.latest_serial = 0;
528 seat_state.has_focus = false;
529 },
530 _ => (),
531 }
532 }
533}
534
535delegate_seat!(State);
536delegate_pointer!(State);
537delegate_data_device!(State);
538delegate_primary_selection!(State);
539delegate_registry!(State);
540
541#[derive(Debug, Clone, Copy)]
542pub enum SelectionTarget {
543 /// The target is clipboard selection.
544 Clipboard,
545 /// The target is primary selection.
546 Primary,
547}
548
549#[derive(Debug, Default)]
550struct ClipboardSeatState {
551 keyboard: Option<WlKeyboard>,
552 pointer: Option<WlPointer>,
553 data_device: Option<DataDevice>,
554 primary_device: Option<PrimarySelectionDevice>,
555 has_focus: bool,
556
557 /// The latest serial used to set the selection content.
558 latest_serial: u32,
559}
560
561impl Drop for ClipboardSeatState {
562 fn drop(&mut self) {
563 if let Some(keyboard) = self.keyboard.take() {
564 if keyboard.version() >= 3 {
565 keyboard.release();
566 }
567 }
568
569 if let Some(pointer) = self.pointer.take() {
570 if pointer.version() >= 3 {
571 pointer.release();
572 }
573 }
574 }
575}
576
577unsafe fn set_non_blocking(raw_fd: RawFd) -> std::io::Result<()> {
578 let flags: i32 = libc::fcntl(raw_fd, cmd:libc::F_GETFL);
579
580 if flags < 0 {
581 return Err(std::io::Error::last_os_error());
582 }
583
584 let result: i32 = libc::fcntl(raw_fd, cmd:libc::F_SETFL, flags | libc::O_NONBLOCK);
585 if result < 0 {
586 return Err(std::io::Error::last_os_error());
587 }
588
589 Ok(())
590}
591

Provided by KDAB

Privacy Policy