adabraka-gpui 0.4.0

Adabraka's GPU-accelerated UI framework (fork of GPUI from Zed - github.com/zed-industries/zed)
Documentation
#![allow(dead_code)]

use anyhow::Result;
use collections::HashMap;

use crate::Keystroke;

pub struct LinuxGlobalHotkey {
    registered: HashMap<u32, Keystroke>,
}

impl LinuxGlobalHotkey {
    pub fn new() -> Self {
        Self {
            registered: HashMap::default(),
        }
    }

    pub fn register(&mut self, id: u32, keystroke: &Keystroke) -> Result<()> {
        self.registered.insert(id, keystroke.clone());
        Ok(())
    }

    pub fn unregister(&mut self, id: u32) {
        self.registered.remove(&id);
    }
}

#[cfg(feature = "x11")]
pub mod x11 {
    use super::*;
    use std::rc::Rc;
    use x11rb::protocol::xproto::{self, ConnectionExt as _, GrabMode, ModMask};
    use x11rb::xcb_ffi::XCBConnection;

    fn keystroke_to_x11_modmask(keystroke: &Keystroke) -> u16 {
        let mut mask = 0u16;
        if keystroke.modifiers.control {
            mask |= u16::from(ModMask::CONTROL);
        }
        if keystroke.modifiers.alt {
            mask |= u16::from(ModMask::M1);
        }
        if keystroke.modifiers.shift {
            mask |= u16::from(ModMask::SHIFT);
        }
        if keystroke.modifiers.platform {
            mask |= u16::from(ModMask::M4);
        }
        mask
    }

    fn keystroke_to_x11_keycode(keystroke: &Keystroke, xcb: &XCBConnection) -> Option<u8> {
        let setup = xcb.setup();
        let min_keycode = setup.min_keycode;
        let max_keycode = setup.max_keycode;

        let keysym = key_name_to_keysym(&keystroke.key)?;

        let reply = xcb
            .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)
            .ok()?
            .reply()
            .ok()?;

        let keysyms_per_keycode = reply.keysyms_per_keycode as usize;
        if keysyms_per_keycode == 0 {
            return None;
        }

        for i in 0..((max_keycode - min_keycode + 1) as usize) {
            let base = i * keysyms_per_keycode;
            for j in 0..keysyms_per_keycode {
                if reply.keysyms[base + j] == keysym {
                    return Some(min_keycode + i as u8);
                }
            }
        }

        None
    }

    fn key_name_to_keysym(key: &str) -> Option<u32> {
        match key.to_lowercase().as_str() {
            "a" => Some(0x61),
            "b" => Some(0x62),
            "c" => Some(0x63),
            "d" => Some(0x64),
            "e" => Some(0x65),
            "f" => Some(0x66),
            "g" => Some(0x67),
            "h" => Some(0x68),
            "i" => Some(0x69),
            "j" => Some(0x6a),
            "k" => Some(0x6b),
            "l" => Some(0x6c),
            "m" => Some(0x6d),
            "n" => Some(0x6e),
            "o" => Some(0x6f),
            "p" => Some(0x70),
            "q" => Some(0x71),
            "r" => Some(0x72),
            "s" => Some(0x73),
            "t" => Some(0x74),
            "u" => Some(0x75),
            "v" => Some(0x76),
            "w" => Some(0x77),
            "x" => Some(0x78),
            "y" => Some(0x79),
            "z" => Some(0x7a),
            "0" => Some(0x30),
            "1" => Some(0x31),
            "2" => Some(0x32),
            "3" => Some(0x33),
            "4" => Some(0x34),
            "5" => Some(0x35),
            "6" => Some(0x36),
            "7" => Some(0x37),
            "8" => Some(0x38),
            "9" => Some(0x39),
            "space" => Some(0x20),
            "enter" | "return" => Some(0xff0d),
            "tab" => Some(0xff09),
            "escape" => Some(0xff1b),
            "backspace" => Some(0xff08),
            "delete" => Some(0xffff),
            "insert" => Some(0xff63),
            "home" => Some(0xff50),
            "end" => Some(0xff57),
            "pageup" => Some(0xff55),
            "pagedown" => Some(0xff56),
            "left" => Some(0xff51),
            "up" => Some(0xff52),
            "right" => Some(0xff53),
            "down" => Some(0xff54),
            "f1" => Some(0xffbe),
            "f2" => Some(0xffbf),
            "f3" => Some(0xffc0),
            "f4" => Some(0xffc1),
            "f5" => Some(0xffc2),
            "f6" => Some(0xffc3),
            "f7" => Some(0xffc4),
            "f8" => Some(0xffc5),
            "f9" => Some(0xffc6),
            "f10" => Some(0xffc7),
            "f11" => Some(0xffc8),
            "f12" => Some(0xffc9),
            "-" => Some(0x2d),
            "=" => Some(0x3d),
            "[" => Some(0x5b),
            "]" => Some(0x5d),
            "\\" => Some(0x5c),
            ";" => Some(0x3b),
            "'" => Some(0x27),
            "`" => Some(0x60),
            "," => Some(0x2c),
            "." => Some(0x2e),
            "/" => Some(0x2f),
            _ => None,
        }
    }

    pub struct X11GlobalHotkey {
        inner: LinuxGlobalHotkey,
        keycodes: HashMap<u32, (u8, u16)>,
    }

    impl X11GlobalHotkey {
        pub fn new() -> Self {
            Self {
                inner: LinuxGlobalHotkey::new(),
                keycodes: HashMap::default(),
            }
        }

        pub fn register(
            &mut self,
            id: u32,
            keystroke: &Keystroke,
            xcb: &Rc<XCBConnection>,
            root_window: xproto::Window,
        ) -> Result<()> {
            let keycode = keystroke_to_x11_keycode(keystroke, xcb).ok_or_else(|| {
                anyhow::anyhow!("Could not resolve keycode for key: {}", keystroke.key)
            })?;
            let modmask = keystroke_to_x11_modmask(keystroke);

            xcb.grab_key(
                false,
                root_window,
                modmask.into(),
                keycode,
                GrabMode::ASYNC,
                GrabMode::ASYNC,
            )?
            .check()?;

            self.keycodes.insert(id, (keycode, modmask));
            self.inner.register(id, keystroke)
        }

        pub fn unregister(
            &mut self,
            id: u32,
            xcb: &Rc<XCBConnection>,
            root_window: xproto::Window,
        ) {
            if let Some((keycode, modmask)) = self.keycodes.remove(&id) {
                let _ = xcb.ungrab_key(keycode, root_window, modmask.into());
            }
            self.inner.unregister(id);
        }
    }
}

#[cfg(feature = "wayland")]
pub mod wayland {
    use super::*;

    pub struct WaylandGlobalHotkey {
        _inner: LinuxGlobalHotkey,
    }

    impl WaylandGlobalHotkey {
        pub fn new() -> Self {
            Self {
                _inner: LinuxGlobalHotkey::new(),
            }
        }

        pub fn register(&mut self, _id: u32, _keystroke: &Keystroke) -> Result<()> {
            Err(anyhow::anyhow!("Global hotkeys not supported on Wayland"))
        }

        pub fn unregister(&mut self, _id: u32) {}
    }
}