1use std::ffi::c_void;
4use std::ptr::NonNull;
5use std::sync::atomic::AtomicBool;
6use std::sync::{Arc, Mutex};
7
8use dpi::{LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
9use rwh_06::RawWindowHandle;
10use sctk::reexports::client::Proxy;
11use sctk::reexports::client::protocol::wl_surface::WlSurface;
12use sctk::shell::WaylandSurface;
13use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations};
14use tracing::warn;
15use wingy_core::cursor::Cursor;
16use wingy_core::error::{CreateWindowError, NotSupportedError, RequestError};
17use wingy_core::event_loop::AsyncRequestSerial;
18use wingy_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
19use wingy_core::window::{
20 CursorGrabMode, ImeCapabilities, ImeRequest, ImeRequestError, ResizeDirection, Theme,
21 UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId,
22 WindowLevel,
23};
24
25use super::ActiveEventLoop;
26use super::types::xdg_activation::XdgActivationTokenData;
27use crate::window::common::WindowCommon;
28use crate::{WindowAttributesWayland, output};
29pub(crate) mod state;
30pub use state::WindowState;
31pub(crate) mod handles;
32pub use handles::Handles;
33use handles::WindowRequests;
34pub(crate) mod common;
35
36#[derive(Debug)]
38pub struct Window {
39 window: SctkWindow,
41
42 #[expect(dead_code, reason = "kept alive for its Drop, not read")]
51 window_state: Arc<Mutex<WindowState>>,
52
53 common: WindowCommon,
54}
55
56impl Window {
57 pub(crate) fn new(
58 event_loop_window_target: &ActiveEventLoop,
59 mut attributes: WindowAttributes,
60 ) -> Result<Self, CreateWindowError> {
61 let queue_handle = event_loop_window_target.queue_handle.clone();
62 let mut state = event_loop_window_target.state.borrow_mut();
63
64 let monitors = state.monitors.clone();
65
66 let surface = state.compositor_state.create_surface(&queue_handle);
67 let compositor = state.compositor_state.clone();
68 let xdg_activation =
69 state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
70 let display = event_loop_window_target.handle.connection.display();
71
72 let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
73
74 let default_decorations = if attributes.decorations {
77 WindowDecorations::RequestServer
78 } else {
79 WindowDecorations::RequestClient
80 };
81
82 let window =
83 state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
84
85 let WindowAttributesWayland { name: app_name, activation_token, prefer_csd, .. } =
86 *attributes
87 .platform
88 .take()
89 .and_then(|p| p.cast::<WindowAttributesWayland>().ok())
90 .unwrap_or_default();
91
92 let mut scale_factor = None;
93 if let Some(RawWindowHandle::Wayland(handle)) = attributes.parent_window() {
94 if let Some(s) =
95 state.windows.borrow().get(&WindowId::from_raw(handle.surface.as_ptr() as usize))
96 {
97 scale_factor = Some(s.lock().unwrap().scale_factor());
98 }
99 }
100 let scale_factor = scale_factor.unwrap_or(1.0);
101
102 let mut window_state = WindowState::new(
103 event_loop_window_target,
104 &state,
105 size,
106 state::WindowType::Window { window: window.clone(), last_configure: None },
107 attributes.preferred_theme,
108 prefer_csd,
109 scale_factor,
110 None,
111 );
112
113 window_state.set_window_icon(attributes.window_icon);
114
115 window_state.set_transparent(attributes.transparent);
117
118 let _ = window_state.set_blur(attributes.blur);
120
121 window_state.set_decorate(attributes.decorations);
123
124 if let Some(name) = app_name.map(|name| name.general) {
126 window.set_app_id(name);
127 }
128
129 window_state.set_title(attributes.title);
131
132 let min_size = attributes.min_surface_size.map(|size| size.to_logical(1.));
135 let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
136 window_state.set_min_surface_size(min_size);
137 window_state.set_max_surface_size(max_size);
138
139 window_state.set_resizable(attributes.resizable);
141
142 match attributes.fullscreen {
144 Some(Fullscreen::Exclusive(..)) => {
145 warn!("`Fullscreen::Exclusive` is ignored on Wayland");
146 },
147 Some(Fullscreen::Borderless(monitor)) => {
148 let output = monitor.as_ref().and_then(|monitor| {
149 monitor.cast_ref::<output::MonitorHandle>().map(|handle| &handle.proxy)
150 });
151
152 window.set_fullscreen(output)
153 },
154 _ if attributes.maximized => window.set_maximized(),
155 _ => (),
156 };
157
158 window_state.set_cursor(attributes.cursor);
159
160 if let Some(increments) = attributes.surface_resize_increments {
162 let increments = increments.to_logical(window_state.scale_factor());
163 window_state.set_resize_increments(Some(increments));
164 }
165
166 if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) {
168 xdg_activation.activate(token.into_raw(), &surface);
169 }
170
171 window.commit();
173
174 let window_state = Arc::new(Mutex::new(window_state));
176 let window_id = super::make_wid(&surface);
177 state.windows.get_mut().insert(window_id, window_state.clone());
178
179 let window_requests = WindowRequests {
180 redraw_requested: AtomicBool::new(true),
181 closed: AtomicBool::new(false),
182 };
183 let window_requests = Arc::new(window_requests);
184 state.window_requests.get_mut().insert(window_id, window_requests.clone());
185
186 let window_events_sink = state.window_events_sink.clone();
188
189 let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
190 let event_queue = wayland_source.queue();
191
192 event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
194
195 while !window_state.lock().unwrap().is_configured() {
197 event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
198 }
199
200 let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
202 event_loop_awakener.ping();
203
204 let state_weak = Arc::downgrade(&window_state);
205
206 Ok(Self {
207 window,
208 window_state,
209 common: WindowCommon {
210 state: state_weak,
211 window_id,
212 display,
213 handles: Handles {
214 queue_handle,
215 window_requests,
216 monitors,
217 event_loop_awakener,
218 window_events_sink,
219
220 compositor,
221
222 xdg_activation,
223 attention_requested: Arc::new(AtomicBool::new(false)),
224 },
225 },
226 })
227 }
228
229 pub(crate) fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
230 NonNull::new(self.window.xdg_toplevel().id().as_ptr().cast())
231 }
232}
233
234impl Window {
235 pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
236 let xdg_activation = match self.common.handles.xdg_activation.as_ref() {
237 Some(xdg_activation) => xdg_activation,
238 None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
239 };
240
241 let serial = AsyncRequestSerial::get();
242
243 let data = XdgActivationTokenData::Obtain((self.common.id(), serial));
244 let xdg_activation_token =
245 xdg_activation.get_activation_token(&self.common.handles.queue_handle, data);
246 xdg_activation_token.set_surface(self.surface());
247 xdg_activation_token.commit();
248
249 Ok(serial)
250 }
251
252 #[inline]
253 pub fn surface(&self) -> &WlSurface {
254 self.window.wl_surface()
255 }
256}
257
258impl CoreWindow for Window {
259 fn window_type(&self) -> wingy_core::window::WindowType {
260 wingy_core::window::WindowType::Window
261 }
262
263 fn id(&self) -> WindowId {
264 self.common.id()
265 }
266
267 fn request_redraw(&self) {
268 self.common.request_redraw();
269 }
270
271 #[inline]
272 fn title(&self) -> String {
273 self.common.title()
274 }
275
276 fn pre_present_notify(&self) {
277 self.common.pre_present_notify();
278 }
279
280 fn reset_dead_keys(&self) {
281 self.common.reset_dead_keys();
282 }
283
284 fn surface_position(&self) -> PhysicalPosition<i32> {
285 (0, 0).into()
286 }
287
288 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
289 Err(NotSupportedError::new("window position information is not available on Wayland")
290 .into())
291 }
292
293 fn set_outer_position(&self, _position: Position) {
294 }
296
297 fn surface_size(&self) -> PhysicalSize<u32> {
298 self.common.surface_size()
299 }
300
301 fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
302 self.common.request_surface_size(size)
303 }
304
305 fn outer_size(&self) -> PhysicalSize<u32> {
306 self.common.outer_size()
307 }
308
309 fn safe_area(&self) -> PhysicalInsets<u32> {
310 self.common.safe_area()
311 }
312
313 fn set_min_surface_size(&self, min_size: Option<Size>) {
314 self.common.set_min_surface_size(min_size);
315 }
316
317 #[inline]
319 fn set_max_surface_size(&self, max_size: Option<Size>) {
320 self.common.set_max_surface_size(max_size);
321 }
322
323 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
324 self.common.surface_resize_increments()
325 }
326
327 fn set_surface_resize_increments(&self, increments: Option<Size>) {
328 self.common.set_surface_resize_increments(increments);
329 }
330
331 fn set_title(&self, title: &str) {
332 self.common.set_title(title);
333 }
334
335 #[inline]
336 fn set_transparent(&self, transparent: bool) {
337 self.common.set_transparent(transparent);
338 }
339
340 fn set_visible(&self, visible: bool) {
341 self.common.set_visible(visible);
342 }
343
344 fn is_visible(&self) -> Option<bool> {
345 self.common.is_visible()
346 }
347
348 fn set_resizable(&self, resizable: bool) {
349 self.common.set_resizable(resizable);
350 }
351
352 fn is_resizable(&self) -> bool {
353 self.common.is_resizable()
354 }
355
356 fn set_enabled_buttons(&self, _buttons: WindowButtons) {
357 }
359
360 fn enabled_buttons(&self) -> WindowButtons {
361 WindowButtons::all()
363 }
364
365 fn set_minimized(&self, minimized: bool) {
366 if !minimized {
368 warn!("Unminimizing is ignored on Wayland.");
369 return;
370 }
371
372 self.window.set_minimized();
373 }
374
375 fn is_minimized(&self) -> Option<bool> {
376 None
378 }
379
380 fn set_maximized(&self, maximized: bool) {
381 self.common.set_maximized(maximized)
382 }
383
384 fn is_maximized(&self) -> bool {
385 self.common.is_maximized()
386 }
387
388 fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
389 self.common.set_fullscreen(fullscreen)
390 }
391
392 fn fullscreen(&self) -> Option<Fullscreen> {
393 self.common.fullscreen()
394 }
395
396 #[inline]
397 fn scale_factor(&self) -> f64 {
398 self.common.scale_factor()
399 }
400
401 #[inline]
402 fn set_blur(&self, blur: bool) {
403 self.common.set_blur(blur);
404 }
405
406 #[inline]
407 fn set_decorations(&self, decorate: bool) {
408 self.common.set_decorations(decorate);
409 }
410
411 #[inline]
412 fn is_decorated(&self) -> bool {
413 self.common.is_decorated().unwrap()
414 }
415
416 fn set_window_level(&self, level: WindowLevel) {
417 self.common.set_window_level(level);
418 }
419
420 fn set_window_icon(&self, window_icon: Option<wingy_core::icon::Icon>) {
421 self.common.set_window_icon(window_icon);
422 }
423
424 #[inline]
425 fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
426 self.common.request_ime_update(request)
427 }
428
429 #[inline]
430 fn ime_capabilities(&self) -> Option<ImeCapabilities> {
431 self.common.ime_capabilities()
432 }
433
434 fn focus_window(&self) {
435 self.common.focus_window();
436 }
437
438 fn has_focus(&self) -> bool {
439 self.common.has_focus()
440 }
441
442 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
443 self.common.request_user_attention(request_type);
444 }
445
446 fn set_theme(&self, theme: Option<Theme>) {
447 self.common.set_theme(theme);
448 }
449
450 fn theme(&self) -> Option<Theme> {
451 self.common.theme()
452 }
453
454 fn set_content_protected(&self, protected: bool) {
455 self.common.set_content_protected(protected);
456 }
457
458 fn set_cursor(&self, cursor: Cursor) {
459 self.common.set_cursor(cursor);
460 }
461
462 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
463 self.common.set_cursor_position(position)
464 }
465
466 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
467 self.common.set_cursor_grab(mode)
468 }
469
470 fn set_cursor_visible(&self, visible: bool) {
471 self.common.set_cursor_visible(visible);
472 }
473
474 fn drag_window(&self) -> Result<(), RequestError> {
475 self.common.drag_window()
476 }
477
478 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
479 self.common.drag_resize_window(direction)
480 }
481
482 fn show_window_menu(&self, position: Position) {
483 self.common.show_window_menu(position);
484 }
485
486 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
487 self.common.set_cursor_hittest(hittest)
488 }
489
490 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
491 self.common.current_monitor()
492 }
493
494 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
495 self.common.available_monitors()
496 }
497
498 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
499 self.common.primary_monitor()
500 }
501
502 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
504 &self.common
505 }
506
507 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
509 &self.common
510 }
511}