winit/platform_impl/linux/common/xkb/
state.rs

1//! XKB state.
2
3use std::os::raw::c_char;
4use std::ptr::NonNull;
5
6use smol_str::SmolStr;
7#[cfg(x11_platform)]
8use x11_dl::xlib_xcb::xcb_connection_t;
9use xkbcommon_dl::{
10    self as xkb, xkb_keycode_t, xkb_keysym_t, xkb_layout_index_t, xkb_state, xkb_state_component,
11};
12
13use crate::platform_impl::common::xkb::keymap::XkbKeymap;
14#[cfg(x11_platform)]
15use crate::platform_impl::common::xkb::XKBXH;
16use crate::platform_impl::common::xkb::{make_string_with, XKBH};
17
18#[derive(Debug)]
19pub struct XkbState {
20    state: NonNull<xkb_state>,
21    modifiers: ModifiersState,
22}
23
24impl XkbState {
25    #[cfg(wayland_platform)]
26    pub fn new_wayland(keymap: &XkbKeymap) -> Option<Self> {
27        let state = NonNull::new(unsafe { (XKBH.xkb_state_new)(keymap.as_ptr()) })?;
28        Some(Self::new_inner(state))
29    }
30
31    #[cfg(x11_platform)]
32    pub fn new_x11(xcb: *mut xcb_connection_t, keymap: &XkbKeymap) -> Option<Self> {
33        let state = unsafe {
34            (XKBXH.xkb_x11_state_new_from_device)(keymap.as_ptr(), xcb, keymap._core_keyboard_id)
35        };
36        let state = NonNull::new(state)?;
37        Some(Self::new_inner(state))
38    }
39
40    fn new_inner(state: NonNull<xkb_state>) -> Self {
41        let modifiers = ModifiersState::default();
42        let mut this = Self { state, modifiers };
43        this.reload_modifiers();
44        this
45    }
46
47    pub fn get_one_sym_raw(&mut self, keycode: xkb_keycode_t) -> xkb_keysym_t {
48        unsafe { (XKBH.xkb_state_key_get_one_sym)(self.state.as_ptr(), keycode) }
49    }
50
51    pub fn layout(&mut self, key: xkb_keycode_t) -> xkb_layout_index_t {
52        unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) }
53    }
54
55    #[cfg(x11_platform)]
56    pub fn depressed_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
57        unsafe {
58            (XKBH.xkb_state_serialize_mods)(
59                self.state.as_ptr(),
60                xkb_state_component::XKB_STATE_MODS_DEPRESSED,
61            )
62        }
63    }
64
65    #[cfg(x11_platform)]
66    pub fn latched_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
67        unsafe {
68            (XKBH.xkb_state_serialize_mods)(
69                self.state.as_ptr(),
70                xkb_state_component::XKB_STATE_MODS_LATCHED,
71            )
72        }
73    }
74
75    #[cfg(x11_platform)]
76    pub fn locked_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
77        unsafe {
78            (XKBH.xkb_state_serialize_mods)(
79                self.state.as_ptr(),
80                xkb_state_component::XKB_STATE_MODS_LOCKED,
81            )
82        }
83    }
84
85    pub fn get_utf8_raw(
86        &mut self,
87        keycode: xkb_keycode_t,
88        scratch_buffer: &mut Vec<u8>,
89    ) -> Option<SmolStr> {
90        make_string_with(scratch_buffer, |ptr, len| unsafe {
91            (XKBH.xkb_state_key_get_utf8)(self.state.as_ptr(), keycode, ptr, len)
92        })
93    }
94
95    pub fn modifiers(&self) -> ModifiersState {
96        self.modifiers
97    }
98
99    pub fn update_modifiers(
100        &mut self,
101        mods_depressed: u32,
102        mods_latched: u32,
103        mods_locked: u32,
104        depressed_group: u32,
105        latched_group: u32,
106        locked_group: u32,
107    ) {
108        let mask = unsafe {
109            (XKBH.xkb_state_update_mask)(
110                self.state.as_ptr(),
111                mods_depressed,
112                mods_latched,
113                mods_locked,
114                depressed_group,
115                latched_group,
116                locked_group,
117            )
118        };
119
120        if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) {
121            // Effective value of mods have changed, we need to update our state.
122            self.reload_modifiers();
123        }
124    }
125
126    /// Reload the modifiers.
127    fn reload_modifiers(&mut self) {
128        self.modifiers.ctrl = self.mod_name_is_active(xkb::XKB_MOD_NAME_CTRL);
129        self.modifiers.alt = self.mod_name_is_active(xkb::XKB_MOD_NAME_ALT);
130        self.modifiers.shift = self.mod_name_is_active(xkb::XKB_MOD_NAME_SHIFT);
131        self.modifiers.caps_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_CAPS);
132        self.modifiers.logo = self.mod_name_is_active(xkb::XKB_MOD_NAME_LOGO);
133        self.modifiers.num_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_NUM);
134    }
135
136    /// Check if the modifier is active within xkb.
137    fn mod_name_is_active(&mut self, name: &[u8]) -> bool {
138        unsafe {
139            (XKBH.xkb_state_mod_name_is_active)(
140                self.state.as_ptr(),
141                name.as_ptr() as *const c_char,
142                xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
143            ) > 0
144        }
145    }
146}
147
148impl Drop for XkbState {
149    fn drop(&mut self) {
150        unsafe {
151            (XKBH.xkb_state_unref)(self.state.as_ptr());
152        }
153    }
154}
155
156/// Represents the current state of the keyboard modifiers
157///
158/// Each field of this struct represents a modifier and is `true` if this modifier is active.
159///
160/// For some modifiers, this means that the key is currently pressed, others are toggled
161/// (like caps lock).
162#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
163pub struct ModifiersState {
164    /// The "control" key
165    pub ctrl: bool,
166    /// The "alt" key
167    pub alt: bool,
168    /// The "shift" key
169    pub shift: bool,
170    /// The "Caps lock" key
171    pub caps_lock: bool,
172    /// The "logo" key
173    ///
174    /// Also known as the "windows" key on most keyboards
175    pub logo: bool,
176    /// The "Num lock" key
177    pub num_lock: bool,
178}
179
180impl From<ModifiersState> for crate::keyboard::ModifiersState {
181    fn from(mods: ModifiersState) -> crate::keyboard::ModifiersState {
182        let mut to_mods = crate::keyboard::ModifiersState::empty();
183        to_mods.set(crate::keyboard::ModifiersState::SHIFT, mods.shift);
184        to_mods.set(crate::keyboard::ModifiersState::CONTROL, mods.ctrl);
185        to_mods.set(crate::keyboard::ModifiersState::ALT, mods.alt);
186        to_mods.set(crate::keyboard::ModifiersState::SUPER, mods.logo);
187        to_mods
188    }
189}