1//todo!(linux): remove
2#![allow(unused)]
3
4use super::BladeRenderer;
5use crate::{
6 Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
7 PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
8};
9use blade_graphics as gpu;
10use parking_lot::Mutex;
11use raw_window_handle as rwh;
12use std::{
13 ffi::c_void,
14 mem,
15 num::NonZeroU32,
16 ptr::NonNull,
17 rc::Rc,
18 sync::{self, Arc},
19};
20use xcb::{
21 x::{self, StackMode},
22 Xid as _,
23};
24
25#[derive(Default)]
26struct Callbacks {
27 request_frame: Option<Box<dyn FnMut()>>,
28 input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
29 active_status_change: Option<Box<dyn FnMut(bool)>>,
30 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
31 fullscreen: Option<Box<dyn FnMut(bool)>>,
32 moved: Option<Box<dyn FnMut()>>,
33 should_close: Option<Box<dyn FnMut() -> bool>>,
34 close: Option<Box<dyn FnOnce()>>,
35 appearance_changed: Option<Box<dyn FnMut()>>,
36}
37
38struct LinuxWindowInner {
39 bounds: Bounds<i32>,
40 scale_factor: f32,
41 renderer: BladeRenderer,
42}
43
44impl LinuxWindowInner {
45 fn content_size(&self) -> Size<Pixels> {
46 let size = self.renderer.viewport_size();
47 Size {
48 width: size.width.into(),
49 height: size.height.into(),
50 }
51 }
52}
53
54fn query_render_extent(xcb_connection: &xcb::Connection, x_window: x::Window) -> gpu::Extent {
55 let cookie = xcb_connection.send_request(&x::GetGeometry {
56 drawable: x::Drawable::Window(x_window),
57 });
58 let reply = xcb_connection.wait_for_reply(cookie).unwrap();
59 gpu::Extent {
60 width: reply.width() as u32,
61 height: reply.height() as u32,
62 depth: 1,
63 }
64}
65
66struct RawWindow {
67 connection: *mut c_void,
68 screen_id: i32,
69 window_id: u32,
70 visual_id: u32,
71}
72
73pub(crate) struct LinuxWindowState {
74 xcb_connection: Arc<xcb::Connection>,
75 display: Rc<dyn PlatformDisplay>,
76 raw: RawWindow,
77 x_window: x::Window,
78 callbacks: Mutex<Callbacks>,
79 inner: Mutex<LinuxWindowInner>,
80}
81
82#[derive(Clone)]
83pub(crate) struct LinuxWindow(pub(crate) Rc<LinuxWindowState>);
84
85//todo!(linux): Remove other RawWindowHandle implementation
86unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
87 fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
88 let mut wh = blade_rwh::XcbWindowHandle::empty();
89 wh.window = self.window_id;
90 wh.visual_id = self.visual_id;
91 wh.into()
92 }
93}
94unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
95 fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
96 let mut dh = blade_rwh::XcbDisplayHandle::empty();
97 dh.connection = self.connection;
98 dh.screen = self.screen_id;
99 dh.into()
100 }
101}
102
103impl rwh::HasWindowHandle for LinuxWindow {
104 fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
105 Ok(unsafe {
106 let non_zero = NonZeroU32::new(self.0.raw.window_id).unwrap();
107 let handle = rwh::XcbWindowHandle::new(non_zero);
108 rwh::WindowHandle::borrow_raw(handle.into())
109 })
110 }
111}
112impl rwh::HasDisplayHandle for LinuxWindow {
113 fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
114 Ok(unsafe {
115 let non_zero = NonNull::new(self.0.raw.connection).unwrap();
116 let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id);
117 rwh::DisplayHandle::borrow_raw(handle.into())
118 })
119 }
120}
121
122impl LinuxWindowState {
123 pub fn new(
124 options: WindowOptions,
125 xcb_connection: &Arc<xcb::Connection>,
126 x_main_screen_index: i32,
127 x_window: x::Window,
128 atoms: &XcbAtoms,
129 ) -> Self {
130 let x_screen_index = options
131 .display_id
132 .map_or(x_main_screen_index, |did| did.0 as i32);
133 let screen = xcb_connection
134 .get_setup()
135 .roots()
136 .nth(x_screen_index as usize)
137 .unwrap();
138
139 let xcb_values = [
140 x::Cw::BackPixel(screen.white_pixel()),
141 x::Cw::EventMask(
142 x::EventMask::EXPOSURE | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::KEY_PRESS,
143 ),
144 ];
145
146 let bounds = match options.bounds {
147 WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
148 origin: Point::default(),
149 size: Size {
150 width: screen.width_in_pixels() as i32,
151 height: screen.height_in_pixels() as i32,
152 },
153 },
154 WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
155 };
156
157 xcb_connection.send_request(&x::CreateWindow {
158 depth: x::COPY_FROM_PARENT as u8,
159 wid: x_window,
160 parent: screen.root(),
161 x: bounds.origin.x as i16,
162 y: bounds.origin.y as i16,
163 width: bounds.size.width as u16,
164 height: bounds.size.height as u16,
165 border_width: 0,
166 class: x::WindowClass::InputOutput,
167 visual: screen.root_visual(),
168 value_list: &xcb_values,
169 });
170
171 if let Some(titlebar) = options.titlebar {
172 if let Some(title) = titlebar.title {
173 xcb_connection.send_request(&x::ChangeProperty {
174 mode: x::PropMode::Replace,
175 window: x_window,
176 property: x::ATOM_WM_NAME,
177 r#type: x::ATOM_STRING,
178 data: title.as_bytes(),
179 });
180 }
181 }
182 xcb_connection
183 .send_and_check_request(&x::ChangeProperty {
184 mode: x::PropMode::Replace,
185 window: x_window,
186 property: atoms.wm_protocols,
187 r#type: x::ATOM_ATOM,
188 data: &[atoms.wm_del_window],
189 })
190 .unwrap();
191
192 xcb_connection.send_request(&x::MapWindow { window: x_window });
193 xcb_connection.flush().unwrap();
194
195 let raw = RawWindow {
196 connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
197 xcb_connection,
198 ) as *mut _,
199 screen_id: x_screen_index,
200 window_id: x_window.resource_id(),
201 visual_id: screen.root_visual(),
202 };
203 let gpu = Arc::new(
204 unsafe {
205 gpu::Context::init_windowed(
206 &raw,
207 gpu::ContextDesc {
208 validation: cfg!(debug_assertions),
209 capture: false,
210 },
211 )
212 }
213 .unwrap(),
214 );
215
216 // Note: this has to be done after the GPU init, or otherwise
217 // the sizes are immediately invalidated.
218 let gpu_extent = query_render_extent(&xcb_connection, x_window);
219
220 Self {
221 xcb_connection: Arc::clone(xcb_connection),
222 display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)),
223 raw,
224 x_window,
225 callbacks: Mutex::new(Callbacks::default()),
226 inner: Mutex::new(LinuxWindowInner {
227 bounds,
228 scale_factor: 1.0,
229 renderer: BladeRenderer::new(gpu, gpu_extent),
230 }),
231 }
232 }
233
234 pub fn destroy(&self) {
235 self.inner.lock().renderer.destroy();
236 self.xcb_connection.send_request(&x::UnmapWindow {
237 window: self.x_window,
238 });
239 self.xcb_connection.send_request(&x::DestroyWindow {
240 window: self.x_window,
241 });
242 if let Some(fun) = self.callbacks.lock().close.take() {
243 fun();
244 }
245 self.xcb_connection.flush().unwrap();
246 }
247
248 pub fn expose(&self) {
249 let mut cb = self.callbacks.lock();
250 if let Some(ref mut fun) = cb.request_frame {
251 fun();
252 }
253 }
254
255 pub fn configure(&self, bounds: Bounds<i32>) {
256 let mut resize_args = None;
257 let do_move;
258 {
259 let mut inner = self.inner.lock();
260 let old_bounds = mem::replace(&mut inner.bounds, bounds);
261 do_move = old_bounds.origin != bounds.origin;
262 let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
263 if inner.renderer.viewport_size() != gpu_size {
264 inner.renderer.resize(gpu_size);
265 resize_args = Some((inner.content_size(), inner.scale_factor));
266 }
267 }
268
269 let mut callbacks = self.callbacks.lock();
270 if let Some((content_size, scale_factor)) = resize_args {
271 if let Some(ref mut fun) = callbacks.resize {
272 fun(content_size, scale_factor)
273 }
274 }
275 if do_move {
276 if let Some(ref mut fun) = callbacks.moved {
277 fun()
278 }
279 }
280 }
281}
282
283impl PlatformWindow for LinuxWindow {
284 fn bounds(&self) -> WindowBounds {
285 WindowBounds::Fixed(self.0.inner.lock().bounds.map(|v| GlobalPixels(v as f32)))
286 }
287
288 fn content_size(&self) -> Size<Pixels> {
289 self.0.inner.lock().content_size()
290 }
291
292 fn scale_factor(&self) -> f32 {
293 self.0.inner.lock().scale_factor
294 }
295
296 //todo!(linux)
297 fn titlebar_height(&self) -> Pixels {
298 unimplemented!()
299 }
300
301 //todo!(linux)
302 fn appearance(&self) -> WindowAppearance {
303 WindowAppearance::Light
304 }
305
306 fn display(&self) -> Rc<dyn PlatformDisplay> {
307 Rc::clone(&self.0.display)
308 }
309
310 //todo!(linux)
311 fn mouse_position(&self) -> Point<Pixels> {
312 Point::default()
313 }
314
315 //todo!(linux)
316 fn modifiers(&self) -> crate::Modifiers {
317 crate::Modifiers::default()
318 }
319
320 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
321 self
322 }
323
324 //todo!(linux)
325 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
326
327 //todo!(linux)
328 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
329 None
330 }
331
332 //todo!(linux)
333 fn prompt(
334 &self,
335 _level: crate::PromptLevel,
336 _msg: &str,
337 _detail: Option<&str>,
338 _answers: &[&str],
339 ) -> futures::channel::oneshot::Receiver<usize> {
340 unimplemented!()
341 }
342
343 fn activate(&self) {
344 self.0.xcb_connection.send_request(&x::ConfigureWindow {
345 window: self.0.x_window,
346 value_list: &[x::ConfigWindow::StackMode(StackMode::Above)],
347 });
348 }
349
350 fn set_title(&mut self, title: &str) {
351 self.0.xcb_connection.send_request(&x::ChangeProperty {
352 mode: x::PropMode::Replace,
353 window: self.0.x_window,
354 property: x::ATOM_WM_NAME,
355 r#type: x::ATOM_STRING,
356 data: title.as_bytes(),
357 });
358 }
359
360 //todo!(linux)
361 fn set_edited(&mut self, edited: bool) {}
362
363 //todo!(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
364 // but it looks like the equivalent for Linux is GTK specific:
365 //
366 // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
367 //
368 // This API might need to change, or we might need to build an emoji picker into GPUI
369 fn show_character_palette(&self) {
370 unimplemented!()
371 }
372
373 //todo!(linux)
374 fn minimize(&self) {
375 unimplemented!()
376 }
377
378 //todo!(linux)
379 fn zoom(&self) {
380 unimplemented!()
381 }
382
383 //todo!(linux)
384 fn toggle_full_screen(&self) {
385 unimplemented!()
386 }
387
388 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
389 self.0.callbacks.lock().request_frame = Some(callback);
390 }
391
392 fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {
393 self.0.callbacks.lock().input = Some(callback);
394 }
395
396 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
397 self.0.callbacks.lock().active_status_change = Some(callback);
398 }
399
400 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
401 self.0.callbacks.lock().resize = Some(callback);
402 }
403
404 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
405 self.0.callbacks.lock().fullscreen = Some(callback);
406 }
407
408 fn on_moved(&self, callback: Box<dyn FnMut()>) {
409 self.0.callbacks.lock().moved = Some(callback);
410 }
411
412 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
413 self.0.callbacks.lock().should_close = Some(callback);
414 }
415
416 fn on_close(&self, callback: Box<dyn FnOnce()>) {
417 self.0.callbacks.lock().close = Some(callback);
418 }
419
420 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
421 self.0.callbacks.lock().appearance_changed = Some(callback);
422 }
423
424 //todo!(linux)
425 fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
426 unimplemented!()
427 }
428
429 fn draw(&self, scene: &crate::Scene) {
430 let mut inner = self.0.inner.lock();
431 inner.renderer.draw(scene);
432 }
433
434 fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
435 let inner = self.0.inner.lock();
436 inner.renderer.atlas().clone()
437 }
438
439 fn set_graphics_profiler_enabled(&self, enabled: bool) {
440 unimplemented!("linux")
441 }
442}