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