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