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