xlib_display_server/xwrap/
mouse.rs

1//! Xlib calls related to a mouse.
2use super::{XlibError, MOUSEMASK};
3use crate::xwrap::BUTTONMASK;
4use crate::XWrap;
5use std::os::raw::{c_int, c_uint, c_ulong};
6use x11_dl::xlib;
7
8impl XWrap {
9    /// Grabs the mouse clicks of a window.
10    pub fn grab_mouse_clicks(&self, handle: xlib::Window, is_focused: bool) {
11        self.ungrab_buttons(handle);
12        if !is_focused {
13            self.grab_buttons(handle, xlib::Button1, xlib::AnyModifier);
14            self.grab_buttons(handle, xlib::Button3, xlib::AnyModifier);
15        }
16        self.grab_buttons(handle, xlib::Button1, u32::from(self.mouse_key_mask.bits()));
17        self.grab_buttons(
18            handle,
19            xlib::Button1,
20            u32::from(self.mouse_key_mask.bits()) | xlib::ShiftMask,
21        );
22        self.grab_buttons(handle, xlib::Button3, u32::from(self.mouse_key_mask.bits()));
23        self.grab_buttons(
24            handle,
25            xlib::Button3,
26            u32::from(self.mouse_key_mask.bits()) | xlib::ShiftMask,
27        );
28    }
29
30    /// Grabs the button with the modifier for a window.
31    // `XGrabButton`: https://tronche.com/gui/x/xlib/input/XGrabButton.html
32    pub fn grab_buttons(&self, window: xlib::Window, button: u32, modifiers: u32) {
33        // Grab the buttons with and without numlock (Mod2).
34        let mods: Vec<u32> = vec![
35            modifiers,
36            modifiers | xlib::Mod2Mask,
37            modifiers | xlib::LockMask,
38        ];
39        for m in mods {
40            unsafe {
41                (self.xlib.XGrabButton)(
42                    self.display,
43                    button,
44                    m,
45                    window,
46                    0,
47                    BUTTONMASK as u32,
48                    xlib::GrabModeAsync,
49                    xlib::GrabModeAsync,
50                    0,
51                    0,
52                );
53            }
54        }
55    }
56
57    /// Cleans all currently grabbed buttons of a window.
58    // `XUngrabButton`: https://tronche.com/gui/x/xlib/input/XUngrabButton.html
59    pub fn ungrab_buttons(&self, handle: xlib::Window) {
60        unsafe {
61            (self.xlib.XUngrabButton)(
62                self.display,
63                xlib::AnyButton as u32,
64                xlib::AnyModifier,
65                handle,
66            );
67        }
68    }
69
70    /// Grabs the cursor and sets its visual.
71    // `XGrabPointer`: https://tronche.com/gui/x/xlib/input/XGrabPointer.html
72    pub fn grab_pointer(&self, cursor: c_ulong) {
73        unsafe {
74            // grab the mouse
75            (self.xlib.XGrabPointer)(
76                self.display,
77                self.root,
78                0,
79                MOUSEMASK as u32,
80                xlib::GrabModeAsync,
81                xlib::GrabModeAsync,
82                0,
83                cursor,
84                xlib::CurrentTime,
85            );
86        }
87    }
88
89    /// Ungrab the cursor.
90    // `XUngrabPointer`: https://tronche.com/gui/x/xlib/input/XUngrabPointer.html
91    pub fn ungrab_pointer(&self) {
92        unsafe {
93            // release the mouse grab
94            (self.xlib.XUngrabPointer)(self.display, xlib::CurrentTime);
95        }
96    }
97
98    /// Move the cursor to a window.
99    /// # Errors
100    ///
101    /// Will error if unable to obtain window attributes. See `get_window_attrs`.
102    pub fn move_cursor_to_window(&self, window: xlib::Window) -> Result<(), XlibError> {
103        let attrs = self.get_window_attrs(window)?;
104        let point = (attrs.x + (attrs.width / 2), attrs.y + (attrs.height / 2));
105        self.move_cursor_to_point(point)
106    }
107
108    /// Move the cursor to a point.
109    /// # Errors
110    ///
111    /// Error indicates `XlibError`.
112    // `XWarpPointer`: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
113    // TODO: Verify that Error is unreachable or specify conditions that may result
114    // in an error.
115    pub fn move_cursor_to_point(&self, point: (i32, i32)) -> Result<(), XlibError> {
116        if point.0 >= 0 && point.1 >= 0 {
117            let none: c_int = 0;
118            unsafe {
119                (self.xlib.XWarpPointer)(
120                    self.display,
121                    none as c_ulong,
122                    self.root,
123                    none,
124                    none,
125                    none as u32,
126                    none as u32,
127                    point.0,
128                    point.1,
129                );
130            }
131        }
132        Ok(())
133    }
134
135    /// Replay a click on a window.
136    // `XQueryPointer`: https://tronche.com/gui/x/xlib/window-information/XQueryPointer.html
137    pub fn replay_click(&self, focused_window: xlib::Window, button: c_uint) {
138        unsafe {
139            let mut event: xlib::XButtonEvent = std::mem::zeroed();
140            event.button = button;
141            event.same_screen = xlib::True;
142            event.subwindow = self.get_default_root();
143
144            while event.subwindow != 0 {
145                event.window = event.subwindow;
146                (self.xlib.XQueryPointer)(
147                    self.display,
148                    event.window,
149                    &mut event.root,
150                    &mut event.subwindow,
151                    &mut event.x_root,
152                    &mut event.y_root,
153                    &mut event.x,
154                    &mut event.y,
155                    &mut event.state,
156                );
157            }
158
159            // Make sure we are clicking on the focused window. This also prevents clicks when
160            // focus is changed by a keybind.
161            if event.window == focused_window {
162                event.type_ = xlib::ButtonPress;
163                self.send_xevent(event.window, 0, xlib::ButtonPressMask, &mut event.into());
164
165                event.type_ = xlib::ButtonRelease;
166                self.send_xevent(event.window, 0, xlib::ButtonReleaseMask, &mut event.into());
167            }
168        }
169    }
170
171    /// Release the pointer if it is frozen.
172    // `XAllowEvents`: https://linux.die.net/man/3/xallowevents
173    pub fn allow_pointer_events(&self) {
174        unsafe { (self.xlib.XAllowEvents)(self.display, xlib::SyncPointer, xlib::CurrentTime) };
175    }
176}