1use std::borrow::Cow;
2use std::ffi::CString;
3use std::mem::replace;
4use std::num::NonZeroU32;
5use std::ops::Deref;
6use std::os::raw::*;
7use std::path::Path;
8use std::sync::{Arc, Mutex, MutexGuard};
9use std::{cmp, env};
10
11use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
12use tracing::{debug, info, warn};
13use winit_core::application::ApplicationHandler;
14use winit_core::cursor::Cursor;
15use winit_core::error::{NotSupportedError, RequestError};
16use winit_core::event::{SurfaceSizeWriter, WindowEvent};
17use winit_core::event_loop::AsyncRequestSerial;
18use winit_core::icon::RgbaIcon;
19use winit_core::monitor::{
20 Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode,
21};
22use winit_core::window::{
23 CursorGrabMode, ImeCapabilities, ImeRequest as CoreImeRequest, ImeRequestError,
24 ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes,
25 WindowButtons, WindowId, WindowLevel,
26};
27use x11rb::connection::{Connection, RequestConnection};
28use x11rb::properties::{WmHints, WmSizeHints, WmSizeHintsSpecification};
29use x11rb::protocol::shape::SK;
30use x11rb::protocol::sync::{ConnectionExt as _, Int64};
31use x11rb::protocol::xfixes::{ConnectionExt, RegionWrapper};
32use x11rb::protocol::xproto::{self, ConnectionExt as _, Rectangle};
33use x11rb::protocol::{randr, xinput};
34
35use crate::atoms::*;
36use crate::event_loop::{
37 ALL_MASTER_DEVICES, ActivationItem, ActiveEventLoop, CookieResultExt, ICONIC_STATE, VoidCookie,
38 WakeSender, X11Error, xinput_fp1616_to_float,
39};
40use crate::ime::{ImeRequest, ImeSender};
41use crate::monitor::MonitorHandle as X11MonitorHandle;
42use crate::util::{self, CustomCursor, SelectedCursor, rgba_to_cardinals};
43use crate::xdisplay::XConnection;
44use crate::{WindowAttributesX11, WindowType, ffi};
45
46#[derive(Debug)]
47pub struct Window(Arc<UnownedWindow>);
48
49impl Deref for Window {
50 type Target = UnownedWindow;
51
52 #[inline]
53 fn deref(&self) -> &UnownedWindow {
54 &self.0
55 }
56}
57
58impl Window {
59 pub(crate) fn new(
60 event_loop: &ActiveEventLoop,
61 attribs: WindowAttributes,
62 ) -> Result<Self, RequestError> {
63 let window = Arc::new(UnownedWindow::new(event_loop, attribs)?);
64 event_loop.windows.borrow_mut().insert(window.id(), Arc::downgrade(&window));
65 Ok(Window(window))
66 }
67}
68
69impl CoreWindow for Window {
70 fn id(&self) -> WindowId {
71 self.0.id()
72 }
73
74 fn scale_factor(&self) -> f64 {
75 self.0.scale_factor()
76 }
77
78 fn request_redraw(&self) {
79 self.0.request_redraw()
80 }
81
82 fn pre_present_notify(&self) {
83 self.0.pre_present_notify()
84 }
85
86 fn reset_dead_keys(&self) {
87 winit_common::xkb::reset_dead_keys();
88 }
89
90 fn surface_position(&self) -> PhysicalPosition<i32> {
91 self.0.surface_position()
92 }
93
94 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
95 self.0.outer_position()
96 }
97
98 fn set_outer_position(&self, position: Position) {
99 self.0.set_outer_position(position)
100 }
101
102 fn surface_size(&self) -> PhysicalSize<u32> {
103 self.0.surface_size()
104 }
105
106 fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
107 self.0.request_surface_size(size)
108 }
109
110 fn outer_size(&self) -> PhysicalSize<u32> {
111 self.0.outer_size()
112 }
113
114 fn safe_area(&self) -> PhysicalInsets<u32> {
115 self.0.safe_area()
116 }
117
118 fn set_min_surface_size(&self, min_size: Option<Size>) {
119 self.0.set_min_surface_size(min_size)
120 }
121
122 fn set_max_surface_size(&self, max_size: Option<Size>) {
123 self.0.set_max_surface_size(max_size)
124 }
125
126 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
127 self.0.surface_resize_increments()
128 }
129
130 fn set_surface_resize_increments(&self, increments: Option<Size>) {
131 self.0.set_surface_resize_increments(increments)
132 }
133
134 fn set_title(&self, title: &str) {
135 self.0.set_title(title);
136 }
137
138 fn set_transparent(&self, transparent: bool) {
139 self.0.set_transparent(transparent);
140 }
141
142 fn set_blur(&self, blur: bool) {
143 self.0.set_blur(blur);
144 }
145
146 fn set_visible(&self, visible: bool) {
147 self.0.set_visible(visible);
148 }
149
150 fn is_visible(&self) -> Option<bool> {
151 self.0.is_visible()
152 }
153
154 fn set_resizable(&self, resizable: bool) {
155 self.0.set_resizable(resizable);
156 }
157
158 fn is_resizable(&self) -> bool {
159 self.0.is_resizable()
160 }
161
162 fn set_enabled_buttons(&self, buttons: WindowButtons) {
163 self.0.set_enabled_buttons(buttons)
164 }
165
166 fn enabled_buttons(&self) -> WindowButtons {
167 self.0.enabled_buttons()
168 }
169
170 fn set_minimized(&self, minimized: bool) {
171 self.0.set_minimized(minimized)
172 }
173
174 fn is_minimized(&self) -> Option<bool> {
175 self.0.is_minimized()
176 }
177
178 fn set_maximized(&self, maximized: bool) {
179 self.0.set_maximized(maximized)
180 }
181
182 fn is_maximized(&self) -> bool {
183 self.0.is_maximized()
184 }
185
186 fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
187 self.0.set_fullscreen(fullscreen)
188 }
189
190 fn fullscreen(&self) -> Option<Fullscreen> {
191 self.0.fullscreen()
192 }
193
194 fn set_decorations(&self, decorations: bool) {
195 self.0.set_decorations(decorations);
196 }
197
198 fn is_decorated(&self) -> bool {
199 self.0.is_decorated()
200 }
201
202 fn set_window_level(&self, level: WindowLevel) {
203 self.0.set_window_level(level);
204 }
205
206 fn set_window_icon(&self, window_icon: Option<winit_core::icon::Icon>) {
207 let icon = match window_icon.as_ref() {
208 Some(icon) => icon.cast_ref::<RgbaIcon>(),
209 None => None,
210 };
211 self.0.set_window_icon(icon)
212 }
213
214 fn request_ime_update(&self, action: CoreImeRequest) -> Result<(), ImeRequestError> {
215 self.0.request_ime_update(action)
216 }
217
218 fn ime_capabilities(&self) -> Option<ImeCapabilities> {
219 self.0.ime_capabilities()
220 }
221
222 fn focus_window(&self) {
223 self.0.focus_window();
224 }
225
226 fn has_focus(&self) -> bool {
227 self.0.has_focus()
228 }
229
230 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
231 self.0.request_user_attention(request_type);
232 }
233
234 fn set_theme(&self, theme: Option<Theme>) {
235 self.0.set_theme(theme);
236 }
237
238 fn theme(&self) -> Option<Theme> {
239 self.0.theme()
240 }
241
242 fn set_content_protected(&self, protected: bool) {
243 self.0.set_content_protected(protected);
244 }
245
246 fn title(&self) -> String {
247 self.0.title()
248 }
249
250 fn set_cursor(&self, cursor: Cursor) {
251 self.0.set_cursor(cursor);
252 }
253
254 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
255 self.0.set_cursor_position(position)
256 }
257
258 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
259 self.0.set_cursor_grab(mode)
260 }
261
262 fn set_cursor_visible(&self, visible: bool) {
263 self.0.set_cursor_visible(visible);
264 }
265
266 fn drag_window(&self) -> Result<(), RequestError> {
267 self.0.drag_window()
268 }
269
270 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
271 self.0.drag_resize_window(direction)
272 }
273
274 fn show_window_menu(&self, position: Position) {
275 self.0.show_window_menu(position);
276 }
277
278 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
279 self.0.set_cursor_hittest(hittest)
280 }
281
282 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
283 self.0.current_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
284 }
285
286 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
287 Box::new(
288 self.0
289 .available_monitors()
290 .into_iter()
291 .map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
292 )
293 }
294
295 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
296 self.0.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
297 }
298
299 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
300 self
301 }
302
303 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
304 self
305 }
306}
307
308impl rwh_06::HasDisplayHandle for Window {
309 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
310 let raw = self.0.raw_display_handle_rwh_06()?;
311 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
312 }
313}
314
315impl rwh_06::HasWindowHandle for Window {
316 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
317 let raw = self.0.raw_window_handle_rwh_06()?;
318 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
319 }
320}
321
322impl Drop for Window {
323 fn drop(&mut self) {
324 let window = &self.0;
325 let xconn = &window.xconn;
326
327 if let Some(Fullscreen::Exclusive(..)) = window.fullscreen() {
329 window.set_fullscreen(None);
330 }
331
332 if let Ok(c) =
333 xconn.xcb_connection().destroy_window(window.id().into_raw() as xproto::Window)
334 {
335 c.ignore_error();
336 }
337 }
338}
339
340#[derive(Debug)]
341pub struct SharedState {
342 pub cursor_pos: Option<(f64, f64)>,
343 pub size: Option<(u32, u32)>,
344 pub position: Option<(i32, i32)>,
345 pub inner_position: Option<(i32, i32)>,
346 pub inner_position_rel_parent: Option<(i32, i32)>,
347 pub is_resizable: bool,
348 pub is_decorated: bool,
349 pub ime_capabilities: Option<ImeCapabilities>,
350 pub last_monitor: X11MonitorHandle,
351 pub dpi_adjusted: Option<(u32, u32)>,
352 pub(crate) fullscreen: Option<Fullscreen>,
353 pub(crate) desired_fullscreen: Option<Option<Fullscreen>>,
355 pub restore_position: Option<(i32, i32)>,
357 pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
359 pub frame_extents: Option<util::FrameExtentsHeuristic>,
360 pub min_surface_size: Option<Size>,
361 pub max_surface_size: Option<Size>,
362 pub surface_resize_increments: Option<Size>,
363 pub base_size: Option<Size>,
364 pub visibility: Visibility,
365 pub has_focus: bool,
366 pub cursor_hittest: Option<bool>,
368}
369
370#[derive(Copy, Clone, Debug, Eq, PartialEq)]
371pub enum Visibility {
372 No,
373 Yes,
374 YesWait,
376}
377
378impl SharedState {
379 fn new(last_monitor: X11MonitorHandle, window_attributes: &WindowAttributes) -> Mutex<Self> {
380 let visibility =
381 if window_attributes.visible { Visibility::YesWait } else { Visibility::No };
382
383 Mutex::new(SharedState {
384 last_monitor,
385 visibility,
386
387 is_resizable: window_attributes.resizable,
388 is_decorated: window_attributes.decorations,
389 cursor_pos: None,
390 size: None,
391 position: None,
392 inner_position: None,
393 ime_capabilities: None,
394 inner_position_rel_parent: None,
395 dpi_adjusted: None,
396 fullscreen: None,
397 desired_fullscreen: None,
398 restore_position: None,
399 desktop_video_mode: None,
400 frame_extents: None,
401 min_surface_size: None,
402 max_surface_size: None,
403 surface_resize_increments: None,
404 base_size: None,
405 has_focus: false,
406 cursor_hittest: None,
407 })
408 }
409}
410
411unsafe impl Send for UnownedWindow {}
412unsafe impl Sync for UnownedWindow {}
413
414#[derive(Debug)]
415pub struct UnownedWindow {
416 pub(crate) xconn: Arc<XConnection>, xwindow: xproto::Window, #[allow(dead_code)]
419 visual: u32, root: xproto::Window, #[allow(dead_code)]
422 screen_id: i32, sync_counter_id: Option<NonZeroU32>, selected_cursor: Mutex<SelectedCursor>,
425 cursor_grabbed_mode: Mutex<CursorGrabMode>,
426 #[allow(clippy::mutex_atomic)]
427 cursor_visible: Mutex<bool>,
428 ime_sender: Mutex<ImeSender>,
429 pub shared_state: Mutex<SharedState>,
430 redraw_sender: WakeSender<WindowId>,
431 activation_sender: WakeSender<ActivationItem>,
432}
433macro_rules! leap {
434 ($e:expr) => {
435 $e.map_err(|err| os_error!(err))?
436 };
437}
438
439impl UnownedWindow {
440 #[allow(clippy::unnecessary_cast)]
441 pub(crate) fn new(
442 event_loop: &ActiveEventLoop,
443 mut window_attrs: WindowAttributes,
444 ) -> Result<UnownedWindow, RequestError> {
445 let xconn = &event_loop.xconn;
446 let atoms = xconn.atoms();
447
448 let x11_attributes = window_attrs
449 .platform
450 .take()
451 .and_then(|attrs| attrs.cast::<WindowAttributesX11>().ok())
452 .unwrap_or_default();
453
454 let screen_id = match x11_attributes.screen_id {
455 Some(id) => id,
456 None => xconn.default_screen_index() as c_int,
457 };
458
459 let screen = {
460 let screen_id_usize = usize::try_from(screen_id)
461 .map_err(|_| NotSupportedError::new("screen id must be non-negative"))?;
462 xconn.xcb_connection().setup().roots.get(screen_id_usize).ok_or(
463 NotSupportedError::new("requested screen id not present in server's response"),
464 )?
465 };
466
467 let root = match window_attrs.parent_window() {
468 Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
469 Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
470 Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
471 None => screen.root,
472 };
473
474 let mut monitors = leap!(xconn.available_monitors());
475 let guessed_monitor = if monitors.is_empty() {
476 X11MonitorHandle::dummy()
477 } else {
478 xconn
479 .query_pointer(root, util::VIRTUAL_CORE_POINTER)
480 .ok()
481 .and_then(|pointer_state| {
482 let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
483
484 for i in 0..monitors.len() {
485 if monitors[i].rect.contains_point(x, y) {
486 return Some(monitors.swap_remove(i));
487 }
488 }
489
490 None
491 })
492 .unwrap_or_else(|| monitors.swap_remove(0))
493 };
494 let scale_factor = guessed_monitor.scale_factor();
495
496 info!("Guessed window scale factor: {}", scale_factor);
497
498 let max_surface_size: Option<(u32, u32)> =
499 window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
500 let min_surface_size: Option<(u32, u32)> =
501 window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
502
503 let position =
504 window_attrs.position.map(|position| position.to_physical::<i32>(scale_factor));
505
506 let dimensions = {
507 let mut dimensions: (u32, u32) = window_attrs
510 .surface_size
511 .map(|size| size.to_physical::<u32>(scale_factor))
512 .or_else(|| Some((800, 600).into()))
513 .map(Into::into)
514 .unwrap();
515 if let Some(max) = max_surface_size {
516 dimensions.0 = cmp::min(dimensions.0, max.0);
517 dimensions.1 = cmp::min(dimensions.1, max.1);
518 }
519 if let Some(min) = min_surface_size {
520 dimensions.0 = cmp::max(dimensions.0, min.0);
521 dimensions.1 = cmp::max(dimensions.1, min.1);
522 }
523 debug!("Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1);
524 dimensions
525 };
526
527 let mut all_visuals = screen
529 .allowed_depths
530 .iter()
531 .flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
532
533 let (visualtype, depth, require_colormap) = match x11_attributes.visual_id {
535 Some(vi) => {
536 let (visualtype, depth) = all_visuals
538 .find(|(visual, _)| visual.visual_id == vi)
539 .ok_or_else(|| os_error!(X11Error::NoSuchVisual(vi)))?;
540
541 (Some(visualtype), depth, true)
542 },
543 None if window_attrs.transparent => {
544 all_visuals
546 .find_map(|(visual, depth)| {
547 (depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR)
548 .then_some((Some(visual), depth, true))
549 })
550 .unwrap_or_else(|| {
551 debug!(
552 "Could not set transparency, because XMatchVisualInfo returned zero \
553 for the required parameters"
554 );
555 (None as _, x11rb::COPY_FROM_PARENT as _, false)
556 })
557 },
558 _ => (None, x11rb::COPY_FROM_PARENT as _, false),
559 };
560 let mut visual = visualtype.map_or(x11rb::COPY_FROM_PARENT, |v| v.visual_id);
561
562 let window_attributes = {
563 use xproto::EventMask;
564
565 let mut aux = xproto::CreateWindowAux::new();
566 let event_mask = EventMask::EXPOSURE
567 | EventMask::STRUCTURE_NOTIFY
568 | EventMask::VISIBILITY_CHANGE
569 | EventMask::KEY_PRESS
570 | EventMask::KEY_RELEASE
571 | EventMask::KEYMAP_STATE
572 | EventMask::BUTTON_PRESS
573 | EventMask::BUTTON_RELEASE
574 | EventMask::POINTER_MOTION
575 | EventMask::PROPERTY_CHANGE;
576
577 aux = aux.event_mask(event_mask).border_pixel(0);
578
579 if x11_attributes.override_redirect {
580 aux = aux.override_redirect(true as u32);
581 }
582
583 let colormap_visual = match x11_attributes.visual_id {
585 Some(vi) => Some(vi),
586 None if require_colormap => Some(visual),
587 _ => None,
588 };
589
590 if let Some(visual) = colormap_visual {
591 let colormap = leap!(xconn.xcb_connection().generate_id());
592 leap!(xconn.xcb_connection().create_colormap(
593 xproto::ColormapAlloc::NONE,
594 colormap,
595 root,
596 visual,
597 ));
598 aux = aux.colormap(colormap);
599 } else {
600 aux = aux.colormap(0);
601 }
602
603 aux
604 };
605
606 let parent = x11_attributes.embed_window.unwrap_or(root);
608
609 let xwindow = {
611 let (x, y) = position.map_or((0, 0), Into::into);
612 let wid = leap!(xconn.xcb_connection().generate_id());
613 let result = xconn.xcb_connection().create_window(
614 depth,
615 wid,
616 parent,
617 x,
618 y,
619 dimensions.0.try_into().unwrap(),
620 dimensions.1.try_into().unwrap(),
621 0,
622 xproto::WindowClass::INPUT_OUTPUT,
623 visual,
624 &window_attributes,
625 );
626 leap!(leap!(result).check());
627
628 wid
629 };
630
631 if visual == x11rb::COPY_FROM_PARENT {
635 visual = leap!(
636 leap!(xconn.xcb_connection().get_window_attributes(xwindow as xproto::Window))
637 .reply()
638 )
639 .visual;
640 }
641
642 #[allow(clippy::mutex_atomic)]
643 let mut window = UnownedWindow {
644 xconn: Arc::clone(xconn),
645 xwindow: xwindow as xproto::Window,
646 visual,
647 root,
648 screen_id,
649 sync_counter_id: None,
650 selected_cursor: Default::default(),
651 cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
652 cursor_visible: Mutex::new(true),
653 ime_sender: Mutex::new(event_loop.ime_sender.clone()),
654 shared_state: SharedState::new(guessed_monitor, &window_attrs),
655 redraw_sender: event_loop.redraw_sender.clone(),
656 activation_sender: event_loop.activation_sender.clone(),
657 };
658
659 leap!(window.set_title_inner(&window_attrs.title)).ignore_error();
663 leap!(window.set_decorations_inner(window_attrs.decorations)).ignore_error();
664
665 if let Some(theme) = window_attrs.preferred_theme {
666 leap!(window.set_theme_inner(Some(theme))).ignore_error();
667 }
668
669 if x11_attributes.embed_window.is_some() {
671 window.embed_window()?;
672 }
673
674 {
675 {
677 let dnd_aware_atom = atoms[XdndAware];
678 let version = &[5u32]; leap!(xconn.change_property(
680 window.xwindow,
681 dnd_aware_atom,
682 u32::from(xproto::AtomEnum::ATOM),
683 xproto::PropMode::REPLACE,
684 version,
685 ))
686 .ignore_error();
687 }
688
689 {
691 let (instance, class) = if let Some(name) = x11_attributes.name {
692 (name.instance, name.general)
693 } else {
694 let class = env::args_os()
695 .next()
696 .as_ref()
697 .and_then(|path| Path::new(path).file_name())
699 .and_then(|bin_name| bin_name.to_str())
700 .map(|bin_name| bin_name.to_owned())
701 .unwrap_or_else(|| window_attrs.title.clone());
702 let instance = env::var("RESOURCE_NAME").ok().unwrap_or_else(|| class.clone());
704 (instance, class)
705 };
706
707 let class = format!("{instance}\0{class}\0");
708 leap!(xconn.change_property(
709 window.xwindow,
710 xproto::Atom::from(xproto::AtomEnum::WM_CLASS),
711 xproto::Atom::from(xproto::AtomEnum::STRING),
712 xproto::PropMode::REPLACE,
713 class.as_bytes(),
714 ))
715 .ignore_error();
716 }
717
718 if let Some(flusher) = leap!(window.set_pid()) {
719 flusher.ignore_error()
720 }
721
722 leap!(window.set_window_types(x11_attributes.x11_window_types)).ignore_error();
723
724 let mut min_surface_size =
726 window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
727 let mut max_surface_size =
728 window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
729
730 if !window_attrs.resizable {
731 if util::wm_name_is_one_of(&["Xfwm4"]) {
732 warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
733 } else {
734 max_surface_size = Some(dimensions.into());
735 min_surface_size = Some(dimensions.into());
736 }
737 }
738
739 let shared_state = window.shared_state.get_mut().unwrap();
740 shared_state.min_surface_size = min_surface_size.map(Into::into);
741 shared_state.max_surface_size = max_surface_size.map(Into::into);
742 shared_state.surface_resize_increments = window_attrs.surface_resize_increments;
743 shared_state.base_size = x11_attributes.base_size;
744
745 let normal_hints = WmSizeHints {
746 position: position.map(|PhysicalPosition { x, y }| {
747 (WmSizeHintsSpecification::UserSpecified, x, y)
748 }),
749 size: Some((
750 WmSizeHintsSpecification::UserSpecified,
751 cast_dimension_to_hint(dimensions.0),
752 cast_dimension_to_hint(dimensions.1),
753 )),
754 max_size: max_surface_size.map(cast_physical_size_to_hint),
755 min_size: min_surface_size.map(cast_physical_size_to_hint),
756 size_increment: window_attrs
757 .surface_resize_increments
758 .map(|size| cast_size_to_hint(size, scale_factor)),
759 base_size: x11_attributes
760 .base_size
761 .map(|size| cast_size_to_hint(size, scale_factor)),
762 aspect: None,
763 win_gravity: None,
764 };
765 leap!(
766 leap!(normal_hints.set(
767 xconn.xcb_connection(),
768 window.xwindow as xproto::Window,
769 xproto::AtomEnum::WM_NORMAL_HINTS,
770 ))
771 .check()
772 );
773
774 if let Some(icon) =
776 window_attrs.window_icon.as_ref().and_then(|icon| icon.cast_ref::<RgbaIcon>())
777 {
778 leap!(window.set_icon_inner(icon)).ignore_error();
779 }
780
781 let result = xconn.xcb_connection().change_property(
783 xproto::PropMode::REPLACE,
784 window.xwindow,
785 atoms[WM_PROTOCOLS],
786 xproto::AtomEnum::ATOM,
787 32,
788 3,
789 bytemuck::cast_slice::<xproto::Atom, u8>(&[
790 atoms[WM_DELETE_WINDOW],
791 atoms[_NET_WM_PING],
792 atoms[_NET_WM_SYNC_REQUEST],
793 ]),
794 );
795 leap!(result).ignore_error();
796
797 if leap!(xconn.xcb_connection().extension_information("SYNC")).is_some() {
799 let sync_counter_id = leap!(xconn.xcb_connection().generate_id());
800 window.sync_counter_id = NonZeroU32::new(sync_counter_id);
801
802 leap!(
803 xconn.xcb_connection().sync_create_counter(sync_counter_id, Int64::default())
804 )
805 .ignore_error();
806
807 let result = xconn.xcb_connection().change_property(
808 xproto::PropMode::REPLACE,
809 window.xwindow,
810 atoms[_NET_WM_SYNC_REQUEST_COUNTER],
811 xproto::AtomEnum::CARDINAL,
812 32,
813 1,
814 bytemuck::cast_slice::<u32, u8>(&[sync_counter_id]),
815 );
816 leap!(result).ignore_error();
817 }
818
819 let mask = xinput::XIEventMask::MOTION
821 | xinput::XIEventMask::BUTTON_PRESS
822 | xinput::XIEventMask::BUTTON_RELEASE
823 | xinput::XIEventMask::ENTER
824 | xinput::XIEventMask::LEAVE
825 | xinput::XIEventMask::FOCUS_IN
826 | xinput::XIEventMask::FOCUS_OUT
827 | xinput::XIEventMask::TOUCH_BEGIN
828 | xinput::XIEventMask::TOUCH_UPDATE
829 | xinput::XIEventMask::TOUCH_END;
830 leap!(xconn.select_xinput_events(window.xwindow, ALL_MASTER_DEVICES, mask))
831 .ignore_error();
832
833 if window_attrs.visible {
835 leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
836 leap!(xconn.xcb_connection().configure_window(
837 xwindow,
838 &xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE)
839 ))
840 .ignore_error();
841 }
842
843 unsafe {
845 let mut supported_ptr = ffi::False;
846 (xconn.xlib.XkbSetDetectableAutoRepeat)(
847 xconn.display,
848 ffi::True,
849 &mut supported_ptr,
850 );
851 if supported_ptr == ffi::False {
852 return Err(os_error!("`XkbSetDetectableAutoRepeat` failed").into());
853 }
854 }
855
856 if let Some(ime) = event_loop.ime.as_ref() {
858 ime.borrow_mut()
859 .create_context(window.xwindow as ffi::Window, false)
860 .map_err(|err| os_error!(err))?;
861 }
862
863 if window_attrs.maximized {
865 leap!(window.set_maximized_inner(window_attrs.maximized)).ignore_error();
866 }
867
868 if window_attrs.fullscreen.is_some() {
869 if let Some(flusher) =
870 leap!(window.set_fullscreen_inner(window_attrs.fullscreen.clone()))
871 {
872 flusher.ignore_error()
873 }
874
875 if let Some(PhysicalPosition { x, y }) = position {
876 let shared_state = window.shared_state.get_mut().unwrap();
877
878 shared_state.restore_position = Some((x, y));
879 }
880 }
881
882 leap!(window.set_window_level_inner(window_attrs.window_level)).ignore_error();
883 }
884
885 window.set_cursor(window_attrs.cursor);
886
887 if let Some(startup) = x11_attributes.activation_token.as_ref() {
889 leap!(xconn.remove_activation_token(xwindow, startup.as_raw()));
890 }
891
892 let window = leap!(xconn.sync_with_server().map(|_| window));
894
895 Ok(window)
896 }
897
898 pub(super) fn embed_window(&self) -> Result<(), RequestError> {
900 let atoms = self.xconn.atoms();
901 leap!(
902 leap!(self.xconn.change_property(
903 self.xwindow,
904 atoms[_XEMBED],
905 atoms[_XEMBED],
906 xproto::PropMode::REPLACE,
907 &[0u32, 1u32],
908 ))
909 .check()
910 );
911
912 Ok(())
913 }
914
915 pub(super) fn shared_state_lock(&self) -> MutexGuard<'_, SharedState> {
916 self.shared_state.lock().unwrap()
917 }
918
919 fn set_pid(&self) -> Result<Option<VoidCookie<'_>>, X11Error> {
920 let atoms = self.xconn.atoms();
921 let pid_atom = atoms[_NET_WM_PID];
922 let client_machine_atom = atoms[WM_CLIENT_MACHINE];
923
924 let uname = rustix::system::uname();
926 let pid = rustix::process::getpid();
927
928 self.xconn
929 .change_property(
930 self.xwindow,
931 pid_atom,
932 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
933 xproto::PropMode::REPLACE,
934 &[pid.as_raw_nonzero().get() as util::Cardinal],
935 )?
936 .ignore_error();
937 let flusher = self.xconn.change_property(
938 self.xwindow,
939 client_machine_atom,
940 xproto::Atom::from(xproto::AtomEnum::STRING),
941 xproto::PropMode::REPLACE,
942 uname.nodename().to_bytes(),
943 );
944 flusher.map(Some)
945 }
946
947 fn set_window_types(&self, window_types: Vec<WindowType>) -> Result<VoidCookie<'_>, X11Error> {
948 let atoms = self.xconn.atoms();
949 let hint_atom = atoms[_NET_WM_WINDOW_TYPE];
950 let atoms: Vec<_> = window_types.iter().map(|t| t.as_atom(&self.xconn)).collect();
951
952 self.xconn.change_property(
953 self.xwindow,
954 hint_atom,
955 xproto::Atom::from(xproto::AtomEnum::ATOM),
956 xproto::PropMode::REPLACE,
957 &atoms,
958 )
959 }
960
961 pub fn set_theme_inner(&self, theme: Option<Theme>) -> Result<VoidCookie<'_>, X11Error> {
962 let atoms = self.xconn.atoms();
963 let hint_atom = atoms[_GTK_THEME_VARIANT];
964 let utf8_atom = atoms[UTF8_STRING];
965 let variant = match theme {
966 Some(Theme::Dark) => "dark",
967 Some(Theme::Light) => "light",
968 None => "dark",
969 };
970 let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
971 self.xconn.change_property(
972 self.xwindow,
973 hint_atom,
974 utf8_atom,
975 xproto::PropMode::REPLACE,
976 variant.as_bytes(),
977 )
978 }
979
980 #[inline]
981 pub fn set_theme(&self, theme: Option<Theme>) {
982 self.set_theme_inner(theme).expect("Failed to change window theme").ignore_error();
983
984 self.xconn.flush_requests().expect("Failed to change window theme");
985 }
986
987 fn set_netwm(
988 &self,
989 operation: util::StateOperation,
990 properties: (u32, u32, u32, u32),
991 ) -> Result<VoidCookie<'_>, X11Error> {
992 let atoms = self.xconn.atoms();
993 let state_atom = atoms[_NET_WM_STATE];
994 self.xconn.send_client_msg(
995 self.xwindow,
996 self.root,
997 state_atom,
998 Some(xproto::EventMask::SUBSTRUCTURE_REDIRECT | xproto::EventMask::SUBSTRUCTURE_NOTIFY),
999 [operation as u32, properties.0, properties.1, properties.2, properties.3],
1000 )
1001 }
1002
1003 fn set_fullscreen_hint(&self, fullscreen: bool) -> Result<VoidCookie<'_>, X11Error> {
1004 let atoms = self.xconn.atoms();
1005 let fullscreen_atom = atoms[_NET_WM_STATE_FULLSCREEN];
1006 let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom, 0, 0, 0));
1007
1008 if fullscreen {
1009 self.xconn
1012 .xcb_connection()
1013 .set_input_focus(xproto::InputFocus::PARENT, self.xwindow, x11rb::CURRENT_TIME)?
1014 .ignore_error();
1015 }
1016
1017 flusher
1018 }
1019
1020 fn set_fullscreen_inner(
1021 &self,
1022 fullscreen: Option<Fullscreen>,
1023 ) -> Result<Option<VoidCookie<'_>>, X11Error> {
1024 let mut shared_state_lock = self.shared_state_lock();
1025
1026 match shared_state_lock.visibility {
1027 Visibility::No | Visibility::YesWait => {
1029 shared_state_lock.desired_fullscreen = Some(fullscreen);
1030 return Ok(None);
1031 },
1032 Visibility::Yes => (),
1033 }
1034
1035 let old_fullscreen = shared_state_lock.fullscreen.clone();
1036 if old_fullscreen == fullscreen {
1037 return Ok(None);
1038 }
1039 shared_state_lock.fullscreen.clone_from(&fullscreen);
1040
1041 match (&old_fullscreen, &fullscreen) {
1042 (&None, &Some(Fullscreen::Exclusive(ref monitor, _)))
1047 | (&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(ref monitor, _))) => {
1048 let id = monitor.native_id() as _;
1049 shared_state_lock.desktop_video_mode = Some((
1050 id,
1051 self.xconn.get_crtc_mode(id).expect("Failed to get desktop video mode"),
1052 ));
1053 },
1054 (&Some(Fullscreen::Exclusive(..)), &None)
1056 | (&Some(Fullscreen::Exclusive(..)), &Some(Fullscreen::Borderless(_))) => {
1057 let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap();
1058 self.xconn
1059 .set_crtc_config(monitor_id, mode_id)
1060 .expect("failed to restore desktop video mode");
1061 },
1062 _ => (),
1063 }
1064
1065 drop(shared_state_lock);
1066
1067 match fullscreen {
1068 None => {
1069 let flusher = self.set_fullscreen_hint(false);
1070 let mut shared_state_lock = self.shared_state_lock();
1071 if let Some(position) = shared_state_lock.restore_position.take() {
1072 drop(shared_state_lock);
1073 self.set_position_inner(position.0, position.1)
1074 .expect_then_ignore_error("Failed to restore window position");
1075 }
1076 flusher.map(Some)
1077 },
1078 Some(fullscreen) => {
1079 let (monitor, video_mode): (Cow<'_, X11MonitorHandle>, Option<&VideoMode>) =
1080 match &fullscreen {
1081 Fullscreen::Exclusive(monitor, video_mode) => {
1082 let monitor = monitor.cast_ref::<X11MonitorHandle>().unwrap();
1083 (Cow::Borrowed(monitor), Some(video_mode))
1084 },
1085 Fullscreen::Borderless(Some(monitor)) => {
1086 let monitor = monitor.cast_ref::<X11MonitorHandle>().unwrap();
1087 (Cow::Borrowed(monitor), None)
1088 },
1089 Fullscreen::Borderless(None) => {
1090 (Cow::Owned(self.shared_state_lock().last_monitor.clone()), None)
1091 },
1092 };
1093
1094 if monitor.is_dummy() {
1096 return Ok(None);
1097 }
1098
1099 if let Some(native_mode) = video_mode.and_then(|requested| {
1100 monitor.video_modes.iter().find_map(|mode| {
1101 if &mode.mode == requested { Some(mode.native_mode) } else { None }
1102 })
1103 }) {
1104 self.xconn
1130 .set_crtc_config(monitor.native_id() as _, native_mode)
1131 .expect("failed to set video mode");
1132 }
1133
1134 let window_position = self.outer_position_physical();
1135 self.shared_state_lock().restore_position = Some(window_position);
1136 let monitor_origin: (i32, i32) = monitor.position;
1137 self.set_position_inner(monitor_origin.0, monitor_origin.1)
1138 .expect_then_ignore_error("Failed to set window position");
1139 self.set_fullscreen_hint(true).map(Some)
1140 },
1141 }
1142 }
1143
1144 #[inline]
1145 pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
1146 let shared_state = self.shared_state_lock();
1147
1148 shared_state.desired_fullscreen.clone().unwrap_or_else(|| shared_state.fullscreen.clone())
1149 }
1150
1151 #[inline]
1152 pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
1153 if let Some(flusher) =
1154 self.set_fullscreen_inner(fullscreen).expect("Failed to change window fullscreen state")
1155 {
1156 flusher.check().expect("Failed to change window fullscreen state");
1157 self.invalidate_cached_frame_extents();
1158 }
1159 }
1160
1161 pub(crate) fn visibility_notify(&self) {
1163 let mut shared_state = self.shared_state_lock();
1164
1165 match shared_state.visibility {
1166 Visibility::No => self
1167 .xconn
1168 .xcb_connection()
1169 .unmap_window(self.xwindow)
1170 .expect_then_ignore_error("Failed to unmap window"),
1171 Visibility::Yes => (),
1172 Visibility::YesWait => {
1173 shared_state.visibility = Visibility::Yes;
1174
1175 if let Some(fullscreen) = shared_state.desired_fullscreen.take() {
1176 drop(shared_state);
1177 self.set_fullscreen(fullscreen);
1178 }
1179 },
1180 }
1181 }
1182
1183 pub fn current_monitor(&self) -> Option<X11MonitorHandle> {
1184 Some(self.shared_state_lock().last_monitor.clone())
1185 }
1186
1187 pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
1188 self.xconn.available_monitors().expect("Failed to get available monitors")
1189 }
1190
1191 pub fn primary_monitor(&self) -> Option<X11MonitorHandle> {
1192 Some(self.xconn.primary_monitor().expect("Failed to get primary monitor"))
1193 }
1194
1195 #[inline]
1196 pub fn is_minimized(&self) -> Option<bool> {
1197 let atoms = self.xconn.atoms();
1198 let state_atom = atoms[_NET_WM_STATE];
1199 let state = self.xconn.get_property(
1200 self.xwindow,
1201 state_atom,
1202 xproto::Atom::from(xproto::AtomEnum::ATOM),
1203 );
1204 let hidden_atom = atoms[_NET_WM_STATE_HIDDEN];
1205
1206 Some(match state {
1207 Ok(atoms) => {
1208 atoms.iter().any(|atom: &xproto::Atom| *atom as xproto::Atom == hidden_atom)
1209 },
1210 _ => false,
1211 })
1212 }
1213
1214 #[inline]
1216 pub(super) fn refresh_dpi_for_monitor(
1217 &self,
1218 new_monitor: &X11MonitorHandle,
1219 maybe_prev_scale_factor: Option<f64>,
1220 app: &mut dyn ApplicationHandler,
1221 event_loop: &ActiveEventLoop,
1222 ) {
1223 let monitor = self.shared_state_lock().last_monitor.clone();
1225 if monitor.name == new_monitor.name {
1226 let (width, height) = self.surface_size_physical();
1227 let (new_width, new_height) = self.adjust_for_dpi(
1228 maybe_prev_scale_factor.unwrap_or(monitor.scale_factor),
1233 new_monitor.scale_factor,
1234 width,
1235 height,
1236 &self.shared_state_lock(),
1237 );
1238
1239 let old_surface_size = PhysicalSize::new(width, height);
1240 let surface_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
1241 app.window_event(event_loop, self.id(), WindowEvent::ScaleFactorChanged {
1242 scale_factor: new_monitor.scale_factor,
1243 surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)),
1244 });
1245
1246 let new_surface_size = *surface_size.lock().unwrap();
1247 drop(surface_size);
1248
1249 if new_surface_size != old_surface_size {
1250 let (new_width, new_height) = new_surface_size.into();
1251 self.request_surface_size_physical(new_width, new_height);
1252 }
1253 }
1254 }
1255
1256 fn set_minimized_inner(&self, minimized: bool) -> Result<VoidCookie<'_>, X11Error> {
1257 let atoms = self.xconn.atoms();
1258
1259 if minimized {
1260 let root_window = self.xconn.default_root().root;
1261
1262 self.xconn.send_client_msg(
1263 self.xwindow,
1264 root_window,
1265 atoms[WM_CHANGE_STATE],
1266 Some(
1267 xproto::EventMask::SUBSTRUCTURE_REDIRECT
1268 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
1269 ),
1270 [3u32, 0, 0, 0, 0],
1271 )
1272 } else {
1273 self.xconn.send_client_msg(
1274 self.xwindow,
1275 self.root,
1276 atoms[_NET_ACTIVE_WINDOW],
1277 Some(
1278 xproto::EventMask::SUBSTRUCTURE_REDIRECT
1279 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
1280 ),
1281 [1, x11rb::CURRENT_TIME, 0, 0, 0],
1282 )
1283 }
1284 }
1285
1286 #[inline]
1287 pub fn set_minimized(&self, minimized: bool) {
1288 self.set_minimized_inner(minimized)
1289 .expect_then_ignore_error("Failed to change window minimization");
1290
1291 self.xconn.flush_requests().expect("Failed to change window minimization");
1292 }
1293
1294 #[inline]
1295 pub fn is_maximized(&self) -> bool {
1296 let atoms = self.xconn.atoms();
1297 let state_atom = atoms[_NET_WM_STATE];
1298 let state = self.xconn.get_property(
1299 self.xwindow,
1300 state_atom,
1301 xproto::Atom::from(xproto::AtomEnum::ATOM),
1302 );
1303 let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
1304 let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
1305 match state {
1306 Ok(atoms) => {
1307 let horz_maximized = atoms.contains(&horz_atom);
1308 let vert_maximized = atoms.contains(&vert_atom);
1309 horz_maximized && vert_maximized
1310 },
1311 _ => false,
1312 }
1313 }
1314
1315 fn set_maximized_inner(&self, maximized: bool) -> Result<VoidCookie<'_>, X11Error> {
1316 let atoms = self.xconn.atoms();
1317 let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
1318 let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
1319
1320 self.set_netwm(maximized.into(), (horz_atom, vert_atom, 0, 0))
1321 }
1322
1323 #[inline]
1324 pub fn set_maximized(&self, maximized: bool) {
1325 self.set_maximized_inner(maximized)
1326 .expect_then_ignore_error("Failed to change window maximization");
1327 self.xconn.flush_requests().expect("Failed to change window maximization");
1328 self.invalidate_cached_frame_extents();
1329 }
1330
1331 fn set_title_inner(&self, title: &str) -> Result<VoidCookie<'_>, X11Error> {
1332 let atoms = self.xconn.atoms();
1333
1334 let title = CString::new(title).expect("Window title contained null byte");
1335 self.xconn
1336 .change_property(
1337 self.xwindow,
1338 xproto::Atom::from(xproto::AtomEnum::WM_NAME),
1339 xproto::Atom::from(xproto::AtomEnum::STRING),
1340 xproto::PropMode::REPLACE,
1341 title.as_bytes(),
1342 )?
1343 .ignore_error();
1344 self.xconn.change_property(
1345 self.xwindow,
1346 atoms[_NET_WM_NAME],
1347 atoms[UTF8_STRING],
1348 xproto::PropMode::REPLACE,
1349 title.as_bytes(),
1350 )
1351 }
1352
1353 #[inline]
1354 pub fn set_title(&self, title: &str) {
1355 self.set_title_inner(title).expect_then_ignore_error("Failed to set window title");
1356
1357 self.xconn.flush_requests().expect("Failed to set window title");
1358 }
1359
1360 #[inline]
1361 pub fn set_transparent(&self, _transparent: bool) {}
1362
1363 #[inline]
1364 pub fn set_blur(&self, _blur: bool) {}
1365
1366 fn set_decorations_inner(&self, decorations: bool) -> Result<VoidCookie<'_>, X11Error> {
1367 self.shared_state_lock().is_decorated = decorations;
1368 let mut hints = self.xconn.get_motif_hints(self.xwindow);
1369
1370 hints.set_decorations(decorations);
1371
1372 self.xconn.set_motif_hints(self.xwindow, &hints)
1373 }
1374
1375 #[inline]
1376 pub fn set_decorations(&self, decorations: bool) {
1377 self.set_decorations_inner(decorations)
1378 .expect_then_ignore_error("Failed to set decoration state");
1379 self.xconn.flush_requests().expect("Failed to set decoration state");
1380 self.invalidate_cached_frame_extents();
1381 }
1382
1383 #[inline]
1384 pub fn is_decorated(&self) -> bool {
1385 self.shared_state_lock().is_decorated
1386 }
1387
1388 fn set_maximizable_inner(&self, maximizable: bool) -> Result<VoidCookie<'_>, X11Error> {
1389 let mut hints = self.xconn.get_motif_hints(self.xwindow);
1390
1391 hints.set_maximizable(maximizable);
1392
1393 self.xconn.set_motif_hints(self.xwindow, &hints)
1394 }
1395
1396 fn toggle_atom(&self, atom_name: AtomName, enable: bool) -> Result<VoidCookie<'_>, X11Error> {
1397 let atoms = self.xconn.atoms();
1398 let atom = atoms[atom_name];
1399 self.set_netwm(enable.into(), (atom, 0, 0, 0))
1400 }
1401
1402 fn set_window_level_inner(&self, level: WindowLevel) -> Result<VoidCookie<'_>, X11Error> {
1403 self.toggle_atom(_NET_WM_STATE_ABOVE, level == WindowLevel::AlwaysOnTop)?.ignore_error();
1404 self.toggle_atom(_NET_WM_STATE_BELOW, level == WindowLevel::AlwaysOnBottom)
1405 }
1406
1407 #[inline]
1408 pub fn set_window_level(&self, level: WindowLevel) {
1409 self.set_window_level_inner(level)
1410 .expect_then_ignore_error("Failed to set window-level state");
1411 self.xconn.flush_requests().expect("Failed to set window-level state");
1412 }
1413
1414 fn set_icon_inner(&self, icon: &RgbaIcon) -> Result<VoidCookie<'_>, X11Error> {
1415 let atoms = self.xconn.atoms();
1416 let icon_atom = atoms[_NET_WM_ICON];
1417 let data = rgba_to_cardinals(icon);
1418 self.xconn.change_property(
1419 self.xwindow,
1420 icon_atom,
1421 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
1422 xproto::PropMode::REPLACE,
1423 data.as_slice(),
1424 )
1425 }
1426
1427 fn unset_icon_inner(&self) -> Result<VoidCookie<'_>, X11Error> {
1428 let atoms = self.xconn.atoms();
1429 let icon_atom = atoms[_NET_WM_ICON];
1430 let empty_data: [util::Cardinal; 0] = [];
1431 self.xconn.change_property(
1432 self.xwindow,
1433 icon_atom,
1434 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
1435 xproto::PropMode::REPLACE,
1436 &empty_data,
1437 )
1438 }
1439
1440 #[inline]
1441 pub(crate) fn set_window_icon(&self, icon: Option<&RgbaIcon>) {
1442 match icon {
1443 Some(icon) => self.set_icon_inner(icon),
1444 None => self.unset_icon_inner(),
1445 }
1446 .expect_then_ignore_error("Failed to set icons");
1447
1448 self.xconn.flush_requests().expect("Failed to set icons");
1449 }
1450
1451 #[inline]
1452 pub fn set_visible(&self, visible: bool) {
1453 let mut shared_state = self.shared_state_lock();
1454
1455 match (visible, shared_state.visibility) {
1456 (true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => {
1457 return;
1458 },
1459 _ => (),
1460 }
1461
1462 if visible {
1463 self.xconn
1464 .xcb_connection()
1465 .map_window(self.xwindow)
1466 .expect_then_ignore_error("Failed to call `xcb_map_window`");
1467 self.xconn
1468 .xcb_connection()
1469 .configure_window(
1470 self.xwindow,
1471 &xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE),
1472 )
1473 .expect_then_ignore_error("Failed to call `xcb_configure_window`");
1474 self.xconn.flush_requests().expect("Failed to call XMapRaised");
1475 shared_state.visibility = Visibility::YesWait;
1476 } else {
1477 self.xconn
1478 .xcb_connection()
1479 .unmap_window(self.xwindow)
1480 .expect_then_ignore_error("Failed to call `xcb_unmap_window`");
1481 self.xconn.flush_requests().expect("Failed to call XUnmapWindow");
1482 shared_state.visibility = Visibility::No;
1483 }
1484 }
1485
1486 #[inline]
1487 pub fn is_visible(&self) -> Option<bool> {
1488 Some(self.shared_state_lock().visibility == Visibility::Yes)
1489 }
1490
1491 fn update_cached_frame_extents(&self) {
1492 let extents = self.xconn.get_frame_extents_heuristic(self.xwindow, self.root);
1493 self.shared_state_lock().frame_extents = Some(extents);
1494 }
1495
1496 pub(crate) fn invalidate_cached_frame_extents(&self) {
1497 self.shared_state_lock().frame_extents.take();
1498 }
1499
1500 pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
1501 let extents = self.shared_state_lock().frame_extents.clone();
1502 if let Some(extents) = extents {
1503 let (x, y) = self.inner_position_physical();
1504 extents.inner_pos_to_outer(x, y)
1505 } else {
1506 self.update_cached_frame_extents();
1507 self.outer_position_physical()
1508 }
1509 }
1510
1511 #[inline]
1512 pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
1513 let extents = self.shared_state_lock().frame_extents.clone();
1514 if let Some(extents) = extents {
1515 let (x, y) = self.inner_position_physical();
1516 Ok(extents.inner_pos_to_outer(x, y).into())
1517 } else {
1518 self.update_cached_frame_extents();
1519 self.outer_position()
1520 }
1521 }
1522
1523 fn inner_position_physical(&self) -> (i32, i32) {
1524 self.xconn
1527 .translate_coords_root(self.xwindow, self.root)
1528 .map(|coords| (coords.dst_x.into(), coords.dst_y.into()))
1529 .unwrap()
1530 }
1531
1532 #[inline]
1533 pub fn surface_position(&self) -> PhysicalPosition<i32> {
1534 let extents = self.shared_state_lock().frame_extents.clone();
1535 if let Some(extents) = extents {
1536 extents.surface_position().into()
1537 } else {
1538 self.update_cached_frame_extents();
1539 self.surface_position()
1540 }
1541 }
1542
1543 pub(crate) fn set_position_inner(
1544 &self,
1545 mut x: i32,
1546 mut y: i32,
1547 ) -> Result<VoidCookie<'_>, X11Error> {
1548 if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
1551 let extents = self.shared_state_lock().frame_extents.clone();
1552 if let Some(extents) = extents {
1553 x += cast_dimension_to_hint(extents.frame_extents.left);
1554 y += cast_dimension_to_hint(extents.frame_extents.top);
1555 } else {
1556 self.update_cached_frame_extents();
1557 return self.set_position_inner(x, y);
1558 }
1559 }
1560
1561 self.xconn
1562 .xcb_connection()
1563 .configure_window(self.xwindow, &xproto::ConfigureWindowAux::new().x(x).y(y))
1564 .map_err(Into::into)
1565 }
1566
1567 pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
1568 self.set_position_inner(x, y).expect_then_ignore_error("Failed to call `XMoveWindow`");
1569 }
1570
1571 #[inline]
1572 pub fn set_outer_position(&self, position: Position) {
1573 let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
1574 self.set_position_physical(x, y);
1575 }
1576
1577 pub(crate) fn surface_size_physical(&self) -> (u32, u32) {
1578 self.xconn
1581 .get_geometry(self.xwindow)
1582 .map(|geo| (geo.width.into(), geo.height.into()))
1583 .unwrap()
1584 }
1585
1586 #[inline]
1587 pub fn surface_size(&self) -> PhysicalSize<u32> {
1588 self.surface_size_physical().into()
1589 }
1590
1591 #[inline]
1592 pub fn outer_size(&self) -> PhysicalSize<u32> {
1593 let extents = self.shared_state_lock().frame_extents.clone();
1594 if let Some(extents) = extents {
1595 let (width, height) = self.surface_size_physical();
1596 extents.surface_size_to_outer(width, height).into()
1597 } else {
1598 self.update_cached_frame_extents();
1599 self.outer_size()
1600 }
1601 }
1602
1603 fn safe_area(&self) -> PhysicalInsets<u32> {
1604 PhysicalInsets::new(0, 0, 0, 0)
1605 }
1606
1607 pub(crate) fn request_surface_size_physical(&self, width: u32, height: u32) {
1608 self.xconn
1609 .xcb_connection()
1610 .configure_window(
1611 self.xwindow,
1612 &xproto::ConfigureWindowAux::new().width(width).height(height),
1613 )
1614 .expect_then_ignore_error("Failed to call `xcb_configure_window`");
1615 self.xconn.flush_requests().expect("Failed to call XResizeWindow");
1616 if self.shared_state_lock().cursor_hittest.unwrap_or(false) {
1618 let _ = self.set_cursor_hittest(true);
1619 }
1620 }
1621
1622 #[inline]
1623 pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
1624 let scale_factor = self.scale_factor();
1625 let size = size.to_physical::<u32>(scale_factor).into();
1626 if !self.shared_state_lock().is_resizable {
1627 self.update_normal_hints(|normal_hints| {
1628 normal_hints.min_size = Some(size);
1629 normal_hints.max_size = Some(size);
1630 })
1631 .expect("Failed to call `XSetWMNormalHints`");
1632 }
1633 self.request_surface_size_physical(size.0 as u32, size.1 as u32);
1634
1635 None
1636 }
1637
1638 fn update_normal_hints<F>(&self, callback: F) -> Result<(), X11Error>
1639 where
1640 F: FnOnce(&mut WmSizeHints),
1641 {
1642 let mut normal_hints = WmSizeHints::get(
1643 self.xconn.xcb_connection(),
1644 self.xwindow as xproto::Window,
1645 xproto::AtomEnum::WM_NORMAL_HINTS,
1646 )?
1647 .reply()?
1648 .unwrap_or_default();
1649 callback(&mut normal_hints);
1650 normal_hints
1651 .set(
1652 self.xconn.xcb_connection(),
1653 self.xwindow as xproto::Window,
1654 xproto::AtomEnum::WM_NORMAL_HINTS,
1655 )?
1656 .ignore_error();
1657 Ok(())
1658 }
1659
1660 pub(crate) fn set_min_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
1661 self.update_normal_hints(|normal_hints| {
1662 normal_hints.min_size =
1663 dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
1664 })
1665 .expect("Failed to call `XSetWMNormalHints`");
1666 }
1667
1668 #[inline]
1669 pub fn set_min_surface_size(&self, dimensions: Option<Size>) {
1670 self.shared_state_lock().min_surface_size = dimensions;
1671 let physical_dimensions =
1672 dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1673 self.set_min_surface_size_physical(physical_dimensions);
1674 }
1675
1676 pub(crate) fn set_max_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
1677 self.update_normal_hints(|normal_hints| {
1678 normal_hints.max_size =
1679 dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
1680 })
1681 .expect("Failed to call `XSetWMNormalHints`");
1682 }
1683
1684 #[inline]
1685 pub fn set_max_surface_size(&self, dimensions: Option<Size>) {
1686 self.shared_state_lock().max_surface_size = dimensions;
1687 let physical_dimensions =
1688 dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1689 self.set_max_surface_size_physical(physical_dimensions);
1690 }
1691
1692 #[inline]
1693 pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
1694 WmSizeHints::get(
1695 self.xconn.xcb_connection(),
1696 self.xwindow as xproto::Window,
1697 xproto::AtomEnum::WM_NORMAL_HINTS,
1698 )
1699 .ok()
1700 .and_then(|cookie| cookie.reply().ok())
1701 .flatten()
1702 .and_then(|hints| hints.size_increment)
1703 .map(|(width, height)| (width as u32, height as u32).into())
1704 }
1705
1706 #[inline]
1707 pub fn set_surface_resize_increments(&self, increments: Option<Size>) {
1708 self.shared_state_lock().surface_resize_increments = increments;
1709 let physical_increments =
1710 increments.map(|increments| cast_size_to_hint(increments, self.scale_factor()));
1711 self.update_normal_hints(|hints| hints.size_increment = physical_increments)
1712 .expect("Failed to call `XSetWMNormalHints`");
1713 }
1714
1715 pub(crate) fn adjust_for_dpi(
1716 &self,
1717 old_scale_factor: f64,
1718 new_scale_factor: f64,
1719 width: u32,
1720 height: u32,
1721 shared_state: &SharedState,
1722 ) -> (u32, u32) {
1723 let scale_factor = new_scale_factor / old_scale_factor;
1724 self.update_normal_hints(|normal_hints| {
1725 let dpi_adjuster = |size: Size| -> (i32, i32) { cast_size_to_hint(size, scale_factor) };
1726 let max_size = shared_state.max_surface_size.map(dpi_adjuster);
1727 let min_size = shared_state.min_surface_size.map(dpi_adjuster);
1728 let surface_resize_increments =
1729 shared_state.surface_resize_increments.map(dpi_adjuster);
1730 let base_size = shared_state.base_size.map(dpi_adjuster);
1731
1732 normal_hints.max_size = max_size;
1733 normal_hints.min_size = min_size;
1734 normal_hints.size_increment = surface_resize_increments;
1735 normal_hints.base_size = base_size;
1736 })
1737 .expect("Failed to update normal hints");
1738
1739 let new_width = (width as f64 * scale_factor).round() as u32;
1740 let new_height = (height as f64 * scale_factor).round() as u32;
1741
1742 (new_width, new_height)
1743 }
1744
1745 pub fn set_resizable(&self, resizable: bool) {
1746 if util::wm_name_is_one_of(&["Xfwm4"]) {
1747 warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
1752 return;
1753 }
1754
1755 let (min_size, max_size) = if resizable {
1756 let shared_state_lock = self.shared_state_lock();
1757 (shared_state_lock.min_surface_size, shared_state_lock.max_surface_size)
1758 } else {
1759 let window_size = Some(Size::from(self.surface_size()));
1760 (window_size, window_size)
1761 };
1762 self.shared_state_lock().is_resizable = resizable;
1763
1764 self.set_maximizable_inner(resizable)
1765 .expect_then_ignore_error("Failed to call `XSetWMNormalHints`");
1766
1767 let scale_factor = self.scale_factor();
1768 let min_surface_size = min_size.map(|size| cast_size_to_hint(size, scale_factor));
1769 let max_surface_size = max_size.map(|size| cast_size_to_hint(size, scale_factor));
1770 self.update_normal_hints(|normal_hints| {
1771 normal_hints.min_size = min_surface_size;
1772 normal_hints.max_size = max_surface_size;
1773 })
1774 .expect("Failed to call `XSetWMNormalHints`");
1775 }
1776
1777 #[inline]
1778 pub fn is_resizable(&self) -> bool {
1779 self.shared_state_lock().is_resizable
1780 }
1781
1782 #[inline]
1783 pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
1784
1785 #[inline]
1786 pub fn enabled_buttons(&self) -> WindowButtons {
1787 WindowButtons::all()
1788 }
1789
1790 #[allow(dead_code)]
1791 #[inline]
1792 pub fn xlib_display(&self) -> *mut c_void {
1793 self.xconn.display as _
1794 }
1795
1796 #[allow(dead_code)]
1797 #[inline]
1798 pub fn xlib_window(&self) -> c_ulong {
1799 self.xwindow as ffi::Window
1800 }
1801
1802 #[inline]
1803 pub fn set_cursor(&self, cursor: Cursor) {
1804 match cursor {
1805 Cursor::Icon(icon) => {
1806 let old_cursor = replace(
1807 &mut *self.selected_cursor.lock().unwrap(),
1808 SelectedCursor::Named(icon),
1809 );
1810
1811 #[allow(clippy::mutex_atomic)]
1812 if SelectedCursor::Named(icon) != old_cursor && *self.cursor_visible.lock().unwrap()
1813 {
1814 if let Err(err) = self.xconn.set_cursor_icon(self.xwindow, Some(icon)) {
1815 tracing::error!("failed to set cursor icon: {err}");
1816 }
1817 }
1818 },
1819 Cursor::Custom(cursor) => {
1820 let cursor = match cursor.cast_ref::<CustomCursor>() {
1821 Some(cursor) => cursor,
1822 None => {
1823 tracing::error!("unrecognized cursor passed to X11 backend");
1824 return;
1825 },
1826 };
1827
1828 #[allow(clippy::mutex_atomic)]
1829 if *self.cursor_visible.lock().unwrap() {
1830 if let Err(err) = self.xconn.set_custom_cursor(self.xwindow, cursor) {
1831 tracing::error!("failed to set window icon: {err}");
1832 }
1833 }
1834
1835 *self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor.clone());
1836 },
1837 }
1838 }
1839
1840 #[inline]
1841 pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
1842 if mode == CursorGrabMode::Locked {
1844 return Err(NotSupportedError::new("locked cursor is not implemented on X11").into());
1845 }
1846
1847 let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
1848 if mode == *grabbed_lock {
1849 return Ok(());
1850 }
1851
1852 self.xconn
1855 .xcb_connection()
1856 .ungrab_pointer(x11rb::CURRENT_TIME)
1857 .expect_then_ignore_error("Failed to call `xcb_ungrab_pointer`");
1858 *grabbed_lock = CursorGrabMode::None;
1859
1860 let result = match mode {
1861 CursorGrabMode::None => self
1862 .xconn
1863 .flush_requests()
1864 .map_err(|err| RequestError::Os(os_error!(X11Error::Xlib(err)))),
1865 CursorGrabMode::Confined => {
1866 let result = self
1867 .xconn
1868 .xcb_connection()
1869 .grab_pointer(
1870 true as _,
1871 self.xwindow,
1872 xproto::EventMask::BUTTON_PRESS
1873 | xproto::EventMask::BUTTON_RELEASE
1874 | xproto::EventMask::ENTER_WINDOW
1875 | xproto::EventMask::LEAVE_WINDOW
1876 | xproto::EventMask::POINTER_MOTION
1877 | xproto::EventMask::POINTER_MOTION_HINT
1878 | xproto::EventMask::BUTTON1_MOTION
1879 | xproto::EventMask::BUTTON2_MOTION
1880 | xproto::EventMask::BUTTON3_MOTION
1881 | xproto::EventMask::BUTTON4_MOTION
1882 | xproto::EventMask::BUTTON5_MOTION
1883 | xproto::EventMask::KEYMAP_STATE,
1884 xproto::GrabMode::ASYNC,
1885 xproto::GrabMode::ASYNC,
1886 self.xwindow,
1887 0u32,
1888 x11rb::CURRENT_TIME,
1889 )
1890 .expect("Failed to call `grab_pointer`")
1891 .reply()
1892 .expect("Failed to receive reply from `grab_pointer`");
1893
1894 match result.status {
1895 xproto::GrabStatus::SUCCESS => Ok(()),
1896 xproto::GrabStatus::ALREADY_GRABBED => {
1897 Err("Cursor could not be confined: already confined by another client")
1898 },
1899 xproto::GrabStatus::INVALID_TIME => {
1900 Err("Cursor could not be confined: invalid time")
1901 },
1902 xproto::GrabStatus::NOT_VIEWABLE => {
1903 Err("Cursor could not be confined: confine location not viewable")
1904 },
1905 xproto::GrabStatus::FROZEN => {
1906 Err("Cursor could not be confined: frozen by another client")
1907 },
1908 _ => unreachable!(),
1909 }
1910 .map_err(|err| RequestError::Os(os_error!(err)))
1911 },
1912 CursorGrabMode::Locked => return Ok(()),
1913 };
1914
1915 if result.is_ok() {
1916 *grabbed_lock = mode;
1917 }
1918
1919 result
1920 }
1921
1922 #[inline]
1923 pub fn set_cursor_visible(&self, visible: bool) {
1924 #[allow(clippy::mutex_atomic)]
1925 let mut visible_lock = self.cursor_visible.lock().unwrap();
1926 if visible == *visible_lock {
1927 return;
1928 }
1929 let cursor =
1930 if visible { Some((*self.selected_cursor.lock().unwrap()).clone()) } else { None };
1931 *visible_lock = visible;
1932 drop(visible_lock);
1933 let result = match cursor {
1934 Some(SelectedCursor::Custom(cursor)) => {
1935 self.xconn.set_custom_cursor(self.xwindow, &cursor)
1936 },
1937 Some(SelectedCursor::Named(cursor)) => {
1938 self.xconn.set_cursor_icon(self.xwindow, Some(cursor))
1939 },
1940 None => self.xconn.set_cursor_icon(self.xwindow, None),
1941 };
1942
1943 if let Err(err) = result {
1944 tracing::error!("failed to set cursor icon: {err}");
1945 }
1946 }
1947
1948 #[inline]
1949 pub fn scale_factor(&self) -> f64 {
1950 self.shared_state_lock().last_monitor.scale_factor
1951 }
1952
1953 pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), RequestError> {
1954 self.xconn
1955 .xcb_connection()
1956 .warp_pointer(x11rb::NONE, self.xwindow, 0, 0, 0, 0, x as _, y as _)
1957 .map_err(|err| os_error!(X11Error::from(err)))?;
1958 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
1959 Ok(())
1960 }
1961
1962 #[inline]
1963 pub fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
1964 let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
1965 self.set_cursor_position_physical(x, y)
1966 }
1967
1968 #[inline]
1969 pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
1970 let mut rectangles: Vec<Rectangle> = Vec::new();
1971 if hittest {
1972 let size = self.surface_size();
1973 rectangles.push(Rectangle {
1974 x: 0,
1975 y: 0,
1976 width: size.width as u16,
1977 height: size.height as u16,
1978 })
1979 }
1980 let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
1981 .map_err(|_e| RequestError::Ignored)?;
1982 self.xconn
1983 .xcb_connection()
1984 .xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
1985 .map_err(|_e| RequestError::Ignored)?;
1986 self.shared_state_lock().cursor_hittest = Some(hittest);
1987 Ok(())
1988 }
1989
1990 pub fn drag_window(&self) -> Result<(), RequestError> {
1992 self.drag_initiate(util::MOVERESIZE_MOVE)
1993 }
1994
1995 #[inline]
1996 pub fn show_window_menu(&self, _position: Position) {}
1997
1998 pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
2000 self.drag_initiate(match direction {
2001 ResizeDirection::East => util::MOVERESIZE_RIGHT,
2002 ResizeDirection::North => util::MOVERESIZE_TOP,
2003 ResizeDirection::NorthEast => util::MOVERESIZE_TOPRIGHT,
2004 ResizeDirection::NorthWest => util::MOVERESIZE_TOPLEFT,
2005 ResizeDirection::South => util::MOVERESIZE_BOTTOM,
2006 ResizeDirection::SouthEast => util::MOVERESIZE_BOTTOMRIGHT,
2007 ResizeDirection::SouthWest => util::MOVERESIZE_BOTTOMLEFT,
2008 ResizeDirection::West => util::MOVERESIZE_LEFT,
2009 })
2010 }
2011
2012 fn drag_initiate(&self, action: isize) -> Result<(), RequestError> {
2014 let pointer = self
2015 .xconn
2016 .query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
2017 .map_err(|err| os_error!(err))?;
2018
2019 let window_position = self.inner_position_physical();
2020
2021 let atoms = self.xconn.atoms();
2022 let message = atoms[_NET_WM_MOVERESIZE];
2023
2024 let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
2027 self.xconn
2028 .xcb_connection()
2029 .ungrab_pointer(x11rb::CURRENT_TIME)
2030 .map_err(|err| os_error!(X11Error::from(err)))?
2031 .ignore_error();
2032 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
2033 *grabbed_lock = CursorGrabMode::None;
2034
2035 self.xconn
2037 .send_client_msg(
2038 self.xwindow,
2039 self.root,
2040 message,
2041 Some(
2042 xproto::EventMask::SUBSTRUCTURE_REDIRECT
2043 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
2044 ),
2045 [
2046 (window_position.0 + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
2047 (window_position.1 + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
2048 action.try_into().unwrap(),
2049 1, 1,
2051 ],
2052 )
2053 .map_err(|err| os_error!(err))?;
2054
2055 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
2056
2057 Ok(())
2058 }
2059
2060 #[inline]
2061 pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
2062 let PhysicalPosition { x, y } = spot.to_physical::<i16>(self.scale_factor());
2063 let PhysicalSize { width, height } = size.to_physical::<u16>(self.scale_factor());
2064 let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Area(
2065 self.xwindow as ffi::Window,
2066 x,
2067 y,
2068 width,
2069 height,
2070 ));
2071 }
2072
2073 #[inline]
2074 pub fn set_ime_allowed(&self, allowed: bool) {
2075 let _ = self
2076 .ime_sender
2077 .lock()
2078 .unwrap()
2079 .send(ImeRequest::Allow(self.xwindow as ffi::Window, allowed));
2080 }
2081
2082 #[inline]
2083 pub fn request_ime_update(&self, request: CoreImeRequest) -> Result<(), ImeRequestError> {
2084 let mut shared_state = self.shared_state_lock();
2085 let (capabilities, state) = match request {
2086 CoreImeRequest::Enable(enable) => {
2087 let (capabilities, request_data) = enable.into_raw();
2088
2089 if shared_state.ime_capabilities.is_some() {
2090 return Err(ImeRequestError::AlreadyEnabled);
2091 }
2092
2093 shared_state.ime_capabilities = Some(capabilities);
2094 drop(shared_state);
2095 self.set_ime_allowed(true);
2096 (capabilities, request_data)
2097 },
2098 CoreImeRequest::Update(state) => {
2099 if let Some(capabilities) = shared_state.ime_capabilities {
2100 drop(shared_state);
2101 (capabilities, state)
2102 } else {
2103 return Err(ImeRequestError::NotEnabled);
2105 }
2106 },
2107 CoreImeRequest::Disable => {
2108 shared_state.ime_capabilities = None;
2109 drop(shared_state);
2110 self.set_ime_allowed(false);
2111 return Ok(());
2112 },
2113 };
2114
2115 if let Some((position, size)) = state.cursor_area {
2116 if capabilities.cursor_area() {
2117 self.set_ime_cursor_area(position, size);
2118 } else {
2119 warn!("discarding IME cursor area update without capability enabled.");
2120 }
2121 }
2122
2123 Ok(())
2127 }
2128
2129 #[inline]
2130 pub fn ime_capabilities(&self) -> Option<ImeCapabilities> {
2131 self.shared_state_lock().ime_capabilities
2132 }
2133
2134 #[inline]
2135 pub fn focus_window(&self) {
2136 let atoms = self.xconn.atoms();
2137 let state_atom = atoms[WM_STATE];
2138 let state_type_atom = atoms[CARD32];
2139 let is_minimized = if let Ok(state) =
2140 self.xconn.get_property::<u32>(self.xwindow, state_atom, state_type_atom)
2141 {
2142 state.contains(&ICONIC_STATE)
2143 } else {
2144 false
2145 };
2146 let is_visible = match self.shared_state_lock().visibility {
2147 Visibility::Yes => true,
2148 Visibility::YesWait | Visibility::No => false,
2149 };
2150
2151 if is_visible && !is_minimized {
2152 self.xconn
2153 .send_client_msg(
2154 self.xwindow,
2155 self.root,
2156 atoms[_NET_ACTIVE_WINDOW],
2157 Some(
2158 xproto::EventMask::SUBSTRUCTURE_REDIRECT
2159 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
2160 ),
2161 [1, x11rb::CURRENT_TIME, 0, 0, 0],
2162 )
2163 .expect_then_ignore_error("Failed to send client message");
2164 if let Err(e) = self.xconn.flush_requests() {
2165 tracing::error!(
2166 "`flush` returned an error when focusing the window. Error was: {}",
2167 e
2168 );
2169 }
2170 }
2171 }
2172
2173 #[inline]
2174 pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
2175 let mut wm_hints =
2176 WmHints::get(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
2177 .ok()
2178 .and_then(|cookie| cookie.reply().ok())
2179 .flatten()
2180 .unwrap_or_default();
2181
2182 wm_hints.urgent = request_type.is_some();
2183 wm_hints
2184 .set(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
2185 .expect_then_ignore_error("Failed to set WM hints");
2186 }
2187
2188 #[inline]
2189 pub(crate) fn generate_activation_token(&self) -> Result<String, X11Error> {
2190 let atoms = self.xconn.atoms();
2192 let title = {
2193 let title_bytes = self
2194 .xconn
2195 .get_property(self.xwindow, atoms[_NET_WM_NAME], atoms[UTF8_STRING])
2196 .expect("Failed to get title");
2197
2198 String::from_utf8(title_bytes).expect("Bad title")
2199 };
2200
2201 let token = self.xconn.request_activation_token(&title)?;
2203
2204 Ok(token)
2205 }
2206
2207 #[inline]
2208 pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
2209 let serial = AsyncRequestSerial::get();
2210 self.activation_sender.send((self.id(), serial));
2211 Ok(serial)
2212 }
2213
2214 #[inline]
2215 pub fn id(&self) -> WindowId {
2216 WindowId::from_raw(self.xwindow as _)
2217 }
2218
2219 pub(super) fn sync_counter_id(&self) -> Option<NonZeroU32> {
2220 self.sync_counter_id
2221 }
2222
2223 #[inline]
2224 pub fn request_redraw(&self) {
2225 self.redraw_sender.send(WindowId::from_raw(self.xwindow as _));
2226 }
2227
2228 #[inline]
2229 pub fn pre_present_notify(&self) {
2230 }
2232
2233 #[inline]
2234 pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
2235 let mut window_handle = rwh_06::XlibWindowHandle::new(self.xlib_window());
2236 window_handle.visual_id = self.visual as c_ulong;
2237 Ok(window_handle.into())
2238 }
2239
2240 #[inline]
2241 pub fn raw_display_handle_rwh_06(
2242 &self,
2243 ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
2244 Ok(rwh_06::XlibDisplayHandle::new(
2245 Some(
2246 std::ptr::NonNull::new(self.xlib_display())
2247 .expect("display pointer should never be null"),
2248 ),
2249 self.screen_id,
2250 )
2251 .into())
2252 }
2253
2254 #[inline]
2255 pub fn theme(&self) -> Option<Theme> {
2256 None
2257 }
2258
2259 pub fn set_content_protected(&self, _protected: bool) {}
2260
2261 #[inline]
2262 pub fn has_focus(&self) -> bool {
2263 self.shared_state_lock().has_focus
2264 }
2265
2266 pub fn title(&self) -> String {
2267 String::new()
2268 }
2269}
2270
2271fn cast_dimension_to_hint(val: u32) -> i32 {
2273 val.try_into().unwrap_or(i32::MAX)
2274}
2275
2276fn cast_physical_size_to_hint(size: PhysicalSize<u32>) -> (i32, i32) {
2278 let PhysicalSize { width, height } = size;
2279 (cast_dimension_to_hint(width), cast_dimension_to_hint(height))
2280}
2281
2282fn cast_size_to_hint(size: Size, scale_factor: f64) -> (i32, i32) {
2284 match size {
2285 Size::Physical(size) => cast_physical_size_to_hint(size),
2286 Size::Logical(size) => size.to_physical::<i32>(scale_factor).into(),
2287 }
2288}