1mod clipboard;
4mod drag_n_drop;
5mod glx;
6mod keycodes;
7pub mod libx11;
8mod libx11_ex;
9mod x_cursor;
10mod xi_input;
11
12use crate::{
13 event::EventHandler,
14 native::{egl, gl, module, NativeDisplayData, Request},
15 CursorIcon,
16};
17
18use libx11::*;
19
20use std::collections::HashMap;
21
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub enum X11Error {
24 LibraryNotFound(module::Error),
25 GLXError(String),
26}
27impl std::fmt::Display for X11Error {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 Self::LibraryNotFound(e) => write!(f, "Library not found error: {e}"),
31 Self::GLXError(msg) => write!(f, "GLX error:\n{msg}"),
32 }
33 }
34}
35impl From<module::Error> for X11Error {
36 fn from(error: module::Error) -> X11Error {
37 X11Error::LibraryNotFound(error)
38 }
39}
40impl std::error::Error for X11Error {}
41
42pub struct X11Display {
43 libx11: LibX11,
44 libxkbcommon: LibXkbCommon,
45 libxi: xi_input::LibXi,
46 display: *mut Display,
47 root: Window,
48 window: Window,
49 repeated_keycodes: [bool; 256],
50 empty_cursor: libx11::Cursor,
51 cursor_cache: HashMap<CursorIcon, libx11::Cursor>,
52 cursor_icon: CursorIcon,
53 cursor_visible: bool,
54 update_requested: bool,
55 drag_n_drop: drag_n_drop::X11DnD,
56}
57
58impl X11Display {
59 unsafe fn process_event(&mut self, event: &mut XEvent, event_handler: &mut dyn EventHandler) {
60 match event.type_0 {
61 2 => {
62 let keycode = event.xkey.keycode as libc::c_int;
63 let key = keycodes::translate_key(&mut self.libx11, self.display, keycode);
64 let repeat = self.repeated_keycodes[(keycode & 0xff) as usize];
65 self.repeated_keycodes[(keycode & 0xff) as usize] = true;
66 let mods = keycodes::translate_mod(event.xkey.state as libc::c_int);
67 let mut keysym: KeySym = 0;
68 (self.libx11.XLookupString)(
69 &mut event.xkey,
70 std::ptr::null_mut(),
71 0 as libc::c_int,
72 &mut keysym,
73 std::ptr::null_mut(),
74 );
75 let chr = keycodes::keysym_to_unicode(&mut self.libxkbcommon, keysym);
76 if chr > 0 {
77 if let Some(chr) = char::from_u32(chr as u32) {
78 event_handler.char_event(chr, mods, repeat);
79 }
80 }
81 event_handler.key_down_event(key, mods, repeat);
82 }
83 3 => {
84 let keycode = event.xkey.keycode;
85 let key = keycodes::translate_key(&mut self.libx11, self.display, keycode as _);
86 self.repeated_keycodes[(keycode & 0xff) as usize] = false;
87 let mods = keycodes::translate_mod(event.xkey.state as libc::c_int);
88 event_handler.key_up_event(key, mods);
89 }
90 4 => {
91 let btn = keycodes::translate_mouse_button(event.xbutton.button as _);
92 let x = event.xmotion.x as libc::c_float;
93 let y = event.xmotion.y as libc::c_float;
94
95 if btn != crate::event::MouseButton::Unknown {
96 event_handler.mouse_button_down_event(btn, x, y);
97 } else {
98 match event.xbutton.button {
99 4 => {
100 event_handler.mouse_wheel_event(0.0, 1.0);
101 }
102 5 => {
103 event_handler.mouse_wheel_event(0.0, -1.0);
104 }
105 6 => {
106 event_handler.mouse_wheel_event(1.0, 0.0);
107 }
108 7 => {
109 event_handler.mouse_wheel_event(-1.0, 0.0);
110 }
111 _ => {}
112 }
113 }
114 }
115 5 => {
116 let btn = keycodes::translate_mouse_button(event.xbutton.button as _);
117 let x = event.xmotion.x as libc::c_float;
118 let y = event.xmotion.y as libc::c_float;
119
120 if btn != crate::event::MouseButton::Unknown {
121 event_handler.mouse_button_up_event(btn, x, y);
122 }
123 }
124 7 => {
125 }
127 8 => {
128 }
130 6 => {
131 let x = event.xmotion.x as libc::c_float;
132 let y = event.xmotion.y as libc::c_float;
133 event_handler.mouse_motion_event(x, y);
134 }
135 9 => {
136 event_handler.window_restored_event();
137 }
138 10 => {
139 event_handler.window_minimized_event();
140 }
141 22 => {
142 let mut d = crate::native_display().try_lock().unwrap();
143 let left = event.xconfigure.x;
144 let top = event.xconfigure.y;
145 d.screen_position = (left as _, top as _);
146 if event.xconfigure.width != d.screen_width
147 || event.xconfigure.height != d.screen_height
148 {
149 let width = event.xconfigure.width;
150 let height = event.xconfigure.height;
151 d.screen_width = width;
152 d.screen_height = height;
153 drop(d);
154 event_handler.resize_event(width as _, height as _);
155 }
156 }
157 33 => match event.xclient.message_type {
159 t if t == self.libx11.extensions.wm_protocols => {
160 let mut d = crate::native_display().try_lock().unwrap();
161 let protocol = event.xclient.data.l[0 as libc::c_int as usize] as Atom;
162 if protocol == self.libx11.extensions.wm_delete_window {
163 d.quit_requested = true;
164 }
165 }
166 t if t == self.libx11.extensions.xdnd_enter => {
167 self.drag_n_drop.on_enter(
168 &mut self.libx11,
169 self.display,
170 self.window,
171 event.xclient.data,
172 );
173 }
174 t if t == self.libx11.extensions.xdnd_position => {
175 self.drag_n_drop.on_position(
176 &mut self.libx11,
177 self.display,
178 self.window,
179 event.xclient.data,
180 );
181 }
182 t if t == self.libx11.extensions.xdnd_drop => {
183 self.drag_n_drop.on_drop(
184 &mut self.libx11,
185 self.display,
186 self.window,
187 event.xclient.data,
188 );
189 }
190
191 _ => (),
192 },
193 31 => match event.xselection.property {
195 p if p == self.libx11.extensions.xdnd_selection => {
196 let bytes = clipboard::get_property_bytes(
197 &mut self.libx11,
198 self.display,
199 self.window,
200 p,
201 );
202 if let Ok(filenames) = std::str::from_utf8(&bytes) {
203 let mut d = crate::native_display().try_lock().unwrap();
204 d.dropped_files = Default::default();
205 for filename in filenames.lines() {
206 let path = std::path::PathBuf::from(filename);
207 if let Ok(bytes) = std::fs::read(&path) {
208 d.dropped_files.paths.push(path);
209 d.dropped_files.bytes.push(bytes);
210 }
211 }
212 drop(d);
214 event_handler.files_dropped_event();
215 }
216 }
217 _ => (),
218 },
219 30 => {
221 clipboard::respond_to_clipboard_request(&mut self.libx11, self.display, event);
225 }
226 29 => {}
228 17 => {}
229
230 35 if Some(event.xcookie.extension) == self.libxi.xi_extension_opcode => {
232 if event.xcookie.evtype == xi_input::XI_RawMotion {
233 let (dx, dy) = self.libxi.read_cookie(&mut event.xcookie, self.display);
234 event_handler.raw_mouse_motion(dx as f32, dy as f32);
235 }
236 }
237 _ => {}
238 };
239
240 let d = crate::native_display().try_lock().unwrap();
241 if d.quit_requested && !d.quit_ordered {
242 drop(d);
243 event_handler.quit_requested_event();
244 let mut d = crate::native_display().try_lock().unwrap();
245 if d.quit_requested {
246 d.quit_ordered = true
247 }
248 }
249 }
250
251 unsafe fn set_fullscreen(&mut self, window: Window, fullscreen: bool) {
254 let wm_state = (self.libx11.XInternAtom)(
255 self.display,
256 b"_NET_WM_STATE\x00" as *const u8 as *const _,
257 false as _,
258 );
259 let wm_fullscreen = (self.libx11.XInternAtom)(
260 self.display,
261 if fullscreen {
262 b"_NET_WM_STATE_FULLSCREEN\x00" as *const u8 as *const _
263 } else {
264 b"\x00" as *const u8 as *const _
265 },
266 false as _,
267 );
268
269 {
273 (self.libx11.XLowerWindow)(self.display, window);
274 (self.libx11.XUnmapWindow)(self.display, window);
275 (self.libx11.XSync)(self.display, false as _);
276
277 let mut atoms: [Atom; 2] = [wm_fullscreen, 0 as _];
278 (self.libx11.XChangeProperty)(
279 self.display,
280 window,
281 wm_state,
282 4 as _,
283 32,
284 PropModeReplace,
285 atoms.as_mut_ptr() as *mut _ as *mut _,
286 1,
287 );
288 (self.libx11.XMapWindow)(self.display, window);
289 (self.libx11.XRaiseWindow)(self.display, window);
290 (self.libx11.XFlush)(self.display);
291 }
292
293 {
296 let mut data = [0isize; 5];
297
298 data[0] = 1;
299 data[1] = wm_fullscreen as isize;
300 data[2] = 0;
301
302 let mut ev = XClientMessageEvent {
303 type_0: 33,
304 serial: 0,
305 send_event: true as _,
306 message_type: wm_state,
307 window,
308 display: self.display,
309 format: 32,
310 data: ClientMessageData {
311 l: std::mem::transmute(data),
312 },
313 };
314 (self.libx11.XSendEvent)(
315 self.display as _,
316 self.root,
317 false as _,
318 (1048576 | 131072) as _,
319 &mut ev as *mut XClientMessageEvent as *mut _,
320 );
321 }
322 }
323
324 unsafe fn set_window_size(&mut self, window: Window, new_width: i32, new_height: i32) {
325 (self.libx11.XResizeWindow)(self.display, window, new_width, new_height);
326 (self.libx11.XFlush)(self.display);
327 }
328
329 unsafe fn set_window_position(&mut self, window: Window, new_x: i32, new_y: i32) {
331 (self.libx11.XMoveWindow)(self.display, window, new_x, new_y);
332 }
333
334 pub unsafe fn set_cursor_grab(&mut self, window: Window, grab: bool) {
335 (self.libx11.XUngrabPointer)(self.display, 0);
336
337 if grab {
338 (self.libx11.XGrabPointer)(
339 self.display,
340 window,
341 true as _,
342 (ButtonPressMask
343 | ButtonReleaseMask
344 | EnterWindowMask
345 | LeaveWindowMask
346 | PointerMotionMask
347 | PointerMotionHintMask
348 | Button1MotionMask
349 | Button2MotionMask
350 | Button3MotionMask
351 | Button4MotionMask
352 | Button5MotionMask
353 | ButtonMotionMask
354 | KeymapStateMask) as libc::c_uint,
355 GrabModeAsync,
356 GrabModeAsync,
357 window,
358 0,
359 0, );
361 }
362
363 (self.libx11.XFlush)(self.display);
364 }
365 pub unsafe fn set_cursor(&mut self, window: Window, cursor: Option<CursorIcon>) {
366 let libx11 = &mut self.libx11;
367 let display = self.display;
368
369 let cursor = match cursor {
370 None => self.empty_cursor,
371 Some(cursor_icon) => *self.cursor_cache.entry(cursor_icon).or_insert_with(|| {
372 (libx11.XCreateFontCursor)(
373 display,
374 match cursor_icon {
375 CursorIcon::Default => libx11::XC_left_ptr,
376 CursorIcon::Help => libx11::XC_question_arrow,
377 CursorIcon::Pointer => libx11::XC_hand2,
378 CursorIcon::Wait => libx11::XC_watch,
379 CursorIcon::Crosshair => libx11::XC_crosshair,
380 CursorIcon::Text => libx11::XC_xterm,
381 CursorIcon::Move => libx11::XC_fleur,
382 CursorIcon::NotAllowed => libx11::XC_pirate,
383 CursorIcon::EWResize => libx11::XC_sb_h_double_arrow,
384 CursorIcon::NSResize => libx11::XC_sb_v_double_arrow,
385 CursorIcon::NESWResize => libx11::XC_top_right_corner,
386 CursorIcon::NWSEResize => libx11::XC_top_left_corner,
387 },
388 )
389 }),
390 };
391 (libx11.XDefineCursor)(display, window, cursor);
392 }
393
394 fn process_request(&mut self, request: Request) {
395 use Request::*;
396 unsafe {
397 match request {
398 ScheduleUpdate => {
399 self.update_requested = true;
400 }
401 SetCursorGrab(grab) => self.set_cursor_grab(self.window, grab),
402 ShowMouse(show) => {
403 self.cursor_visible = show;
404 self.set_cursor(self.window, self.cursor_visible.then_some(self.cursor_icon));
405 }
406 SetMouseCursor(icon) => {
407 self.cursor_icon = icon;
408 self.set_cursor(self.window, self.cursor_visible.then_some(self.cursor_icon));
409 }
410 SetWindowSize {
411 new_width,
412 new_height,
413 } => self.set_window_size(self.window, new_width as _, new_height as _),
414 SetWindowPosition { new_x, new_y } => {
415 self.set_window_position(self.window, new_x as _, new_y as _)
416 }
417 SetFullscreen(fullscreen) => self.set_fullscreen(self.window, fullscreen),
418 ShowKeyboard(..) => {
419 eprintln!("Not implemented for X11")
420 }
421 SetImePosition { .. } => {
422 }
424 SetImeEnabled(..) => {
425 }
427 }
428 }
429 }
430}
431
432unsafe fn glx_main_loop<F>(
433 mut display: X11Display,
434 conf: &crate::conf::Conf,
435 f: &mut Option<F>,
436 screen: i32,
437) -> Result<(), X11Display>
438where
439 F: 'static + FnOnce() -> Box<dyn EventHandler>,
440{
441 let mut glx = match glx::Glx::init(&mut display.libx11, display.display, screen, conf) {
442 Ok(glx) => glx,
443 _ => return Err(display),
444 };
445 let visual = glx.visual;
446 let depth = glx.depth;
447 display.window =
448 display
449 .libx11
450 .create_window(display.root, display.display, visual, depth, conf);
451
452 let (glx_context, glx_window) = glx.create_context(display.display, display.window);
453 glx.swap_interval(
454 display.display,
455 glx_window,
456 glx_context,
457 conf.platform.swap_interval.unwrap_or(1),
458 );
459 gl::load_gl_funcs(|proc| glx.libgl.get_procaddr(proc));
460
461 display.init_drag_n_drop();
462 display.libx11.show_window(display.display, display.window);
463
464 (display.libx11.XFlush)(display.display);
465
466 let (w, h) = display
467 .libx11
468 .query_window_size(display.display, display.window);
469
470 let (tx, rx) = std::sync::mpsc::channel();
471 let clipboard = Box::new(clipboard::X11Clipboard::new(
472 display.libx11.clone(),
473 display.display,
474 display.window,
475 ));
476 crate::set_display(NativeDisplayData {
477 high_dpi: conf.high_dpi,
478 dpi_scale: display.libx11.update_system_dpi(display.display),
479 blocking_event_loop: conf.platform.blocking_event_loop,
480 ..NativeDisplayData::new(w, h, tx, clipboard)
481 });
482 if conf.fullscreen {
483 display.set_fullscreen(display.window, true);
484 }
485
486 let mut event_handler = (f.take().unwrap())();
487
488 while !crate::native_display().try_lock().unwrap().quit_ordered {
489 while let Ok(request) = rx.try_recv() {
490 display.process_request(request);
491 }
492 glx.make_current(display.display, glx_window, glx_context);
493
494 let mut count = (display.libx11.XPending)(display.display);
495 let block_on_wait = conf.platform.blocking_event_loop && !display.update_requested;
496 if block_on_wait {
497 count += 1;
502 }
503
504 for _ in 0..count {
505 let mut xevent = _XEvent { type_0: 0 };
506 (display.libx11.XNextEvent)(display.display, &mut xevent);
507 display.process_event(&mut xevent, &mut *event_handler);
508 }
509
510 if !conf.platform.blocking_event_loop || display.update_requested {
511 display.update_requested = false;
512 event_handler.update();
513 event_handler.draw();
514
515 glx.swap_buffers(display.display, glx_window);
516 (display.libx11.XFlush)(display.display);
517 }
518 }
519
520 glx.destroy_context(display.display, glx_window, glx_context);
521 (display.libx11.XUnmapWindow)(display.display, display.window);
522 (display.libx11.XDestroyWindow)(display.display, display.window);
523 (display.libx11.XCloseDisplay)(display.display);
524
525 Ok(())
526}
527
528unsafe fn egl_main_loop<F>(
529 mut display: X11Display,
530 conf: &crate::conf::Conf,
531 f: &mut Option<F>,
532) -> Result<(), X11Display>
533where
534 F: 'static + FnOnce() -> Box<dyn EventHandler>,
535{
536 let mut egl_lib = match egl::LibEgl::try_load().ok() {
537 Some(glx) => glx,
538 _ => return Err(display),
539 };
540
541 display.window =
542 display
543 .libx11
544 .create_window(display.root, display.display, std::ptr::null_mut(), 0, conf);
545
546 let (context, config, egl_display) = egl::create_egl_context(
547 &mut egl_lib,
548 display.display as *mut _,
549 conf.platform.framebuffer_alpha,
550 conf.sample_count,
551 )
552 .unwrap();
553
554 let egl_surface =
555 (egl_lib.eglCreateWindowSurface)(egl_display, config, display.window, std::ptr::null_mut());
556
557 if egl_surface.is_null() {
558 panic!("surface creation failed");
560 }
561 if (egl_lib.eglMakeCurrent)(egl_display, egl_surface, egl_surface, context) == 0 {
562 panic!("eglMakeCurrent failed");
563 }
564
565 if (egl_lib.eglSwapInterval)(egl_display, conf.platform.swap_interval.unwrap_or(1)) == 0 {
566 eprintln!("eglSwapInterval failed");
567 }
568
569 crate::native::gl::load_gl_funcs(|proc| {
570 let name = std::ffi::CString::new(proc).unwrap();
571 (egl_lib.eglGetProcAddress)(name.as_ptr() as _)
572 });
573
574 display.init_drag_n_drop();
575 display.libx11.show_window(display.display, display.window);
576 let (w, h) = display
577 .libx11
578 .query_window_size(display.display, display.window);
579
580 let (tx, rx) = std::sync::mpsc::channel();
581 let clipboard = Box::new(clipboard::X11Clipboard::new(
582 display.libx11.clone(),
583 display.display,
584 display.window,
585 ));
586 crate::set_display(NativeDisplayData {
587 high_dpi: conf.high_dpi,
588 dpi_scale: display.libx11.update_system_dpi(display.display),
589 blocking_event_loop: conf.platform.blocking_event_loop,
590 ..NativeDisplayData::new(w, h, tx, clipboard)
591 });
592 if conf.fullscreen {
593 display.set_fullscreen(display.window, true)
594 }
595
596 (display.libx11.XFlush)(display.display);
597
598 let mut event_handler = (f.take().unwrap())();
599
600 while !crate::native_display().try_lock().unwrap().quit_ordered {
601 while let Ok(request) = rx.try_recv() {
602 display.process_request(request);
603 }
604
605 let mut count = (display.libx11.XPending)(display.display);
606 let block_on_wait = conf.platform.blocking_event_loop && !display.update_requested;
607 if block_on_wait {
608 count += 1;
610 }
611 for _ in 0..count {
612 let mut xevent = _XEvent { type_0: 0 };
613 (display.libx11.XNextEvent)(display.display, &mut xevent);
614 display.process_event(&mut xevent, &mut *event_handler);
615 }
616
617 if !conf.platform.blocking_event_loop || display.update_requested {
618 display.update_requested = false;
619 event_handler.update();
620 event_handler.draw();
621
622 (egl_lib.eglSwapBuffers)(egl_display, egl_surface);
623 (display.libx11.XFlush)(display.display);
624 }
625 }
626
627 (display.libx11.XUnmapWindow)(display.display, display.window);
628 (display.libx11.XDestroyWindow)(display.display, display.window);
629 (display.libx11.XCloseDisplay)(display.display);
630
631 Ok(())
632}
633
634pub fn run<F>(conf: &crate::conf::Conf, f: &mut Option<F>) -> Result<(), X11Error>
635where
636 F: 'static + FnOnce() -> Box<dyn EventHandler>,
637{
638 unsafe {
639 let mut libx11 = LibX11::try_load()?;
640 let libxkbcommon = LibXkbCommon::try_load()?;
641 let libxi = xi_input::LibXi::try_load()?;
642
643 (libx11.XInitThreads)();
644 (libx11.XrmInitialize)();
645
646 let x11_display = (libx11.XOpenDisplay)(std::ptr::null());
647 if x11_display.is_null() {
648 panic!("XOpenDisplay() failed!");
649 }
650
651 let x11_screen = (*(x11_display as _XPrivDisplay)).default_screen;
654 let x11_root = (*(*(x11_display as _XPrivDisplay))
655 .screens
656 .offset(x11_screen as isize))
657 .root;
658
659 (libx11.XkbSetDetectableAutoRepeat)(x11_display, true as _, std::ptr::null_mut());
667
668 libx11.load_extensions(x11_display);
669 let mut display = X11Display {
670 empty_cursor: x_cursor::create_empty_cursor(x11_display, x11_root, &mut libx11),
671 display: x11_display,
672 root: x11_root,
673 window: 0,
674 libx11,
675 libxkbcommon,
676 libxi,
677 repeated_keycodes: [false; 256],
678 cursor_cache: HashMap::new(),
679 update_requested: true,
680 drag_n_drop: Default::default(),
681 cursor_icon: CursorIcon::Default,
682 cursor_visible: true,
683 };
684
685 display
686 .libxi
687 .query_xi_extension(&mut display.libx11, display.display);
688
689 match conf.platform.linux_x11_gl {
690 crate::conf::LinuxX11Gl::GLXOnly => {
691 glx_main_loop(display, conf, f, x11_screen).ok().unwrap();
692 }
693 crate::conf::LinuxX11Gl::EGLOnly => {
694 egl_main_loop(display, conf, f).ok().unwrap();
695 }
696 crate::conf::LinuxX11Gl::GLXWithEGLFallback => {
697 if let Err(display) = glx_main_loop(display, conf, f, x11_screen) {
698 egl_main_loop(display, conf, f).ok().unwrap();
699 }
700 }
701 crate::conf::LinuxX11Gl::EGLWithGLXFallback => {
702 if let Err(display) = egl_main_loop(display, conf, f) {
703 glx_main_loop(display, conf, f, x11_screen).ok().unwrap();
704 }
705 }
706 }
707 }
708 Ok(())
709}