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 if let Some(ime_key) = &event.keystroke.ime_key {
485 let mut state = self.state.borrow_mut();
486 if let Some(mut input_handler) = state.input_handler.take() {
487 drop(state);
488 input_handler.replace_text_in_range(None, ime_key);
489 self.state.borrow_mut().input_handler = Some(input_handler);
490 }
491 }
492 }
493 }
494
495 pub fn set_focused(&self, focus: bool) {
496 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
497 fun(focus);
498 }
499 }
500}
501
502impl HasWindowHandle for WaylandWindow {
503 fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
504 unimplemented!()
505 }
506}
507
508impl HasDisplayHandle for WaylandWindow {
509 fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
510 unimplemented!()
511 }
512}
513
514impl PlatformWindow for WaylandWindow {
515 fn bounds(&self) -> Bounds<DevicePixels> {
516 self.borrow().bounds.map(|p| DevicePixels(p as i32))
517 }
518
519 fn is_maximized(&self) -> bool {
520 self.borrow().maximized
521 }
522
523 fn is_minimized(&self) -> bool {
524 // This cannot be determined by the client
525 false
526 }
527
528 fn content_size(&self) -> Size<Pixels> {
529 let state = self.borrow();
530 Size {
531 width: Pixels(state.bounds.size.width as f32),
532 height: Pixels(state.bounds.size.height as f32),
533 }
534 }
535
536 fn scale_factor(&self) -> f32 {
537 self.borrow().scale
538 }
539
540 // todo(linux)
541 fn appearance(&self) -> WindowAppearance {
542 WindowAppearance::Light
543 }
544
545 // todo(linux)
546 fn display(&self) -> Rc<dyn PlatformDisplay> {
547 Rc::new(WaylandDisplay {})
548 }
549
550 // todo(linux)
551 fn mouse_position(&self) -> Point<Pixels> {
552 Point::default()
553 }
554
555 // todo(linux)
556 fn modifiers(&self) -> Modifiers {
557 crate::Modifiers::default()
558 }
559
560 fn as_any_mut(&mut self) -> &mut dyn Any {
561 self
562 }
563
564 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
565 self.borrow_mut().input_handler = Some(input_handler);
566 }
567
568 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
569 self.borrow_mut().input_handler.take()
570 }
571
572 fn prompt(
573 &self,
574 level: PromptLevel,
575 msg: &str,
576 detail: Option<&str>,
577 answers: &[&str],
578 ) -> Option<Receiver<usize>> {
579 None
580 }
581
582 fn activate(&self) {
583 // todo(linux)
584 }
585
586 // todo(linux)
587 fn is_active(&self) -> bool {
588 false
589 }
590
591 fn set_title(&mut self, title: &str) {
592 self.borrow_mut().toplevel.set_title(title.to_string());
593 }
594
595 fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
596 // todo(linux)
597 }
598
599 fn set_edited(&mut self, edited: bool) {
600 // todo(linux)
601 }
602
603 fn show_character_palette(&self) {
604 // todo(linux)
605 }
606
607 fn minimize(&self) {
608 self.borrow_mut().toplevel.set_minimized();
609 }
610
611 fn zoom(&self) {
612 // todo(linux)
613 }
614
615 fn toggle_fullscreen(&self) {
616 let state = self.borrow_mut();
617 if !state.fullscreen {
618 state.toplevel.set_fullscreen(None);
619 } else {
620 state.toplevel.unset_fullscreen();
621 }
622 }
623
624 fn is_fullscreen(&self) -> bool {
625 self.borrow().fullscreen
626 }
627
628 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
629 self.0.callbacks.borrow_mut().request_frame = Some(callback);
630 }
631
632 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
633 self.0.callbacks.borrow_mut().input = Some(callback);
634 }
635
636 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
637 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
638 }
639
640 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
641 self.0.callbacks.borrow_mut().resize = Some(callback);
642 }
643
644 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
645 self.0.callbacks.borrow_mut().fullscreen = Some(callback);
646 }
647
648 fn on_moved(&self, callback: Box<dyn FnMut()>) {
649 self.0.callbacks.borrow_mut().moved = Some(callback);
650 }
651
652 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
653 self.0.callbacks.borrow_mut().should_close = Some(callback);
654 }
655
656 fn on_close(&self, callback: Box<dyn FnOnce()>) {
657 self.0.callbacks.borrow_mut().close = Some(callback);
658 }
659
660 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
661 // todo(linux)
662 }
663
664 // todo(linux)
665 fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
666 false
667 }
668
669 fn draw(&self, scene: &Scene) {
670 let mut state = self.borrow_mut();
671 state.renderer.draw(scene);
672 }
673
674 fn completed_frame(&self) {
675 let mut state = self.borrow_mut();
676 state.surface.commit();
677 }
678
679 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
680 let state = self.borrow();
681 state.renderer.sprite_atlas().clone()
682 }
683}
684
685#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
686pub enum WaylandDecorationState {
687 /// Decorations are to be provided by the client
688 Client,
689
690 /// Decorations are provided by the server
691 Server,
692}