1use std::rc::Rc;
2use std::sync::Arc;
3
4use parking_lot::Mutex;
5use xcb::{x, Xid};
6
7use collections::HashMap;
8
9use crate::platform::linux::client::Client;
10use crate::platform::{
11 LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
12};
13use crate::{AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, Point, Size, WindowOptions};
14
15pub(crate) struct X11ClientState {
16 pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
17}
18
19pub(crate) struct X11Client {
20 platform_inner: Arc<LinuxPlatformInner>,
21 xcb_connection: Arc<xcb::Connection>,
22 x_root_index: i32,
23 atoms: XcbAtoms,
24 state: Mutex<X11ClientState>,
25}
26
27impl X11Client {
28 pub(crate) fn new(
29 inner: Arc<LinuxPlatformInner>,
30 xcb_connection: Arc<xcb::Connection>,
31 x_root_index: i32,
32 atoms: XcbAtoms,
33 ) -> Self {
34 Self {
35 platform_inner: inner,
36 xcb_connection,
37 x_root_index,
38 atoms,
39 state: Mutex::new(X11ClientState {
40 windows: HashMap::default(),
41 }),
42 }
43 }
44}
45
46impl Client for X11Client {
47 fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
48 on_finish_launching();
49 //Note: here and below, don't keep the lock() open when calling
50 // into window functions as they may invoke callbacks that need
51 // to immediately access the platform (self).
52 while !self.platform_inner.state.lock().quit_requested {
53 let event = self.xcb_connection.wait_for_event().unwrap();
54 match event {
55 xcb::Event::X(x::Event::ClientMessage(ev)) => {
56 if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
57 if atom == self.atoms.wm_del_window.resource_id() {
58 // window "x" button clicked by user, we gracefully exit
59 let window = self.state.lock().windows.remove(&ev.window()).unwrap();
60 window.destroy();
61 let mut state = self.state.lock();
62 self.platform_inner.state.lock().quit_requested |=
63 state.windows.is_empty();
64 }
65 }
66 }
67 xcb::Event::X(x::Event::Expose(ev)) => {
68 let window = {
69 let state = self.state.lock();
70 Rc::clone(&state.windows[&ev.window()])
71 };
72 window.expose();
73 }
74 xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
75 let bounds = Bounds {
76 origin: Point {
77 x: ev.x().into(),
78 y: ev.y().into(),
79 },
80 size: Size {
81 width: ev.width().into(),
82 height: ev.height().into(),
83 },
84 };
85 let window = {
86 let state = self.state.lock();
87 Rc::clone(&state.windows[&ev.window()])
88 };
89 window.configure(bounds)
90 }
91 _ => {}
92 }
93
94 if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
95 runnable.run();
96 }
97 }
98
99 if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
100 fun();
101 }
102 }
103 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
104 let setup = self.xcb_connection.get_setup();
105 setup
106 .roots()
107 .enumerate()
108 .map(|(root_id, _)| {
109 Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
110 as Rc<dyn PlatformDisplay>
111 })
112 .collect()
113 }
114 fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
115 Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
116 }
117
118 fn open_window(
119 &self,
120 handle: AnyWindowHandle,
121 options: WindowOptions,
122 ) -> Box<dyn PlatformWindow> {
123 let x_window = self.xcb_connection.generate_id();
124
125 let window_ptr = Rc::new(X11WindowState::new(
126 options,
127 &self.xcb_connection,
128 self.x_root_index,
129 x_window,
130 &self.atoms,
131 ));
132
133 self.state
134 .lock()
135 .windows
136 .insert(x_window, Rc::clone(&window_ptr));
137 Box::new(X11Window(window_ptr))
138 }
139}