xlib_display_server/xwrap/
getters.rs

1//! `XWrap` getters.
2use super::{Screen, WindowHandle, XlibError, MAX_PROPERTY_VALUE_LEN, MOUSEMASK};
3use crate::{XWrap, XlibWindowHandle};
4use leftwm_core::models::{BBox, DockArea, WindowState, WindowType, XyhwChange};
5use std::ffi::{CStr, CString};
6use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong};
7use std::slice;
8use x11_dl::xinerama::XineramaScreenInfo;
9use x11_dl::xlib::{self, XWindowAttributes};
10use x11_dl::xrandr::XRRCrtcInfo;
11
12impl XWrap {
13    // Public functions.
14
15    /// Returns the child windows of all roots.
16    /// # Errors
17    ///
18    /// Will error if root has no windows or there is an error
19    /// obtaining the root windows. See `get_windows_for_root`.
20    pub fn get_all_windows(&self) -> Result<Vec<xlib::Window>, String> {
21        let mut all = Vec::new();
22        for root in self.get_roots() {
23            match self.get_windows_for_root(root) {
24                Ok(some_windows) => {
25                    for w in some_windows {
26                        all.push(*w);
27                    }
28                }
29                Err(err) => return Err(err),
30            }
31        }
32        Ok(all)
33    }
34
35    /// Returns a `XColor` for a color.
36    // `XDefaultScreen`: https://tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen
37    // `XDefaultColormap`: https://tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap
38    // `XAllocNamedColor`: https://tronche.com/gui/x/xlib/color/XAllocNamedColor.html
39    #[must_use]
40    pub fn get_color(&self, color: String) -> c_ulong {
41        unsafe {
42            let screen = (self.xlib.XDefaultScreen)(self.display);
43            let cmap: xlib::Colormap = (self.xlib.XDefaultColormap)(self.display, screen);
44            let color_cstr = CString::new(color).unwrap_or_default().into_raw();
45            let mut color: xlib::XColor = std::mem::zeroed();
46            (self.xlib.XAllocNamedColor)(self.display, cmap, color_cstr, &mut color, &mut color);
47            color.pixel
48        }
49    }
50
51    /// Returns the current position of the cursor.
52    /// # Errors
53    ///
54    /// Will error if root window cannot be found.
55    // `XQueryPointer`: https://tronche.com/gui/x/xlib/window-information/XQueryPointer.html
56    pub fn get_cursor_point(&self) -> Result<(i32, i32), XlibError> {
57        let roots = self.get_roots();
58        for w in roots {
59            let mut root_return: xlib::Window = 0;
60            let mut child_return: xlib::Window = 0;
61            let mut root_x_return: c_int = 0;
62            let mut root_y_return: c_int = 0;
63            let mut win_x_return: c_int = 0;
64            let mut win_y_return: c_int = 0;
65            let mut mask_return: c_uint = 0;
66            let success = unsafe {
67                (self.xlib.XQueryPointer)(
68                    self.display,
69                    w,
70                    &mut root_return,
71                    &mut child_return,
72                    &mut root_x_return,
73                    &mut root_y_return,
74                    &mut win_x_return,
75                    &mut win_y_return,
76                    &mut mask_return,
77                )
78            };
79            if success > 0 {
80                return Ok((win_x_return, win_y_return));
81            }
82        }
83        Err(XlibError::RootWindowNotFound)
84    }
85
86    /// Returns the current window under the cursor.
87    /// # Errors
88    ///
89    /// Will error if root window cannot be found.
90    // `XQueryPointer`: https://tronche.com/gui/x/xlib/window-information/XQueryPointer.html
91    pub fn get_cursor_window(&self) -> Result<WindowHandle<XlibWindowHandle>, XlibError> {
92        let roots = self.get_roots();
93        for w in roots {
94            let mut root_return: xlib::Window = 0;
95            let mut child_return: xlib::Window = 0;
96            let mut root_x_return: c_int = 0;
97            let mut root_y_return: c_int = 0;
98            let mut win_x_return: c_int = 0;
99            let mut win_y_return: c_int = 0;
100            let mut mask_return: c_uint = 0;
101            let success = unsafe {
102                (self.xlib.XQueryPointer)(
103                    self.display,
104                    w,
105                    &mut root_return,
106                    &mut child_return,
107                    &mut root_x_return,
108                    &mut root_y_return,
109                    &mut win_x_return,
110                    &mut win_y_return,
111                    &mut mask_return,
112                )
113            };
114            if success > 0 {
115                return Ok(WindowHandle(XlibWindowHandle(child_return)));
116            }
117        }
118        Err(XlibError::RootWindowNotFound)
119    }
120
121    /// Returns the handle of the default root.
122    #[must_use]
123    pub const fn get_default_root_handle(&self) -> WindowHandle<XlibWindowHandle> {
124        WindowHandle(XlibWindowHandle(self.root))
125    }
126
127    /// Returns the default root.
128    #[must_use]
129    pub const fn get_default_root(&self) -> xlib::Window {
130        self.root
131    }
132
133    /// Returns the `WM_SIZE_HINTS`/`WM_NORMAL_HINTS` of a window as a `XyhwChange`.
134    #[must_use]
135    pub fn get_hint_sizing_as_xyhw(&self, window: xlib::Window) -> Option<XyhwChange> {
136        let hint = self.get_hint_sizing(window);
137        if let Some(size) = hint {
138            let mut xyhw = XyhwChange::default();
139
140            if (size.flags & xlib::PSize) != 0 || (size.flags & xlib::USSize) != 0 {
141                // These are obsolete but are still used sometimes.
142                xyhw.w = Some(size.width);
143                xyhw.h = Some(size.height);
144            } else if (size.flags & xlib::PBaseSize) != 0 {
145                xyhw.w = Some(size.base_width);
146                xyhw.h = Some(size.base_height);
147            }
148
149            if (size.flags & xlib::PResizeInc) != 0 {
150                xyhw.w = Some(size.width_inc);
151                xyhw.h = Some(size.height_inc);
152            }
153
154            if (size.flags & xlib::PMaxSize) != 0 {
155                xyhw.maxw = Some(size.max_width);
156                xyhw.maxh = Some(size.max_height);
157            }
158
159            if (size.flags & xlib::PMinSize) != 0 {
160                xyhw.minw = Some(size.min_width);
161                xyhw.minh = Some(size.min_height);
162            }
163            // Make sure that width and height are not smaller than the min values.
164            xyhw.w = std::cmp::max(xyhw.w, xyhw.minw);
165            xyhw.h = std::cmp::max(xyhw.h, xyhw.minh);
166            // Ignore the sizing if the sizing is set to 0.
167            xyhw.w = xyhw.w.filter(|&w| w != 0);
168            xyhw.h = xyhw.h.filter(|&h| h != 0);
169
170            if (size.flags & xlib::PPosition) != 0 || (size.flags & xlib::USPosition) != 0 {
171                // These are obsolete but are still used sometimes.
172                xyhw.x = Some(size.x);
173                xyhw.y = Some(size.y);
174            }
175            // TODO: support min/max aspect
176            // if size.flags & xlib::PAspect != 0 {
177            //     //c->mina = (float)size.min_aspect.y / size.min_aspect.x;
178            //     //c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
179            // }
180
181            return Some(xyhw);
182        }
183        None
184    }
185
186    /// Returns the next `Xevent` that matches the mask of the xserver.
187    // `XMaskEvent`: https://tronche.com/gui/x/xlib/event-handling/manipulating-event-queue/XMaskEvent.html
188    #[must_use]
189    pub fn get_mask_event(&self) -> xlib::XEvent {
190        unsafe {
191            let mut event: xlib::XEvent = std::mem::zeroed();
192            (self.xlib.XMaskEvent)(
193                self.display,
194                MOUSEMASK | xlib::SubstructureRedirectMask | xlib::ExposureMask,
195                &mut event,
196            );
197            event
198        }
199    }
200
201    /// Returns the next `Xevent` of the xserver.
202    // `XNextEvent`: https://tronche.com/gui/x/xlib/event-handling/manipulating-event-queue/XNextEvent.html
203    #[must_use]
204    pub fn get_next_event(&self) -> xlib::XEvent {
205        unsafe {
206            let mut event: xlib::XEvent = std::mem::zeroed();
207            (self.xlib.XNextEvent)(self.display, &mut event);
208            event
209        }
210    }
211
212    /// Returns all the screens of the display.
213    /// # Panics
214    ///
215    /// Panics if xorg cannot be contacted (xlib missing, not started, etc.)
216    /// Also panics if window attrs cannot be obtained.
217    #[must_use]
218    pub fn get_screens(&self) -> Vec<Screen<XlibWindowHandle>> {
219        use x11_dl::xinerama::Xlib;
220        use x11_dl::xrandr::Xrandr;
221        let xlib = Xlib::open().expect("Couldn't not connect to Xorg Server");
222
223        // Use randr for screen detection if possible, otherwise fall back to Xinerama.
224        // Only randr supports screen names.
225        if let Ok(xrandr) = Xrandr::open() {
226            unsafe {
227                let screen_resources = (xrandr.XRRGetScreenResources)(self.display, self.root);
228                let outputs = slice::from_raw_parts(
229                    (*screen_resources).outputs,
230                    (*screen_resources).noutput as usize,
231                );
232
233                return outputs
234                    .iter()
235                    .map(|output| {
236                        (xrandr.XRRGetOutputInfo)(self.display, screen_resources, *output)
237                    })
238                    .filter(|&output_info| (*output_info).crtc != 0)
239                    .map(|output_info| {
240                        let crtc_info = (xrandr.XRRGetCrtcInfo)(
241                            self.display,
242                            screen_resources,
243                            (*output_info).crtc,
244                        );
245                        let mut s: Screen<XlibWindowHandle> =
246                            XRRCrtcInfoIntoScreen(*crtc_info).into();
247                        s.root = self.get_default_root_handle();
248                        s.output = CStr::from_ptr((*output_info).name)
249                            .to_string_lossy()
250                            .into_owned();
251                        s
252                    })
253                    .collect();
254            }
255        }
256
257        let xinerama = unsafe { (xlib.XineramaIsActive)(self.display) } > 0;
258        if xinerama {
259            let root = self.get_default_root_handle();
260            let mut screen_count = 0;
261            let info_array_raw =
262                unsafe { (xlib.XineramaQueryScreens)(self.display, &mut screen_count) };
263            // Take ownership of the array.
264            let xinerama_infos: &[XineramaScreenInfo] =
265                unsafe { slice::from_raw_parts(info_array_raw, screen_count as usize) };
266            xinerama_infos
267                .iter()
268                .map(|i| {
269                    let mut s: Screen<XlibWindowHandle> = XineramaScreenInfoIntoScreen(i).into();
270                    s.root = root;
271                    s
272                })
273                .collect()
274        } else {
275            // NON-XINERAMA
276            let roots: Result<Vec<xlib::XWindowAttributes>, _> =
277                self.get_roots().map(|w| self.get_window_attrs(w)).collect();
278            let roots = roots.expect("Error: No screen were detected");
279            roots
280                .iter()
281                .map(|attrs| XWindowAttributesIntoScreen(attrs).into())
282                .collect()
283        }
284    }
285
286    /// Returns the dimensions of the screens.
287    #[must_use]
288    pub fn get_screens_area_dimensions(&self) -> (i32, i32) {
289        let mut height = 0;
290        let mut width = 0;
291        for s in self.get_screens() {
292            height = std::cmp::max(height, s.bbox.height + s.bbox.y);
293            width = std::cmp::max(width, s.bbox.width + s.bbox.x);
294        }
295        (height, width)
296    }
297
298    /// Returns the transient parent of a window.
299    // `XGetTransientForHint`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XGetTransientForHint.html
300    #[must_use]
301    pub fn get_transient_for(&self, window: xlib::Window) -> Option<xlib::Window> {
302        unsafe {
303            let mut transient: xlib::Window = std::mem::zeroed();
304            let status: c_int =
305                (self.xlib.XGetTransientForHint)(self.display, window, &mut transient);
306            if status > 0 {
307                Some(transient)
308            } else {
309                None
310            }
311        }
312    }
313
314    /// Returns the atom actions of a window.
315    // `XGetWindowProperty`: https://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html
316    #[must_use]
317    pub fn get_window_actions_atoms(&self, window: xlib::Window) -> Vec<xlib::Atom> {
318        let mut format_return: i32 = 0;
319        let mut nitems_return: c_ulong = 0;
320        let mut bytes_remaining: c_ulong = 0;
321        let mut type_return: xlib::Atom = 0;
322        let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
323        unsafe {
324            let status = (self.xlib.XGetWindowProperty)(
325                self.display,
326                window,
327                self.atoms.NetWMAction,
328                0,
329                MAX_PROPERTY_VALUE_LEN / 4,
330                xlib::False,
331                xlib::XA_ATOM,
332                &mut type_return,
333                &mut format_return,
334                &mut nitems_return,
335                &mut bytes_remaining,
336                &mut prop_return,
337            );
338            if status == i32::from(xlib::Success) && !prop_return.is_null() {
339                #[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
340                let ptr = prop_return as *const c_ulong;
341                let results: &[xlib::Atom] = slice::from_raw_parts(ptr, nitems_return as usize);
342                return results.to_vec();
343            }
344            vec![]
345        }
346    }
347
348    /// Returns the attributes of a window.
349    /// # Errors
350    ///
351    /// Will error if window status is 0 (no attributes).
352    // `XGetWindowAttributes`: https://tronche.com/gui/x/xlib/window-information/XGetWindowAttributes.html
353    pub fn get_window_attrs(
354        &self,
355        window: xlib::Window,
356    ) -> Result<xlib::XWindowAttributes, XlibError> {
357        let mut attrs: xlib::XWindowAttributes = unsafe { std::mem::zeroed() };
358        let status = unsafe { (self.xlib.XGetWindowAttributes)(self.display, window, &mut attrs) };
359        if status == 0 {
360            return Err(XlibError::FailedStatus);
361        }
362        Ok(attrs)
363    }
364
365    /// Returns a windows class `WM_CLASS`
366    // `XGetClassHint`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XGetClassHint.html
367    #[must_use]
368    pub fn get_window_class(&self, window: xlib::Window) -> Option<(String, String)> {
369        unsafe {
370            let mut class_return: xlib::XClassHint = std::mem::zeroed();
371            let status = (self.xlib.XGetClassHint)(self.display, window, &mut class_return);
372            if status == 0 {
373                return None;
374            }
375            let Ok(res_name) =
376                CString::from_raw(class_return.res_name.cast::<c_char>()).into_string()
377            else {
378                return None;
379            };
380            let Ok(res_class) =
381                CString::from_raw(class_return.res_class.cast::<c_char>()).into_string()
382            else {
383                return None;
384            };
385            Some((res_name, res_class))
386        }
387    }
388
389    /// Returns the geometry of a window as a `XyhwChange` struct.
390    /// # Errors
391    ///
392    /// Errors if Xlib returns a status of 0.
393    // `XGetGeometry`: https://tronche.com/gui/x/xlib/window-information/XGetGeometry.html
394    pub fn get_window_geometry(&self, window: xlib::Window) -> Result<XyhwChange, XlibError> {
395        let mut root_return: xlib::Window = 0;
396        let mut x_return: c_int = 0;
397        let mut y_return: c_int = 0;
398        let mut width_return: c_uint = 0;
399        let mut height_return: c_uint = 0;
400        let mut border_width_return: c_uint = 0;
401        let mut depth_return: c_uint = 0;
402        unsafe {
403            let status = (self.xlib.XGetGeometry)(
404                self.display,
405                window,
406                &mut root_return,
407                &mut x_return,
408                &mut y_return,
409                &mut width_return,
410                &mut height_return,
411                &mut border_width_return,
412                &mut depth_return,
413            );
414            if status == 0 {
415                return Err(XlibError::FailedStatus);
416            }
417        }
418        Ok(XyhwChange {
419            x: Some(x_return),
420            y: Some(y_return),
421            w: Some(width_return as i32),
422            h: Some(height_return as i32),
423            ..XyhwChange::default()
424        })
425    }
426
427    /// Returns a windows name.
428    #[must_use]
429    pub fn get_window_name(&self, window: xlib::Window) -> Option<String> {
430        if let Ok(text) = self.get_text_prop(window, self.atoms.NetWMName) {
431            return Some(text);
432        }
433        if let Ok(text) = self.get_text_prop(window, xlib::XA_WM_NAME) {
434            return Some(text);
435        }
436        None
437    }
438
439    /// Returns a `WM_NAME` (not `_NET`windows name).
440    #[must_use]
441    pub fn get_window_legacy_name(&self, window: xlib::Window) -> Option<String> {
442        if let Ok(text) = self.get_text_prop(window, xlib::XA_WM_NAME) {
443            return Some(text);
444        }
445        None
446    }
447
448    /// Returns a windows `_NET_WM_PID`.
449    #[must_use]
450    pub fn get_window_pid(&self, window: xlib::Window) -> Option<u32> {
451        let (prop_return, _) = self
452            .get_property(window, self.atoms.NetWMPid, xlib::XA_CARDINAL)
453            .ok()?;
454        unsafe {
455            #[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
456            let pid = *prop_return.cast::<u32>();
457            Some(pid)
458        }
459    }
460
461    /// Returns the states of a window.
462    #[must_use]
463    pub fn get_window_states(&self, window: xlib::Window) -> Vec<WindowState> {
464        let window_states_atoms = self.get_window_states_atoms(window);
465
466        // if window is maximized both horizontally and vertically
467        // `WindowState::Maximized` is used
468        // instead of `WindowState::MaximizedVert` and `WindowState::MaximizedHorz`
469        let maximized = window_states_atoms.contains(&self.atoms.NetWMStateMaximizedVert)
470            && window_states_atoms.contains(&self.atoms.NetWMStateMaximizedHorz);
471
472        let mut window_states: Vec<WindowState> = window_states_atoms
473            .iter()
474            .map(|a| match a {
475                x if x == &self.atoms.NetWMStateModal => WindowState::Modal,
476                x if x == &self.atoms.NetWMStateSticky => WindowState::Sticky,
477                x if x == &self.atoms.NetWMStateMaximizedVert && !maximized => {
478                    WindowState::MaximizedVert
479                }
480                x if x == &self.atoms.NetWMStateMaximizedHorz && !maximized => {
481                    WindowState::MaximizedHorz
482                }
483                x if x == &self.atoms.NetWMStateShaded => WindowState::Shaded,
484                x if x == &self.atoms.NetWMStateSkipTaskbar => WindowState::SkipTaskbar,
485                x if x == &self.atoms.NetWMStateSkipPager => WindowState::SkipPager,
486                x if x == &self.atoms.NetWMStateHidden => WindowState::Hidden,
487                x if x == &self.atoms.NetWMStateFullscreen => WindowState::Fullscreen,
488                x if x == &self.atoms.NetWMStateAbove => WindowState::Above,
489                x if x == &self.atoms.NetWMStateBelow => WindowState::Below,
490                _ => WindowState::Modal,
491            })
492            .collect();
493
494        if maximized {
495            window_states.push(WindowState::Maximized);
496        }
497
498        window_states
499    }
500
501    /// Returns the atom states of a window.
502    // `XGetWindowProperty`: https://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html
503    #[must_use]
504    pub fn get_window_states_atoms(&self, window: xlib::Window) -> Vec<xlib::Atom> {
505        let mut format_return: i32 = 0;
506        let mut nitems_return: c_ulong = 0;
507        let mut bytes_remaining: c_ulong = 0;
508        let mut type_return: xlib::Atom = 0;
509        let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
510        unsafe {
511            let status = (self.xlib.XGetWindowProperty)(
512                self.display,
513                window,
514                self.atoms.NetWMState,
515                0,
516                MAX_PROPERTY_VALUE_LEN / 4,
517                xlib::False,
518                xlib::XA_ATOM,
519                &mut type_return,
520                &mut format_return,
521                &mut nitems_return,
522                &mut bytes_remaining,
523                &mut prop_return,
524            );
525            if status == i32::from(xlib::Success) && !prop_return.is_null() {
526                #[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
527                let ptr = prop_return as *const c_ulong;
528                let results: &[xlib::Atom] = slice::from_raw_parts(ptr, nitems_return as usize);
529                return results.to_vec();
530            }
531            vec![]
532        }
533    }
534
535    /// Returns structure of a window as a `DockArea`.
536    #[must_use]
537    pub fn get_window_strut_array(&self, window: xlib::Window) -> Option<DockArea> {
538        // More modern structure.
539        if let Some(d) = self.get_window_strut_array_strut_partial(window) {
540            tracing::trace!("STRUT:[{:?}] {:?}", window, d);
541            return Some(d);
542        }
543        // Older structure.
544        if let Some(d) = self.get_window_strut_array_strut(window) {
545            tracing::trace!("STRUT:[{:?}] {:?}", window, d);
546            return Some(d);
547        }
548        None
549    }
550
551    /// Returns the type of a window.
552    #[must_use]
553    pub fn get_window_type(&self, window: xlib::Window) -> WindowType {
554        let mut atom = None;
555        if let Ok((prop_return, _)) =
556            self.get_property(window, self.atoms.NetWMWindowType, xlib::XA_ATOM)
557        {
558            #[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
559            let atom_ = unsafe { *prop_return.cast::<xlib::Atom>() };
560            atom = Some(atom_);
561        }
562        match atom {
563            x if x == Some(self.atoms.NetWMWindowTypeDesktop) => WindowType::Desktop,
564            x if x == Some(self.atoms.NetWMWindowTypeDock) => WindowType::Dock,
565            x if x == Some(self.atoms.NetWMWindowTypeToolbar) => WindowType::Toolbar,
566            x if x == Some(self.atoms.NetWMWindowTypeMenu) => WindowType::Menu,
567            x if x == Some(self.atoms.NetWMWindowTypeUtility) => WindowType::Utility,
568            x if x == Some(self.atoms.NetWMWindowTypeSplash) => WindowType::Splash,
569            x if x == Some(self.atoms.NetWMWindowTypeDialog) => WindowType::Dialog,
570            _ => WindowType::Normal,
571        }
572    }
573
574    /// Returns the `WM_HINTS` of a window.
575    // `XGetWMHints`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XGetWMHints.html
576    #[must_use]
577    pub fn get_wmhints(&self, window: xlib::Window) -> Option<xlib::XWMHints> {
578        unsafe {
579            let hints_ptr: *const xlib::XWMHints = (self.xlib.XGetWMHints)(self.display, window);
580            if hints_ptr.is_null() {
581                return None;
582            }
583            let hints: xlib::XWMHints = *hints_ptr;
584            Some(hints)
585        }
586    }
587
588    /// Returns the `WM_STATE` of a window.
589    #[must_use]
590    pub fn get_wm_state(&self, window: xlib::Window) -> Option<c_long> {
591        let (prop_return, nitems_return) = self
592            .get_property(window, self.atoms.WMState, self.atoms.WMState)
593            .ok()?;
594        if nitems_return == 0 {
595            return None;
596        }
597        #[allow(clippy::cast_ptr_alignment)]
598        Some(unsafe { *prop_return.cast::<c_long>() })
599    }
600
601    /// Returns the name of a `XAtom`.
602    /// # Errors
603    ///
604    /// Errors if `XAtom` is not valid.
605    // `XGetAtomName`: https://tronche.com/gui/x/xlib/window-information/XGetAtomName.html
606    pub fn get_xatom_name(&self, atom: xlib::Atom) -> Result<String, XlibError> {
607        unsafe {
608            let cstring = (self.xlib.XGetAtomName)(self.display, atom);
609            if let Ok(s) = CString::from_raw(cstring).into_string() {
610                return Ok(s);
611            }
612        };
613        Err(XlibError::InvalidXAtom)
614    }
615
616    // Internal functions.
617
618    /// Returns the `WM_SIZE_HINTS`/`WM_NORMAL_HINTS` of a window.
619    // `XGetWMNormalHints`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XGetWMNormalHints.html
620    #[must_use]
621    fn get_hint_sizing(&self, window: xlib::Window) -> Option<xlib::XSizeHints> {
622        let mut xsize: xlib::XSizeHints = unsafe { std::mem::zeroed() };
623        let mut msize: c_long = xlib::PSize;
624        let status =
625            unsafe { (self.xlib.XGetWMNormalHints)(self.display, window, &mut xsize, &mut msize) };
626        match status {
627            0 => None,
628            _ => Some(xsize),
629        }
630    }
631
632    /// Returns a cardinal property of a window.
633    /// # Errors
634    ///
635    /// Errors if window status = 0.
636    // `XGetWindowProperty`: https://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html
637    fn get_property(
638        &self,
639        window: xlib::Window,
640        property: xlib::Atom,
641        r#type: xlib::Atom,
642    ) -> Result<(*const c_uchar, c_ulong), XlibError> {
643        let mut format_return: i32 = 0;
644        let mut nitems_return: c_ulong = 0;
645        let mut type_return: xlib::Atom = 0;
646        let mut bytes_after_return: xlib::Atom = 0;
647        let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
648        unsafe {
649            let status = (self.xlib.XGetWindowProperty)(
650                self.display,
651                window,
652                property,
653                0,
654                MAX_PROPERTY_VALUE_LEN / 4,
655                xlib::False,
656                r#type,
657                &mut type_return,
658                &mut format_return,
659                &mut nitems_return,
660                &mut bytes_after_return,
661                &mut prop_return,
662            );
663            if status == i32::from(xlib::Success) && !prop_return.is_null() {
664                return Ok((prop_return, nitems_return));
665            }
666        };
667        Err(XlibError::FailedStatus)
668    }
669
670    /// Returns all the roots of the display.
671    // `XRootWindowOfScreen`: https://tronche.com/gui/x/xlib/display/screen-information.html#RootWindowOfScreen
672    fn get_roots(&self) -> impl Iterator<Item = xlib::Window> + '_ {
673        self.get_xscreens()
674            .map(|mut s| unsafe { (self.xlib.XRootWindowOfScreen)(&mut s) })
675    }
676
677    /// Returns a text property for a window.
678    /// # Errors
679    ///
680    /// Errors if window status = 0.
681    // `XGetTextProperty`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XGetTextProperty.html
682    // `XTextPropertyToStringList`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XTextPropertyToStringList.html
683    // `XmbTextPropertyToTextList`: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XmbTextPropertyToTextList.html
684    fn get_text_prop(&self, window: xlib::Window, atom: xlib::Atom) -> Result<String, XlibError> {
685        unsafe {
686            let mut text_prop: xlib::XTextProperty = std::mem::zeroed();
687            let status: c_int =
688                (self.xlib.XGetTextProperty)(self.display, window, &mut text_prop, atom);
689            if status == 0 {
690                return Err(XlibError::FailedStatus);
691            }
692            if let Ok(s) = CString::from_raw(text_prop.value.cast::<c_char>()).into_string() {
693                return Ok(s);
694            }
695        };
696        Err(XlibError::FailedStatus)
697    }
698
699    /// Returns the child windows of a root.
700    /// # Errors
701    ///
702    /// Will error if unknown window status is returned.
703    // `XQueryTree`: https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
704    fn get_windows_for_root<'w>(&self, root: xlib::Window) -> Result<&'w [xlib::Window], String> {
705        unsafe {
706            let mut root_return: xlib::Window = std::mem::zeroed();
707            let mut parent_return: xlib::Window = std::mem::zeroed();
708            let mut array: *mut xlib::Window = std::mem::zeroed();
709            let mut length: c_uint = std::mem::zeroed();
710            let status: xlib::Status = (self.xlib.XQueryTree)(
711                self.display,
712                root,
713                &mut root_return,
714                &mut parent_return,
715                &mut array,
716                &mut length,
717            );
718            let windows: &[xlib::Window] = slice::from_raw_parts(array, length as usize);
719            match status {
720                0 /* XcmsFailure */ => { Err("Could not load list of windows".to_string() ) }
721                1 /* XcmsSuccess */ | 2 /* XcmsSuccessWithCompression */ => { Ok(windows) }
722                _ => { Err("Unknown return status".to_string() ) }
723            }
724        }
725    }
726
727    /// Returns the `_NET_WM_STRUT` as a `DockArea`.
728    fn get_window_strut_array_strut(&self, window: xlib::Window) -> Option<DockArea> {
729        let (prop_return, nitems_return) = self
730            .get_property(window, self.atoms.NetWMStrut, xlib::XA_CARDINAL)
731            .ok()?;
732        unsafe {
733            #[allow(clippy::cast_ptr_alignment)]
734            let array_ptr = prop_return.cast::<c_long>();
735            let slice = slice::from_raw_parts(array_ptr, nitems_return as usize);
736            if slice.len() == 12 {
737                return Some(SliceIntoDockArea(slice).into());
738            }
739            None
740        }
741    }
742
743    /// Returns the `_NET_WM_STRUT_PARTIAL` as a `DockArea`.
744    fn get_window_strut_array_strut_partial(&self, window: xlib::Window) -> Option<DockArea> {
745        let (prop_return, nitems_return) = self
746            .get_property(window, self.atoms.NetWMStrutPartial, xlib::XA_CARDINAL)
747            .ok()?;
748        unsafe {
749            #[allow(clippy::cast_ptr_alignment)]
750            let array_ptr = prop_return.cast::<c_long>();
751            let slice = slice::from_raw_parts(array_ptr, nitems_return as usize);
752            if slice.len() == 12 {
753                return Some(SliceIntoDockArea(slice).into());
754            }
755            None
756        }
757    }
758
759    /// Returns all the xscreens of the display.
760    // `XScreenCount`: https://tronche.com/gui/x/xlib/display/display-macros.html#ScreenCount
761    // `XScreenOfDisplay`: https://tronche.com/gui/x/xlib/display/display-macros.html#ScreensOfDisplay
762    fn get_xscreens(&self) -> impl Iterator<Item = xlib::Screen> + '_ {
763        let screen_count = unsafe { (self.xlib.XScreenCount)(self.display) };
764
765        let screen_ids = 0..screen_count;
766
767        screen_ids
768            .map(|screen_id| unsafe { *(self.xlib.XScreenOfDisplay)(self.display, screen_id) })
769    }
770}
771
772struct XRRCrtcInfoIntoScreen(XRRCrtcInfo);
773
774impl From<XRRCrtcInfoIntoScreen> for Screen<XlibWindowHandle> {
775    fn from(val: XRRCrtcInfoIntoScreen) -> Self {
776        Screen {
777            bbox: BBox {
778                x: val.0.x,
779                y: val.0.y,
780                width: val.0.width as i32,
781                height: val.0.height as i32,
782            },
783            ..Default::default()
784        }
785    }
786}
787
788struct XineramaScreenInfoIntoScreen<'a>(&'a XineramaScreenInfo);
789
790impl From<XineramaScreenInfoIntoScreen<'_>> for Screen<XlibWindowHandle> {
791    fn from(val: XineramaScreenInfoIntoScreen<'_>) -> Self {
792        Screen {
793            bbox: BBox {
794                height: val.0.height.into(),
795                width: val.0.width.into(),
796                x: val.0.x_org.into(),
797                y: val.0.y_org.into(),
798            },
799            ..Default::default()
800        }
801    }
802}
803
804struct XWindowAttributesIntoScreen<'a>(&'a XWindowAttributes);
805
806impl From<XWindowAttributesIntoScreen<'_>> for Screen<XlibWindowHandle> {
807    fn from(val: XWindowAttributesIntoScreen<'_>) -> Self {
808        Screen {
809            root: WindowHandle(XlibWindowHandle(val.0.root)),
810            bbox: BBox {
811                height: val.0.height,
812                width: val.0.width,
813                x: val.0.x,
814                y: val.0.y,
815            },
816            ..Default::default()
817        }
818    }
819}
820
821#[cfg(target_pointer_width = "64")]
822struct SliceIntoDockArea<'a>(&'a [i64]);
823
824#[cfg(target_pointer_width = "32")]
825struct SliceIntoDockArea<'a>(&'a [i32]);
826
827impl From<SliceIntoDockArea<'_>> for DockArea {
828    fn from(val: SliceIntoDockArea<'_>) -> Self {
829        DockArea {
830            left: val.0[0] as i32,
831            right: val.0[1] as i32,
832            top: val.0[2] as i32,
833            bottom: val.0[3] as i32,
834            left_start_y: val.0[4] as i32,
835            left_end_y: val.0[5] as i32,
836            right_start_y: val.0[6] as i32,
837            right_end_y: val.0[7] as i32,
838            top_start_x: val.0[8] as i32,
839            top_end_x: val.0[9] as i32,
840            bottom_start_x: val.0[10] as i32,
841            bottom_end_x: val.0[11] as i32,
842        }
843    }
844}