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