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