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::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::{protocol::wl_surface, Proxy};
17use wayland_protocols::wp::viewporter::client::wp_viewport;
18use wayland_protocols::xdg::shell::client::xdg_toplevel;
19
20use crate::platform::blade::BladeRenderer;
21use crate::platform::linux::wayland::display::WaylandDisplay;
22use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
23use crate::scene::Scene;
24use crate::{
25 px, size, Bounds, GlobalPixels, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point,
26 PromptLevel, Size, WindowAppearance, WindowParams,
27};
28
29#[derive(Default)]
30pub(crate) struct Callbacks {
31 request_frame: Option<Box<dyn FnMut()>>,
32 input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
33 active_status_change: Option<Box<dyn FnMut(bool)>>,
34 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
35 fullscreen: Option<Box<dyn FnMut(bool)>>,
36 moved: Option<Box<dyn FnMut()>>,
37 should_close: Option<Box<dyn FnMut() -> bool>>,
38 close: Option<Box<dyn FnOnce()>>,
39 appearance_changed: Option<Box<dyn FnMut()>>,
40}
41
42struct WaylandWindowInner {
43 renderer: BladeRenderer,
44 bounds: Bounds<u32>,
45 scale: f32,
46 fullscreen: bool,
47 input_handler: Option<PlatformInputHandler>,
48 decoration_state: WaylandDecorationState,
49}
50
51struct RawWindow {
52 window: *mut c_void,
53 display: *mut c_void,
54}
55
56unsafe impl HasRawWindowHandle for RawWindow {
57 fn raw_window_handle(&self) -> RawWindowHandle {
58 let mut wh = blade_rwh::WaylandWindowHandle::empty();
59 wh.surface = self.window;
60 wh.into()
61 }
62}
63
64unsafe impl HasRawDisplayHandle for RawWindow {
65 fn raw_display_handle(&self) -> RawDisplayHandle {
66 let mut dh = blade_rwh::WaylandDisplayHandle::empty();
67 dh.display = self.display;
68 dh.into()
69 }
70}
71
72impl WaylandWindowInner {
73 fn new(wl_surf: &Arc<wl_surface::WlSurface>, bounds: Bounds<u32>) -> Self {
74 let raw = RawWindow {
75 window: wl_surf.id().as_ptr().cast::<c_void>(),
76 display: wl_surf
77 .backend()
78 .upgrade()
79 .unwrap()
80 .display_ptr()
81 .cast::<c_void>(),
82 };
83 let gpu = Arc::new(
84 unsafe {
85 gpu::Context::init_windowed(
86 &raw,
87 gpu::ContextDesc {
88 validation: false,
89 capture: false,
90 overlay: false,
91 },
92 )
93 }
94 .unwrap(),
95 );
96 let extent = gpu::Extent {
97 width: bounds.size.width,
98 height: bounds.size.height,
99 depth: 1,
100 };
101 Self {
102 renderer: BladeRenderer::new(gpu, extent),
103 bounds,
104 scale: 1.0,
105 fullscreen: false,
106 input_handler: None,
107
108 // On wayland, decorations are by default provided by the client
109 decoration_state: WaylandDecorationState::Client,
110 }
111 }
112}
113
114pub(crate) struct WaylandWindowState {
115 inner: RefCell<WaylandWindowInner>,
116 pub(crate) callbacks: RefCell<Callbacks>,
117 pub(crate) surface: Arc<wl_surface::WlSurface>,
118 pub(crate) toplevel: Arc<xdg_toplevel::XdgToplevel>,
119 pub(crate) outputs: RefCell<HashSet<ObjectId>>,
120 viewport: Option<wp_viewport::WpViewport>,
121}
122
123impl WaylandWindowState {
124 pub(crate) fn new(
125 wl_surf: Arc<wl_surface::WlSurface>,
126 viewport: Option<wp_viewport::WpViewport>,
127 toplevel: Arc<xdg_toplevel::XdgToplevel>,
128 options: WindowParams,
129 ) -> Self {
130 let bounds = options.bounds.map(|p| p.0 as u32);
131
132 Self {
133 surface: Arc::clone(&wl_surf),
134 inner: RefCell::new(WaylandWindowInner::new(&wl_surf, bounds)),
135 callbacks: RefCell::new(Callbacks::default()),
136 outputs: RefCell::new(HashSet::default()),
137 toplevel,
138 viewport,
139 }
140 }
141
142 pub fn update(&self) {
143 let mut cb = self.callbacks.borrow_mut();
144 if let Some(mut fun) = cb.request_frame.take() {
145 drop(cb);
146 fun();
147 self.callbacks.borrow_mut().request_frame = Some(fun);
148 }
149 }
150
151 pub fn set_size_and_scale(
152 &self,
153 width: Option<NonZeroU32>,
154 height: Option<NonZeroU32>,
155 scale: Option<f32>,
156 ) {
157 let (width, height, scale) = {
158 let mut inner = self.inner.borrow_mut();
159 if width.map_or(true, |width| width.get() == inner.bounds.size.width)
160 && height.map_or(true, |height| height.get() == inner.bounds.size.height)
161 && scale.map_or(true, |scale| scale == inner.scale)
162 {
163 return;
164 }
165 if let Some(width) = width {
166 inner.bounds.size.width = width.get();
167 }
168 if let Some(height) = height {
169 inner.bounds.size.height = height.get();
170 }
171 if let Some(scale) = scale {
172 inner.scale = scale;
173 }
174 let width = inner.bounds.size.width;
175 let height = inner.bounds.size.height;
176 let scale = inner.scale;
177 inner.renderer.update_drawable_size(size(
178 width as f64 * scale as f64,
179 height as f64 * scale as f64,
180 ));
181 (width, height, scale)
182 };
183
184 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
185 fun(
186 Size {
187 width: px(width as f32),
188 height: px(height as f32),
189 },
190 scale,
191 );
192 }
193
194 if let Some(viewport) = &self.viewport {
195 viewport.set_destination(width as i32, height as i32);
196 }
197 }
198
199 pub fn resize(&self, width: Option<NonZeroU32>, height: Option<NonZeroU32>) {
200 let scale = self.inner.borrow_mut().scale;
201 self.set_size_and_scale(width, height, None);
202 }
203
204 pub fn rescale(&self, scale: f32) {
205 self.set_size_and_scale(None, None, Some(scale));
206 }
207
208 pub fn set_fullscreen(&self, fullscreen: bool) {
209 let mut callbacks = self.callbacks.borrow_mut();
210 if let Some(ref mut fun) = callbacks.fullscreen {
211 fun(fullscreen)
212 }
213 self.inner.borrow_mut().fullscreen = fullscreen;
214 }
215
216 /// Notifies the window of the state of the decorations.
217 ///
218 /// # Note
219 ///
220 /// This API is indirectly called by the wayland compositor and
221 /// not meant to be called by a user who wishes to change the state
222 /// of the decorations. This is because the state of the decorations
223 /// is managed by the compositor and not the client.
224 pub fn set_decoration_state(&self, state: WaylandDecorationState) {
225 self.inner.borrow_mut().decoration_state = state;
226 log::trace!("Window decorations are now handled by {:?}", state);
227 // todo(linux) - Handle this properly
228 }
229
230 pub fn close(&self) {
231 let mut callbacks = self.callbacks.borrow_mut();
232 if let Some(fun) = callbacks.close.take() {
233 fun()
234 }
235 self.toplevel.destroy();
236 }
237
238 pub fn handle_input(&self, input: PlatformInput) {
239 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
240 if !fun(input.clone()).propagate {
241 return;
242 }
243 }
244 if let PlatformInput::KeyDown(event) = input {
245 let mut inner = self.inner.borrow_mut();
246 if let Some(ref mut input_handler) = inner.input_handler {
247 if let Some(ime_key) = &event.keystroke.ime_key {
248 input_handler.replace_text_in_range(None, ime_key);
249 }
250 }
251 }
252 }
253
254 pub fn set_focused(&self, focus: bool) {
255 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
256 fun(focus);
257 }
258 }
259}
260
261#[derive(Clone)]
262pub(crate) struct WaylandWindow(pub(crate) Rc<WaylandWindowState>);
263
264impl HasWindowHandle for WaylandWindow {
265 fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
266 unimplemented!()
267 }
268}
269
270impl HasDisplayHandle for WaylandWindow {
271 fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
272 unimplemented!()
273 }
274}
275
276impl PlatformWindow for WaylandWindow {
277 // todo(linux)
278 fn bounds(&self) -> Bounds<GlobalPixels> {
279 unimplemented!()
280 }
281
282 // todo(linux)
283 fn is_maximized(&self) -> bool {
284 false
285 }
286
287 fn content_size(&self) -> Size<Pixels> {
288 let inner = self.0.inner.borrow_mut();
289 Size {
290 width: Pixels(inner.bounds.size.width as f32),
291 height: Pixels(inner.bounds.size.height as f32),
292 }
293 }
294
295 fn scale_factor(&self) -> f32 {
296 self.0.inner.borrow_mut().scale
297 }
298
299 // todo(linux)
300 fn appearance(&self) -> WindowAppearance {
301 WindowAppearance::Light
302 }
303
304 // todo(linux)
305 fn display(&self) -> Rc<dyn PlatformDisplay> {
306 Rc::new(WaylandDisplay {})
307 }
308
309 // todo(linux)
310 fn mouse_position(&self) -> Point<Pixels> {
311 Point::default()
312 }
313
314 // todo(linux)
315 fn modifiers(&self) -> Modifiers {
316 crate::Modifiers::default()
317 }
318
319 fn as_any_mut(&mut self) -> &mut dyn Any {
320 self
321 }
322
323 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
324 self.0.inner.borrow_mut().input_handler = Some(input_handler);
325 }
326
327 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
328 self.0.inner.borrow_mut().input_handler.take()
329 }
330
331 fn prompt(
332 &self,
333 level: PromptLevel,
334 msg: &str,
335 detail: Option<&str>,
336 answers: &[&str],
337 ) -> Option<Receiver<usize>> {
338 None
339 }
340
341 fn activate(&self) {
342 // todo(linux)
343 }
344
345 fn set_title(&mut self, title: &str) {
346 self.0.toplevel.set_title(title.to_string());
347 }
348
349 fn set_edited(&mut self, edited: bool) {
350 // todo(linux)
351 }
352
353 fn show_character_palette(&self) {
354 // todo(linux)
355 }
356
357 fn minimize(&self) {
358 self.0.toplevel.set_minimized();
359 }
360
361 fn zoom(&self) {
362 // todo(linux)
363 }
364
365 fn toggle_fullscreen(&self) {
366 if !self.0.inner.borrow().fullscreen {
367 self.0.toplevel.set_fullscreen(None);
368 } else {
369 self.0.toplevel.unset_fullscreen();
370 }
371 }
372
373 fn is_fullscreen(&self) -> bool {
374 self.0.inner.borrow_mut().fullscreen
375 }
376
377 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
378 self.0.callbacks.borrow_mut().request_frame = Some(callback);
379 }
380
381 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
382 self.0.callbacks.borrow_mut().input = Some(callback);
383 }
384
385 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
386 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
387 }
388
389 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
390 self.0.callbacks.borrow_mut().resize = Some(callback);
391 }
392
393 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
394 self.0.callbacks.borrow_mut().fullscreen = Some(callback);
395 }
396
397 fn on_moved(&self, callback: Box<dyn FnMut()>) {
398 self.0.callbacks.borrow_mut().moved = Some(callback);
399 }
400
401 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
402 self.0.callbacks.borrow_mut().should_close = Some(callback);
403 }
404
405 fn on_close(&self, callback: Box<dyn FnOnce()>) {
406 self.0.callbacks.borrow_mut().close = Some(callback);
407 }
408
409 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
410 // todo(linux)
411 }
412
413 // todo(linux)
414 fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
415 false
416 }
417
418 fn draw(&self, scene: &Scene) {
419 let mut inner = self.0.inner.borrow_mut();
420 inner.renderer.draw(scene);
421 }
422
423 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
424 let inner = self.0.inner.borrow_mut();
425 inner.renderer.sprite_atlas().clone()
426 }
427}
428
429#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
430pub enum WaylandDecorationState {
431 /// Decorations are to be provided by the client
432 Client,
433
434 /// Decorations are provided by the server
435 Server,
436}