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 fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
46 let state = self.state.lock();
47 Rc::clone(&state.windows[&win])
48 }
49}
50
51impl Client for X11Client {
52 fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
53 on_finish_launching();
54 //Note: here and below, don't keep the lock() open when calling
55 // into window functions as they may invoke callbacks that need
56 // to immediately access the platform (self).
57 while !self.platform_inner.state.lock().quit_requested {
58 let event = self.xcb_connection.wait_for_event().unwrap();
59 match event {
60 xcb::Event::X(x::Event::ClientMessage(ev)) => {
61 if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
62 if atom == self.atoms.wm_del_window.resource_id() {
63 // window "x" button clicked by user, we gracefully exit
64 let window = self.state.lock().windows.remove(&ev.window()).unwrap();
65 window.destroy();
66 let state = self.state.lock();
67 self.platform_inner.state.lock().quit_requested |=
68 state.windows.is_empty();
69 }
70 }
71 }
72 xcb::Event::X(x::Event::Expose(ev)) => {
73 self.get_window(ev.window()).refresh();
74 }
75 xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
76 let bounds = Bounds {
77 origin: Point {
78 x: ev.x().into(),
79 y: ev.y().into(),
80 },
81 size: Size {
82 width: ev.width().into(),
83 height: ev.height().into(),
84 },
85 };
86 self.get_window(ev.window()).configure(bounds)
87 }
88 xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
89 let window = self.get_window(ev.window());
90 window.refresh();
91 window.request_refresh();
92 }
93 xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
94 _ => {}
95 }
96
97 if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
98 runnable.run();
99 }
100 }
101
102 if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
103 fun();
104 }
105 }
106 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
107 let setup = self.xcb_connection.get_setup();
108 setup
109 .roots()
110 .enumerate()
111 .map(|(root_id, _)| {
112 Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
113 as Rc<dyn PlatformDisplay>
114 })
115 .collect()
116 }
117 fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
118 Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
119 }
120
121 fn open_window(
122 &self,
123 _handle: AnyWindowHandle,
124 options: WindowOptions,
125 ) -> Box<dyn PlatformWindow> {
126 let x_window = self.xcb_connection.generate_id();
127
128 let window_ptr = Rc::new(X11WindowState::new(
129 options,
130 &self.xcb_connection,
131 self.x_root_index,
132 x_window,
133 &self.atoms,
134 ));
135 window_ptr.request_refresh();
136
137 self.state
138 .lock()
139 .windows
140 .insert(x_window, Rc::clone(&window_ptr));
141 Box::new(X11Window(window_ptr))
142 }
143}