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