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