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