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