1 | //! XKB state. |
2 | |
3 | use std::os::raw::c_char; |
4 | use std::ptr::NonNull; |
5 | |
6 | use smol_str::SmolStr; |
7 | #[cfg (x11_platform)] |
8 | use x11_dl::xlib_xcb::xcb_connection_t; |
9 | use xkbcommon_dl::{ |
10 | self as xkb, xkb_keycode_t, xkb_keysym_t, xkb_layout_index_t, xkb_state, xkb_state_component, |
11 | }; |
12 | |
13 | use crate::platform_impl::common::xkb::keymap::XkbKeymap; |
14 | #[cfg (x11_platform)] |
15 | use crate::platform_impl::common::xkb::XKBXH; |
16 | use crate::platform_impl::common::xkb::{make_string_with, XKBH}; |
17 | |
18 | #[derive (Debug)] |
19 | pub struct XkbState { |
20 | state: NonNull<xkb_state>, |
21 | modifiers: ModifiersState, |
22 | } |
23 | |
24 | impl 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 | |
148 | impl 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)] |
163 | pub 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 | |
180 | impl From<ModifiersState> for crate::keyboard::ModifiersState { |
181 | fn from(mods: ModifiersState) -> crate::keyboard::ModifiersState { |
182 | let mut to_mods: ModifiersState = crate::keyboard::ModifiersState::empty(); |
183 | to_mods.set(other:crate::keyboard::ModifiersState::SHIFT, value:mods.shift); |
184 | to_mods.set(other:crate::keyboard::ModifiersState::CONTROL, value:mods.ctrl); |
185 | to_mods.set(other:crate::keyboard::ModifiersState::ALT, value:mods.alt); |
186 | to_mods.set(other:crate::keyboard::ModifiersState::SUPER, value:mods.logo); |
187 | to_mods |
188 | } |
189 | } |
190 | |