1 #![cfg(any(
2 target_os = "linux",
3 target_os = "dragonfly",
4 target_os = "freebsd",
5 target_os = "netbsd",
6 target_os = "openbsd"
7 ))]
8
9 mod dnd;
10 mod event_processor;
11 mod events;
12 pub mod ffi;
13 mod ime;
14 mod monitor;
15 pub mod util;
16 mod window;
17 mod xdisplay;
18
19 pub use self::{
20 monitor::{MonitorHandle, VideoMode},
21 window::UnownedWindow,
22 xdisplay::{XConnection, XError, XNotSupported},
23 };
24
25 use std::{
26 cell::RefCell,
27 collections::{HashMap, HashSet},
28 ffi::CStr,
29 mem::{self, MaybeUninit},
30 ops::Deref,
31 os::raw::*,
32 ptr,
33 rc::Rc,
34 slice,
35 sync::{mpsc, Arc, Mutex, Weak},
36 time::{Duration, Instant},
37 };
38
39 use libc::{self, setlocale, LC_CTYPE};
40
41 use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
42
43 use mio_extras::channel::{channel, Receiver, SendError, Sender};
44
45 use self::{
46 dnd::{Dnd, DndState},
47 event_processor::EventProcessor,
48 ime::{Ime, ImeCreationError, ImeReceiver, ImeSender},
49 util::modifiers::ModifierKeymap,
50 };
51 use crate::{
52 error::OsError as RootOsError,
53 event::{Event, StartCause},
54 event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
55 platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
56 window::WindowAttributes,
57 };
58
59 const X_TOKEN: Token = Token(0);
60 const USER_TOKEN: Token = Token(1);
61
62 pub struct EventLoopWindowTarget<T> {
63 xconn: Arc<XConnection>,
64 wm_delete_window: ffi::Atom,
65 net_wm_ping: ffi::Atom,
66 ime_sender: ImeSender,
67 root: ffi::Window,
68 ime: RefCell<Ime>,
69 windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
70 pending_redraws: Arc<Mutex<HashSet<WindowId>>>,
71 _marker: ::std::marker::PhantomData<T>,
72 }
73
74 pub struct EventLoop<T: 'static> {
75 poll: Poll,
76 event_processor: EventProcessor<T>,
77 user_channel: Receiver<T>,
78 user_sender: Sender<T>,
79 target: Rc<RootELW<T>>,
80 }
81
82 pub struct EventLoopProxy<T: 'static> {
83 user_sender: Sender<T>,
84 }
85
86 impl<T: 'static> Clone for EventLoopProxy<T> {
clone(&self) -> Self87 fn clone(&self) -> Self {
88 EventLoopProxy {
89 user_sender: self.user_sender.clone(),
90 }
91 }
92 }
93
94 impl<T: 'static> EventLoop<T> {
new(xconn: Arc<XConnection>) -> EventLoop<T>95 pub fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
96 let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
97
98 let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
99
100 let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") };
101
102 let dnd = Dnd::new(Arc::clone(&xconn))
103 .expect("Failed to call XInternAtoms when initializing drag and drop");
104
105 let (ime_sender, ime_receiver) = mpsc::channel();
106 // Input methods will open successfully without setting the locale, but it won't be
107 // possible to actually commit pre-edit sequences.
108 unsafe {
109 // Remember default locale to restore it if target locale is unsupported
110 // by Xlib
111 let default_locale = setlocale(LC_CTYPE, ptr::null());
112 setlocale(LC_CTYPE, b"\0".as_ptr() as *const _);
113
114 // Check if set locale is supported by Xlib.
115 // If not, calls to some Xlib functions like `XSetLocaleModifiers`
116 // will fail.
117 let locale_supported = (xconn.xlib.XSupportsLocale)() == 1;
118 if !locale_supported {
119 let unsupported_locale = setlocale(LC_CTYPE, ptr::null());
120 warn!(
121 "Unsupported locale \"{}\". Restoring default locale \"{}\".",
122 CStr::from_ptr(unsupported_locale).to_string_lossy(),
123 CStr::from_ptr(default_locale).to_string_lossy()
124 );
125 // Restore default locale
126 setlocale(LC_CTYPE, default_locale);
127 }
128 }
129 let ime = RefCell::new({
130 let result = Ime::new(Arc::clone(&xconn));
131 if let Err(ImeCreationError::OpenFailure(ref state)) = result {
132 panic!(format!("Failed to open input method: {:#?}", state));
133 }
134 result.expect("Failed to set input method destruction callback")
135 });
136
137 let randr_event_offset = xconn
138 .select_xrandr_input(root)
139 .expect("Failed to query XRandR extension");
140
141 let xi2ext = unsafe {
142 let mut ext = XExtension::default();
143
144 let res = (xconn.xlib.XQueryExtension)(
145 xconn.display,
146 b"XInputExtension\0".as_ptr() as *const c_char,
147 &mut ext.opcode,
148 &mut ext.first_event_id,
149 &mut ext.first_error_id,
150 );
151
152 if res == ffi::False {
153 panic!("X server missing XInput extension");
154 }
155
156 ext
157 };
158
159 unsafe {
160 let mut xinput_major_ver = ffi::XI_2_Major;
161 let mut xinput_minor_ver = ffi::XI_2_Minor;
162 if (xconn.xinput2.XIQueryVersion)(
163 xconn.display,
164 &mut xinput_major_ver,
165 &mut xinput_minor_ver,
166 ) != ffi::Success as libc::c_int
167 {
168 panic!(
169 "X server has XInput extension {}.{} but does not support XInput2",
170 xinput_major_ver, xinput_minor_ver,
171 );
172 }
173 }
174
175 xconn.update_cached_wm_info(root);
176
177 let pending_redraws: Arc<Mutex<HashSet<WindowId>>> = Default::default();
178
179 let mut mod_keymap = ModifierKeymap::new();
180 mod_keymap.reset_from_x_connection(&xconn);
181
182 let target = Rc::new(RootELW {
183 p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
184 ime,
185 root,
186 windows: Default::default(),
187 _marker: ::std::marker::PhantomData,
188 ime_sender,
189 xconn,
190 wm_delete_window,
191 net_wm_ping,
192 pending_redraws: pending_redraws.clone(),
193 }),
194 _marker: ::std::marker::PhantomData,
195 });
196
197 let poll = Poll::new().unwrap();
198
199 let (user_sender, user_channel) = channel();
200
201 poll.register(
202 &EventedFd(&get_xtarget(&target).xconn.x11_fd),
203 X_TOKEN,
204 Ready::readable(),
205 PollOpt::level(),
206 )
207 .unwrap();
208
209 poll.register(
210 &user_channel,
211 USER_TOKEN,
212 Ready::readable(),
213 PollOpt::level(),
214 )
215 .unwrap();
216
217 let event_processor = EventProcessor {
218 target: target.clone(),
219 dnd,
220 devices: Default::default(),
221 randr_event_offset,
222 ime_receiver,
223 xi2ext,
224 mod_keymap,
225 device_mod_state: Default::default(),
226 num_touch: 0,
227 first_touch: None,
228 active_window: None,
229 };
230
231 // Register for device hotplug events
232 // (The request buffer is flushed during `init_device`)
233 get_xtarget(&target)
234 .xconn
235 .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
236 .queue();
237
238 event_processor.init_device(ffi::XIAllDevices);
239
240 let result = EventLoop {
241 poll,
242 user_channel,
243 user_sender,
244 event_processor,
245 target,
246 };
247
248 result
249 }
250
create_proxy(&self) -> EventLoopProxy<T>251 pub fn create_proxy(&self) -> EventLoopProxy<T> {
252 EventLoopProxy {
253 user_sender: self.user_sender.clone(),
254 }
255 }
256
window_target(&self) -> &RootELW<T>257 pub(crate) fn window_target(&self) -> &RootELW<T> {
258 &self.target
259 }
260
run_return<F>(&mut self, mut callback: F) where F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),261 pub fn run_return<F>(&mut self, mut callback: F)
262 where
263 F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
264 {
265 let mut control_flow = ControlFlow::default();
266 let mut events = Events::with_capacity(8);
267 let mut cause = StartCause::Init;
268
269 loop {
270 sticky_exit_callback(
271 crate::event::Event::NewEvents(cause),
272 &self.target,
273 &mut control_flow,
274 &mut callback,
275 );
276
277 // Process all pending events
278 self.drain_events(&mut callback, &mut control_flow);
279
280 let wt = get_xtarget(&self.target);
281
282 // Empty the user event buffer
283 {
284 while let Ok(event) = self.user_channel.try_recv() {
285 sticky_exit_callback(
286 crate::event::Event::UserEvent(event),
287 &self.target,
288 &mut control_flow,
289 &mut callback,
290 );
291 }
292 }
293 // send MainEventsCleared
294 {
295 sticky_exit_callback(
296 crate::event::Event::MainEventsCleared,
297 &self.target,
298 &mut control_flow,
299 &mut callback,
300 );
301 }
302 // Empty the redraw requests
303 {
304 // Release the lock to prevent deadlock
305 let windows: Vec<_> = wt.pending_redraws.lock().unwrap().drain().collect();
306
307 for wid in windows {
308 sticky_exit_callback(
309 Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))),
310 &self.target,
311 &mut control_flow,
312 &mut callback,
313 );
314 }
315 }
316 // send RedrawEventsCleared
317 {
318 sticky_exit_callback(
319 crate::event::Event::RedrawEventsCleared,
320 &self.target,
321 &mut control_flow,
322 &mut callback,
323 );
324 }
325
326 let start = Instant::now();
327 let (deadline, timeout);
328
329 match control_flow {
330 ControlFlow::Exit => break,
331 ControlFlow::Poll => {
332 cause = StartCause::Poll;
333 deadline = None;
334 timeout = Some(Duration::from_millis(0));
335 }
336 ControlFlow::Wait => {
337 cause = StartCause::WaitCancelled {
338 start,
339 requested_resume: None,
340 };
341 deadline = None;
342 timeout = None;
343 }
344 ControlFlow::WaitUntil(wait_deadline) => {
345 cause = StartCause::ResumeTimeReached {
346 start,
347 requested_resume: wait_deadline,
348 };
349 timeout = if wait_deadline > start {
350 Some(wait_deadline - start)
351 } else {
352 Some(Duration::from_millis(0))
353 };
354 deadline = Some(wait_deadline);
355 }
356 }
357
358 // If the XConnection already contains buffered events, we don't
359 // need to wait for data on the socket.
360 if !self.event_processor.poll() {
361 self.poll.poll(&mut events, timeout).unwrap();
362 events.clear();
363 }
364
365 let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
366
367 if wait_cancelled {
368 cause = StartCause::WaitCancelled {
369 start,
370 requested_resume: deadline,
371 };
372 }
373 }
374
375 callback(
376 crate::event::Event::LoopDestroyed,
377 &self.target,
378 &mut control_flow,
379 );
380 }
381
run<F>(mut self, callback: F) -> ! where F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),382 pub fn run<F>(mut self, callback: F) -> !
383 where
384 F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
385 {
386 self.run_return(callback);
387 ::std::process::exit(0);
388 }
389
drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow) where F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),390 fn drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow)
391 where
392 F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
393 {
394 let target = &self.target;
395 let mut xev = MaybeUninit::uninit();
396
397 let wt = get_xtarget(&self.target);
398
399 while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
400 let mut xev = unsafe { xev.assume_init() };
401 self.event_processor.process_event(&mut xev, |event| {
402 sticky_exit_callback(
403 event,
404 target,
405 control_flow,
406 &mut |event, window_target, control_flow| {
407 if let Event::RedrawRequested(crate::window::WindowId(
408 super::WindowId::X(wid),
409 )) = event
410 {
411 wt.pending_redraws.lock().unwrap().insert(wid);
412 } else {
413 callback(event, window_target, control_flow);
414 }
415 },
416 );
417 });
418 }
419 }
420 }
421
get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T>422 pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
423 match target.p {
424 super::EventLoopWindowTarget::X(ref target) => target,
425 #[cfg(feature = "wayland")]
426 _ => unreachable!(),
427 }
428 }
429
430 impl<T> EventLoopWindowTarget<T> {
431 /// Returns the `XConnection` of this events loop.
432 #[inline]
x_connection(&self) -> &Arc<XConnection>433 pub fn x_connection(&self) -> &Arc<XConnection> {
434 &self.xconn
435 }
436 }
437
438 impl<T: 'static> EventLoopProxy<T> {
send_event(&self, event: T) -> Result<(), EventLoopClosed<T>>439 pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
440 self.user_sender.send(event).map_err(|e| {
441 EventLoopClosed(if let SendError::Disconnected(x) = e {
442 x
443 } else {
444 unreachable!()
445 })
446 })
447 }
448 }
449
450 struct DeviceInfo<'a> {
451 xconn: &'a XConnection,
452 info: *const ffi::XIDeviceInfo,
453 count: usize,
454 }
455
456 impl<'a> DeviceInfo<'a> {
get(xconn: &'a XConnection, device: c_int) -> Option<Self>457 fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
458 unsafe {
459 let mut count = 0;
460 let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
461 xconn.check_errors().ok()?;
462
463 if info.is_null() || count == 0 {
464 None
465 } else {
466 Some(DeviceInfo {
467 xconn,
468 info,
469 count: count as usize,
470 })
471 }
472 }
473 }
474 }
475
476 impl<'a> Drop for DeviceInfo<'a> {
drop(&mut self)477 fn drop(&mut self) {
478 assert!(!self.info.is_null());
479 unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
480 }
481 }
482
483 impl<'a> Deref for DeviceInfo<'a> {
484 type Target = [ffi::XIDeviceInfo];
deref(&self) -> &Self::Target485 fn deref(&self) -> &Self::Target {
486 unsafe { slice::from_raw_parts(self.info, self.count) }
487 }
488 }
489
490 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
491 pub struct WindowId(ffi::Window);
492
493 impl WindowId {
dummy() -> Self494 pub unsafe fn dummy() -> Self {
495 WindowId(0)
496 }
497 }
498
499 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
500 pub struct DeviceId(c_int);
501
502 impl DeviceId {
dummy() -> Self503 pub unsafe fn dummy() -> Self {
504 DeviceId(0)
505 }
506 }
507
508 pub struct Window(Arc<UnownedWindow>);
509
510 impl Deref for Window {
511 type Target = UnownedWindow;
512 #[inline]
deref(&self) -> &UnownedWindow513 fn deref(&self) -> &UnownedWindow {
514 &*self.0
515 }
516 }
517
518 impl Window {
new<T>( event_loop: &EventLoopWindowTarget<T>, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result<Self, RootOsError>519 pub fn new<T>(
520 event_loop: &EventLoopWindowTarget<T>,
521 attribs: WindowAttributes,
522 pl_attribs: PlatformSpecificWindowBuilderAttributes,
523 ) -> Result<Self, RootOsError> {
524 let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
525 event_loop
526 .windows
527 .borrow_mut()
528 .insert(window.id(), Arc::downgrade(&window));
529 Ok(Window(window))
530 }
531 }
532
533 impl Drop for Window {
drop(&mut self)534 fn drop(&mut self) {
535 let window = self.deref();
536 let xconn = &window.xconn;
537 unsafe {
538 (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
539 // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
540 let _ = xconn.check_errors();
541 }
542 }
543 }
544
545 /// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
546 /// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
547 struct GenericEventCookie<'a> {
548 xconn: &'a XConnection,
549 cookie: ffi::XGenericEventCookie,
550 }
551
552 impl<'a> GenericEventCookie<'a> {
from_event<'b>( xconn: &'b XConnection, event: ffi::XEvent, ) -> Option<GenericEventCookie<'b>>553 fn from_event<'b>(
554 xconn: &'b XConnection,
555 event: ffi::XEvent,
556 ) -> Option<GenericEventCookie<'b>> {
557 unsafe {
558 let mut cookie: ffi::XGenericEventCookie = From::from(event);
559 if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
560 Some(GenericEventCookie { xconn, cookie })
561 } else {
562 None
563 }
564 }
565 }
566 }
567
568 impl<'a> Drop for GenericEventCookie<'a> {
drop(&mut self)569 fn drop(&mut self) {
570 unsafe {
571 (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
572 }
573 }
574 }
575
576 #[derive(Debug, Default, Copy, Clone)]
577 struct XExtension {
578 opcode: c_int,
579 first_event_id: c_int,
580 first_error_id: c_int,
581 }
582
mkwid(w: ffi::Window) -> crate::window::WindowId583 fn mkwid(w: ffi::Window) -> crate::window::WindowId {
584 crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w)))
585 }
mkdid(w: c_int) -> crate::event::DeviceId586 fn mkdid(w: c_int) -> crate::event::DeviceId {
587 crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
588 }
589
590 #[derive(Debug)]
591 struct Device {
592 name: String,
593 scroll_axes: Vec<(i32, ScrollAxis)>,
594 // For master devices, this is the paired device (pointer <-> keyboard).
595 // For slave devices, this is the master.
596 attachment: c_int,
597 }
598
599 #[derive(Debug, Copy, Clone)]
600 struct ScrollAxis {
601 increment: f64,
602 orientation: ScrollOrientation,
603 position: f64,
604 }
605
606 #[derive(Debug, Copy, Clone)]
607 enum ScrollOrientation {
608 Vertical,
609 Horizontal,
610 }
611
612 impl Device {
new<T: 'static>(el: &EventProcessor<T>, info: &ffi::XIDeviceInfo) -> Self613 fn new<T: 'static>(el: &EventProcessor<T>, info: &ffi::XIDeviceInfo) -> Self {
614 let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
615 let mut scroll_axes = Vec::new();
616
617 let wt = get_xtarget(&el.target);
618
619 if Device::physical_device(info) {
620 // Register for global raw events
621 let mask = ffi::XI_RawMotionMask
622 | ffi::XI_RawButtonPressMask
623 | ffi::XI_RawButtonReleaseMask
624 | ffi::XI_RawKeyPressMask
625 | ffi::XI_RawKeyReleaseMask;
626 // The request buffer is flushed when we poll for events
627 wt.xconn
628 .select_xinput_events(wt.root, info.deviceid, mask)
629 .queue();
630
631 // Identify scroll axes
632 for class_ptr in Device::classes(info) {
633 let class = unsafe { &**class_ptr };
634 match class._type {
635 ffi::XIScrollClass => {
636 let info = unsafe {
637 mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
638 };
639 scroll_axes.push((
640 info.number,
641 ScrollAxis {
642 increment: info.increment,
643 orientation: match info.scroll_type {
644 ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
645 ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
646 _ => unreachable!(),
647 },
648 position: 0.0,
649 },
650 ));
651 }
652 _ => {}
653 }
654 }
655 }
656
657 let mut device = Device {
658 name: name.into_owned(),
659 scroll_axes,
660 attachment: info.attachment,
661 };
662 device.reset_scroll_position(info);
663 device
664 }
665
reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo)666 fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
667 if Device::physical_device(info) {
668 for class_ptr in Device::classes(info) {
669 let class = unsafe { &**class_ptr };
670 match class._type {
671 ffi::XIValuatorClass => {
672 let info = unsafe {
673 mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
674 };
675 if let Some(&mut (_, ref mut axis)) = self
676 .scroll_axes
677 .iter_mut()
678 .find(|&&mut (axis, _)| axis == info.number)
679 {
680 axis.position = info.value;
681 }
682 }
683 _ => {}
684 }
685 }
686 }
687 }
688
689 #[inline]
physical_device(info: &ffi::XIDeviceInfo) -> bool690 fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
691 info._use == ffi::XISlaveKeyboard
692 || info._use == ffi::XISlavePointer
693 || info._use == ffi::XIFloatingSlave
694 }
695
696 #[inline]
classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo]697 fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
698 unsafe {
699 slice::from_raw_parts(
700 info.classes as *const *const ffi::XIAnyClassInfo,
701 info.num_classes as usize,
702 )
703 }
704 }
705 }
706