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