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