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