xlib-display-server 0.1.0

A display server library for LeftWM
Documentation
//! Xlib calls related to a mouse.
use super::{XlibError, MOUSEMASK};
use crate::xwrap::BUTTONMASK;
use crate::XWrap;
use std::os::raw::{c_int, c_uint, c_ulong};
use x11_dl::xlib;

impl XWrap {
    /// Grabs the mouse clicks of a window.
    pub fn grab_mouse_clicks(&self, handle: xlib::Window, is_focused: bool) {
        self.ungrab_buttons(handle);
        if !is_focused {
            self.grab_buttons(handle, xlib::Button1, xlib::AnyModifier);
            self.grab_buttons(handle, xlib::Button3, xlib::AnyModifier);
        }
        self.grab_buttons(handle, xlib::Button1, self.mouse_key_mask);
        self.grab_buttons(handle, xlib::Button1, self.mouse_key_mask | xlib::ShiftMask);
        self.grab_buttons(handle, xlib::Button3, self.mouse_key_mask);
        self.grab_buttons(handle, xlib::Button3, self.mouse_key_mask | xlib::ShiftMask);
    }

    /// Grabs the button with the modifier for a window.
    // `XGrabButton`: https://tronche.com/gui/x/xlib/input/XGrabButton.html
    pub fn grab_buttons(&self, window: xlib::Window, button: u32, modifiers: u32) {
        // Grab the buttons with and without numlock (Mod2).
        let mods: Vec<u32> = vec![
            modifiers,
            modifiers | xlib::Mod2Mask,
            modifiers | xlib::LockMask,
        ];
        for m in mods {
            unsafe {
                (self.xlib.XGrabButton)(
                    self.display,
                    button,
                    m,
                    window,
                    0,
                    BUTTONMASK as u32,
                    xlib::GrabModeAsync,
                    xlib::GrabModeAsync,
                    0,
                    0,
                );
            }
        }
    }

    /// Cleans all currently grabbed buttons of a window.
    // `XUngrabButton`: https://tronche.com/gui/x/xlib/input/XUngrabButton.html
    pub fn ungrab_buttons(&self, handle: xlib::Window) {
        unsafe {
            (self.xlib.XUngrabButton)(
                self.display,
                xlib::AnyButton as u32,
                xlib::AnyModifier,
                handle,
            );
        }
    }

    /// Grabs the cursor and sets its visual.
    // `XGrabPointer`: https://tronche.com/gui/x/xlib/input/XGrabPointer.html
    pub fn grab_pointer(&self, cursor: c_ulong) {
        unsafe {
            //grab the mouse
            (self.xlib.XGrabPointer)(
                self.display,
                self.root,
                0,
                MOUSEMASK as u32,
                xlib::GrabModeAsync,
                xlib::GrabModeAsync,
                0,
                cursor,
                xlib::CurrentTime,
            );
        }
    }

    /// Ungrab the cursor.
    // `XUngrabPointer`: https://tronche.com/gui/x/xlib/input/XUngrabPointer.html
    pub fn ungrab_pointer(&self) {
        unsafe {
            //release the mouse grab
            (self.xlib.XUngrabPointer)(self.display, xlib::CurrentTime);
        }
    }

    /// Move the cursor to a window.
    /// # Errors
    ///
    /// Will error if unable to obtain window attributes. See `get_window_attrs`.
    pub fn move_cursor_to_window(&self, window: xlib::Window) -> Result<(), XlibError> {
        let attrs = self.get_window_attrs(window)?;
        let point = (attrs.x + (attrs.width / 2), attrs.y + (attrs.height / 2));
        self.move_cursor_to_point(point)
    }

    /// Move the cursor to a point.
    /// # Errors
    ///
    /// Error indicates `XlibError`.
    // `XWarpPointer`: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
    // TODO: Verify that Error is unreachable or specify conditions that may result
    // in an error.
    pub fn move_cursor_to_point(&self, point: (i32, i32)) -> Result<(), XlibError> {
        if point.0 >= 0 && point.1 >= 0 {
            let none: c_int = 0;
            unsafe {
                (self.xlib.XWarpPointer)(
                    self.display,
                    none as c_ulong,
                    self.root,
                    none,
                    none,
                    none as u32,
                    none as u32,
                    point.0,
                    point.1,
                );
            }
        }
        Ok(())
    }

    /// Replay a click on a window.
    // `XQueryPointer`: https://tronche.com/gui/x/xlib/window-information/XQueryPointer.html
    pub fn replay_click(&self, focused_window: xlib::Window, button: c_uint) {
        unsafe {
            let mut event: xlib::XButtonEvent = std::mem::zeroed();
            event.button = button;
            event.same_screen = xlib::True;
            event.subwindow = self.get_default_root();

            while event.subwindow != 0 {
                event.window = event.subwindow;
                (self.xlib.XQueryPointer)(
                    self.display,
                    event.window,
                    &mut event.root,
                    &mut event.subwindow,
                    &mut event.x_root,
                    &mut event.y_root,
                    &mut event.x,
                    &mut event.y,
                    &mut event.state,
                );
            }

            // Make sure we are clicking on the focused window. This also prevents clicks when
            // focus is changed by a keybind.
            if event.window == focused_window {
                event.type_ = xlib::ButtonPress;
                self.send_xevent(event.window, 0, xlib::ButtonPressMask, &mut event.into());

                event.type_ = xlib::ButtonRelease;
                self.send_xevent(event.window, 0, xlib::ButtonReleaseMask, &mut event.into());
            }
        }
    }

    /// Release the pointer if it is frozen.
    // `XAllowEvents`: https://linux.die.net/man/3/xallowevents
    pub fn allow_pointer_events(&self) {
        unsafe { (self.xlib.XAllowEvents)(self.display, xlib::SyncPointer, xlib::CurrentTime) };
    }
}