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 destroy(&mut self) {
170 self.sprite_atlas.destroy();
171 self.renderer.destroy();
172 }
173
174 pub fn resize(self_ptr: &LinuxWindowStatePtr, width: u16, height: u16) {
175 let content_size = Size {
176 width: Pixels(width as f32),
177 height: Pixels(height as f32),
178 };
179
180 let mut fun = match self_ptr.lock().callbacks.resize.take() {
181 Some(fun) => fun,
182 None => return,
183 };
184 fun(content_size, 1.0);
185
186 let mut this = self_ptr.lock();
187 this.callbacks.resize = Some(fun);
188 this.content_size = content_size;
189 this.renderer.resize(blade::Extent {
190 width: width as u32,
191 height: height as u32,
192 depth: 1,
193 });
194 }
195
196 pub fn request_frame(self_ptr: &LinuxWindowStatePtr) {
197 let mut fun = match self_ptr.lock().callbacks.request_frame.take() {
198 Some(fun) => fun,
199 None => return,
200 };
201 fun();
202
203 self_ptr.lock().callbacks.request_frame = Some(fun);
204 }
205}
206
207impl PlatformWindow for LinuxWindow {
208 fn bounds(&self) -> WindowBounds {
209 //TODO: update when window moves
210 self.0.lock().window_bounds
211 }
212
213 fn content_size(&self) -> Size<Pixels> {
214 self.0.lock().content_size
215 }
216
217 fn scale_factor(&self) -> f32 {
218 1.0
219 }
220
221 fn titlebar_height(&self) -> Pixels {
222 unimplemented!()
223 }
224
225 fn appearance(&self) -> WindowAppearance {
226 unimplemented!()
227 }
228
229 fn display(&self) -> Rc<dyn PlatformDisplay> {
230 Rc::clone(&self.0.lock().display)
231 }
232
233 fn mouse_position(&self) -> Point<Pixels> {
234 Point::default()
235 }
236
237 fn modifiers(&self) -> crate::Modifiers {
238 crate::Modifiers::default()
239 }
240
241 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
242 self
243 }
244
245 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
246
247 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
248 None
249 }
250
251 fn prompt(
252 &self,
253 _level: crate::PromptLevel,
254 _msg: &str,
255 _detail: Option<&str>,
256 _answers: &[&str],
257 ) -> futures::channel::oneshot::Receiver<usize> {
258 unimplemented!()
259 }
260
261 fn activate(&self) {}
262
263 fn set_title(&mut self, title: &str) {}
264
265 fn set_edited(&mut self, edited: bool) {}
266
267 fn show_character_palette(&self) {
268 unimplemented!()
269 }
270
271 fn minimize(&self) {
272 unimplemented!()
273 }
274
275 fn zoom(&self) {
276 unimplemented!()
277 }
278
279 fn toggle_full_screen(&self) {
280 unimplemented!()
281 }
282
283 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
284 self.0.lock().callbacks.request_frame = Some(callback);
285 }
286
287 fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {}
288
289 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {}
290
291 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
292 self.0.lock().callbacks.resize = Some(callback);
293 }
294
295 fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {}
296
297 fn on_moved(&self, callback: Box<dyn FnMut()>) {}
298
299 fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {}
300
301 fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
302
303 fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
304
305 fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
306 unimplemented!()
307 }
308
309 fn invalidate(&self) {}
310
311 fn draw(&self, scene: &crate::Scene) {
312 self.0.lock().renderer.draw(scene);
313 }
314
315 fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
316 self.0.lock().sprite_atlas.clone()
317 }
318}