Skip to main content

xlib_display_server/xwrap/
getters.rs

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