1 #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
2
3 pub mod ffi;
4 mod events;
5 mod monitor;
6 mod window;
7 mod xdisplay;
8 mod dnd;
9 mod ime;
10 pub mod util;
11
12 pub use self::monitor::MonitorId;
13 pub use self::window::UnownedWindow;
14 pub use self::xdisplay::{XConnection, XNotSupported, XError};
15
16 use std::{mem, ptr, slice};
17 use std::cell::RefCell;
18 use std::collections::HashMap;
19 use std::ffi::CStr;
20 use std::ops::Deref;
21 use std::os::raw::*;
22 use libc::{select, fd_set, FD_SET, FD_ZERO, FD_ISSET, EINTR, EINVAL, ENOMEM, EBADF};
23 #[cfg(target_os = "linux")]
24 use libc::__errno_location;
25 #[cfg(target_os = "freebsd")]
26 use libc::__error as __errno_location;
27 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
28 use libc::__errno as __errno_location;
29 use std::sync::{Arc, mpsc, Weak};
30 use std::sync::atomic::{self, AtomicBool};
31
32 use libc::{self, setlocale, LC_CTYPE};
33
34 use {
35 ControlFlow,
36 CreationError,
37 DeviceEvent,
38 Event,
39 EventsLoopClosed,
40 KeyboardInput,
41 LogicalPosition,
42 LogicalSize,
43 WindowAttributes,
44 WindowEvent,
45 };
46 use events::ModifiersState;
47 use platform::PlatformSpecificWindowBuilderAttributes;
48 use self::dnd::{Dnd, DndState};
49 use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
50
51 pub struct EventsLoop {
52 xconn: Arc<XConnection>,
53 wm_delete_window: ffi::Atom,
54 dnd: Dnd,
55 ime_receiver: ImeReceiver,
56 ime_sender: ImeSender,
57 ime: RefCell<Ime>,
58 randr_event_offset: c_int,
59 windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
60 devices: RefCell<HashMap<DeviceId, Device>>,
61 xi2ext: XExtension,
62 pending_wakeup: Arc<AtomicBool>,
63 root: ffi::Window,
64 // A dummy, `InputOnly` window that we can use to receive wakeup events and interrupt blocking
65 // `XNextEvent` calls.
66 wakeup_dummy_window: ffi::Window,
67 }
68
69 #[derive(Clone)]
70 pub struct EventsLoopProxy {
71 pending_wakeup: Weak<AtomicBool>,
72 xconn: Weak<XConnection>,
73 wakeup_dummy_window: ffi::Window,
74 }
75
76 impl EventsLoop {
new(xconn: Arc<XConnection>) -> EventsLoop77 pub fn new(xconn: Arc<XConnection>) -> EventsLoop {
78 let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
79
80 let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
81
82 let dnd = Dnd::new(Arc::clone(&xconn))
83 .expect("Failed to call XInternAtoms when initializing drag and drop");
84
85 let (ime_sender, ime_receiver) = mpsc::channel();
86 // Input methods will open successfully without setting the locale, but it won't be
87 // possible to actually commit pre-edit sequences.
88 unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); }
89 let ime = RefCell::new({
90 let result = Ime::new(Arc::clone(&xconn));
91 if let Err(ImeCreationError::OpenFailure(ref state)) = result {
92 panic!(format!("Failed to open input method: {:#?}", state));
93 }
94 result.expect("Failed to set input method destruction callback")
95 });
96
97 let randr_event_offset = xconn.select_xrandr_input(root)
98 .expect("Failed to query XRandR extension");
99
100 let xi2ext = unsafe {
101 let mut result = XExtension {
102 opcode: mem::uninitialized(),
103 first_event_id: mem::uninitialized(),
104 first_error_id: mem::uninitialized(),
105 };
106 let res = (xconn.xlib.XQueryExtension)(
107 xconn.display,
108 b"XInputExtension\0".as_ptr() as *const c_char,
109 &mut result.opcode as *mut c_int,
110 &mut result.first_event_id as *mut c_int,
111 &mut result.first_error_id as *mut c_int);
112 if res == ffi::False {
113 panic!("X server missing XInput extension");
114 }
115 result
116 };
117
118 unsafe {
119 let mut xinput_major_ver = ffi::XI_2_Major;
120 let mut xinput_minor_ver = ffi::XI_2_Minor;
121 if (xconn.xinput2.XIQueryVersion)(
122 xconn.display,
123 &mut xinput_major_ver,
124 &mut xinput_minor_ver,
125 ) != ffi::Success as libc::c_int {
126 panic!(
127 "X server has XInput extension {}.{} but does not support XInput2",
128 xinput_major_ver,
129 xinput_minor_ver,
130 );
131 }
132 }
133
134 xconn.update_cached_wm_info(root);
135
136 let wakeup_dummy_window = unsafe {
137 let (x, y, w, h) = (10, 10, 10, 10);
138 let (border_w, border_px, background_px) = (0, 0, 0);
139 (xconn.xlib.XCreateSimpleWindow)(
140 xconn.display,
141 root,
142 x,
143 y,
144 w,
145 h,
146 border_w,
147 border_px,
148 background_px,
149 )
150 };
151
152 let result = EventsLoop {
153 xconn,
154 wm_delete_window,
155 dnd,
156 ime_receiver,
157 ime_sender,
158 ime,
159 randr_event_offset,
160 windows: Default::default(),
161 devices: Default::default(),
162 xi2ext,
163 pending_wakeup: Default::default(),
164 root,
165 wakeup_dummy_window,
166 };
167
168 // Register for device hotplug events
169 // (The request buffer is flushed during `init_device`)
170 result.xconn.select_xinput_events(
171 root,
172 ffi::XIAllDevices,
173 ffi::XI_HierarchyChangedMask,
174 ).queue();
175
176 result.init_device(ffi::XIAllDevices);
177
178 result
179 }
180
181 /// Returns the `XConnection` of this events loop.
182 #[inline]
x_connection(&self) -> &Arc<XConnection>183 pub fn x_connection(&self) -> &Arc<XConnection> {
184 &self.xconn
185 }
186
create_proxy(&self) -> EventsLoopProxy187 pub fn create_proxy(&self) -> EventsLoopProxy {
188 EventsLoopProxy {
189 pending_wakeup: Arc::downgrade(&self.pending_wakeup),
190 xconn: Arc::downgrade(&self.xconn),
191 wakeup_dummy_window: self.wakeup_dummy_window,
192 }
193 }
194
poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool195 unsafe fn poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool {
196 // This function is used to poll and remove a single event
197 // from the Xlib event queue in a non-blocking, atomic way.
198 // XCheckIfEvent is non-blocking and removes events from queue.
199 // XNextEvent can't be used because it blocks while holding the
200 // global Xlib mutex.
201 // XPeekEvent does not remove events from the queue.
202 unsafe extern "C" fn predicate(
203 _display: *mut ffi::Display,
204 _event: *mut ffi::XEvent,
205 _arg : *mut c_char) -> c_int {
206 // This predicate always returns "true" (1) to accept all events
207 1
208 }
209
210 let result = (self.xconn.xlib.XCheckIfEvent)(
211 self.xconn.display,
212 event_ptr,
213 Some(predicate),
214 std::ptr::null_mut());
215
216 result != 0
217 }
218
wait_for_input(&mut self)219 unsafe fn wait_for_input(&mut self) {
220 // XNextEvent can not be used in multi-threaded applications
221 // because it is blocking for input while holding the global
222 // Xlib mutex.
223 // To work around this issue, first flush the X11 display, then
224 // use select(2) to wait for input to arrive
225 loop {
226 // First use XFlush to flush any buffered x11 requests
227 (self.xconn.xlib.XFlush)(self.xconn.display);
228
229 // Then use select(2) to wait for input data
230 let mut fds : fd_set = mem::uninitialized();
231 FD_ZERO(&mut fds);
232 FD_SET(self.xconn.x11_fd, &mut fds);
233 let err = select(
234 self.xconn.x11_fd + 1,
235 &mut fds, // read fds
236 std::ptr::null_mut(), // write fds
237 std::ptr::null_mut(), // except fds (could be used to detect errors)
238 std::ptr::null_mut()); // timeout
239
240 if err < 0 {
241 let errno_ptr = __errno_location();
242 let errno = *errno_ptr;
243
244 if errno == EINTR {
245 // try again if errno is EINTR
246 continue;
247 }
248
249 assert!(errno == EBADF || errno == EINVAL || errno == ENOMEM);
250 panic!("select(2) returned fatal error condition");
251 }
252
253 if FD_ISSET(self.xconn.x11_fd, &mut fds) {
254 break;
255 }
256 }
257 }
258
poll_events<F>(&mut self, mut callback: F) where F: FnMut(Event)259 pub fn poll_events<F>(&mut self, mut callback: F)
260 where F: FnMut(Event)
261 {
262 let mut xev = unsafe { mem::uninitialized() };
263 loop {
264 // Get next event
265 unsafe {
266 if !self.poll_one_event(&mut xev) {
267 break;
268 }
269 }
270 self.process_event(&mut xev, &mut callback);
271 }
272 }
273
run_forever<F>(&mut self, mut callback: F) where F: FnMut(Event) -> ControlFlow274 pub fn run_forever<F>(&mut self, mut callback: F)
275 where F: FnMut(Event) -> ControlFlow
276 {
277 let mut xev = unsafe { mem::uninitialized() };
278
279 loop {
280 unsafe {
281 while !self.poll_one_event(&mut xev) {
282 // block until input is available
283 self.wait_for_input();
284 }
285 };
286
287 let mut control_flow = ControlFlow::Continue;
288
289 // Track whether or not `Break` was returned when processing the event.
290 {
291 let mut cb = |event| {
292 if let ControlFlow::Break = callback(event) {
293 control_flow = ControlFlow::Break;
294 }
295 };
296
297 self.process_event(&mut xev, &mut cb);
298 }
299
300 if let ControlFlow::Break = control_flow {
301 break;
302 }
303 }
304 }
305
process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event)306 fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
307 where F: FnMut(Event)
308 {
309 // XFilterEvent tells us when an event has been discarded by the input method.
310 // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
311 // along with an extra copy of the KeyRelease events. This also prevents backspace and
312 // arrow keys from being detected twice.
313 if ffi::True == unsafe { (self.xconn.xlib.XFilterEvent)(
314 xev,
315 { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }
316 ) } {
317 return;
318 }
319
320 let event_type = xev.get_type();
321 match event_type {
322 ffi::MappingNotify => {
323 unsafe { (self.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
324 self.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping");
325 }
326
327 ffi::ClientMessage => {
328 let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
329
330 let window = client_msg.window;
331 let window_id = mkwid(window);
332
333 if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
334 callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested });
335 } else if client_msg.message_type == self.dnd.atoms.enter {
336 let source_window = client_msg.data.get_long(0) as c_ulong;
337 let flags = client_msg.data.get_long(1);
338 let version = flags >> 24;
339 self.dnd.version = Some(version);
340 let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
341 if !has_more_types {
342 let type_list = vec![
343 client_msg.data.get_long(2) as c_ulong,
344 client_msg.data.get_long(3) as c_ulong,
345 client_msg.data.get_long(4) as c_ulong
346 ];
347 self.dnd.type_list = Some(type_list);
348 } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } {
349 self.dnd.type_list = Some(more_types);
350 }
351 } else if client_msg.message_type == self.dnd.atoms.position {
352 // This event occurs every time the mouse moves while a file's being dragged
353 // over our window. We emit HoveredFile in response; while the macOS backend
354 // does that upon a drag entering, XDND doesn't have access to the actual drop
355 // data until this event. For parity with other platforms, we only emit
356 // `HoveredFile` the first time, though if winit's API is later extended to
357 // supply position updates with `HoveredFile` or another event, implementing
358 // that here would be trivial.
359
360 let source_window = client_msg.data.get_long(0) as c_ulong;
361
362 // Equivalent to `(x << shift) | y`
363 // where `shift = mem::size_of::<c_short>() * 8`
364 // Note that coordinates are in "desktop space", not "window space"
365 // (in X11 parlance, they're root window coordinates)
366 //let packed_coordinates = client_msg.data.get_long(2);
367 //let shift = mem::size_of::<libc::c_short>() * 8;
368 //let x = packed_coordinates >> shift;
369 //let y = packed_coordinates & !(x << shift);
370
371 // By our own state flow, `version` should never be `None` at this point.
372 let version = self.dnd.version.unwrap_or(5);
373
374 // Action is specified in versions 2 and up, though we don't need it anyway.
375 //let action = client_msg.data.get_long(4);
376
377 let accepted = if let Some(ref type_list) = self.dnd.type_list {
378 type_list.contains(&self.dnd.atoms.uri_list)
379 } else {
380 false
381 };
382
383 if accepted {
384 self.dnd.source_window = Some(source_window);
385 unsafe {
386 if self.dnd.result.is_none() {
387 let time = if version >= 1 {
388 client_msg.data.get_long(3) as c_ulong
389 } else {
390 // In version 0, time isn't specified
391 ffi::CurrentTime
392 };
393 // This results in the `SelectionNotify` event below
394 self.dnd.convert_selection(window, time);
395 }
396 self.dnd.send_status(window, source_window, DndState::Accepted)
397 .expect("Failed to send `XdndStatus` message.");
398 }
399 } else {
400 unsafe {
401 self.dnd.send_status(window, source_window, DndState::Rejected)
402 .expect("Failed to send `XdndStatus` message.");
403 }
404 self.dnd.reset();
405 }
406 } else if client_msg.message_type == self.dnd.atoms.drop {
407 let (source_window, state) = if let Some(source_window) = self.dnd.source_window {
408 if let Some(Ok(ref path_list)) = self.dnd.result {
409 for path in path_list {
410 callback(Event::WindowEvent {
411 window_id,
412 event: WindowEvent::DroppedFile(path.clone()),
413 });
414 }
415 }
416 (source_window, DndState::Accepted)
417 } else {
418 // `source_window` won't be part of our DND state if we already rejected the drop in our
419 // `XdndPosition` handler.
420 let source_window = client_msg.data.get_long(0) as c_ulong;
421 (source_window, DndState::Rejected)
422 };
423 unsafe {
424 self.dnd.send_finished(window, source_window, state)
425 .expect("Failed to send `XdndFinished` message.");
426 }
427 self.dnd.reset();
428 } else if client_msg.message_type == self.dnd.atoms.leave {
429 self.dnd.reset();
430 callback(Event::WindowEvent {
431 window_id,
432 event: WindowEvent::HoveredFileCancelled,
433 });
434 } else if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
435 self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
436 callback(Event::Awakened);
437 }
438 }
439
440 ffi::SelectionNotify => {
441 let xsel: &ffi::XSelectionEvent = xev.as_ref();
442
443 let window = xsel.requestor;
444 let window_id = mkwid(window);
445
446 if xsel.property == self.dnd.atoms.selection {
447 let mut result = None;
448
449 // This is where we receive data from drag and drop
450 if let Ok(mut data) = unsafe { self.dnd.read_data(window) } {
451 let parse_result = self.dnd.parse_data(&mut data);
452 if let Ok(ref path_list) = parse_result {
453 for path in path_list {
454 callback(Event::WindowEvent {
455 window_id,
456 event: WindowEvent::HoveredFile(path.clone()),
457 });
458 }
459 }
460 result = Some(parse_result);
461 }
462
463 self.dnd.result = result;
464 }
465 }
466
467 ffi::ConfigureNotify => {
468 #[derive(Debug, Default)]
469 struct Events {
470 resized: Option<WindowEvent>,
471 moved: Option<WindowEvent>,
472 dpi_changed: Option<WindowEvent>,
473 }
474
475 let xev: &ffi::XConfigureEvent = xev.as_ref();
476 let xwindow = xev.window;
477 let events = self.with_window(xwindow, |window| {
478 // So apparently...
479 // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
480 // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
481 // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
482 // We don't want to send `Moved` when this is false, since then every `Resized`
483 // (whether the window moved or not) is accompanied by an extraneous `Moved` event
484 // that has a position relative to the parent window.
485 let is_synthetic = xev.send_event == ffi::True;
486
487 // These are both in physical space.
488 let new_inner_size = (xev.width as u32, xev.height as u32);
489 let new_inner_position = (xev.x as i32, xev.y as i32);
490
491 let mut monitor = window.get_current_monitor(); // This must be done *before* locking!
492 let mut shared_state_lock = window.shared_state.lock();
493
494 let (mut resized, moved) = {
495 let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
496 let moved = if is_synthetic {
497 util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
498 } else {
499 // Detect when frame extents change.
500 // Since this isn't synthetic, as per the notes above, this position is relative to the
501 // parent window.
502 let rel_parent = new_inner_position;
503 if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) {
504 // This ensures we process the next `Moved`.
505 shared_state_lock.inner_position = None;
506 // Extra insurance against stale frame extents.
507 shared_state_lock.frame_extents = None;
508 }
509 false
510 };
511 (resized, moved)
512 };
513
514 let mut events = Events::default();
515
516 let new_outer_position = if moved || shared_state_lock.position.is_none() {
517 // We need to convert client area position to window position.
518 let frame_extents = shared_state_lock.frame_extents
519 .as_ref()
520 .cloned()
521 .unwrap_or_else(|| {
522 let frame_extents = self.xconn.get_frame_extents_heuristic(xwindow, self.root);
523 shared_state_lock.frame_extents = Some(frame_extents.clone());
524 frame_extents
525 });
526 let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
527 shared_state_lock.position = Some(outer);
528 if moved {
529 let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
530 events.moved = Some(WindowEvent::Moved(logical_position));
531 }
532 outer
533 } else {
534 shared_state_lock.position.unwrap()
535 };
536
537 if is_synthetic {
538 // If we don't use the existing adjusted value when available, then the user can screw up the
539 // resizing by dragging across monitors *without* dropping the window.
540 let (width, height) = shared_state_lock.dpi_adjusted
541 .unwrap_or_else(|| (xev.width as f64, xev.height as f64));
542 let last_hidpi_factor = shared_state_lock.guessed_dpi
543 .take()
544 .unwrap_or_else(|| {
545 shared_state_lock.last_monitor
546 .as_ref()
547 .map(|last_monitor| last_monitor.hidpi_factor)
548 .unwrap_or(1.0)
549 });
550 let new_hidpi_factor = {
551 let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
552 monitor = self.xconn.get_monitor_for_window(Some(window_rect));
553 let new_hidpi_factor = monitor.hidpi_factor;
554 shared_state_lock.last_monitor = Some(monitor.clone());
555 new_hidpi_factor
556 };
557 if last_hidpi_factor != new_hidpi_factor {
558 events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
559 let (new_width, new_height, flusher) = window.adjust_for_dpi(
560 last_hidpi_factor,
561 new_hidpi_factor,
562 width,
563 height,
564 );
565 flusher.queue();
566 shared_state_lock.dpi_adjusted = Some((new_width, new_height));
567 // if the DPI factor changed, force a resize event to ensure the logical
568 // size is computed with the right DPI factor
569 resized = true;
570 }
571 }
572
573 // This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
574 // doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling
575 // WMs constrain the window size, making the resize fail. This would cause an endless stream of
576 // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
577 if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
578 let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
579 if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
580 // When this finally happens, the event will not be synthetic.
581 shared_state_lock.dpi_adjusted = None;
582 } else {
583 unsafe {
584 (self.xconn.xlib.XResizeWindow)(
585 self.xconn.display,
586 xwindow,
587 rounded_size.0 as c_uint,
588 rounded_size.1 as c_uint,
589 );
590 }
591 }
592 }
593
594 if resized {
595 let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
596 events.resized = Some(WindowEvent::Resized(logical_size));
597 }
598
599 events
600 });
601
602 if let Some(events) = events {
603 let window_id = mkwid(xwindow);
604 if let Some(event) = events.dpi_changed {
605 callback(Event::WindowEvent { window_id, event });
606 }
607 if let Some(event) = events.resized {
608 callback(Event::WindowEvent { window_id, event });
609 }
610 if let Some(event) = events.moved {
611 callback(Event::WindowEvent { window_id, event });
612 }
613 }
614 }
615
616 ffi::ReparentNotify => {
617 let xev: &ffi::XReparentEvent = xev.as_ref();
618
619 // This is generally a reliable way to detect when the window manager's been
620 // replaced, though this event is only fired by reparenting window managers
621 // (which is almost all of them). Failing to correctly update WM info doesn't
622 // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
623 // effect is that we waste some time trying to query unsupported properties.
624 self.xconn.update_cached_wm_info(self.root);
625
626 self.with_window(xev.window, |window| {
627 window.invalidate_cached_frame_extents();
628 });
629 }
630
631 ffi::DestroyNotify => {
632 let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
633
634 let window = xev.window;
635 let window_id = mkwid(window);
636
637 // In the event that the window's been destroyed without being dropped first, we
638 // cleanup again here.
639 self.windows.borrow_mut().remove(&WindowId(window));
640
641 // Since all XIM stuff needs to happen from the same thread, we destroy the input
642 // context here instead of when dropping the window.
643 self.ime
644 .borrow_mut()
645 .remove_context(window)
646 .expect("Failed to destroy input context");
647
648 callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed });
649 }
650
651 ffi::Expose => {
652 let xev: &ffi::XExposeEvent = xev.as_ref();
653
654 let window = xev.window;
655 let window_id = mkwid(window);
656
657 callback(Event::WindowEvent { window_id, event: WindowEvent::Refresh });
658 }
659
660 ffi::KeyPress | ffi::KeyRelease => {
661 use events::ElementState::{Pressed, Released};
662
663 // Note that in compose/pre-edit sequences, this will always be Released.
664 let state = if xev.get_type() == ffi::KeyPress {
665 Pressed
666 } else {
667 Released
668 };
669
670 let xkev: &mut ffi::XKeyEvent = xev.as_mut();
671
672 let window = xkev.window;
673 let window_id = mkwid(window);
674
675 // Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
676 // value, though this should only be an issue under multiseat configurations.
677 let device = util::VIRTUAL_CORE_KEYBOARD;
678 let device_id = mkdid(device);
679
680 // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
681 // a keycode of 0.
682 if xkev.keycode != 0 {
683 let modifiers = ModifiersState {
684 alt: xkev.state & ffi::Mod1Mask != 0,
685 shift: xkev.state & ffi::ShiftMask != 0,
686 ctrl: xkev.state & ffi::ControlMask != 0,
687 logo: xkev.state & ffi::Mod4Mask != 0,
688 };
689
690 let keysym = unsafe {
691 let mut keysym = 0;
692 (self.xconn.xlib.XLookupString)(
693 xkev,
694 ptr::null_mut(),
695 0,
696 &mut keysym,
697 ptr::null_mut(),
698 );
699 self.xconn.check_errors().expect("Failed to lookup keysym");
700 keysym
701 };
702 let virtual_keycode = events::keysym_to_element(keysym as c_uint);
703
704 callback(Event::WindowEvent {
705 window_id,
706 event: WindowEvent::KeyboardInput {
707 device_id,
708 input: KeyboardInput {
709 state,
710 scancode: xkev.keycode - 8,
711 virtual_keycode,
712 modifiers,
713 },
714 }
715 });
716 }
717
718 if state == Pressed {
719 let written = if let Some(ic) = self.ime.borrow().get_context(window) {
720 self.xconn.lookup_utf8(ic, xkev)
721 } else {
722 return;
723 };
724
725 for chr in written.chars() {
726 let event = Event::WindowEvent {
727 window_id,
728 event: WindowEvent::ReceivedCharacter(chr),
729 };
730 callback(event);
731 }
732 }
733 }
734
735 ffi::GenericEvent => {
736 let guard = if let Some(e) = GenericEventCookie::from_event(&self.xconn, *xev) { e } else { return };
737 let xev = &guard.cookie;
738 if self.xi2ext.opcode != xev.extension {
739 return;
740 }
741
742 use events::WindowEvent::{Focused, CursorEntered, MouseInput, CursorLeft, CursorMoved, MouseWheel, AxisMotion};
743 use events::ElementState::{Pressed, Released};
744 use events::MouseButton::{Left, Right, Middle, Other};
745 use events::MouseScrollDelta::LineDelta;
746 use events::{Touch, TouchPhase};
747
748 match xev.evtype {
749 ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
750 let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
751 let window_id = mkwid(xev.event);
752 let device_id = mkdid(xev.deviceid);
753 if (xev.flags & ffi::XIPointerEmulated) != 0 {
754 // Deliver multi-touch events instead of emulated mouse events.
755 let return_now = self
756 .with_window(xev.event, |window| window.multitouch)
757 .unwrap_or(true);
758 if return_now { return; }
759 }
760
761 let modifiers = ModifiersState::from(xev.mods);
762
763 let state = if xev.evtype == ffi::XI_ButtonPress {
764 Pressed
765 } else {
766 Released
767 };
768 match xev.detail as u32 {
769 ffi::Button1 => callback(Event::WindowEvent {
770 window_id,
771 event: MouseInput {
772 device_id,
773 state,
774 button: Left,
775 modifiers,
776 },
777 }),
778 ffi::Button2 => callback(Event::WindowEvent {
779 window_id,
780 event: MouseInput {
781 device_id,
782 state,
783 button: Middle,
784 modifiers,
785 },
786 }),
787 ffi::Button3 => callback(Event::WindowEvent {
788 window_id,
789 event: MouseInput {
790 device_id,
791 state,
792 button: Right,
793 modifiers,
794 },
795 }),
796
797 // Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
798 // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
799 // turn) as axis motion, so we don't otherwise special-case these button presses.
800 4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 {
801 callback(Event::WindowEvent {
802 window_id,
803 event: MouseWheel {
804 device_id,
805 delta: match xev.detail {
806 4 => LineDelta(0.0, 1.0),
807 5 => LineDelta(0.0, -1.0),
808 6 => LineDelta(-1.0, 0.0),
809 7 => LineDelta(1.0, 0.0),
810 _ => unreachable!(),
811 },
812 phase: TouchPhase::Moved,
813 modifiers,
814 },
815 });
816 },
817
818 x => callback(Event::WindowEvent {
819 window_id,
820 event: MouseInput {
821 device_id,
822 state,
823 button: Other(x as u8),
824 modifiers,
825 },
826 }),
827 }
828 }
829 ffi::XI_Motion => {
830 let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
831 let device_id = mkdid(xev.deviceid);
832 let window_id = mkwid(xev.event);
833 let new_cursor_pos = (xev.event_x, xev.event_y);
834
835 let modifiers = ModifiersState::from(xev.mods);
836
837 let cursor_moved = self.with_window(xev.event, |window| {
838 let mut shared_state_lock = window.shared_state.lock();
839 util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
840 });
841 if cursor_moved == Some(true) {
842 let dpi_factor = self.with_window(xev.event, |window| {
843 window.get_hidpi_factor()
844 });
845 if let Some(dpi_factor) = dpi_factor {
846 let position = LogicalPosition::from_physical(
847 (xev.event_x as f64, xev.event_y as f64),
848 dpi_factor,
849 );
850 callback(Event::WindowEvent {
851 window_id,
852 event: CursorMoved {
853 device_id,
854 position,
855 modifiers,
856 },
857 });
858 } else {
859 return;
860 }
861 } else if cursor_moved.is_none() {
862 return;
863 }
864
865 // More gymnastics, for self.devices
866 let mut events = Vec::new();
867 {
868 let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
869 let mut devices = self.devices.borrow_mut();
870 let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
871 Some(device) => device,
872 None => return,
873 };
874
875 let mut value = xev.valuators.values;
876 for i in 0..xev.valuators.mask_len*8 {
877 if ffi::XIMaskIsSet(mask, i) {
878 let x = unsafe { *value };
879 if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) {
880 let delta = (x - info.position) / info.increment;
881 info.position = x;
882 events.push(Event::WindowEvent {
883 window_id,
884 event: MouseWheel {
885 device_id,
886 delta: match info.orientation {
887 ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0),
888 // X11 vertical scroll coordinates are opposite to winit's
889 ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32),
890 },
891 phase: TouchPhase::Moved,
892 modifiers,
893 },
894 });
895 } else {
896 events.push(Event::WindowEvent {
897 window_id,
898 event: AxisMotion {
899 device_id,
900 axis: i as u32,
901 value: unsafe { *value },
902 },
903 });
904 }
905 value = unsafe { value.offset(1) };
906 }
907 }
908 }
909 for event in events {
910 callback(event);
911 }
912 }
913
914 ffi::XI_Enter => {
915 let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
916
917 let window_id = mkwid(xev.event);
918 let device_id = mkdid(xev.deviceid);
919
920 if let Some(all_info) = DeviceInfo::get(&self.xconn, ffi::XIAllDevices) {
921 let mut devices = self.devices.borrow_mut();
922 for device_info in all_info.iter() {
923 if device_info.deviceid == xev.sourceid
924 // This is needed for resetting to work correctly on i3, and
925 // presumably some other WMs. On those, `XI_Enter` doesn't include
926 // the physical device ID, so both `sourceid` and `deviceid` are
927 // the virtual device.
928 || device_info.attachment == xev.sourceid {
929 let device_id = DeviceId(device_info.deviceid);
930 if let Some(device) = devices.get_mut(&device_id) {
931 device.reset_scroll_position(device_info);
932 }
933 }
934 }
935 }
936 callback(Event::WindowEvent {
937 window_id,
938 event: CursorEntered { device_id },
939 });
940
941 if let Some(dpi_factor) = self.with_window(xev.event, |window| {
942 window.get_hidpi_factor()
943 }) {
944 let position = LogicalPosition::from_physical(
945 (xev.event_x as f64, xev.event_y as f64),
946 dpi_factor,
947 );
948
949 // The mods field on this event isn't actually populated, so query the
950 // pointer device. In the future, we can likely remove this round-trip by
951 // relying on `Xkb` for modifier values.
952 //
953 // This needs to only be done after confirming the window still exists,
954 // since otherwise we risk getting a `BadWindow` error if the window was
955 // dropped with queued events.
956 let modifiers = self.xconn
957 .query_pointer(xev.event, xev.deviceid)
958 .expect("Failed to query pointer device")
959 .get_modifier_state();
960
961 callback(Event::WindowEvent {
962 window_id,
963 event: CursorMoved {
964 device_id,
965 position,
966 modifiers,
967 },
968 });
969 }
970 }
971 ffi::XI_Leave => {
972 let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
973
974 // Leave, FocusIn, and FocusOut can be received by a window that's already
975 // been destroyed, which the user presumably doesn't want to deal with.
976 let window_closed = !self.window_exists(xev.event);
977 if !window_closed {
978 callback(Event::WindowEvent {
979 window_id: mkwid(xev.event),
980 event: CursorLeft { device_id: mkdid(xev.deviceid) },
981 });
982 }
983 }
984 ffi::XI_FocusIn => {
985 let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
986
987 let dpi_factor = match self.with_window(xev.event, |window| {
988 window.get_hidpi_factor()
989 }) {
990 Some(dpi_factor) => dpi_factor,
991 None => return,
992 };
993 let window_id = mkwid(xev.event);
994
995 self.ime
996 .borrow_mut()
997 .focus(xev.event)
998 .expect("Failed to focus input context");
999
1000 callback(Event::WindowEvent { window_id, event: Focused(true) });
1001
1002 // The deviceid for this event is for a keyboard instead of a pointer,
1003 // so we have to do a little extra work.
1004 let pointer_id = self.devices
1005 .borrow()
1006 .get(&DeviceId(xev.deviceid))
1007 .map(|device| device.attachment)
1008 .unwrap_or(2);
1009
1010 let position = LogicalPosition::from_physical(
1011 (xev.event_x as f64, xev.event_y as f64),
1012 dpi_factor,
1013 );
1014 callback(Event::WindowEvent {
1015 window_id,
1016 event: CursorMoved {
1017 device_id: mkdid(pointer_id),
1018 position,
1019 modifiers: ModifiersState::from(xev.mods),
1020 }
1021 });
1022 }
1023 ffi::XI_FocusOut => {
1024 let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
1025 if !self.window_exists(xev.event) { return; }
1026 self.ime
1027 .borrow_mut()
1028 .unfocus(xev.event)
1029 .expect("Failed to unfocus input context");
1030 callback(Event::WindowEvent {
1031 window_id: mkwid(xev.event),
1032 event: Focused(false),
1033 })
1034 }
1035
1036 ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
1037 let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
1038 let window_id = mkwid(xev.event);
1039 let phase = match xev.evtype {
1040 ffi::XI_TouchBegin => TouchPhase::Started,
1041 ffi::XI_TouchUpdate => TouchPhase::Moved,
1042 ffi::XI_TouchEnd => TouchPhase::Ended,
1043 _ => unreachable!()
1044 };
1045 let dpi_factor = self.with_window(xev.event, |window| {
1046 window.get_hidpi_factor()
1047 });
1048 if let Some(dpi_factor) = dpi_factor {
1049 let location = LogicalPosition::from_physical(
1050 (xev.event_x as f64, xev.event_y as f64),
1051 dpi_factor,
1052 );
1053 callback(Event::WindowEvent {
1054 window_id,
1055 event: WindowEvent::Touch(Touch {
1056 device_id: mkdid(xev.deviceid),
1057 phase,
1058 location,
1059 id: xev.detail as u64,
1060 }),
1061 })
1062 }
1063 }
1064
1065 ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
1066 let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1067 if xev.flags & ffi::XIPointerEmulated == 0 {
1068 callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Button {
1069 button: xev.detail as u32,
1070 state: match xev.evtype {
1071 ffi::XI_RawButtonPress => Pressed,
1072 ffi::XI_RawButtonRelease => Released,
1073 _ => unreachable!(),
1074 },
1075 }});
1076 }
1077 }
1078
1079 ffi::XI_RawMotion => {
1080 let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1081 let did = mkdid(xev.deviceid);
1082
1083 let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
1084 let mut value = xev.raw_values;
1085 let mut mouse_delta = (0.0, 0.0);
1086 let mut scroll_delta = (0.0, 0.0);
1087 for i in 0..xev.valuators.mask_len*8 {
1088 if ffi::XIMaskIsSet(mask, i) {
1089 let x = unsafe { *value };
1090 // We assume that every XInput2 device with analog axes is a pointing device emitting
1091 // relative coordinates.
1092 match i {
1093 0 => mouse_delta.0 = x,
1094 1 => mouse_delta.1 = x,
1095 2 => scroll_delta.0 = x as f32,
1096 3 => scroll_delta.1 = x as f32,
1097 _ => {},
1098 }
1099 callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::Motion {
1100 axis: i as u32,
1101 value: x,
1102 }});
1103 value = unsafe { value.offset(1) };
1104 }
1105 }
1106 if mouse_delta != (0.0, 0.0) {
1107 callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseMotion {
1108 delta: mouse_delta,
1109 }});
1110 }
1111 if scroll_delta != (0.0, 0.0) {
1112 callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseWheel {
1113 delta: LineDelta(scroll_delta.0, scroll_delta.1),
1114 }});
1115 }
1116 }
1117
1118 ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
1119 let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1120
1121 let state = match xev.evtype {
1122 ffi::XI_RawKeyPress => Pressed,
1123 ffi::XI_RawKeyRelease => Released,
1124 _ => unreachable!(),
1125 };
1126
1127 let device_id = xev.sourceid;
1128 let keycode = xev.detail;
1129 if keycode < 8 { return; }
1130 let scancode = (keycode - 8) as u32;
1131
1132 let keysym = unsafe {
1133 (self.xconn.xlib.XKeycodeToKeysym)(
1134 self.xconn.display,
1135 xev.detail as ffi::KeyCode,
1136 0,
1137 )
1138 };
1139 self.xconn.check_errors().expect("Failed to lookup raw keysym");
1140
1141 let virtual_keycode = events::keysym_to_element(keysym as c_uint);
1142
1143 callback(Event::DeviceEvent {
1144 device_id: mkdid(device_id),
1145 event: DeviceEvent::Key(KeyboardInput {
1146 scancode,
1147 virtual_keycode,
1148 state,
1149 // So, in an ideal world we can use libxkbcommon to get modifiers.
1150 // However, libxkbcommon-x11 isn't as commonly installed as one
1151 // would hope. We can still use the Xkb extension to get
1152 // comprehensive keyboard state updates, but interpreting that
1153 // info manually is going to be involved.
1154 modifiers: ModifiersState::default(),
1155 }),
1156 });
1157 }
1158
1159 ffi::XI_HierarchyChanged => {
1160 let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
1161 for info in unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } {
1162 if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
1163 self.init_device(info.deviceid);
1164 callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added });
1165 } else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) {
1166 callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed });
1167 let mut devices = self.devices.borrow_mut();
1168 devices.remove(&DeviceId(info.deviceid));
1169 }
1170 }
1171 }
1172
1173 _ => {}
1174 }
1175 },
1176 _ => {
1177 if event_type == self.randr_event_offset {
1178 // In the future, it would be quite easy to emit monitor hotplug events.
1179 let prev_list = monitor::invalidate_cached_monitor_list();
1180 if let Some(prev_list) = prev_list {
1181 let new_list = self.xconn.get_available_monitors();
1182 for new_monitor in new_list {
1183 prev_list
1184 .iter()
1185 .find(|prev_monitor| prev_monitor.name == new_monitor.name)
1186 .map(|prev_monitor| {
1187 if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
1188 for (window_id, window) in self.windows.borrow().iter() {
1189 if let Some(window) = window.upgrade() {
1190 // Check if the window is on this monitor
1191 let monitor = window.get_current_monitor();
1192 if monitor.name == new_monitor.name {
1193 callback(Event::WindowEvent {
1194 window_id: mkwid(window_id.0),
1195 event: WindowEvent::HiDpiFactorChanged(
1196 new_monitor.hidpi_factor
1197 ),
1198 });
1199 let (width, height) = match window.get_inner_size_physical() {
1200 Some(result) => result,
1201 None => continue,
1202 };
1203 let (_, _, flusher) = window.adjust_for_dpi(
1204 prev_monitor.hidpi_factor,
1205 new_monitor.hidpi_factor,
1206 width as f64,
1207 height as f64,
1208 );
1209 flusher.queue();
1210 }
1211 }
1212 }
1213 }
1214 });
1215 }
1216 }
1217 }
1218 },
1219 }
1220
1221 match self.ime_receiver.try_recv() {
1222 Ok((window_id, x, y)) => {
1223 self.ime.borrow_mut().send_xim_spot(window_id, x, y);
1224 },
1225 Err(_) => (),
1226 }
1227 }
1228
init_device(&self, device: c_int)1229 fn init_device(&self, device: c_int) {
1230 let mut devices = self.devices.borrow_mut();
1231 if let Some(info) = DeviceInfo::get(&self.xconn, device) {
1232 for info in info.iter() {
1233 devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
1234 }
1235 }
1236 }
1237
with_window<F, T>(&self, window_id: ffi::Window, callback: F) -> Option<T> where F: Fn(&UnownedWindow) -> T1238 fn with_window<F, T>(&self, window_id: ffi::Window, callback: F) -> Option<T>
1239 where F: Fn(&UnownedWindow) -> T
1240 {
1241 let mut deleted = false;
1242 let window_id = WindowId(window_id);
1243 let result = self.windows
1244 .borrow()
1245 .get(&window_id)
1246 .and_then(|window| {
1247 let arc = window.upgrade();
1248 deleted = arc.is_none();
1249 arc
1250 })
1251 .map(|window| callback(&*window));
1252 if deleted {
1253 // Garbage collection
1254 self.windows.borrow_mut().remove(&window_id);
1255 }
1256 result
1257 }
1258
window_exists(&self, window_id: ffi::Window) -> bool1259 fn window_exists(&self, window_id: ffi::Window) -> bool {
1260 self.with_window(window_id, |_| ()).is_some()
1261 }
1262 }
1263
1264 impl EventsLoopProxy {
wakeup(&self) -> Result<(), EventsLoopClosed>1265 pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
1266 // Update the `EventsLoop`'s `pending_wakeup` flag.
1267 let display = match (self.pending_wakeup.upgrade(), self.xconn.upgrade()) {
1268 (Some(wakeup), Some(display)) => {
1269 wakeup.store(true, atomic::Ordering::Relaxed);
1270 display
1271 },
1272 _ => return Err(EventsLoopClosed),
1273 };
1274
1275 // Push an event on the X event queue so that methods run_forever will advance.
1276 //
1277 // NOTE: This design is taken from the old `WindowProxy::wakeup` implementation. It
1278 // assumes that X11 is thread safe. Is this true?
1279 // (WARNING: it's probably not true)
1280 display.send_client_msg(
1281 self.wakeup_dummy_window,
1282 self.wakeup_dummy_window,
1283 0,
1284 None,
1285 [0, 0, 0, 0, 0],
1286 ).flush().expect("Failed to call XSendEvent after wakeup");
1287
1288 Ok(())
1289 }
1290 }
1291
1292 struct DeviceInfo<'a> {
1293 xconn: &'a XConnection,
1294 info: *const ffi::XIDeviceInfo,
1295 count: usize,
1296 }
1297
1298 impl<'a> DeviceInfo<'a> {
get(xconn: &'a XConnection, device: c_int) -> Option<Self>1299 fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
1300 unsafe {
1301 let mut count = mem::uninitialized();
1302 let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
1303 xconn.check_errors()
1304 .ok()
1305 .and_then(|_| {
1306 if info.is_null() || count == 0 {
1307 None
1308 } else {
1309 Some(DeviceInfo {
1310 xconn,
1311 info,
1312 count: count as usize,
1313 })
1314 }
1315 })
1316 }
1317 }
1318 }
1319
1320 impl<'a> Drop for DeviceInfo<'a> {
drop(&mut self)1321 fn drop(&mut self) {
1322 assert!(!self.info.is_null());
1323 unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
1324 }
1325 }
1326
1327 impl<'a> Deref for DeviceInfo<'a> {
1328 type Target = [ffi::XIDeviceInfo];
deref(&self) -> &Self::Target1329 fn deref(&self) -> &Self::Target {
1330 unsafe { slice::from_raw_parts(self.info, self.count) }
1331 }
1332 }
1333
1334 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1335 pub struct WindowId(ffi::Window);
1336
1337 impl WindowId {
dummy() -> Self1338 pub unsafe fn dummy() -> Self {
1339 WindowId(0)
1340 }
1341 }
1342
1343 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1344 pub struct DeviceId(c_int);
1345
1346 impl DeviceId {
dummy() -> Self1347 pub unsafe fn dummy() -> Self {
1348 DeviceId(0)
1349 }
1350 }
1351
1352 pub struct Window(Arc<UnownedWindow>);
1353
1354 impl Deref for Window {
1355 type Target = UnownedWindow;
1356 #[inline]
deref(&self) -> &UnownedWindow1357 fn deref(&self) -> &UnownedWindow {
1358 &*self.0
1359 }
1360 }
1361
1362 impl Window {
new( event_loop: &EventsLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes ) -> Result<Self, CreationError>1363 pub fn new(
1364 event_loop: &EventsLoop,
1365 attribs: WindowAttributes,
1366 pl_attribs: PlatformSpecificWindowBuilderAttributes
1367 ) -> Result<Self, CreationError> {
1368 let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
1369 event_loop.windows
1370 .borrow_mut()
1371 .insert(window.id(), Arc::downgrade(&window));
1372 Ok(Window(window))
1373 }
1374 }
1375
1376 impl Drop for Window {
drop(&mut self)1377 fn drop(&mut self) {
1378 let window = self.deref();
1379 let xconn = &window.xconn;
1380 unsafe {
1381 (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
1382 // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
1383 let _ = xconn.check_errors();
1384 }
1385 }
1386 }
1387
1388 /// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
1389 /// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
1390 struct GenericEventCookie<'a> {
1391 xconn: &'a XConnection,
1392 cookie: ffi::XGenericEventCookie
1393 }
1394
1395 impl<'a> GenericEventCookie<'a> {
from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>>1396 fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
1397 unsafe {
1398 let mut cookie: ffi::XGenericEventCookie = From::from(event);
1399 if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
1400 Some(GenericEventCookie { xconn, cookie })
1401 } else {
1402 None
1403 }
1404 }
1405 }
1406 }
1407
1408 impl<'a> Drop for GenericEventCookie<'a> {
drop(&mut self)1409 fn drop(&mut self) {
1410 unsafe {
1411 (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
1412 }
1413 }
1414 }
1415
1416 #[derive(Debug, Copy, Clone)]
1417 struct XExtension {
1418 opcode: c_int,
1419 first_event_id: c_int,
1420 first_error_id: c_int,
1421 }
1422
mkwid(w: ffi::Window) -> ::WindowId1423 fn mkwid(w: ffi::Window) -> ::WindowId { ::WindowId(::platform::WindowId::X(WindowId(w))) }
mkdid(w: c_int) -> ::DeviceId1424 fn mkdid(w: c_int) -> ::DeviceId { ::DeviceId(::platform::DeviceId::X(DeviceId(w))) }
1425
1426 #[derive(Debug)]
1427 struct Device {
1428 name: String,
1429 scroll_axes: Vec<(i32, ScrollAxis)>,
1430 // For master devices, this is the paired device (pointer <-> keyboard).
1431 // For slave devices, this is the master.
1432 attachment: c_int,
1433 }
1434
1435 #[derive(Debug, Copy, Clone)]
1436 struct ScrollAxis {
1437 increment: f64,
1438 orientation: ScrollOrientation,
1439 position: f64,
1440 }
1441
1442 #[derive(Debug, Copy, Clone)]
1443 enum ScrollOrientation {
1444 Vertical,
1445 Horizontal,
1446 }
1447
1448 impl Device {
new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self1449 fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self {
1450 let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
1451 let mut scroll_axes = Vec::new();
1452
1453 if Device::physical_device(info) {
1454 // Register for global raw events
1455 let mask = ffi::XI_RawMotionMask
1456 | ffi::XI_RawButtonPressMask
1457 | ffi::XI_RawButtonReleaseMask
1458 | ffi::XI_RawKeyPressMask
1459 | ffi::XI_RawKeyReleaseMask;
1460 // The request buffer is flushed when we poll for events
1461 el.xconn.select_xinput_events(el.root, info.deviceid, mask).queue();
1462
1463 // Identify scroll axes
1464 for class_ptr in Device::classes(info) {
1465 let class = unsafe { &**class_ptr };
1466 match class._type {
1467 ffi::XIScrollClass => {
1468 let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) };
1469 scroll_axes.push((info.number, ScrollAxis {
1470 increment: info.increment,
1471 orientation: match info.scroll_type {
1472 ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
1473 ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
1474 _ => { unreachable!() }
1475 },
1476 position: 0.0,
1477 }));
1478 }
1479 _ => {}
1480 }
1481 }
1482 }
1483
1484 let mut device = Device {
1485 name: name.into_owned(),
1486 scroll_axes: scroll_axes,
1487 attachment: info.attachment,
1488 };
1489 device.reset_scroll_position(info);
1490 device
1491 }
1492
reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo)1493 fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
1494 if Device::physical_device(info) {
1495 for class_ptr in Device::classes(info) {
1496 let class = unsafe { &**class_ptr };
1497 match class._type {
1498 ffi::XIValuatorClass => {
1499 let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) };
1500 if let Some(&mut (_, ref mut axis)) = self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) {
1501 axis.position = info.value;
1502 }
1503 }
1504 _ => {}
1505 }
1506 }
1507 }
1508 }
1509
1510 #[inline]
physical_device(info: &ffi::XIDeviceInfo) -> bool1511 fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
1512 info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave
1513 }
1514
1515 #[inline]
classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo]1516 fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
1517 unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) }
1518 }
1519 }
1520