1use super::BladeRenderer;
2use crate::{
3 AnyWindowHandle, BladeAtlas, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
4 PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
5};
6use parking_lot::Mutex;
7use std::{
8 ffi::c_void,
9 rc::Rc,
10 sync::{self, Arc},
11};
12use xcb::{x, Xid as _};
13
14#[derive(Default)]
15struct Callbacks {
16 request_frame: Option<Box<dyn FnMut()>>,
17 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
18}
19
20pub(crate) struct LinuxWindowState {
21 display: Rc<dyn PlatformDisplay>,
22 x_window: x::Window,
23 window_bounds: WindowBounds,
24 content_size: Size<Pixels>,
25 sprite_atlas: Arc<BladeAtlas>,
26 renderer: BladeRenderer,
27 callbacks: Callbacks,
28}
29
30pub(crate) type LinuxWindowStatePtr = Arc<Mutex<LinuxWindowState>>;
31#[derive(Clone)]
32pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr);
33
34struct RawWindow {
35 connection: *mut c_void,
36 screen_id: i32,
37 window_id: u32,
38}
39unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow {
40 fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
41 let mut wh = raw_window_handle::XcbWindowHandle::empty();
42 wh.window = self.window_id;
43 wh.into()
44 }
45}
46unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow {
47 fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
48 let mut dh = raw_window_handle::XcbDisplayHandle::empty();
49 dh.connection = self.connection;
50 dh.screen = self.screen_id;
51 dh.into()
52 }
53}
54
55impl LinuxWindowState {
56 pub fn new_ptr(
57 options: WindowOptions,
58 handle: AnyWindowHandle,
59 xcb_connection: &xcb::Connection,
60 x_main_screen_index: i32,
61 x_window: x::Window,
62 atoms: &XcbAtoms,
63 ) -> LinuxWindowStatePtr {
64 let x_screen_index = options
65 .display_id
66 .map_or(x_main_screen_index, |did| did.0 as i32);
67 let screen = xcb_connection
68 .get_setup()
69 .roots()
70 .nth(x_screen_index as usize)
71 .unwrap();
72
73 let xcb_values = [
74 x::Cw::BackPixel(screen.white_pixel()),
75 x::Cw::EventMask(
76 x::EventMask::EXPOSURE | x::EventMask::RESIZE_REDIRECT | x::EventMask::KEY_PRESS,
77 ),
78 ];
79
80 let (bound_x, bound_y, bound_width, bound_height) = match options.bounds {
81 WindowBounds::Fullscreen | WindowBounds::Maximized => {
82 (0, 0, screen.width_in_pixels(), screen.height_in_pixels())
83 }
84 WindowBounds::Fixed(bounds) => (
85 bounds.origin.x.0 as i16,
86 bounds.origin.y.0 as i16,
87 bounds.size.width.0 as u16,
88 bounds.size.height.0 as u16,
89 ),
90 };
91
92 xcb_connection.send_request(&x::CreateWindow {
93 depth: x::COPY_FROM_PARENT as u8,
94 wid: x_window,
95 parent: screen.root(),
96 x: bound_x,
97 y: bound_y,
98 width: bound_width,
99 height: bound_height,
100 border_width: 0,
101 class: x::WindowClass::InputOutput,
102 visual: screen.root_visual(),
103 value_list: &xcb_values,
104 });
105
106 if let Some(titlebar) = options.titlebar {
107 if let Some(title) = titlebar.title {
108 xcb_connection.send_request(&x::ChangeProperty {
109 mode: x::PropMode::Replace,
110 window: x_window,
111 property: x::ATOM_WM_NAME,
112 r#type: x::ATOM_STRING,
113 data: title.as_bytes(),
114 });
115 }
116 }
117 xcb_connection
118 .send_and_check_request(&x::ChangeProperty {
119 mode: x::PropMode::Replace,
120 window: x_window,
121 property: atoms.wm_protocols,
122 r#type: x::ATOM_ATOM,
123 data: &[atoms.wm_del_window],
124 })
125 .unwrap();
126
127 xcb_connection.send_request(&x::MapWindow { window: x_window });
128 xcb_connection.flush().unwrap();
129
130 let raw_window = RawWindow {
131 connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
132 xcb_connection,
133 ) as *mut _,
134 screen_id: x_screen_index,
135 window_id: x_window.resource_id(),
136 };
137 let gpu = Arc::new(
138 unsafe {
139 blade::Context::init_windowed(
140 &raw_window,
141 blade::ContextDesc {
142 validation: cfg!(debug_assertions),
143 capture: false,
144 },
145 )
146 }
147 .unwrap(),
148 );
149 let gpu_extent = blade::Extent {
150 width: bound_width as u32,
151 height: bound_height as u32,
152 depth: 1,
153 };
154
155 Arc::new(Mutex::new(Self {
156 display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)),
157 x_window,
158 window_bounds: options.bounds,
159 content_size: Size {
160 width: Pixels(bound_width as f32),
161 height: Pixels(bound_height as f32),
162 },
163 sprite_atlas: Arc::new(BladeAtlas::new(&gpu)),
164 renderer: BladeRenderer::new(gpu, gpu_extent),
165 callbacks: Callbacks::default(),
166 }))
167 }
168
169 pub fn resize(&mut self, width: u16, height: u16) {
170 self.content_size = Size {
171 width: Pixels(width as f32),
172 height: Pixels(height as f32),
173 };
174 self.renderer.resize(blade::Extent {
175 width: width as u32,
176 height: height as u32,
177 depth: 1,
178 });
179 if let Some(ref mut fun) = self.callbacks.resize {
180 fun(self.content_size, 1.0);
181 }
182 }
183
184 pub fn request_frame(&mut self) {
185 if let Some(ref mut fun) = self.callbacks.request_frame {
186 fun();
187 }
188 }
189
190 pub fn destroy(&mut self) {
191 self.sprite_atlas.destroy();
192 self.renderer.destroy();
193 }
194}
195
196impl PlatformWindow for LinuxWindow {
197 fn bounds(&self) -> WindowBounds {
198 //TODO: update when window moves
199 self.0.lock().window_bounds
200 }
201
202 fn content_size(&self) -> Size<Pixels> {
203 self.0.lock().content_size
204 }
205
206 fn scale_factor(&self) -> f32 {
207 1.0
208 }
209
210 fn titlebar_height(&self) -> Pixels {
211 unimplemented!()
212 }
213
214 fn appearance(&self) -> WindowAppearance {
215 unimplemented!()
216 }
217
218 fn display(&self) -> Rc<dyn PlatformDisplay> {
219 Rc::clone(&self.0.lock().display)
220 }
221
222 fn mouse_position(&self) -> Point<Pixels> {
223 Point::default()
224 }
225
226 fn modifiers(&self) -> crate::Modifiers {
227 crate::Modifiers::default()
228 }
229
230 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
231 self
232 }
233
234 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
235
236 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
237 None
238 }
239
240 fn prompt(
241 &self,
242 _level: crate::PromptLevel,
243 _msg: &str,
244 _detail: Option<&str>,
245 _answers: &[&str],
246 ) -> futures::channel::oneshot::Receiver<usize> {
247 unimplemented!()
248 }
249
250 fn activate(&self) {}
251
252 fn set_title(&mut self, title: &str) {}
253
254 fn set_edited(&mut self, edited: bool) {}
255
256 fn show_character_palette(&self) {
257 unimplemented!()
258 }
259
260 fn minimize(&self) {
261 unimplemented!()
262 }
263
264 fn zoom(&self) {
265 unimplemented!()
266 }
267
268 fn toggle_full_screen(&self) {
269 unimplemented!()
270 }
271
272 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
273 self.0.lock().callbacks.request_frame = Some(callback);
274 }
275
276 fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {}
277
278 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {}
279
280 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
281 self.0.lock().callbacks.resize = Some(callback);
282 }
283
284 fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {}
285
286 fn on_moved(&self, callback: Box<dyn FnMut()>) {}
287
288 fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {}
289
290 fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
291
292 fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
293
294 fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
295 unimplemented!()
296 }
297
298 fn invalidate(&self) {}
299
300 fn draw(&self, scene: &crate::Scene) {
301 self.0.lock().renderer.draw(scene);
302 }
303
304 fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
305 self.0.lock().sprite_atlas.clone()
306 }
307}