1// todo(linux): remove
2#![allow(unused)]
3
4use crate::{
5 platform::blade::BladeRenderer, size, Bounds, DevicePixels, Modifiers, Pixels, PlatformAtlas,
6 PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
7 Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams,
8 X11Client, X11ClientState,
9};
10use blade_graphics as gpu;
11use parking_lot::Mutex;
12use raw_window_handle as rwh;
13use util::ResultExt;
14use x11rb::{
15 connection::Connection,
16 protocol::{
17 xinput,
18 xproto::{self, ConnectionExt as _, CreateWindowAux},
19 },
20 wrapper::ConnectionExt,
21 xcb_ffi::XCBConnection,
22};
23
24use std::{
25 cell::{Ref, RefCell, RefMut},
26 ffi::c_void,
27 iter::Zip,
28 mem,
29 num::NonZeroU32,
30 ptr::NonNull,
31 rc::Rc,
32 sync::{self, Arc},
33};
34
35use super::X11Display;
36
37x11rb::atom_manager! {
38 pub XcbAtoms: AtomsCookie {
39 UTF8_STRING,
40 WM_PROTOCOLS,
41 WM_DELETE_WINDOW,
42 _NET_WM_NAME,
43 _NET_WM_STATE,
44 _NET_WM_STATE_MAXIMIZED_VERT,
45 _NET_WM_STATE_MAXIMIZED_HORZ,
46 }
47}
48
49fn query_render_extent(xcb_connection: &XCBConnection, x_window: xproto::Window) -> gpu::Extent {
50 let reply = xcb_connection
51 .get_geometry(x_window)
52 .unwrap()
53 .reply()
54 .unwrap();
55 gpu::Extent {
56 width: reply.width as u32,
57 height: reply.height as u32,
58 depth: 1,
59 }
60}
61
62struct RawWindow {
63 connection: *mut c_void,
64 screen_id: usize,
65 window_id: u32,
66 visual_id: u32,
67}
68
69#[derive(Default)]
70pub struct Callbacks {
71 request_frame: Option<Box<dyn FnMut()>>,
72 input: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
73 active_status_change: Option<Box<dyn FnMut(bool)>>,
74 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
75 fullscreen: Option<Box<dyn FnMut(bool)>>,
76 moved: Option<Box<dyn FnMut()>>,
77 should_close: Option<Box<dyn FnMut() -> bool>>,
78 close: Option<Box<dyn FnOnce()>>,
79 appearance_changed: Option<Box<dyn FnMut()>>,
80}
81
82pub(crate) struct X11WindowState {
83 raw: RawWindow,
84 atoms: XcbAtoms,
85 bounds: Bounds<i32>,
86 scale_factor: f32,
87 renderer: BladeRenderer,
88 display: Rc<dyn PlatformDisplay>,
89
90 input_handler: Option<PlatformInputHandler>,
91}
92
93#[derive(Clone)]
94pub(crate) struct X11Window {
95 pub(crate) state: Rc<RefCell<X11WindowState>>,
96 pub(crate) callbacks: Rc<RefCell<Callbacks>>,
97 xcb_connection: Rc<XCBConnection>,
98 x_window: xproto::Window,
99}
100
101// todo(linux): Remove other RawWindowHandle implementation
102unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
103 fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
104 let mut wh = blade_rwh::XcbWindowHandle::empty();
105 wh.window = self.window_id;
106 wh.visual_id = self.visual_id;
107 wh.into()
108 }
109}
110unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
111 fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
112 let mut dh = blade_rwh::XcbDisplayHandle::empty();
113 dh.connection = self.connection;
114 dh.screen = self.screen_id as i32;
115 dh.into()
116 }
117}
118
119impl rwh::HasWindowHandle for X11Window {
120 fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
121 Ok(unsafe {
122 let non_zero = NonZeroU32::new(self.state.borrow().raw.window_id).unwrap();
123 let handle = rwh::XcbWindowHandle::new(non_zero);
124 rwh::WindowHandle::borrow_raw(handle.into())
125 })
126 }
127}
128impl rwh::HasDisplayHandle for X11Window {
129 fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
130 Ok(unsafe {
131 let this = self.state.borrow();
132 let non_zero = NonNull::new(this.raw.connection).unwrap();
133 let handle = rwh::XcbDisplayHandle::new(Some(non_zero), this.raw.screen_id as i32);
134 rwh::DisplayHandle::borrow_raw(handle.into())
135 })
136 }
137}
138
139impl X11WindowState {
140 pub fn new(
141 params: WindowParams,
142 xcb_connection: &Rc<XCBConnection>,
143 x_main_screen_index: usize,
144 x_window: xproto::Window,
145 atoms: &XcbAtoms,
146 scroll_devices: &Vec<xinput::DeviceInfo>,
147 ) -> Self {
148 let x_screen_index = params
149 .display_id
150 .map_or(x_main_screen_index, |did| did.0 as usize);
151 let screen = xcb_connection.setup().roots.get(x_screen_index).unwrap();
152
153 let win_aux = xproto::CreateWindowAux::new().event_mask(
154 xproto::EventMask::EXPOSURE
155 | xproto::EventMask::STRUCTURE_NOTIFY
156 | xproto::EventMask::ENTER_WINDOW
157 | xproto::EventMask::LEAVE_WINDOW
158 | xproto::EventMask::FOCUS_CHANGE
159 | xproto::EventMask::KEY_PRESS
160 | xproto::EventMask::KEY_RELEASE
161 | xproto::EventMask::BUTTON_PRESS
162 | xproto::EventMask::BUTTON_RELEASE
163 | xproto::EventMask::POINTER_MOTION
164 | xproto::EventMask::BUTTON1_MOTION
165 | xproto::EventMask::BUTTON2_MOTION
166 | xproto::EventMask::BUTTON3_MOTION
167 | xproto::EventMask::BUTTON_MOTION,
168 );
169
170 xcb_connection
171 .create_window(
172 x11rb::COPY_FROM_PARENT as _,
173 x_window,
174 screen.root,
175 params.bounds.origin.x.0 as i16,
176 params.bounds.origin.y.0 as i16,
177 params.bounds.size.width.0 as u16,
178 params.bounds.size.height.0 as u16,
179 0,
180 xproto::WindowClass::INPUT_OUTPUT,
181 screen.root_visual,
182 &win_aux,
183 )
184 .unwrap();
185
186 for device in scroll_devices {
187 xinput::ConnectionExt::xinput_xi_select_events(
188 &xcb_connection,
189 x_window,
190 &[xinput::EventMask {
191 deviceid: device.device_id as u16,
192 mask: vec![xinput::XIEventMask::MOTION],
193 }],
194 )
195 .unwrap()
196 .check()
197 .unwrap();
198 }
199
200 if let Some(titlebar) = params.titlebar {
201 if let Some(title) = titlebar.title {
202 xcb_connection
203 .change_property8(
204 xproto::PropMode::REPLACE,
205 x_window,
206 xproto::AtomEnum::WM_NAME,
207 xproto::AtomEnum::STRING,
208 title.as_bytes(),
209 )
210 .unwrap();
211 }
212 }
213
214 xcb_connection
215 .change_property32(
216 xproto::PropMode::REPLACE,
217 x_window,
218 atoms.WM_PROTOCOLS,
219 xproto::AtomEnum::ATOM,
220 &[atoms.WM_DELETE_WINDOW],
221 )
222 .unwrap();
223
224 xcb_connection.map_window(x_window).unwrap();
225 xcb_connection.flush().unwrap();
226
227 let raw = RawWindow {
228 connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
229 xcb_connection,
230 ) as *mut _,
231 screen_id: x_screen_index,
232 window_id: x_window,
233 visual_id: screen.root_visual,
234 };
235 let gpu = Arc::new(
236 unsafe {
237 gpu::Context::init_windowed(
238 &raw,
239 gpu::ContextDesc {
240 validation: false,
241 capture: false,
242 overlay: 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 display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
255 raw,
256 bounds: params.bounds.map(|v| v.0),
257 scale_factor: 1.0,
258 renderer: BladeRenderer::new(gpu, gpu_extent),
259 atoms: *atoms,
260
261 input_handler: None,
262 }
263 }
264
265 fn content_size(&self) -> Size<Pixels> {
266 let size = self.renderer.viewport_size();
267 Size {
268 width: size.width.into(),
269 height: size.height.into(),
270 }
271 }
272}
273
274impl X11Window {
275 pub fn new(
276 params: WindowParams,
277 xcb_connection: &Rc<XCBConnection>,
278 x_main_screen_index: usize,
279 x_window: xproto::Window,
280 atoms: &XcbAtoms,
281 scroll_devices: &Vec<xinput::DeviceInfo>,
282 ) -> Self {
283 X11Window {
284 state: Rc::new(RefCell::new(X11WindowState::new(
285 params,
286 xcb_connection,
287 x_main_screen_index,
288 x_window,
289 atoms,
290 scroll_devices,
291 ))),
292 callbacks: Rc::new(RefCell::new(Callbacks::default())),
293 xcb_connection: xcb_connection.clone(),
294 x_window,
295 }
296 }
297
298 pub fn destroy(&self) {
299 let mut state = self.state.borrow_mut();
300 state.renderer.destroy();
301 drop(state);
302
303 self.xcb_connection.unmap_window(self.x_window).unwrap();
304 self.xcb_connection.destroy_window(self.x_window).unwrap();
305 if let Some(fun) = self.callbacks.borrow_mut().close.take() {
306 fun();
307 }
308 self.xcb_connection.flush().unwrap();
309 }
310
311 pub fn refresh(&self) {
312 let mut cb = self.callbacks.borrow_mut();
313 if let Some(ref mut fun) = cb.request_frame {
314 fun();
315 }
316 }
317
318 pub fn handle_input(&self, input: PlatformInput) {
319 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
320 if !fun(input.clone()).propagate {
321 return;
322 }
323 }
324 if let PlatformInput::KeyDown(event) = input {
325 let mut state = self.state.borrow_mut();
326 if let Some(mut input_handler) = state.input_handler.take() {
327 if let Some(ime_key) = &event.keystroke.ime_key {
328 drop(state);
329 input_handler.replace_text_in_range(None, ime_key);
330 state = self.state.borrow_mut();
331 }
332 state.input_handler = Some(input_handler);
333 }
334 }
335 }
336
337 pub fn configure(&self, bounds: Bounds<i32>) {
338 let mut resize_args = None;
339 let do_move;
340 {
341 let mut state = self.state.borrow_mut();
342 let old_bounds = mem::replace(&mut state.bounds, bounds);
343 do_move = old_bounds.origin != bounds.origin;
344 // todo(linux): use normal GPUI types here, refactor out the double
345 // viewport check and extra casts ( )
346 let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
347 if state.renderer.viewport_size() != gpu_size {
348 state
349 .renderer
350 .update_drawable_size(size(gpu_size.width as f64, gpu_size.height as f64));
351 resize_args = Some((state.content_size(), state.scale_factor));
352 }
353 }
354
355 let mut callbacks = self.callbacks.borrow_mut();
356 if let Some((content_size, scale_factor)) = resize_args {
357 if let Some(ref mut fun) = callbacks.resize {
358 fun(content_size, scale_factor)
359 }
360 }
361 if do_move {
362 if let Some(ref mut fun) = callbacks.moved {
363 fun()
364 }
365 }
366 }
367
368 pub fn set_focused(&self, focus: bool) {
369 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
370 fun(focus);
371 }
372 }
373}
374
375impl PlatformWindow for X11Window {
376 fn bounds(&self) -> Bounds<DevicePixels> {
377 self.state.borrow_mut().bounds.map(|v| v.into())
378 }
379
380 // todo(linux)
381 fn is_maximized(&self) -> bool {
382 false
383 }
384
385 // todo(linux)
386 fn is_minimized(&self) -> bool {
387 false
388 }
389
390 fn content_size(&self) -> Size<Pixels> {
391 self.state.borrow_mut().content_size()
392 }
393
394 fn scale_factor(&self) -> f32 {
395 self.state.borrow_mut().scale_factor
396 }
397
398 // todo(linux)
399 fn appearance(&self) -> WindowAppearance {
400 WindowAppearance::Light
401 }
402
403 fn display(&self) -> Rc<dyn PlatformDisplay> {
404 self.state.borrow().display.clone()
405 }
406
407 fn mouse_position(&self) -> Point<Pixels> {
408 let reply = self
409 .xcb_connection
410 .query_pointer(self.x_window)
411 .unwrap()
412 .reply()
413 .unwrap();
414 Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
415 }
416
417 // todo(linux)
418 fn modifiers(&self) -> Modifiers {
419 Modifiers::default()
420 }
421
422 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
423 self
424 }
425
426 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
427 self.state.borrow_mut().input_handler = Some(input_handler);
428 }
429
430 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
431 self.state.borrow_mut().input_handler.take()
432 }
433
434 fn prompt(
435 &self,
436 _level: PromptLevel,
437 _msg: &str,
438 _detail: Option<&str>,
439 _answers: &[&str],
440 ) -> Option<futures::channel::oneshot::Receiver<usize>> {
441 None
442 }
443
444 fn activate(&self) {
445 let win_aux = xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE);
446 self.xcb_connection
447 .configure_window(self.x_window, &win_aux)
448 .log_err();
449 }
450
451 // todo(linux)
452 fn is_active(&self) -> bool {
453 false
454 }
455
456 fn set_title(&mut self, title: &str) {
457 self.xcb_connection
458 .change_property8(
459 xproto::PropMode::REPLACE,
460 self.x_window,
461 xproto::AtomEnum::WM_NAME,
462 xproto::AtomEnum::STRING,
463 title.as_bytes(),
464 )
465 .unwrap();
466
467 self.xcb_connection
468 .change_property8(
469 xproto::PropMode::REPLACE,
470 self.x_window,
471 self.state.borrow().atoms._NET_WM_NAME,
472 self.state.borrow().atoms.UTF8_STRING,
473 title.as_bytes(),
474 )
475 .unwrap();
476 }
477
478 // todo(linux)
479 fn set_edited(&mut self, edited: bool) {}
480
481 fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
482 // todo(linux)
483 }
484
485 // todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
486 // but it looks like the equivalent for Linux is GTK specific:
487 //
488 // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
489 //
490 // This API might need to change, or we might need to build an emoji picker into GPUI
491 fn show_character_palette(&self) {
492 unimplemented!()
493 }
494
495 // todo(linux)
496 fn minimize(&self) {
497 unimplemented!()
498 }
499
500 // todo(linux)
501 fn zoom(&self) {
502 unimplemented!()
503 }
504
505 // todo(linux)
506 fn toggle_fullscreen(&self) {
507 unimplemented!()
508 }
509
510 // todo(linux)
511 fn is_fullscreen(&self) -> bool {
512 false
513 }
514
515 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
516 self.callbacks.borrow_mut().request_frame = Some(callback);
517 }
518
519 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
520 self.callbacks.borrow_mut().input = Some(callback);
521 }
522
523 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
524 self.callbacks.borrow_mut().active_status_change = Some(callback);
525 }
526
527 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
528 self.callbacks.borrow_mut().resize = Some(callback);
529 }
530
531 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
532 self.callbacks.borrow_mut().fullscreen = Some(callback);
533 }
534
535 fn on_moved(&self, callback: Box<dyn FnMut()>) {
536 self.callbacks.borrow_mut().moved = Some(callback);
537 }
538
539 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
540 self.callbacks.borrow_mut().should_close = Some(callback);
541 }
542
543 fn on_close(&self, callback: Box<dyn FnOnce()>) {
544 self.callbacks.borrow_mut().close = Some(callback);
545 }
546
547 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
548 self.callbacks.borrow_mut().appearance_changed = Some(callback);
549 }
550
551 // todo(linux)
552 fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
553 unimplemented!()
554 }
555
556 fn draw(&self, scene: &Scene) {
557 let mut inner = self.state.borrow_mut();
558 inner.renderer.draw(scene);
559 }
560
561 fn sprite_atlas(&self) -> sync::Arc<dyn PlatformAtlas> {
562 let inner = self.state.borrow();
563 inner.renderer.sprite_atlas().clone()
564 }
565}