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