1 | //! The Wayland window. |
2 | |
3 | use std::sync::atomic::{AtomicBool, Ordering}; |
4 | use std::sync::{Arc, Mutex}; |
5 | |
6 | use sctk::reexports::client::protocol::wl_display::WlDisplay; |
7 | use sctk::reexports::client::protocol::wl_surface::WlSurface; |
8 | use sctk::reexports::client::{Proxy, QueueHandle}; |
9 | |
10 | use sctk::compositor::{CompositorState, Region, SurfaceData}; |
11 | use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1; |
12 | use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations}; |
13 | use sctk::shell::WaylandSurface; |
14 | |
15 | use tracing::warn; |
16 | |
17 | use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; |
18 | use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; |
19 | use crate::event::{Ime, WindowEvent}; |
20 | use crate::event_loop::AsyncRequestSerial; |
21 | use crate::platform_impl::{ |
22 | Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon, |
23 | }; |
24 | use crate::window::{ |
25 | Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, |
26 | WindowAttributes, WindowButtons, WindowLevel, |
27 | }; |
28 | |
29 | use super::event_loop::sink::EventSink; |
30 | use super::output::MonitorHandle; |
31 | use super::state::WinitState; |
32 | use super::types::xdg_activation::XdgActivationTokenData; |
33 | use super::{ActiveEventLoop, WaylandError, WindowId}; |
34 | |
35 | pub(crate) mod state; |
36 | |
37 | pub use state::WindowState; |
38 | |
39 | /// The Wayland window. |
40 | pub struct Window { |
41 | /// Reference to the underlying SCTK window. |
42 | window: SctkWindow, |
43 | |
44 | /// Window id. |
45 | window_id: WindowId, |
46 | |
47 | /// The state of the window. |
48 | window_state: Arc<Mutex<WindowState>>, |
49 | |
50 | /// Compositor to handle WlRegion stuff. |
51 | compositor: Arc<CompositorState>, |
52 | |
53 | /// The wayland display used solely for raw window handle. |
54 | #[allow (dead_code)] |
55 | display: WlDisplay, |
56 | |
57 | /// Xdg activation to request user attention. |
58 | xdg_activation: Option<XdgActivationV1>, |
59 | |
60 | /// The state of the requested attention from the `xdg_activation`. |
61 | attention_requested: Arc<AtomicBool>, |
62 | |
63 | /// Handle to the main queue to perform requests. |
64 | queue_handle: QueueHandle<WinitState>, |
65 | |
66 | /// Window requests to the event loop. |
67 | window_requests: Arc<WindowRequests>, |
68 | |
69 | /// Observed monitors. |
70 | monitors: Arc<Mutex<Vec<MonitorHandle>>>, |
71 | |
72 | /// Source to wake-up the event-loop for window requests. |
73 | event_loop_awakener: calloop::ping::Ping, |
74 | |
75 | /// The event sink to deliver synthetic events. |
76 | window_events_sink: Arc<Mutex<EventSink>>, |
77 | } |
78 | |
79 | impl Window { |
80 | pub(crate) fn new( |
81 | event_loop_window_target: &ActiveEventLoop, |
82 | attributes: WindowAttributes, |
83 | ) -> Result<Self, RootOsError> { |
84 | let queue_handle = event_loop_window_target.queue_handle.clone(); |
85 | let mut state = event_loop_window_target.state.borrow_mut(); |
86 | |
87 | let monitors = state.monitors.clone(); |
88 | |
89 | let surface = state.compositor_state.create_surface(&queue_handle); |
90 | let compositor = state.compositor_state.clone(); |
91 | let xdg_activation = |
92 | state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone()); |
93 | let display = event_loop_window_target.connection.display(); |
94 | |
95 | let size: Size = attributes.inner_size.unwrap_or(LogicalSize::new(800., 600.).into()); |
96 | |
97 | // We prefer server side decorations, however to not have decorations we ask for client |
98 | // side decorations instead. |
99 | let default_decorations = if attributes.decorations { |
100 | WindowDecorations::RequestServer |
101 | } else { |
102 | WindowDecorations::RequestClient |
103 | }; |
104 | |
105 | let window = |
106 | state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle); |
107 | |
108 | let mut window_state = WindowState::new( |
109 | event_loop_window_target.connection.clone(), |
110 | &event_loop_window_target.queue_handle, |
111 | &state, |
112 | size, |
113 | window.clone(), |
114 | attributes.preferred_theme, |
115 | ); |
116 | |
117 | // Set transparency hint. |
118 | window_state.set_transparent(attributes.transparent); |
119 | |
120 | window_state.set_blur(attributes.blur); |
121 | |
122 | // Set the decorations hint. |
123 | window_state.set_decorate(attributes.decorations); |
124 | |
125 | // Set the app_id. |
126 | if let Some(name) = attributes.platform_specific.name.map(|name| name.general) { |
127 | window.set_app_id(name); |
128 | } |
129 | |
130 | // Set the window title. |
131 | window_state.set_title(attributes.title); |
132 | |
133 | // Set the min and max sizes. We must set the hints upon creating a window, so |
134 | // we use the default `1.` scaling... |
135 | let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.)); |
136 | let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.)); |
137 | window_state.set_min_inner_size(min_size); |
138 | window_state.set_max_inner_size(max_size); |
139 | |
140 | // Non-resizable implies that the min and max sizes are set to the same value. |
141 | window_state.set_resizable(attributes.resizable); |
142 | |
143 | // Set startup mode. |
144 | match attributes.fullscreen.map(Into::into) { |
145 | Some(Fullscreen::Exclusive(_)) => { |
146 | warn!("`Fullscreen::Exclusive` is ignored on Wayland" ); |
147 | }, |
148 | #[cfg_attr (not(x11_platform), allow(clippy::bind_instead_of_map))] |
149 | Some(Fullscreen::Borderless(monitor)) => { |
150 | let output = monitor.and_then(|monitor| match monitor { |
151 | PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy), |
152 | #[cfg (x11_platform)] |
153 | PlatformMonitorHandle::X(_) => None, |
154 | }); |
155 | |
156 | window.set_fullscreen(output.as_ref()) |
157 | }, |
158 | _ if attributes.maximized => window.set_maximized(), |
159 | _ => (), |
160 | }; |
161 | |
162 | match attributes.cursor { |
163 | Cursor::Icon(icon) => window_state.set_cursor(icon), |
164 | Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor), |
165 | } |
166 | |
167 | // Activate the window when the token is passed. |
168 | if let (Some(xdg_activation), Some(token)) = |
169 | (xdg_activation.as_ref(), attributes.platform_specific.activation_token) |
170 | { |
171 | xdg_activation.activate(token.token, &surface); |
172 | } |
173 | |
174 | // XXX Do initial commit. |
175 | window.commit(); |
176 | |
177 | // Add the window and window requests into the state. |
178 | let window_state = Arc::new(Mutex::new(window_state)); |
179 | let window_id = super::make_wid(&surface); |
180 | state.windows.get_mut().insert(window_id, window_state.clone()); |
181 | |
182 | let window_requests = WindowRequests { |
183 | redraw_requested: AtomicBool::new(true), |
184 | closed: AtomicBool::new(false), |
185 | }; |
186 | let window_requests = Arc::new(window_requests); |
187 | state.window_requests.get_mut().insert(window_id, window_requests.clone()); |
188 | |
189 | // Setup the event sync to insert `WindowEvents` right from the window. |
190 | let window_events_sink = state.window_events_sink.clone(); |
191 | |
192 | let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut(); |
193 | let event_queue = wayland_source.queue(); |
194 | |
195 | // Do a roundtrip. |
196 | event_queue.roundtrip(&mut state).map_err(|error| { |
197 | os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error)))) |
198 | })?; |
199 | |
200 | // XXX Wait for the initial configure to arrive. |
201 | while !window_state.lock().unwrap().is_configured() { |
202 | event_queue.blocking_dispatch(&mut state).map_err(|error| { |
203 | os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error)))) |
204 | })?; |
205 | } |
206 | |
207 | // Wake-up event loop, so it'll send initial redraw requested. |
208 | let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone(); |
209 | event_loop_awakener.ping(); |
210 | |
211 | Ok(Self { |
212 | window, |
213 | display, |
214 | monitors, |
215 | window_id, |
216 | compositor, |
217 | window_state, |
218 | queue_handle, |
219 | xdg_activation, |
220 | attention_requested: Arc::new(AtomicBool::new(false)), |
221 | event_loop_awakener, |
222 | window_requests, |
223 | window_events_sink, |
224 | }) |
225 | } |
226 | } |
227 | |
228 | impl Window { |
229 | #[inline ] |
230 | pub fn id(&self) -> WindowId { |
231 | self.window_id |
232 | } |
233 | |
234 | #[inline ] |
235 | pub fn set_title(&self, title: impl ToString) { |
236 | let new_title = title.to_string(); |
237 | self.window_state.lock().unwrap().set_title(new_title); |
238 | } |
239 | |
240 | #[inline ] |
241 | pub fn set_visible(&self, _visible: bool) { |
242 | // Not possible on Wayland. |
243 | } |
244 | |
245 | #[inline ] |
246 | pub fn is_visible(&self) -> Option<bool> { |
247 | None |
248 | } |
249 | |
250 | #[inline ] |
251 | pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> { |
252 | Err(NotSupportedError::new()) |
253 | } |
254 | |
255 | #[inline ] |
256 | pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> { |
257 | Err(NotSupportedError::new()) |
258 | } |
259 | |
260 | #[inline ] |
261 | pub fn set_outer_position(&self, _: Position) { |
262 | // Not possible on Wayland. |
263 | } |
264 | |
265 | #[inline ] |
266 | pub fn inner_size(&self) -> PhysicalSize<u32> { |
267 | let window_state = self.window_state.lock().unwrap(); |
268 | let scale_factor = window_state.scale_factor(); |
269 | super::logical_to_physical_rounded(window_state.inner_size(), scale_factor) |
270 | } |
271 | |
272 | #[inline ] |
273 | pub fn request_redraw(&self) { |
274 | // NOTE: try to not wake up the loop when the event was already scheduled and not yet |
275 | // processed by the loop, because if at this point the value was `true` it could only |
276 | // mean that the loop still haven't dispatched the value to the client and will do |
277 | // eventually, resetting it to `false`. |
278 | if self |
279 | .window_requests |
280 | .redraw_requested |
281 | .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) |
282 | .is_ok() |
283 | { |
284 | self.event_loop_awakener.ping(); |
285 | } |
286 | } |
287 | |
288 | #[inline ] |
289 | pub fn pre_present_notify(&self) { |
290 | self.window_state.lock().unwrap().request_frame_callback(); |
291 | } |
292 | |
293 | #[inline ] |
294 | pub fn outer_size(&self) -> PhysicalSize<u32> { |
295 | let window_state = self.window_state.lock().unwrap(); |
296 | let scale_factor = window_state.scale_factor(); |
297 | super::logical_to_physical_rounded(window_state.outer_size(), scale_factor) |
298 | } |
299 | |
300 | #[inline ] |
301 | pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> { |
302 | let mut window_state = self.window_state.lock().unwrap(); |
303 | let new_size = window_state.request_inner_size(size); |
304 | self.request_redraw(); |
305 | Some(new_size) |
306 | } |
307 | |
308 | /// Set the minimum inner size for the window. |
309 | #[inline ] |
310 | pub fn set_min_inner_size(&self, min_size: Option<Size>) { |
311 | let scale_factor = self.scale_factor(); |
312 | let min_size = min_size.map(|size| size.to_logical(scale_factor)); |
313 | self.window_state.lock().unwrap().set_min_inner_size(min_size); |
314 | // NOTE: Requires commit to be applied. |
315 | self.request_redraw(); |
316 | } |
317 | |
318 | /// Set the maximum inner size for the window. |
319 | #[inline ] |
320 | pub fn set_max_inner_size(&self, max_size: Option<Size>) { |
321 | let scale_factor = self.scale_factor(); |
322 | let max_size = max_size.map(|size| size.to_logical(scale_factor)); |
323 | self.window_state.lock().unwrap().set_max_inner_size(max_size); |
324 | // NOTE: Requires commit to be applied. |
325 | self.request_redraw(); |
326 | } |
327 | |
328 | #[inline ] |
329 | pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> { |
330 | None |
331 | } |
332 | |
333 | #[inline ] |
334 | pub fn set_resize_increments(&self, _increments: Option<Size>) { |
335 | warn!("`set_resize_increments` is not implemented for Wayland" ); |
336 | } |
337 | |
338 | #[inline ] |
339 | pub fn set_transparent(&self, transparent: bool) { |
340 | self.window_state.lock().unwrap().set_transparent(transparent); |
341 | } |
342 | |
343 | #[inline ] |
344 | pub fn has_focus(&self) -> bool { |
345 | self.window_state.lock().unwrap().has_focus() |
346 | } |
347 | |
348 | #[inline ] |
349 | pub fn is_minimized(&self) -> Option<bool> { |
350 | // XXX clients don't know whether they are minimized or not. |
351 | None |
352 | } |
353 | |
354 | #[inline ] |
355 | pub fn show_window_menu(&self, position: Position) { |
356 | let scale_factor = self.scale_factor(); |
357 | let position = position.to_logical(scale_factor); |
358 | self.window_state.lock().unwrap().show_window_menu(position); |
359 | } |
360 | |
361 | #[inline ] |
362 | pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> { |
363 | self.window_state.lock().unwrap().drag_resize_window(direction) |
364 | } |
365 | |
366 | #[inline ] |
367 | pub fn set_resizable(&self, resizable: bool) { |
368 | if self.window_state.lock().unwrap().set_resizable(resizable) { |
369 | // NOTE: Requires commit to be applied. |
370 | self.request_redraw(); |
371 | } |
372 | } |
373 | |
374 | #[inline ] |
375 | pub fn is_resizable(&self) -> bool { |
376 | self.window_state.lock().unwrap().resizable() |
377 | } |
378 | |
379 | #[inline ] |
380 | pub fn set_enabled_buttons(&self, _buttons: WindowButtons) { |
381 | // TODO(kchibisov) v5 of the xdg_shell allows that. |
382 | } |
383 | |
384 | #[inline ] |
385 | pub fn enabled_buttons(&self) -> WindowButtons { |
386 | // TODO(kchibisov) v5 of the xdg_shell allows that. |
387 | WindowButtons::all() |
388 | } |
389 | |
390 | #[inline ] |
391 | pub fn scale_factor(&self) -> f64 { |
392 | self.window_state.lock().unwrap().scale_factor() |
393 | } |
394 | |
395 | #[inline ] |
396 | pub fn set_blur(&self, blur: bool) { |
397 | self.window_state.lock().unwrap().set_blur(blur); |
398 | } |
399 | |
400 | #[inline ] |
401 | pub fn set_decorations(&self, decorate: bool) { |
402 | self.window_state.lock().unwrap().set_decorate(decorate) |
403 | } |
404 | |
405 | #[inline ] |
406 | pub fn is_decorated(&self) -> bool { |
407 | self.window_state.lock().unwrap().is_decorated() |
408 | } |
409 | |
410 | #[inline ] |
411 | pub fn set_window_level(&self, _level: WindowLevel) {} |
412 | |
413 | #[inline ] |
414 | pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {} |
415 | |
416 | #[inline ] |
417 | pub fn set_minimized(&self, minimized: bool) { |
418 | // You can't unminimize the window on Wayland. |
419 | if !minimized { |
420 | warn!("Unminimizing is ignored on Wayland." ); |
421 | return; |
422 | } |
423 | |
424 | self.window.set_minimized(); |
425 | } |
426 | |
427 | #[inline ] |
428 | pub fn is_maximized(&self) -> bool { |
429 | self.window_state |
430 | .lock() |
431 | .unwrap() |
432 | .last_configure |
433 | .as_ref() |
434 | .map(|last_configure| last_configure.is_maximized()) |
435 | .unwrap_or_default() |
436 | } |
437 | |
438 | #[inline ] |
439 | pub fn set_maximized(&self, maximized: bool) { |
440 | if maximized { |
441 | self.window.set_maximized() |
442 | } else { |
443 | self.window.unset_maximized() |
444 | } |
445 | } |
446 | |
447 | #[inline ] |
448 | pub(crate) fn fullscreen(&self) -> Option<Fullscreen> { |
449 | let is_fullscreen = self |
450 | .window_state |
451 | .lock() |
452 | .unwrap() |
453 | .last_configure |
454 | .as_ref() |
455 | .map(|last_configure| last_configure.is_fullscreen()) |
456 | .unwrap_or_default(); |
457 | |
458 | if is_fullscreen { |
459 | let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland); |
460 | Some(Fullscreen::Borderless(current_monitor)) |
461 | } else { |
462 | None |
463 | } |
464 | } |
465 | |
466 | #[inline ] |
467 | pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) { |
468 | match fullscreen { |
469 | Some(Fullscreen::Exclusive(_)) => { |
470 | warn!("`Fullscreen::Exclusive` is ignored on Wayland" ); |
471 | }, |
472 | #[cfg_attr (not(x11_platform), allow(clippy::bind_instead_of_map))] |
473 | Some(Fullscreen::Borderless(monitor)) => { |
474 | let output = monitor.and_then(|monitor| match monitor { |
475 | PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy), |
476 | #[cfg (x11_platform)] |
477 | PlatformMonitorHandle::X(_) => None, |
478 | }); |
479 | |
480 | self.window.set_fullscreen(output.as_ref()) |
481 | }, |
482 | None => self.window.unset_fullscreen(), |
483 | } |
484 | } |
485 | |
486 | #[inline ] |
487 | pub fn set_cursor(&self, cursor: Cursor) { |
488 | let window_state = &mut self.window_state.lock().unwrap(); |
489 | |
490 | match cursor { |
491 | Cursor::Icon(icon) => window_state.set_cursor(icon), |
492 | Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor), |
493 | } |
494 | } |
495 | |
496 | #[inline ] |
497 | pub fn set_cursor_visible(&self, visible: bool) { |
498 | self.window_state.lock().unwrap().set_cursor_visible(visible); |
499 | } |
500 | |
501 | pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) { |
502 | let xdg_activation = match self.xdg_activation.as_ref() { |
503 | Some(xdg_activation) => xdg_activation, |
504 | None => { |
505 | warn!("`request_user_attention` isn't supported" ); |
506 | return; |
507 | }, |
508 | }; |
509 | |
510 | // Urgency is only removed by the compositor and there's no need to raise urgency when it |
511 | // was already raised. |
512 | if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) { |
513 | return; |
514 | } |
515 | |
516 | self.attention_requested.store(true, Ordering::Relaxed); |
517 | let surface = self.surface().clone(); |
518 | let data = XdgActivationTokenData::Attention(( |
519 | surface.clone(), |
520 | Arc::downgrade(&self.attention_requested), |
521 | )); |
522 | let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data); |
523 | xdg_activation_token.set_surface(&surface); |
524 | xdg_activation_token.commit(); |
525 | } |
526 | |
527 | pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> { |
528 | let xdg_activation = match self.xdg_activation.as_ref() { |
529 | Some(xdg_activation) => xdg_activation, |
530 | None => return Err(NotSupportedError::new()), |
531 | }; |
532 | |
533 | let serial = AsyncRequestSerial::get(); |
534 | |
535 | let data = XdgActivationTokenData::Obtain((self.window_id, serial)); |
536 | let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data); |
537 | xdg_activation_token.set_surface(self.surface()); |
538 | xdg_activation_token.commit(); |
539 | |
540 | Ok(serial) |
541 | } |
542 | |
543 | #[inline ] |
544 | pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { |
545 | self.window_state.lock().unwrap().set_cursor_grab(mode) |
546 | } |
547 | |
548 | #[inline ] |
549 | pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { |
550 | let scale_factor = self.scale_factor(); |
551 | let position = position.to_logical(scale_factor); |
552 | self.window_state |
553 | .lock() |
554 | .unwrap() |
555 | .set_cursor_position(position) |
556 | // Request redraw on success, since the state is double buffered. |
557 | .map(|_| self.request_redraw()) |
558 | } |
559 | |
560 | #[inline ] |
561 | pub fn drag_window(&self) -> Result<(), ExternalError> { |
562 | self.window_state.lock().unwrap().drag_window() |
563 | } |
564 | |
565 | #[inline ] |
566 | pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> { |
567 | let surface = self.window.wl_surface(); |
568 | |
569 | if hittest { |
570 | surface.set_input_region(None); |
571 | Ok(()) |
572 | } else { |
573 | let region = Region::new(&*self.compositor).map_err(|_| { |
574 | ExternalError::Os(os_error!(OsError::Misc("failed to set input region." ))) |
575 | })?; |
576 | region.add(0, 0, 0, 0); |
577 | surface.set_input_region(Some(region.wl_region())); |
578 | Ok(()) |
579 | } |
580 | } |
581 | |
582 | #[inline ] |
583 | pub fn set_ime_cursor_area(&self, position: Position, size: Size) { |
584 | let window_state = self.window_state.lock().unwrap(); |
585 | if window_state.ime_allowed() { |
586 | let scale_factor = window_state.scale_factor(); |
587 | let position = position.to_logical(scale_factor); |
588 | let size = size.to_logical(scale_factor); |
589 | window_state.set_ime_cursor_area(position, size); |
590 | } |
591 | } |
592 | |
593 | #[inline ] |
594 | pub fn set_ime_allowed(&self, allowed: bool) { |
595 | let mut window_state = self.window_state.lock().unwrap(); |
596 | |
597 | if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) { |
598 | let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled }); |
599 | self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id); |
600 | self.event_loop_awakener.ping(); |
601 | } |
602 | } |
603 | |
604 | #[inline ] |
605 | pub fn set_ime_purpose(&self, purpose: ImePurpose) { |
606 | self.window_state.lock().unwrap().set_ime_purpose(purpose); |
607 | } |
608 | |
609 | #[inline ] |
610 | pub fn focus_window(&self) {} |
611 | |
612 | #[inline ] |
613 | pub fn surface(&self) -> &WlSurface { |
614 | self.window.wl_surface() |
615 | } |
616 | |
617 | #[inline ] |
618 | pub fn current_monitor(&self) -> Option<MonitorHandle> { |
619 | let data = self.window.wl_surface().data::<SurfaceData>()?; |
620 | data.outputs().next().map(MonitorHandle::new) |
621 | } |
622 | |
623 | #[inline ] |
624 | pub fn available_monitors(&self) -> Vec<MonitorHandle> { |
625 | self.monitors.lock().unwrap().clone() |
626 | } |
627 | |
628 | #[inline ] |
629 | pub fn primary_monitor(&self) -> Option<MonitorHandle> { |
630 | // XXX there's no such concept on Wayland. |
631 | None |
632 | } |
633 | |
634 | #[cfg (feature = "rwh_04" )] |
635 | #[inline ] |
636 | pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle { |
637 | let mut window_handle = rwh_04::WaylandHandle::empty(); |
638 | window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _; |
639 | window_handle.display = self.display.id().as_ptr() as *mut _; |
640 | rwh_04::RawWindowHandle::Wayland(window_handle) |
641 | } |
642 | |
643 | #[cfg (feature = "rwh_05" )] |
644 | #[inline ] |
645 | pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle { |
646 | let mut window_handle = rwh_05::WaylandWindowHandle::empty(); |
647 | window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _; |
648 | rwh_05::RawWindowHandle::Wayland(window_handle) |
649 | } |
650 | |
651 | #[cfg (feature = "rwh_05" )] |
652 | #[inline ] |
653 | pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle { |
654 | let mut display_handle = rwh_05::WaylandDisplayHandle::empty(); |
655 | display_handle.display = self.display.id().as_ptr() as *mut _; |
656 | rwh_05::RawDisplayHandle::Wayland(display_handle) |
657 | } |
658 | |
659 | #[cfg (feature = "rwh_06" )] |
660 | #[inline ] |
661 | pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> { |
662 | Ok(rwh_06::WaylandWindowHandle::new({ |
663 | let ptr = self.window.wl_surface().id().as_ptr(); |
664 | std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null" ) |
665 | }) |
666 | .into()) |
667 | } |
668 | |
669 | #[cfg (feature = "rwh_06" )] |
670 | #[inline ] |
671 | pub fn raw_display_handle_rwh_06( |
672 | &self, |
673 | ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { |
674 | Ok(rwh_06::WaylandDisplayHandle::new({ |
675 | let ptr = self.display.id().as_ptr(); |
676 | std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null" ) |
677 | }) |
678 | .into()) |
679 | } |
680 | |
681 | #[inline ] |
682 | pub fn set_theme(&self, theme: Option<Theme>) { |
683 | self.window_state.lock().unwrap().set_theme(theme) |
684 | } |
685 | |
686 | #[inline ] |
687 | pub fn theme(&self) -> Option<Theme> { |
688 | self.window_state.lock().unwrap().theme() |
689 | } |
690 | |
691 | pub fn set_content_protected(&self, _protected: bool) {} |
692 | |
693 | #[inline ] |
694 | pub fn title(&self) -> String { |
695 | self.window_state.lock().unwrap().title().to_owned() |
696 | } |
697 | } |
698 | |
699 | impl Drop for Window { |
700 | fn drop(&mut self) { |
701 | self.window_requests.closed.store(val:true, order:Ordering::Relaxed); |
702 | self.event_loop_awakener.ping(); |
703 | } |
704 | } |
705 | |
706 | /// The request from the window to the event loop. |
707 | #[derive (Debug)] |
708 | pub struct WindowRequests { |
709 | /// The window was closed. |
710 | pub closed: AtomicBool, |
711 | |
712 | /// Redraw Requested. |
713 | pub redraw_requested: AtomicBool, |
714 | } |
715 | |
716 | impl WindowRequests { |
717 | pub fn take_closed(&self) -> bool { |
718 | self.closed.swap(val:false, order:Ordering::Relaxed) |
719 | } |
720 | |
721 | pub fn take_redraw_requested(&self) -> bool { |
722 | self.redraw_requested.swap(val:false, order:Ordering::Relaxed) |
723 | } |
724 | } |
725 | |
726 | impl TryFrom<&str> for Theme { |
727 | type Error = (); |
728 | |
729 | /// ``` |
730 | /// use winit::window::Theme; |
731 | /// |
732 | /// assert_eq!("dark" .try_into(), Ok(Theme::Dark)); |
733 | /// assert_eq!("lIghT" .try_into(), Ok(Theme::Light)); |
734 | /// ``` |
735 | fn try_from(theme: &str) -> Result<Self, Self::Error> { |
736 | if theme.eq_ignore_ascii_case("dark" ) { |
737 | Ok(Self::Dark) |
738 | } else if theme.eq_ignore_ascii_case("light" ) { |
739 | Ok(Self::Light) |
740 | } else { |
741 | Err(()) |
742 | } |
743 | } |
744 | } |
745 | |