1use std::cell::{Cell, RefCell};
2use std::collections::{HashMap, HashSet, VecDeque};
3use std::ffi::CStr;
4use std::mem::MaybeUninit;
5use std::ops::Deref;
6use std::os::raw::*;
7use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
10use std::sync::{Arc, LazyLock, Mutex, Weak};
11use std::time::{Duration, Instant};
12use std::{fmt, mem, ptr, slice, str};
13
14use calloop::generic::Generic;
15use calloop::ping::Ping;
16use calloop::{EventLoop as Loop, Readiness};
17use libc::{LC_CTYPE, setlocale};
18use tracing::warn;
19use wingy_common::xkb::Context;
20use wingy_core::application::ApplicationHandler;
21use wingy_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
22use wingy_core::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType};
23use wingy_core::error::{
24 CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, TransferError,
25};
26use wingy_core::event::{DeviceId, StartCause, WindowEvent};
27use wingy_core::event_loop::primary_selection::PrimarySelectionExt;
28use wingy_core::event_loop::pump_events::PumpStatus;
29use wingy_core::event_loop::{
30 ActiveEventLoop as RootActiveEventLoop, AsyncRequestSerial, ControlFlow, DeviceEvents,
31 DndAction, EventLoopProvider, EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
32 OwnedDisplayHandle as CoreOwnedDisplayHandle,
33};
34use wingy_core::monitor::MonitorHandle as CoreMonitorHandle;
35use wingy_core::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId};
36use x11rb::connection::RequestConnection;
37use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
38use x11rb::protocol::xinput::{self, ConnectionExt as _};
39use x11rb::protocol::{ErrorKind, xkb, xproto};
40use x11rb::x11_utils::X11Error as LogicalError;
41use x11rb::xcb_ffi::ReplyOrIdError;
42
43use crate::atoms::{
44 _NET_WM_PING, _NET_WM_SYNC_REQUEST, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, Atoms,
45 WM_DELETE_WINDOW,
46};
47use crate::data_transfer::{ClipboardSelectionType, DataTransferState};
48use crate::event_processor::{EventProcessor, MAX_MOD_REPLAY_LEN};
49use crate::ime::{self, Ime, ImeCreationError, ImeSender};
50use crate::util::{self, CustomCursor};
51use crate::window::{UnownedWindow, Window};
52use crate::xdisplay::{XConnection, XError, XNotSupported};
53use crate::{Selection, XlibErrorHook, ffi, xsettings};
54
55pub(crate) const ALL_DEVICES: u16 = 0;
57pub(crate) const ALL_MASTER_DEVICES: u16 = 1;
58pub(crate) const ICONIC_STATE: u32 = 3;
59
60type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
62
63type X11Source = Generic<BorrowedFd<'static>>;
64
65pub(crate) static X11_BACKEND: LazyLock<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
66 LazyLock::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
67
68pub(crate) static XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
70
71unsafe extern "C" fn x_error_callback(
72 display: *mut ffi::Display,
73 event: *mut ffi::XErrorEvent,
74) -> c_int {
75 let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
76 if let Ok(ref xconn) = *xconn_lock {
77 let mut error_handled = false;
79 for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
80 error_handled |= hook(display as *mut _, event as *mut _);
81 }
82
83 let mut buf: [MaybeUninit<c_char>; 1024] = unsafe { MaybeUninit::uninit().assume_init() };
86 unsafe {
87 (xconn.xlib.XGetErrorText)(
88 display,
89 (*event).error_code as c_int,
90 buf.as_mut_ptr() as *mut c_char,
91 buf.len() as c_int,
92 )
93 };
94 let description =
95 unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) }.to_string_lossy();
96
97 let error = unsafe {
98 XError {
99 description: description.into_owned(),
100 error_code: (*event).error_code,
101 request_code: (*event).request_code,
102 minor_code: (*event).minor_code,
103 }
104 };
105
106 if !error_handled {
108 tracing::error!("X11 error: {:#?}", error);
109 *xconn.latest_error.lock().unwrap() = Some(error);
111 }
112 }
113 0
115}
116
117#[derive(Debug)]
118pub(crate) struct WakeSender<T> {
119 sender: Sender<T>,
120 waker: Ping,
121}
122
123impl<T> Clone for WakeSender<T> {
124 fn clone(&self) -> Self {
125 Self { sender: self.sender.clone(), waker: self.waker.clone() }
126 }
127}
128
129impl<T> WakeSender<T> {
130 pub fn send(&self, t: T) {
131 let res = self.sender.send(t);
132 if res.is_ok() {
133 self.waker.ping();
134 }
135 }
136}
137
138#[derive(Debug)]
139struct PeekableReceiver<T> {
140 recv: Receiver<T>,
141 first: Option<T>,
142}
143
144impl<T> PeekableReceiver<T> {
145 pub fn from_recv(recv: Receiver<T>) -> Self {
146 Self { recv, first: None }
147 }
148
149 pub fn has_incoming(&mut self) -> bool {
150 if self.first.is_some() {
151 return true;
152 }
153
154 match self.recv.try_recv() {
155 Ok(v) => {
156 self.first = Some(v);
157 true
158 },
159 Err(TryRecvError::Empty) => false,
160 Err(TryRecvError::Disconnected) => {
161 warn!("Channel was disconnected when checking incoming");
162 false
163 },
164 }
165 }
166
167 pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
168 if let Some(first) = self.first.take() {
169 return Ok(first);
170 }
171 self.recv.try_recv()
172 }
173}
174
175#[derive(Debug)]
176pub struct ActiveEventLoop {
177 pub(crate) xconn: Arc<XConnection>,
178 pub(crate) data_transfer_state: RefCell<DataTransferState>,
179 pub(crate) wm_delete_window: xproto::Atom,
180 pub(crate) net_wm_ping: xproto::Atom,
181 pub(crate) net_wm_sync_request: xproto::Atom,
182 pub(crate) ime_sender: ImeSender,
183 control_flow: Cell<ControlFlow>,
184 exit: Cell<Option<i32>>,
185 pub(crate) root: xproto::Window,
186 pub(crate) ime: Option<RefCell<Ime>>,
187 pub(crate) windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
188 pub(crate) redraw_sender: WakeSender<WindowId>,
189 pub(crate) activation_sender: WakeSender<ActivationItem>,
190 event_loop_proxy: CoreEventLoopProxy,
191 device_events: Cell<DeviceEvents>,
192}
193
194#[derive(Debug)]
195pub struct EventLoop {
196 loop_running: bool,
197 event_loop: Loop<'static, EventLoopState>,
198 event_processor: EventProcessor,
199 redraw_receiver: PeekableReceiver<WindowId>,
200 activation_receiver: PeekableReceiver<ActivationItem>,
201
202 state: EventLoopState,
204}
205
206pub(crate) type ActivationItem = (WindowId, wingy_core::event_loop::AsyncRequestSerial);
207
208#[derive(Debug)]
209struct EventLoopState {
210 x11_readiness: Readiness,
212
213 proxy_wake_up: bool,
215}
216
217impl EventLoop {
218 pub fn new() -> Result<EventLoop, EventLoopError> {
219 static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
220 if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
221 return Err(EventLoopError::RecreationAttempt);
223 }
224
225 let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
226 Ok(xconn) => xconn.clone(),
227 Err(XNotSupported::ExtensionNotSupported(reason)) => {
228 return Err(NotSupportedError::new(reason).into());
229 },
230 Err(err) => return Err(os_error!(err.clone()).into()),
231 };
232
233 let root = xconn.default_root().root;
234 let atoms = xconn.atoms();
235
236 let wm_delete_window = atoms[WM_DELETE_WINDOW];
237 let net_wm_ping = atoms[_NET_WM_PING];
238 let net_wm_sync_request = atoms[_NET_WM_SYNC_REQUEST];
239
240 let (ime_sender, ime_receiver) = mpsc::channel();
241 let (ime_event_sender, ime_event_receiver) = mpsc::channel();
242 unsafe {
245 let default_locale = setlocale(LC_CTYPE, ptr::null());
248 setlocale(LC_CTYPE, c"".as_ptr() as *const _);
249
250 let locale_supported = (xconn.xlib.XSupportsLocale)() == 1;
254 if !locale_supported {
255 let unsupported_locale = setlocale(LC_CTYPE, ptr::null());
256 warn!(
257 "Unsupported locale \"{}\". Restoring default locale \"{}\".",
258 CStr::from_ptr(unsupported_locale).to_string_lossy(),
259 CStr::from_ptr(default_locale).to_string_lossy()
260 );
261 setlocale(LC_CTYPE, default_locale);
263 }
264 }
265
266 let ime = Ime::new(Arc::clone(&xconn), ime_event_sender);
267 if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() {
268 warn!("Failed to open input method: {state:#?}");
269 } else if let Err(err) = ime.as_ref() {
270 warn!("Failed to set input method destruction callback: {err:?}");
271 }
272
273 let ime = ime.ok().map(RefCell::new);
274
275 let randr_event_offset = xconn.select_xrandr_input(root).map_err(|err| match err {
276 X11Error::MissingExtension(_) => EventLoopError::NotSupported(NotSupportedError::new(
277 "the X11 backend requires XRandR 1.2 or newer",
278 )),
279 error => os_error!(error).into(),
280 })?;
281
282 let xi2ext = xconn
283 .xcb_connection()
284 .extension_information(xinput::X11_EXTENSION_NAME)
285 .map_err(|err| os_error!(X11Error::from(err)))?
286 .ok_or_else(|| {
287 NotSupportedError::new("the X11 backend requires XInput 2.0 or newer")
288 })?;
289 let xkbext = xconn
290 .xcb_connection()
291 .extension_information(xkb::X11_EXTENSION_NAME)
292 .map_err(|err| os_error!(X11Error::from(err)))?
293 .ok_or_else(|| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
294 let xfixesext = xconn
295 .xcb_connection()
296 .extension_information(x11rb::protocol::xfixes::X11_EXTENSION_NAME)
297 .map_err(|err| os_error!(X11Error::from(err)))?
298 .ok_or_else(|| {
299 NotSupportedError::new("the X11 backend requires the XFixes extension")
300 })?;
301
302 let data_transfer_state = DataTransferState::new(Arc::clone(&xconn)).into();
303 xconn
305 .xcb_connection()
306 .xinput_xi_query_version(2, 3)
307 .map_err(|err| os_error!(X11Error::from(err)))?
308 .reply()
309 .map_err(|err| match err {
310 ReplyError::X11Error(error) if error.error_kind == ErrorKind::Request => {
311 EventLoopError::NotSupported(NotSupportedError::new(
312 "the X11 backend requires XInput 2.0 or newer",
313 ))
314 },
315 error => os_error!(X11Error::from(error)).into(),
316 })?;
317
318 xconn.update_cached_wm_info(root);
319
320 let event_loop =
322 Loop::<EventLoopState>::try_new().expect("Failed to initialize the event loop");
323 let handle = event_loop.handle();
324
325 let source = X11Source::new(
327 unsafe { BorrowedFd::borrow_raw(xconn.xcb_connection().as_raw_fd()) },
329 calloop::Interest::READ,
330 calloop::Mode::Level,
331 );
332 handle
333 .insert_source(source, |readiness, _, state| {
334 state.x11_readiness = readiness;
335 Ok(calloop::PostAction::Continue)
336 })
337 .expect("Failed to register the X11 event dispatcher");
338
339 let (waker, waker_source) =
340 calloop::ping::make_ping().expect("Failed to create event loop waker");
341 event_loop
342 .handle()
343 .insert_source(waker_source, move |_, _, _| {
344 })
346 .expect("Failed to register the event loop waker source");
347
348 let (redraw_sender, redraw_channel) = mpsc::channel();
350
351 let (activation_token_sender, activation_token_channel) = mpsc::channel();
353
354 let (user_waker, user_waker_source) =
356 calloop::ping::make_ping().expect("Failed to create user event loop waker.");
357 event_loop
358 .handle()
359 .insert_source(user_waker_source, move |_, _, state| {
360 state.proxy_wake_up = true;
362 })
363 .expect("Failed to register the event loop waker source");
364 let event_loop_proxy = EventLoopProxy::new(user_waker);
365
366 let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection())
367 .map_err(|_| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
368
369 let mut xmodmap = util::ModifierKeymap::new();
370 xmodmap.reload_from_x_connection(&xconn);
371
372 let window_target = ActiveEventLoop {
373 ime,
374 data_transfer_state,
375 root,
376 control_flow: Cell::new(ControlFlow::default()),
377 exit: Cell::new(None),
378 windows: Default::default(),
379 ime_sender,
380 xconn,
381 wm_delete_window,
382 net_wm_ping,
383 net_wm_sync_request,
384 redraw_sender: WakeSender {
385 sender: redraw_sender, waker: waker.clone(),
387 },
388 activation_sender: WakeSender {
389 sender: activation_token_sender, waker: waker.clone(),
391 },
392 event_loop_proxy: event_loop_proxy.into(),
393 device_events: Default::default(),
394 };
395
396 window_target.update_listen_device_events(true);
398
399 let event_processor = EventProcessor {
400 target: window_target,
401 devices: Default::default(),
402 randr_event_offset,
403 ime_receiver,
404 ime_event_receiver,
405 xi2ext,
406 xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN),
407 xmodmap,
408 xkbext,
409 xfixesext,
410 xkb_context,
411 num_touch: 0,
412 held_key_press: None,
413 first_touch: None,
414 active_window: None,
415 modifiers: Default::default(),
416 is_composing: false,
417 };
418
419 event_processor
422 .target
423 .xconn
424 .select_xinput_events(
425 root,
426 ALL_DEVICES,
427 x11rb::protocol::xinput::XIEventMask::HIERARCHY,
428 )
429 .expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
430
431 event_processor
432 .target
433 .xconn
434 .select_xkb_events(
435 0x100, xkb::EventType::NEW_KEYBOARD_NOTIFY
437 | xkb::EventType::MAP_NOTIFY
438 | xkb::EventType::STATE_NOTIFY,
439 )
440 .map_err(|err| os_error!(err))?;
441
442 event_processor.init_device(ALL_DEVICES);
443
444 let event_loop = EventLoop {
445 loop_running: false,
446 event_loop,
447 event_processor,
448 redraw_receiver: PeekableReceiver::from_recv(redraw_channel),
449 activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
450 state: EventLoopState { x11_readiness: Readiness::EMPTY, proxy_wake_up: false },
451 };
452
453 Ok(event_loop)
454 }
455
456 pub fn window_target(&self) -> &dyn RootActiveEventLoop {
457 &self.event_processor.target
458 }
459
460 pub fn run_app_on_demand<A: ApplicationHandler>(
461 &mut self,
462 mut app: A,
463 ) -> Result<(), EventLoopError> {
464 self.event_processor.target.clear_exit();
465 let exit = loop {
466 match self.pump_app_events(None, &mut app) {
467 PumpStatus::Exit(0) => {
468 break Ok(());
469 },
470 PumpStatus::Exit(code) => {
471 break Err(EventLoopError::ExitFailure(code));
472 },
473 _ => {
474 continue;
475 },
476 }
477 };
478
479 self.event_processor
484 .target
485 .x_connection()
486 .sync_with_server()
487 .map_err(|x_err| EventLoopError::Os(os_error!(X11Error::Xlib(x_err))))?;
488
489 exit
490 }
491
492 pub fn pump_app_events<A: ApplicationHandler>(
493 &mut self,
494 timeout: Option<Duration>,
495 mut app: A,
496 ) -> PumpStatus {
497 if !self.loop_running {
498 self.loop_running = true;
499
500 self.single_iteration(&mut app, StartCause::Init);
502 }
503
504 if !self.exiting() {
507 self.poll_events_with_timeout(timeout, &mut app);
508 }
509 if let Some(code) = self.exit_code() {
510 self.loop_running = false;
511
512 PumpStatus::Exit(code)
513 } else {
514 PumpStatus::Continue
515 }
516 }
517
518 fn has_pending(&mut self) -> bool {
519 self.event_processor.poll()
520 || self.state.proxy_wake_up
521 || self.redraw_receiver.has_incoming()
522 }
523
524 fn poll_events_with_timeout<A: ApplicationHandler>(
525 &mut self,
526 mut timeout: Option<Duration>,
527 app: &mut A,
528 ) {
529 let start = Instant::now();
530
531 let has_pending = self.has_pending();
532
533 timeout = if has_pending {
534 Some(Duration::ZERO)
536 } else {
537 let control_flow_timeout = match self.control_flow() {
538 ControlFlow::Wait => None,
539 ControlFlow::Poll => Some(Duration::ZERO),
540 ControlFlow::WaitUntil(wait_deadline) => {
541 Some(wait_deadline.saturating_duration_since(start))
542 },
543 };
544
545 min_timeout(control_flow_timeout, timeout)
546 };
547
548 self.state.x11_readiness = Readiness::EMPTY;
549 if let Err(error) =
550 self.event_loop.dispatch(timeout, &mut self.state).map_err(std::io::Error::from)
551 {
552 tracing::error!("Failed to poll for events: {error:?}");
553 let exit_code = error.raw_os_error().unwrap_or(1);
554 self.set_exit_code(exit_code);
555 return;
556 }
557
558 let cause = match self.control_flow() {
561 ControlFlow::Poll => StartCause::Poll,
562 ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
563 ControlFlow::WaitUntil(deadline) => {
564 if Instant::now() < deadline {
565 StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
566 } else {
567 StartCause::ResumeTimeReached { start, requested_resume: deadline }
568 }
569 },
570 };
571
572 if !self.has_pending()
581 && !matches!(&cause, StartCause::ResumeTimeReached { .. } | StartCause::Poll)
582 && timeout.is_none()
583 {
584 return;
585 }
586
587 self.single_iteration(app, cause);
588 }
589
590 fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
591 app.new_events(&self.event_processor.target, cause);
592
593 if cause == StartCause::Init {
596 app.can_create_surfaces(&self.event_processor.target)
597 }
598
599 self.drain_events(app);
601
602 while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
604 let token = self
605 .event_processor
606 .with_window(window_id.into_raw() as xproto::Window, |window| {
607 window.generate_activation_token()
608 });
609
610 match token {
611 Some(Ok(token)) => {
612 let event = WindowEvent::ActivationTokenDone {
613 serial,
614 token: wingy_core::window::ActivationToken::from_raw(token),
615 };
616 app.window_event(&self.event_processor.target, window_id, event);
617 },
618 Some(Err(e)) => {
619 tracing::error!("Failed to get activation token: {}", e);
620 },
621 None => {},
622 }
623 }
624
625 if mem::take(&mut self.state.proxy_wake_up) {
627 app.proxy_wake_up(&self.event_processor.target);
628 }
629
630 {
632 let mut windows = HashSet::new();
633
634 while let Ok(window_id) = self.redraw_receiver.try_recv() {
635 windows.insert(window_id);
636 }
637
638 for window_id in windows {
639 app.window_event(
640 &self.event_processor.target,
641 window_id,
642 WindowEvent::RedrawRequested,
643 );
644 }
645 }
646
647 app.about_to_wait(&self.event_processor.target);
649 }
650
651 fn drain_events<A: ApplicationHandler>(&mut self, app: &mut A) {
652 let mut xev = MaybeUninit::uninit();
653
654 while let Some(xev) = self.event_processor.poll_one_event(&mut xev) {
655 self.event_processor.process_event(xev, app);
656 }
657 }
658
659 fn control_flow(&self) -> ControlFlow {
660 self.event_processor.target.control_flow()
661 }
662
663 fn exiting(&self) -> bool {
664 self.event_processor.target.exiting()
665 }
666
667 fn set_exit_code(&self, code: i32) {
668 self.event_processor.target.set_exit_code(code);
669 }
670
671 fn exit_code(&self) -> Option<i32> {
672 self.event_processor.target.exit_code()
673 }
674}
675
676impl EventLoopProvider for EventLoop {
677 fn run_app<A: ApplicationHandler + 'static>(
678 mut self,
679 mut app: A,
680 ) -> Result<(), EventLoopError> {
681 let result = self.run_app_on_demand(&mut app);
682 drop(app);
684 result
685 }
686
687 fn create_proxy(&self) -> CoreEventLoopProxy {
688 self.window_target().create_proxy()
689 }
690
691 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
692 self.window_target().owned_display_handle()
693 }
694
695 fn listen_device_events(&self, allowed: DeviceEvents) {
696 self.window_target().listen_device_events(allowed);
697 }
698
699 fn set_control_flow(&self, control_flow: ControlFlow) {
700 self.window_target().set_control_flow(control_flow);
701 }
702
703 fn create_custom_cursor(
704 &self,
705 custom_cursor: CustomCursorSource,
706 ) -> Result<CoreCustomCursor, CustomCursorError> {
707 self.window_target().create_custom_cursor(custom_cursor)
708 }
709}
710
711impl AsFd for EventLoop {
712 fn as_fd(&self) -> BorrowedFd<'_> {
713 self.event_loop.as_fd()
714 }
715}
716
717impl AsRawFd for EventLoop {
718 fn as_raw_fd(&self) -> RawFd {
719 self.event_loop.as_raw_fd()
720 }
721}
722
723impl ActiveEventLoop {
724 #[inline]
726 pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
727 &self.xconn
728 }
729
730 pub fn update_listen_device_events(&self, focus: bool) {
732 let device_events = self.device_events.get() == DeviceEvents::Always
733 || (focus && self.device_events.get() == DeviceEvents::WhenFocused);
734
735 let mut mask = xinput::XIEventMask::from(0u32);
736 if device_events {
737 mask = xinput::XIEventMask::RAW_MOTION
738 | xinput::XIEventMask::RAW_BUTTON_PRESS
739 | xinput::XIEventMask::RAW_BUTTON_RELEASE
740 | xinput::XIEventMask::RAW_KEY_PRESS
741 | xinput::XIEventMask::RAW_KEY_RELEASE;
742 }
743
744 self.xconn
745 .select_xinput_events(self.root, ALL_MASTER_DEVICES, mask)
746 .expect_then_ignore_error("Failed to update device event filter");
747 }
748
749 pub(crate) fn clear_exit(&self) {
750 self.exit.set(None)
751 }
752
753 pub(crate) fn set_exit_code(&self, code: i32) {
754 self.exit.set(Some(code))
755 }
756
757 pub(crate) fn exit_code(&self) -> Option<i32> {
758 self.exit.get()
759 }
760
761 pub(crate) fn clipboard_window(&self) -> Option<xproto::Window> {
764 self.windows
765 .borrow()
766 .iter()
767 .find(|(_, window)| window.upgrade().is_some_and(|window| window.has_focus()))
768 .map(|(id, _)| id.into_raw() as _)
769 .or_else(|| {
770 self.windows
771 .borrow()
772 .iter()
773 .find(|(_, window)| window.upgrade().is_some())
774 .map(|(id, _)| id.into_raw() as _)
775 })
776 }
777
778 fn read_clipboard(
779 &self,
780 clipboard: ClipboardSelectionType,
781 ) -> Result<Option<DataTransferId>, TransferError> {
782 let selection = clipboard.selection_atom(self.xconn.atoms());
783 if self.xconn.selection_owner(selection).map_err(|err| os_error!(err))?.is_none() {
784 return Ok(None);
785 }
786
787 let mut data_transfer = self.data_transfer_state.borrow_mut();
788
789 Ok(Some(data_transfer.request_clipboard_read(clipboard)))
790 }
791
792 fn write_clipboard(
793 &self,
794 clipboard: ClipboardSelectionType,
795 send_data: Box<dyn DataTransferSend>,
796 ) -> Result<(), TransferError> {
797 let Some(xwindow) = self.clipboard_window() else {
798 return Err(os_error!("no active window").into());
799 };
800 let mut data_transfer = self.data_transfer_state.borrow_mut();
801 data_transfer.set_clipboard(xwindow, send_data, clipboard);
802
803 Ok(())
804 }
805}
806
807impl RootActiveEventLoop for ActiveEventLoop {
808 fn create_proxy(&self) -> CoreEventLoopProxy {
809 self.event_loop_proxy.clone()
810 }
811
812 fn create_window(
813 &self,
814 window_attributes: WindowAttributes,
815 ) -> Result<Box<dyn CoreWindow>, CreateWindowError> {
816 Ok(Box::new(Window::new(self, window_attributes)?))
817 }
818
819 fn create_custom_cursor(
820 &self,
821 custom_cursor: CustomCursorSource,
822 ) -> Result<CoreCustomCursor, CustomCursorError> {
823 Ok(CoreCustomCursor(Arc::new(CustomCursor::new(self, custom_cursor)?)))
824 }
825
826 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
827 Box::new(
828 self.xconn
829 .available_monitors()
830 .into_iter()
831 .flatten()
832 .map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
833 )
834 }
835
836 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
837 self.xconn.primary_monitor().ok().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
838 }
839
840 fn system_theme(&self) -> Option<Theme> {
841 None
842 }
843
844 fn listen_device_events(&self, allowed: DeviceEvents) {
845 self.device_events.set(allowed);
846 }
847
848 fn set_control_flow(&self, control_flow: ControlFlow) {
849 self.control_flow.set(control_flow)
850 }
851
852 fn control_flow(&self) -> ControlFlow {
853 self.control_flow.get()
854 }
855
856 fn exit(&self) {
857 self.exit.set(Some(0))
858 }
859
860 fn exiting(&self) -> bool {
861 self.exit.get().is_some()
862 }
863
864 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
865 CoreOwnedDisplayHandle::new(self.x_connection().clone())
866 }
867
868 fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
869 self
870 }
871
872 fn data_transfer(&self, id: DataTransferId) -> Result<Box<dyn DataTransfer>, TransferError> {
873 let data_transfer = self.data_transfer_state.borrow();
874
875 if let Some(state) = data_transfer.state() {
876 if state.transfer_id == id {
877 return Ok(Box::new(Selection::new(state.types.clone())));
878 }
879 }
880
881 if let Some(clipboard) = data_transfer.resolve_clipboard_type(id) {
882 return Ok(Box::new(
883 data_transfer.get_clipboard(clipboard).get_types(self.x_connection().atoms()),
884 ));
885 }
886
887 Err(TransferError::UnknownTransfer(id))
888 }
889
890 fn fetch_data_transfer(
891 &self,
892 id: DataTransferId,
893 type_: &dyn TransferType,
894 ) -> Result<AsyncRequestSerial, TransferError> {
895 let mut data_transfer = self.data_transfer_state.borrow_mut();
896
897 if let Some(state) = data_transfer.state() {
898 if state.transfer_id == id {
899 return data_transfer.fetch_dnd_data_transfer(type_);
900 }
901 }
902
903 if let Some(clipboard) = data_transfer.resolve_clipboard_type(id) {
904 let Some(clip_win) = self.clipboard_window() else {
905 return Err(TransferError::Failed);
906 };
907 data_transfer.fetch_clipboard_data_transfer(clipboard, type_, clip_win)
908 } else {
909 Err(TransferError::UnknownTransfer(id))
910 }
911 }
912
913 fn set_valid_dnd_actions(
914 &self,
915 id: DataTransferId,
916 actions: &[DndAction],
917 ) -> Result<(), TransferError> {
918 let mut data_transfer = self.data_transfer_state.borrow_mut();
919
920 let Some(state) = data_transfer.state_mut() else {
921 return Err(TransferError::UnknownTransfer(id));
922 };
923
924 if state.transfer_id != id {
925 return Err(TransferError::UnknownTransfer(id));
926 }
927
928 state.accepted = !actions.is_empty();
929
930 Ok(())
931 }
932
933 fn clipboard(&self) -> Result<Option<DataTransferId>, TransferError> {
934 self.read_clipboard(ClipboardSelectionType::Clipboard)
935 }
936
937 fn set_clipboard(&self, send_data: Box<dyn DataTransferSend>) -> Result<(), TransferError> {
938 self.write_clipboard(ClipboardSelectionType::Clipboard, send_data)
939 }
940
941 fn primary_selection_ext(&self) -> Option<&dyn PrimarySelectionExt> {
942 Some(self)
943 }
944}
945
946impl PrimarySelectionExt for ActiveEventLoop {
947 fn primary_selection(&self) -> Result<Option<DataTransferId>, TransferError> {
948 self.read_clipboard(ClipboardSelectionType::Primary)
949 }
950
951 fn set_primary_selection(
952 &self,
953 send_data: Box<dyn DataTransferSend>,
954 ) -> Result<(), TransferError> {
955 self.write_clipboard(ClipboardSelectionType::Primary, send_data)
956 }
957}
958
959impl rwh_06::HasDisplayHandle for ActiveEventLoop {
960 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
961 self.xconn.display_handle()
962 }
963}
964
965pub(crate) struct DeviceInfo<'a> {
966 xconn: &'a XConnection,
967 info: *const ffi::XIDeviceInfo,
968 count: usize,
969}
970
971impl<'a> DeviceInfo<'a> {
972 pub(crate) fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
973 unsafe {
974 let mut count = 0;
975 let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
976 xconn.check_errors().ok()?;
977
978 if info.is_null() || count == 0 {
979 None
980 } else {
981 Some(DeviceInfo { xconn, info, count: count as usize })
982 }
983 }
984 }
985}
986
987impl Drop for DeviceInfo<'_> {
988 fn drop(&mut self) {
989 assert!(!self.info.is_null());
990 unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
991 }
992}
993
994impl Deref for DeviceInfo<'_> {
995 type Target = [ffi::XIDeviceInfo];
996
997 fn deref(&self) -> &Self::Target {
998 unsafe { slice::from_raw_parts(self.info, self.count) }
999 }
1000}
1001
1002#[derive(Clone, Debug)]
1003pub struct EventLoopProxy {
1004 ping: Ping,
1005}
1006
1007impl EventLoopProxyProvider for EventLoopProxy {
1008 fn wake_up(&self) {
1009 self.ping.ping();
1010 }
1011}
1012
1013impl EventLoopProxy {
1014 fn new(ping: Ping) -> Self {
1015 Self { ping }
1016 }
1017}
1018
1019impl From<EventLoopProxy> for CoreEventLoopProxy {
1020 fn from(value: EventLoopProxy) -> Self {
1021 CoreEventLoopProxy::new(Arc::new(value))
1022 }
1023}
1024
1025#[derive(Debug)]
1027pub enum X11Error {
1028 Xlib(XError),
1030
1031 Connect(ConnectError),
1033
1034 Connection(ConnectionError),
1036
1037 X11(LogicalError),
1039
1040 XidsExhausted(IdsExhausted),
1042
1043 UnexpectedNull(&'static str),
1045
1046 InvalidActivationToken(Vec<u8>),
1048
1049 MissingExtension(&'static str),
1051
1052 NoSuchVisual(xproto::Visualid),
1054
1055 XsettingsParse(xsettings::ParserError),
1057
1058 GetProperty(util::GetPropertyError),
1060
1061 NoArgb32Format,
1063}
1064
1065impl fmt::Display for X11Error {
1066 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1067 match self {
1068 X11Error::Xlib(e) => write!(f, "Xlib error: {e}"),
1069 X11Error::Connect(e) => write!(f, "X11 connection error: {e}"),
1070 X11Error::Connection(e) => write!(f, "X11 connection error: {e}"),
1071 X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {e}"),
1072 X11Error::GetProperty(e) => write!(f, "Failed to get X property {e}"),
1073 X11Error::X11(e) => write!(f, "X11 error: {e:?}"),
1074 X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {s}"),
1075 X11Error::InvalidActivationToken(s) => write!(
1076 f,
1077 "Invalid activation token: {}",
1078 std::str::from_utf8(s).unwrap_or("<invalid utf8>")
1079 ),
1080 X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {s}"),
1081 X11Error::NoSuchVisual(visualid) => {
1082 write!(f, "Could not find a matching X11 visual for ID `{visualid:x}`")
1083 },
1084 X11Error::XsettingsParse(err) => {
1085 write!(f, "Failed to parse xsettings: {err:?}")
1086 },
1087 X11Error::NoArgb32Format => {
1088 f.write_str("wingy only supports X11 displays with ARGB32 picture formats")
1089 },
1090 }
1091 }
1092}
1093
1094impl std::error::Error for X11Error {
1095 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1096 match self {
1097 X11Error::Xlib(e) => Some(e),
1098 X11Error::Connect(e) => Some(e),
1099 X11Error::Connection(e) => Some(e),
1100 X11Error::XidsExhausted(e) => Some(e),
1101 _ => None,
1102 }
1103 }
1104}
1105
1106impl From<XError> for X11Error {
1107 fn from(e: XError) -> Self {
1108 X11Error::Xlib(e)
1109 }
1110}
1111
1112impl From<ConnectError> for X11Error {
1113 fn from(e: ConnectError) -> Self {
1114 X11Error::Connect(e)
1115 }
1116}
1117
1118impl From<ConnectionError> for X11Error {
1119 fn from(e: ConnectionError) -> Self {
1120 X11Error::Connection(e)
1121 }
1122}
1123
1124impl From<LogicalError> for X11Error {
1125 fn from(e: LogicalError) -> Self {
1126 X11Error::X11(e)
1127 }
1128}
1129
1130impl From<ReplyError> for X11Error {
1131 fn from(value: ReplyError) -> Self {
1132 match value {
1133 ReplyError::ConnectionError(e) => e.into(),
1134 ReplyError::X11Error(e) => e.into(),
1135 }
1136 }
1137}
1138
1139impl From<ime::ImeContextCreationError> for X11Error {
1140 fn from(value: ime::ImeContextCreationError) -> Self {
1141 match value {
1142 ime::ImeContextCreationError::XError(e) => e.into(),
1143 ime::ImeContextCreationError::Null => Self::UnexpectedNull("XOpenIM"),
1144 }
1145 }
1146}
1147
1148impl From<ReplyOrIdError> for X11Error {
1149 fn from(value: ReplyOrIdError) -> Self {
1150 match value {
1151 ReplyOrIdError::ConnectionError(e) => e.into(),
1152 ReplyOrIdError::X11Error(e) => e.into(),
1153 ReplyOrIdError::IdsExhausted => Self::XidsExhausted(IdsExhausted),
1154 }
1155 }
1156}
1157
1158impl From<xsettings::ParserError> for X11Error {
1159 fn from(value: xsettings::ParserError) -> Self {
1160 Self::XsettingsParse(value)
1161 }
1162}
1163
1164impl From<util::GetPropertyError> for X11Error {
1165 fn from(value: util::GetPropertyError) -> Self {
1166 Self::GetProperty(value)
1167 }
1168}
1169
1170pub(crate) type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
1172
1173pub(crate) trait CookieResultExt {
1175 fn expect_then_ignore_error(self, msg: &str);
1177}
1178
1179impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
1180 fn expect_then_ignore_error(self, msg: &str) {
1181 self.expect(msg).ignore_error()
1182 }
1183}
1184
1185pub(crate) fn mkwid(w: xproto::Window) -> wingy_core::window::WindowId {
1186 wingy_core::window::WindowId::from_raw(w as _)
1187}
1188
1189pub(crate) fn mkdid(w: xinput::DeviceId) -> DeviceId {
1190 DeviceId::from_raw(w as i64)
1191}
1192
1193#[derive(Debug)]
1194pub struct Device {
1195 _name: String,
1196 pub(crate) scroll_axes: Vec<(i32, ScrollAxis)>,
1197 pub(crate) attachment: c_int,
1200 pub(crate) r#type: DeviceType,
1201}
1202
1203#[derive(Clone, Copy, Debug)]
1204pub(crate) enum DeviceType {
1205 Mouse,
1206 Touch,
1207 Pen,
1208 Eraser,
1209}
1210
1211#[derive(Debug, Copy, Clone)]
1212pub(crate) struct ScrollAxis {
1213 pub(crate) increment: f64,
1214 pub(crate) orientation: ScrollOrientation,
1215 pub(crate) position: f64,
1216}
1217
1218#[derive(Debug, Copy, Clone)]
1219pub(crate) enum ScrollOrientation {
1220 Vertical,
1221 Horizontal,
1222}
1223
1224impl Device {
1225 pub(crate) fn new(info: &ffi::XIDeviceInfo, atoms: &Atoms) -> Self {
1226 let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
1227 let mut scroll_axes = Vec::new();
1228 let mut r#type = None;
1229
1230 if Device::physical_device(info) {
1231 for &class_ptr in Device::classes(info) {
1233 let ty = unsafe { (*class_ptr)._type };
1234 if ty == ffi::XIScrollClass {
1235 let info = unsafe { &*(class_ptr as *const ffi::XIScrollClassInfo) };
1236 scroll_axes.push((
1237 info.number,
1238 ScrollAxis {
1239 increment: info.increment,
1240 orientation: match info.scroll_type {
1241 ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
1242 ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
1243 _ => unreachable!(),
1244 },
1245 position: 0.0,
1246 },
1247 ));
1248 } else if ty == ffi::XITouchClass {
1249 r#type = Some(DeviceType::Touch);
1250 } else if r#type.is_none() && ty == ffi::XIValuatorClass {
1251 let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) };
1252 let atom = info.label as xproto::Atom;
1253
1254 if atom == atoms[ABS_PRESSURE]
1263 || atom == atoms[ABS_TILT_X]
1264 || atom == atoms[ABS_TILT_Y]
1265 {
1266 if name.contains("eraser") {
1267 r#type = Some(DeviceType::Eraser);
1268 } else {
1269 r#type = Some(DeviceType::Pen);
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 let mut device = Device {
1277 _name: name.into_owned(),
1278 scroll_axes,
1279 attachment: info.attachment,
1280 r#type: r#type.unwrap_or(DeviceType::Mouse),
1281 };
1282 device.reset_scroll_position(info);
1283 device
1284 }
1285
1286 pub(crate) fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
1287 if Device::physical_device(info) {
1288 for &class_ptr in Device::classes(info) {
1289 let ty = unsafe { (*class_ptr)._type };
1290 if ty == ffi::XIValuatorClass {
1291 let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) };
1292 if let Some(&mut (_, ref mut axis)) =
1293 self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number)
1294 {
1295 axis.position = info.value;
1296 }
1297 }
1298 }
1299 }
1300 }
1301
1302 #[inline]
1303 fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
1304 info._use == ffi::XISlaveKeyboard
1305 || info._use == ffi::XISlavePointer
1306 || info._use == ffi::XIFloatingSlave
1307 }
1308
1309 #[inline]
1310 fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
1311 unsafe {
1312 slice::from_raw_parts(
1313 info.classes as *const *const ffi::XIAnyClassInfo,
1314 info.num_classes as usize,
1315 )
1316 }
1317 }
1318}
1319
1320#[inline]
1322pub(crate) fn xinput_fp1616_to_float(fp: xinput::Fp1616) -> f64 {
1323 (fp as f64) / ((1 << 16) as f64)
1324}
1325
1326fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
1330 a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
1331}