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