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