1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::ffi::c_void;
4use std::num::NonZeroU32;
5use std::rc::{Rc, Weak};
6use std::sync::Arc;
7
8use blade_graphics as gpu;
9use blade_rwh::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle};
10use collections::{HashMap, HashSet};
11use futures::channel::oneshot::Receiver;
12use raw_window_handle::{
13 DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
14};
15use wayland_backend::client::ObjectId;
16use wayland_client::WEnum;
17use wayland_client::{protocol::wl_surface, Proxy};
18use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
19use wayland_protocols::wp::viewporter::client::wp_viewport;
20use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
21use wayland_protocols::xdg::shell::client::xdg_surface;
22use wayland_protocols::xdg::shell::client::xdg_toplevel::{self, WmCapabilities};
23
24use crate::platform::blade::BladeRenderer;
25use crate::platform::linux::wayland::display::WaylandDisplay;
26use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
27use crate::scene::Scene;
28use crate::{
29 px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput,
30 Point, PromptLevel, Size, WaylandClientState, WaylandClientStatePtr, WindowAppearance,
31 WindowBackgroundAppearance, WindowParams,
32};
33
34#[derive(Default)]
35pub(crate) struct Callbacks {
36 request_frame: Option<Box<dyn FnMut()>>,
37 input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
38 active_status_change: Option<Box<dyn FnMut(bool)>>,
39 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
40 fullscreen: Option<Box<dyn FnMut(bool)>>,
41 moved: Option<Box<dyn FnMut()>>,
42 should_close: Option<Box<dyn FnMut() -> bool>>,
43 close: Option<Box<dyn FnOnce()>>,
44 appearance_changed: Option<Box<dyn FnMut()>>,
45}
46
47struct RawWindow {
48 window: *mut c_void,
49 display: *mut c_void,
50}
51
52unsafe impl HasRawWindowHandle for RawWindow {
53 fn raw_window_handle(&self) -> RawWindowHandle {
54 let mut wh = blade_rwh::WaylandWindowHandle::empty();
55 wh.surface = self.window;
56 wh.into()
57 }
58}
59
60unsafe impl HasRawDisplayHandle for RawWindow {
61 fn raw_display_handle(&self) -> RawDisplayHandle {
62 let mut dh = blade_rwh::WaylandDisplayHandle::empty();
63 dh.display = self.display;
64 dh.into()
65 }
66}
67
68pub struct WaylandWindowState {
69 xdg_surface: xdg_surface::XdgSurface,
70 pub surface: wl_surface::WlSurface,
71 toplevel: xdg_toplevel::XdgToplevel,
72 viewport: Option<wp_viewport::WpViewport>,
73 outputs: HashSet<ObjectId>,
74 globals: Globals,
75 renderer: BladeRenderer,
76 bounds: Bounds<u32>,
77 scale: f32,
78 input_handler: Option<PlatformInputHandler>,
79 decoration_state: WaylandDecorationState,
80 fullscreen: bool,
81 maximized: bool,
82 client: WaylandClientStatePtr,
83 callbacks: Callbacks,
84}
85
86#[derive(Clone)]
87pub struct WaylandWindowStatePtr {
88 state: Rc<RefCell<WaylandWindowState>>,
89 callbacks: Rc<RefCell<Callbacks>>,
90}
91
92impl WaylandWindowState {
93 pub(crate) fn new(
94 surface: wl_surface::WlSurface,
95 xdg_surface: xdg_surface::XdgSurface,
96 viewport: Option<wp_viewport::WpViewport>,
97 toplevel: xdg_toplevel::XdgToplevel,
98 client: WaylandClientStatePtr,
99 globals: Globals,
100 options: WindowParams,
101 ) -> Self {
102 let bounds = options.bounds.map(|p| p.0 as u32);
103
104 let raw = RawWindow {
105 window: surface.id().as_ptr().cast::<c_void>(),
106 display: surface
107 .backend()
108 .upgrade()
109 .unwrap()
110 .display_ptr()
111 .cast::<c_void>(),
112 };
113 let gpu = Arc::new(
114 unsafe {
115 gpu::Context::init_windowed(
116 &raw,
117 gpu::ContextDesc {
118 validation: false,
119 capture: false,
120 overlay: false,
121 },
122 )
123 }
124 .unwrap(),
125 );
126 let extent = gpu::Extent {
127 width: bounds.size.width,
128 height: bounds.size.height,
129 depth: 1,
130 };
131
132 Self {
133 xdg_surface,
134 surface,
135 toplevel,
136 viewport,
137 globals,
138
139 outputs: HashSet::default(),
140
141 renderer: BladeRenderer::new(gpu, extent),
142 bounds,
143 scale: 1.0,
144 input_handler: None,
145 decoration_state: WaylandDecorationState::Client,
146 fullscreen: false,
147 maximized: false,
148 callbacks: Callbacks::default(),
149 client,
150 }
151 }
152}
153
154pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
155
156impl Drop for WaylandWindow {
157 fn drop(&mut self) {
158 let mut state = self.0.state.borrow_mut();
159 let surface_id = state.surface.id();
160 let client = state.client.clone();
161 state.renderer.destroy();
162 state.toplevel.destroy();
163 state.xdg_surface.destroy();
164 state.surface.destroy();
165
166 let state_ptr = self.0.clone();
167 state.globals.executor.spawn(async move {
168 state_ptr.close();
169 client.drop_window(&surface_id)
170 });
171 drop(state);
172 }
173}
174
175impl WaylandWindow {
176 fn borrow(&self) -> Ref<WaylandWindowState> {
177 self.0.state.borrow()
178 }
179
180 fn borrow_mut(&self) -> RefMut<WaylandWindowState> {
181 self.0.state.borrow_mut()
182 }
183
184 pub fn new(
185 globals: Globals,
186 client: WaylandClientStatePtr,
187 params: WindowParams,
188 ) -> (Self, ObjectId) {
189 let surface = globals.compositor.create_surface(&globals.qh, ());
190 let xdg_surface = globals
191 .wm_base
192 .get_xdg_surface(&surface, &globals.qh, surface.id());
193 let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
194
195 if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
196 fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
197 }
198
199 // Attempt to set up window decorations based on the requested configuration
200 if let Some(decoration_manager) = globals.decoration_manager.as_ref() {
201 let decoration =
202 decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id());
203
204 // Request client side decorations if possible
205 decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
206 }
207
208 let viewport = globals
209 .viewporter
210 .as_ref()
211 .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
212
213 surface.frame(&globals.qh, surface.id());
214
215 let this = Self(WaylandWindowStatePtr {
216 state: Rc::new(RefCell::new(WaylandWindowState::new(
217 surface.clone(),
218 xdg_surface,
219 viewport,
220 toplevel,
221 client,
222 globals,
223 params,
224 ))),
225 callbacks: Rc::new(RefCell::new(Callbacks::default())),
226 });
227
228 // Kick things off
229 surface.commit();
230
231 (this, surface.id())
232 }
233}
234
235impl WaylandWindowStatePtr {
236 pub fn ptr_eq(&self, other: &Self) -> bool {
237 Rc::ptr_eq(&self.state, &other.state)
238 }
239
240 pub fn frame(&self, from_frame_callback: bool) {
241 if from_frame_callback {
242 let state = self.state.borrow_mut();
243 state.surface.frame(&state.globals.qh, state.surface.id());
244 drop(state);
245 }
246 let mut cb = self.callbacks.borrow_mut();
247 if let Some(fun) = cb.request_frame.as_mut() {
248 fun();
249 }
250 }
251
252 pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
253 match event {
254 xdg_surface::Event::Configure { serial } => {
255 let state = self.state.borrow();
256 state.xdg_surface.ack_configure(serial);
257 drop(state);
258 self.frame(false);
259 }
260 _ => {}
261 }
262 }
263
264 pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
265 match event {
266 zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
267 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
268 self.set_decoration_state(WaylandDecorationState::Server)
269 }
270 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
271 self.set_decoration_state(WaylandDecorationState::Server)
272 }
273 WEnum::Value(_) => {
274 log::warn!("Unknown decoration mode");
275 }
276 WEnum::Unknown(v) => {
277 log::warn!("Unknown decoration mode: {}", v);
278 }
279 },
280 _ => {}
281 }
282 }
283
284 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
285 match event {
286 wp_fractional_scale_v1::Event::PreferredScale { scale } => {
287 self.rescale(scale as f32 / 120.0);
288 }
289 _ => {}
290 }
291 }
292
293 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
294 match event {
295 xdg_toplevel::Event::Configure {
296 width,
297 height,
298 states,
299 } => {
300 let width = NonZeroU32::new(width as u32);
301 let height = NonZeroU32::new(height as u32);
302 let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
303 let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
304 self.resize(width, height);
305 self.set_fullscreen(fullscreen);
306 let mut state = self.state.borrow_mut();
307 state.maximized = true;
308
309 false
310 }
311 xdg_toplevel::Event::Close => {
312 let mut cb = self.callbacks.borrow_mut();
313 if let Some(mut should_close) = cb.should_close.take() {
314 let result = (should_close)();
315 cb.should_close = Some(should_close);
316 if result {
317 drop(cb);
318 self.close();
319 }
320 result
321 } else {
322 false
323 }
324 }
325 _ => false,
326 }
327 }
328
329 pub fn handle_surface_event(
330 &self,
331 event: wl_surface::Event,
332 output_scales: HashMap<ObjectId, i32>,
333 ) {
334 let mut state = self.state.borrow_mut();
335
336 // We use `WpFractionalScale` instead to set the scale if it's available
337 if state.globals.fractional_scale_manager.is_some() {
338 return;
339 }
340
341 match event {
342 wl_surface::Event::Enter { output } => {
343 // We use `PreferredBufferScale` instead to set the scale if it's available
344 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
345 return;
346 }
347
348 state.outputs.insert(output.id());
349
350 let mut scale = 1;
351 for output in state.outputs.iter() {
352 if let Some(s) = output_scales.get(output) {
353 scale = scale.max(*s)
354 }
355 }
356
357 state.surface.set_buffer_scale(scale);
358 drop(state);
359 self.rescale(scale as f32);
360 }
361 wl_surface::Event::Leave { output } => {
362 // We use `PreferredBufferScale` instead to set the scale if it's available
363 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
364 return;
365 }
366
367 state.outputs.remove(&output.id());
368
369 let mut scale = 1;
370 for output in state.outputs.iter() {
371 if let Some(s) = output_scales.get(output) {
372 scale = scale.max(*s)
373 }
374 }
375
376 state.surface.set_buffer_scale(scale);
377 drop(state);
378 self.rescale(scale as f32);
379 }
380 wl_surface::Event::PreferredBufferScale { factor } => {
381 state.surface.set_buffer_scale(factor);
382 drop(state);
383 self.rescale(factor as f32);
384 }
385 _ => {}
386 }
387 }
388
389 pub fn set_size_and_scale(
390 &self,
391 width: Option<NonZeroU32>,
392 height: Option<NonZeroU32>,
393 scale: Option<f32>,
394 ) {
395 let (width, height, scale) = {
396 let mut state = self.state.borrow_mut();
397 if width.map_or(true, |width| width.get() == state.bounds.size.width)
398 && height.map_or(true, |height| height.get() == state.bounds.size.height)
399 && scale.map_or(true, |scale| scale == state.scale)
400 {
401 return;
402 }
403 if let Some(width) = width {
404 state.bounds.size.width = width.get();
405 }
406 if let Some(height) = height {
407 state.bounds.size.height = height.get();
408 }
409 if let Some(scale) = scale {
410 state.scale = scale;
411 }
412 let width = state.bounds.size.width;
413 let height = state.bounds.size.height;
414 let scale = state.scale;
415 state.renderer.update_drawable_size(size(
416 width as f64 * scale as f64,
417 height as f64 * scale as f64,
418 ));
419 (width, height, scale)
420 };
421
422 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
423 fun(
424 Size {
425 width: px(width as f32),
426 height: px(height as f32),
427 },
428 scale,
429 );
430 }
431
432 {
433 let state = self.state.borrow();
434 if let Some(viewport) = &state.viewport {
435 viewport.set_destination(width as i32, height as i32);
436 }
437 }
438 }
439
440 pub fn resize(&self, width: Option<NonZeroU32>, height: Option<NonZeroU32>) {
441 self.set_size_and_scale(width, height, None);
442 }
443
444 pub fn rescale(&self, scale: f32) {
445 self.set_size_and_scale(None, None, Some(scale));
446 }
447
448 pub fn set_fullscreen(&self, fullscreen: bool) {
449 let mut state = self.state.borrow_mut();
450 state.fullscreen = fullscreen;
451
452 let mut callbacks = self.callbacks.borrow_mut();
453 if let Some(ref mut fun) = callbacks.fullscreen {
454 fun(fullscreen)
455 }
456 }
457
458 /// Notifies the window of the state of the decorations.
459 ///
460 /// # Note
461 ///
462 /// This API is indirectly called by the wayland compositor and
463 /// not meant to be called by a user who wishes to change the state
464 /// of the decorations. This is because the state of the decorations
465 /// is managed by the compositor and not the client.
466 pub fn set_decoration_state(&self, state: WaylandDecorationState) {
467 self.state.borrow_mut().decoration_state = state;
468 }
469
470 pub fn close(&self) {
471 let mut callbacks = self.callbacks.borrow_mut();
472 if let Some(fun) = callbacks.close.take() {
473 fun()
474 }
475 }
476
477 pub fn handle_input(&self, input: PlatformInput) {
478 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
479 if !fun(input.clone()).propagate {
480 return;
481 }
482 }
483 if let PlatformInput::KeyDown(event) = input {
484 let mut state = self.state.borrow_mut();
485 if let Some(mut input_handler) = state.input_handler.take() {
486 if let Some(ime_key) = &event.keystroke.ime_key {
487 drop(state);
488 input_handler.replace_text_in_range(None, ime_key);
489 let mut state = self.state.borrow_mut();
490 state.input_handler = Some(input_handler);
491 }
492 }
493 }
494 }
495
496 pub fn set_focused(&self, focus: bool) {
497 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
498 fun(focus);
499 }
500 }
501}
502
503impl HasWindowHandle for WaylandWindow {
504 fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
505 unimplemented!()
506 }
507}
508
509impl HasDisplayHandle for WaylandWindow {
510 fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
511 unimplemented!()
512 }
513}
514
515impl PlatformWindow for WaylandWindow {
516 fn bounds(&self) -> Bounds<DevicePixels> {
517 self.borrow().bounds.map(|p| DevicePixels(p as i32))
518 }
519
520 fn is_maximized(&self) -> bool {
521 self.borrow().maximized
522 }
523
524 fn is_minimized(&self) -> bool {
525 // This cannot be determined by the client
526 false
527 }
528
529 fn content_size(&self) -> Size<Pixels> {
530 let state = self.borrow();
531 Size {
532 width: Pixels(state.bounds.size.width as f32),
533 height: Pixels(state.bounds.size.height as f32),
534 }
535 }
536
537 fn scale_factor(&self) -> f32 {
538 self.borrow().scale
539 }
540
541 // todo(linux)
542 fn appearance(&self) -> WindowAppearance {
543 WindowAppearance::Light
544 }
545
546 // todo(linux)
547 fn display(&self) -> Rc<dyn PlatformDisplay> {
548 Rc::new(WaylandDisplay {})
549 }
550
551 // todo(linux)
552 fn mouse_position(&self) -> Point<Pixels> {
553 Point::default()
554 }
555
556 // todo(linux)
557 fn modifiers(&self) -> Modifiers {
558 crate::Modifiers::default()
559 }
560
561 fn as_any_mut(&mut self) -> &mut dyn Any {
562 self
563 }
564
565 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
566 self.borrow_mut().input_handler = Some(input_handler);
567 }
568
569 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
570 self.borrow_mut().input_handler.take()
571 }
572
573 fn prompt(
574 &self,
575 level: PromptLevel,
576 msg: &str,
577 detail: Option<&str>,
578 answers: &[&str],
579 ) -> Option<Receiver<usize>> {
580 None
581 }
582
583 fn activate(&self) {
584 // todo(linux)
585 }
586
587 // todo(linux)
588 fn is_active(&self) -> bool {
589 false
590 }
591
592 fn set_title(&mut self, title: &str) {
593 self.borrow_mut().toplevel.set_title(title.to_string());
594 }
595
596 fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
597 // todo(linux)
598 }
599
600 fn set_edited(&mut self, edited: bool) {
601 // todo(linux)
602 }
603
604 fn show_character_palette(&self) {
605 // todo(linux)
606 }
607
608 fn minimize(&self) {
609 self.borrow_mut().toplevel.set_minimized();
610 }
611
612 fn zoom(&self) {
613 // todo(linux)
614 }
615
616 fn toggle_fullscreen(&self) {
617 let state = self.borrow_mut();
618 if !state.fullscreen {
619 state.toplevel.set_fullscreen(None);
620 } else {
621 state.toplevel.unset_fullscreen();
622 }
623 }
624
625 fn is_fullscreen(&self) -> bool {
626 self.borrow().fullscreen
627 }
628
629 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
630 self.0.callbacks.borrow_mut().request_frame = Some(callback);
631 }
632
633 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
634 self.0.callbacks.borrow_mut().input = Some(callback);
635 }
636
637 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
638 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
639 }
640
641 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
642 self.0.callbacks.borrow_mut().resize = Some(callback);
643 }
644
645 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
646 self.0.callbacks.borrow_mut().fullscreen = Some(callback);
647 }
648
649 fn on_moved(&self, callback: Box<dyn FnMut()>) {
650 self.0.callbacks.borrow_mut().moved = Some(callback);
651 }
652
653 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
654 self.0.callbacks.borrow_mut().should_close = Some(callback);
655 }
656
657 fn on_close(&self, callback: Box<dyn FnOnce()>) {
658 self.0.callbacks.borrow_mut().close = Some(callback);
659 }
660
661 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
662 // todo(linux)
663 }
664
665 // todo(linux)
666 fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
667 false
668 }
669
670 fn draw(&self, scene: &Scene) {
671 let mut state = self.borrow_mut();
672 state.renderer.draw(scene);
673 }
674
675 fn completed_frame(&self) {
676 let mut state = self.borrow_mut();
677 state.surface.commit();
678 }
679
680 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
681 let state = self.borrow();
682 state.renderer.sprite_atlas().clone()
683 }
684}
685
686#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
687pub enum WaylandDecorationState {
688 /// Decorations are to be provided by the client
689 Client,
690
691 /// Decorations are provided by the server
692 Server,
693}