1use std::{rc::Rc, sync::Arc};
2
3use parking_lot::Mutex;
4use xcb::{x, Xid as _};
5use xkbcommon::xkb;
6
7use collections::HashMap;
8
9use crate::platform::linux::client::Client;
10use crate::platform::{
11 LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
12};
13use crate::{
14 AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, PlatformInput, Point, Size, WindowOptions,
15};
16
17pub(crate) struct X11ClientState {
18 pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
19 xkb: xkbcommon::xkb::State,
20}
21
22pub(crate) struct X11Client {
23 platform_inner: Rc<LinuxPlatformInner>,
24 xcb_connection: Arc<xcb::Connection>,
25 x_root_index: i32,
26 atoms: XcbAtoms,
27 state: Mutex<X11ClientState>,
28}
29
30impl X11Client {
31 pub(crate) fn new(
32 inner: Rc<LinuxPlatformInner>,
33 xcb_connection: Arc<xcb::Connection>,
34 x_root_index: i32,
35 atoms: XcbAtoms,
36 ) -> Self {
37 let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
38 let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection);
39 let xkb_keymap = xkb::x11::keymap_new_from_device(
40 &xkb_context,
41 &xcb_connection,
42 xkb_device_id,
43 xkb::KEYMAP_COMPILE_NO_FLAGS,
44 );
45 let xkb_state =
46 xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id);
47
48 Self {
49 platform_inner: inner,
50 xcb_connection,
51 x_root_index,
52 atoms,
53 state: Mutex::new(X11ClientState {
54 windows: HashMap::default(),
55 xkb: xkb_state,
56 }),
57 }
58 }
59
60 fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
61 let state = self.state.lock();
62 Rc::clone(&state.windows[&win])
63 }
64}
65
66impl Client for X11Client {
67 fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
68 on_finish_launching();
69 //Note: here and below, don't keep the lock() open when calling
70 // into window functions as they may invoke callbacks that need
71 // to immediately access the platform (self).
72 while !self.platform_inner.state.lock().quit_requested {
73 let event = self.xcb_connection.wait_for_event().unwrap();
74 match event {
75 xcb::Event::X(x::Event::ClientMessage(ev)) => {
76 if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
77 if atom == self.atoms.wm_del_window.resource_id() {
78 // window "x" button clicked by user, we gracefully exit
79 let window = self.state.lock().windows.remove(&ev.window()).unwrap();
80 window.destroy();
81 let state = self.state.lock();
82 self.platform_inner.state.lock().quit_requested |=
83 state.windows.is_empty();
84 }
85 }
86 }
87 xcb::Event::X(x::Event::Expose(ev)) => {
88 self.get_window(ev.window()).refresh();
89 }
90 xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
91 let bounds = Bounds {
92 origin: Point {
93 x: ev.x().into(),
94 y: ev.y().into(),
95 },
96 size: Size {
97 width: ev.width().into(),
98 height: ev.height().into(),
99 },
100 };
101 self.get_window(ev.window()).configure(bounds)
102 }
103 xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
104 let window = self.get_window(ev.window());
105 window.refresh();
106 window.request_refresh();
107 }
108 xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
109 xcb::Event::X(x::Event::KeyPress(ev)) => {
110 let window = self.get_window(ev.event());
111 let modifiers = super::modifiers_from_state(ev.state());
112 let key = {
113 let code = ev.detail().into();
114 let mut state = self.state.lock();
115 let key = state.xkb.key_get_utf8(code);
116 state.xkb.update_key(code, xkb::KeyDirection::Down);
117 key
118 };
119 window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
120 keystroke: crate::Keystroke {
121 modifiers,
122 key,
123 ime_key: None,
124 },
125 is_held: false,
126 }));
127 }
128 xcb::Event::X(x::Event::KeyRelease(ev)) => {
129 let window = self.get_window(ev.event());
130 let modifiers = super::modifiers_from_state(ev.state());
131 let key = {
132 let code = ev.detail().into();
133 let mut state = self.state.lock();
134 let key = state.xkb.key_get_utf8(code);
135 state.xkb.update_key(code, xkb::KeyDirection::Up);
136 key
137 };
138 window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent {
139 keystroke: crate::Keystroke {
140 modifiers,
141 key,
142 ime_key: None,
143 },
144 }));
145 }
146 xcb::Event::X(x::Event::ButtonPress(ev)) => {
147 let window = self.get_window(ev.event());
148 let modifiers = super::modifiers_from_state(ev.state());
149 let position =
150 Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
151 if let Some(button) = super::button_of_key(ev.detail()) {
152 window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
153 button,
154 position,
155 modifiers,
156 click_count: 1,
157 }));
158 } else {
159 log::warn!("Unknown button press: {ev:?}");
160 }
161 }
162 xcb::Event::X(x::Event::ButtonRelease(ev)) => {
163 let window = self.get_window(ev.event());
164 let modifiers = super::modifiers_from_state(ev.state());
165 let position =
166 Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
167 if let Some(button) = super::button_of_key(ev.detail()) {
168 window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
169 button,
170 position,
171 modifiers,
172 click_count: 1,
173 }));
174 }
175 }
176 xcb::Event::X(x::Event::MotionNotify(ev)) => {
177 let window = self.get_window(ev.event());
178 let pressed_button = super::button_from_state(ev.state());
179 let position =
180 Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
181 let modifiers = super::modifiers_from_state(ev.state());
182 window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
183 pressed_button,
184 position,
185 modifiers,
186 }));
187 }
188 xcb::Event::X(x::Event::LeaveNotify(ev)) => {
189 let window = self.get_window(ev.event());
190 let pressed_button = super::button_from_state(ev.state());
191 let position =
192 Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
193 let modifiers = super::modifiers_from_state(ev.state());
194 window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
195 pressed_button,
196 position,
197 modifiers,
198 }));
199 }
200 _ => {}
201 }
202
203 if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
204 runnable.run();
205 }
206 }
207
208 if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
209 fun();
210 }
211 }
212 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
213 let setup = self.xcb_connection.get_setup();
214 setup
215 .roots()
216 .enumerate()
217 .map(|(root_id, _)| {
218 Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
219 as Rc<dyn PlatformDisplay>
220 })
221 .collect()
222 }
223 fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
224 Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
225 }
226
227 fn open_window(
228 &self,
229 _handle: AnyWindowHandle,
230 options: WindowOptions,
231 ) -> Box<dyn PlatformWindow> {
232 let x_window = self.xcb_connection.generate_id();
233
234 let window_ptr = Rc::new(X11WindowState::new(
235 options,
236 &self.xcb_connection,
237 self.x_root_index,
238 x_window,
239 &self.atoms,
240 ));
241 window_ptr.request_refresh();
242
243 self.state
244 .lock()
245 .windows
246 .insert(x_window, Rc::clone(&window_ptr));
247 Box::new(X11Window(window_ptr))
248 }
249}