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 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_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
213 match event {
214 zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
215 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
216 self.set_decoration_state(WaylandDecorationState::Server)
217 }
218 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
219 self.set_decoration_state(WaylandDecorationState::Server)
220 }
221 WEnum::Value(_) => {
222 log::warn!("Unknown decoration mode");
223 }
224 WEnum::Unknown(v) => {
225 log::warn!("Unknown decoration mode: {}", v);
226 }
227 },
228 _ => {}
229 }
230 }
231
232 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
233 match event {
234 wp_fractional_scale_v1::Event::PreferredScale { scale } => {
235 self.rescale(scale as f32 / 120.0);
236 }
237 _ => {}
238 }
239 }
240
241 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
242 match event {
243 xdg_toplevel::Event::Configure {
244 width,
245 height,
246 states,
247 } => {
248 let width = NonZeroU32::new(width as u32);
249 let height = NonZeroU32::new(height as u32);
250 let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
251 let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
252 self.resize(width, height);
253 self.set_fullscreen(fullscreen);
254 let mut state = self.state.borrow_mut();
255 state.maximized = true;
256 false
257 }
258 xdg_toplevel::Event::Close => {
259 let mut cb = self.callbacks.borrow_mut();
260 if let Some(mut should_close) = cb.should_close.take() {
261 let result = (should_close)();
262 cb.should_close = Some(should_close);
263 result
264 } else {
265 false
266 }
267 }
268 _ => false,
269 }
270 }
271
272 pub fn handle_surface_event(
273 &self,
274 event: wl_surface::Event,
275 output_scales: HashMap<ObjectId, i32>,
276 ) {
277 let mut state = self.state.borrow_mut();
278
279 // We use `WpFractionalScale` instead to set the scale if it's available
280 if state.globals.fractional_scale_manager.is_some() {
281 return;
282 }
283
284 match event {
285 wl_surface::Event::Enter { output } => {
286 // We use `PreferredBufferScale` instead to set the scale if it's available
287 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
288 return;
289 }
290
291 state.outputs.insert(output.id());
292
293 let mut scale = 1;
294 for output in state.outputs.iter() {
295 if let Some(s) = output_scales.get(output) {
296 scale = scale.max(*s)
297 }
298 }
299
300 state.surface.set_buffer_scale(scale);
301 drop(state);
302 self.rescale(scale as f32);
303 }
304 wl_surface::Event::Leave { output } => {
305 // We use `PreferredBufferScale` instead to set the scale if it's available
306 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
307 return;
308 }
309
310 state.outputs.remove(&output.id());
311
312 let mut scale = 1;
313 for output in state.outputs.iter() {
314 if let Some(s) = output_scales.get(output) {
315 scale = scale.max(*s)
316 }
317 }
318
319 state.surface.set_buffer_scale(scale);
320 drop(state);
321 self.rescale(scale as f32);
322 }
323 wl_surface::Event::PreferredBufferScale { factor } => {
324 state.surface.set_buffer_scale(factor);
325 drop(state);
326 self.rescale(factor as f32);
327 }
328 _ => {}
329 }
330 }
331
332 pub fn set_size_and_scale(
333 &self,
334 width: Option<NonZeroU32>,
335 height: Option<NonZeroU32>,
336 scale: Option<f32>,
337 ) {
338 let (width, height, scale) = {
339 let mut state = self.state.borrow_mut();
340 if width.map_or(true, |width| width.get() == state.bounds.size.width)
341 && height.map_or(true, |height| height.get() == state.bounds.size.height)
342 && scale.map_or(true, |scale| scale == state.scale)
343 {
344 return;
345 }
346 if let Some(width) = width {
347 state.bounds.size.width = width.get();
348 }
349 if let Some(height) = height {
350 state.bounds.size.height = height.get();
351 }
352 if let Some(scale) = scale {
353 state.scale = scale;
354 }
355 let width = state.bounds.size.width;
356 let height = state.bounds.size.height;
357 let scale = state.scale;
358 state.renderer.update_drawable_size(size(
359 width as f64 * scale as f64,
360 height as f64 * scale as f64,
361 ));
362 (width, height, scale)
363 };
364
365 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
366 fun(
367 Size {
368 width: px(width as f32),
369 height: px(height as f32),
370 },
371 scale,
372 );
373 }
374
375 {
376 let state = self.state.borrow();
377 if let Some(viewport) = &state.viewport {
378 viewport.set_destination(width as i32, height as i32);
379 }
380 }
381 }
382
383 pub fn resize(&self, width: Option<NonZeroU32>, height: Option<NonZeroU32>) {
384 self.set_size_and_scale(width, height, None);
385 }
386
387 pub fn rescale(&self, scale: f32) {
388 self.set_size_and_scale(None, None, Some(scale));
389 }
390
391 pub fn set_fullscreen(&self, fullscreen: bool) {
392 let mut state = self.state.borrow_mut();
393 state.fullscreen = fullscreen;
394
395 let mut callbacks = self.callbacks.borrow_mut();
396 if let Some(ref mut fun) = callbacks.fullscreen {
397 fun(fullscreen)
398 }
399 }
400
401 /// Notifies the window of the state of the decorations.
402 ///
403 /// # Note
404 ///
405 /// This API is indirectly called by the wayland compositor and
406 /// not meant to be called by a user who wishes to change the state
407 /// of the decorations. This is because the state of the decorations
408 /// is managed by the compositor and not the client.
409 pub fn set_decoration_state(&self, state: WaylandDecorationState) {
410 self.state.borrow_mut().decoration_state = state;
411 }
412
413 pub fn close(&self) {
414 let mut callbacks = self.callbacks.borrow_mut();
415 if let Some(fun) = callbacks.close.take() {
416 fun()
417 }
418 self.state.borrow_mut().toplevel.destroy();
419 }
420
421 pub fn handle_input(&self, input: PlatformInput) {
422 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
423 if !fun(input.clone()).propagate {
424 return;
425 }
426 }
427 if let PlatformInput::KeyDown(event) = input {
428 let mut state = self.state.borrow_mut();
429 if let Some(mut input_handler) = state.input_handler.take() {
430 if let Some(ime_key) = &event.keystroke.ime_key {
431 drop(state);
432 input_handler.replace_text_in_range(None, ime_key);
433 let mut state = self.state.borrow_mut();
434 state.input_handler = Some(input_handler);
435 }
436 }
437 }
438 }
439
440 pub fn set_focused(&self, focus: bool) {
441 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
442 fun(focus);
443 }
444 }
445}
446
447impl HasWindowHandle for WaylandWindow {
448 fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
449 unimplemented!()
450 }
451}
452
453impl HasDisplayHandle for WaylandWindow {
454 fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
455 unimplemented!()
456 }
457}
458
459impl PlatformWindow for WaylandWindow {
460 fn bounds(&self) -> Bounds<DevicePixels> {
461 self.state.borrow().bounds.map(|p| DevicePixels(p as i32))
462 }
463
464 fn is_maximized(&self) -> bool {
465 self.state.borrow().maximized
466 }
467
468 fn is_minimized(&self) -> bool {
469 // This cannot be determined by the client
470 false
471 }
472
473 fn content_size(&self) -> Size<Pixels> {
474 let state = self.state.borrow();
475 Size {
476 width: Pixels(state.bounds.size.width as f32),
477 height: Pixels(state.bounds.size.height as f32),
478 }
479 }
480
481 fn scale_factor(&self) -> f32 {
482 self.state.borrow().scale
483 }
484
485 // todo(linux)
486 fn appearance(&self) -> WindowAppearance {
487 WindowAppearance::Light
488 }
489
490 // todo(linux)
491 fn display(&self) -> Rc<dyn PlatformDisplay> {
492 Rc::new(WaylandDisplay {})
493 }
494
495 // todo(linux)
496 fn mouse_position(&self) -> Point<Pixels> {
497 Point::default()
498 }
499
500 // todo(linux)
501 fn modifiers(&self) -> Modifiers {
502 crate::Modifiers::default()
503 }
504
505 fn as_any_mut(&mut self) -> &mut dyn Any {
506 self
507 }
508
509 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
510 self.state.borrow_mut().input_handler = Some(input_handler);
511 }
512
513 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
514 self.state.borrow_mut().input_handler.take()
515 }
516
517 fn prompt(
518 &self,
519 level: PromptLevel,
520 msg: &str,
521 detail: Option<&str>,
522 answers: &[&str],
523 ) -> Option<Receiver<usize>> {
524 None
525 }
526
527 fn activate(&self) {
528 // todo(linux)
529 }
530
531 // todo(linux)
532 fn is_active(&self) -> bool {
533 false
534 }
535
536 fn set_title(&mut self, title: &str) {
537 self.state
538 .borrow_mut()
539 .toplevel
540 .set_title(title.to_string());
541 }
542
543 fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
544 // todo(linux)
545 }
546
547 fn set_edited(&mut self, edited: bool) {
548 // todo(linux)
549 }
550
551 fn show_character_palette(&self) {
552 // todo(linux)
553 }
554
555 fn minimize(&self) {
556 self.state.borrow_mut().toplevel.set_minimized();
557 }
558
559 fn zoom(&self) {
560 // todo(linux)
561 }
562
563 fn toggle_fullscreen(&self) {
564 let state = self.state.borrow_mut();
565 if !state.fullscreen {
566 state.toplevel.set_fullscreen(None);
567 } else {
568 state.toplevel.unset_fullscreen();
569 }
570 }
571
572 fn is_fullscreen(&self) -> bool {
573 self.state.borrow().fullscreen
574 }
575
576 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
577 self.callbacks.borrow_mut().request_frame = Some(callback);
578 }
579
580 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
581 self.callbacks.borrow_mut().input = Some(callback);
582 }
583
584 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
585 self.callbacks.borrow_mut().active_status_change = Some(callback);
586 }
587
588 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
589 self.callbacks.borrow_mut().resize = Some(callback);
590 }
591
592 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
593 self.callbacks.borrow_mut().fullscreen = Some(callback);
594 }
595
596 fn on_moved(&self, callback: Box<dyn FnMut()>) {
597 self.callbacks.borrow_mut().moved = Some(callback);
598 }
599
600 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
601 self.callbacks.borrow_mut().should_close = Some(callback);
602 }
603
604 fn on_close(&self, callback: Box<dyn FnOnce()>) {
605 self.callbacks.borrow_mut().close = Some(callback);
606 }
607
608 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
609 // todo(linux)
610 }
611
612 // todo(linux)
613 fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
614 false
615 }
616
617 fn draw(&self, scene: &Scene) {
618 let mut state = self.state.borrow_mut();
619 state.renderer.draw(scene);
620 }
621
622 fn completed_frame(&self) {
623 let mut state = self.state.borrow_mut();
624 state.surface.commit();
625 }
626
627 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
628 let state = self.state.borrow();
629 state.renderer.sprite_atlas().clone()
630 }
631}
632
633#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
634pub enum WaylandDecorationState {
635 /// Decorations are to be provided by the client
636 Client,
637
638 /// Decorations are provided by the server
639 Server,
640}