├── .gitignore ├── src ├── lib.rs └── xkb │ ├── x11 │ ├── ffi.rs │ └── mod.rs │ ├── compose.rs │ ├── ffi.rs │ └── mod.rs ├── README.md ├── Cargo.toml ├── LICENSE ├── .github └── workflows │ └── ci.yml └── examples ├── quick-evdev.rs └── how-to-type.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | #[cfg(feature = "wayland")] 3 | extern crate memmap2; 4 | 5 | pub mod xkb; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xkbcommon-rs 2 | 3 | xkbcommon-rs is a set of bindings and safe wrappers for [libxkbcommon](http://xkbcommon.org/). 4 | 5 | For use with wayland: 6 | ```toml 7 | [dependencies] 8 | xkbcommon = { version = "0.9", features = ["wayland"] } 9 | ``` 10 | For use with X11: 11 | ```toml 12 | [dependencies] 13 | xkbcommon = { version = "0.9", features = ["x11"] } 14 | ``` 15 | 16 | # example 17 | 18 | Living example for X11 here: 19 | https://github.com/rust-x-bindings/toy_xcb/blob/master/src/keyboard.rs 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xkbcommon" 3 | version = "0.9.0" 4 | authors = ["Remi THEBAULT "] 5 | description = "Rust bindings and wrappers for libxkbcommon" 6 | repository = "https://github.com/rust-x-bindings/xkbcommon-rs" 7 | readme = "README.md" 8 | keywords = ["keyboard", "x11", "wayland", "xcb", "input"] 9 | license = "MIT" 10 | edition = "2021" 11 | 12 | [package.metadata.docs.rs] 13 | all-features = true 14 | 15 | [dependencies] 16 | libc = "0.2.148" 17 | memmap2 = { version = "0.9.0", optional = true } 18 | as-raw-xcb-connection = { version = "1.0", optional = true } 19 | xkeysym = "0.2.0" 20 | 21 | [dev-dependencies] 22 | evdev = "0.11.4" 23 | 24 | [features] 25 | default = ["wayland"] 26 | x11 = ["as-raw-xcb-connection"] 27 | wayland = ["memmap2"] 28 | 29 | [[example]] 30 | name = "quick-evdev" 31 | 32 | [[example]] 33 | name = "how-to-type" 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Remi Thebault 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous Integration 4 | 5 | jobs: 6 | check: 7 | name: Lint, Build and Test 8 | runs-on: ubuntu-latest 9 | env: 10 | RUSTDOCFLAGS: -Dwarnings 11 | steps: 12 | - name: Checkout sources 13 | uses: actions/checkout@v2 14 | 15 | - name: Install stable toolchain 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | 22 | - name: Run cargo fmt 23 | uses: actions-rs/cargo@v1 24 | with: 25 | command: fmt 26 | args: --all -- --check 27 | 28 | - name: Install dependencies 29 | run: | 30 | sudo apt-get update 31 | sudo apt-get install \ 32 | libxkbcommon-dev \ 33 | libxkbcommon-x11-dev 34 | 35 | - name: Run cargo build 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: build 39 | args: --all-features 40 | 41 | - name: Run cargo test 42 | uses: actions-rs/cargo@v1 43 | with: 44 | command: test 45 | args: --all-features 46 | 47 | - name: Run cargo doc 48 | uses: actions-rs/cargo@v1 49 | with: 50 | command: doc 51 | args: --all-features 52 | -------------------------------------------------------------------------------- /src/xkb/x11/ffi.rs: -------------------------------------------------------------------------------- 1 | use crate::xkb::ffi::{xkb_context, xkb_keymap, xkb_keymap_compile_flags, xkb_state}; 2 | 3 | use as_raw_xcb_connection::xcb_connection_t; 4 | use std::os::raw::c_int; 5 | 6 | pub const XKB_X11_MIN_MAJOR_XKB_VERSION: u16 = 1; 7 | pub const XKB_X11_MIN_MINOR_XKB_VERSION: u16 = 0; 8 | 9 | #[repr(C)] 10 | #[allow(non_camel_case_types)] 11 | pub enum xkb_x11_setup_xkb_extension_flags { 12 | /** Do not apply any flags. */ 13 | NO_FLAGS = 0, 14 | } 15 | 16 | #[link(name = "xkbcommon-x11")] 17 | extern "C" { 18 | 19 | pub fn xkb_x11_setup_xkb_extension( 20 | connection: *mut xcb_connection_t, 21 | major_xkb_version: u16, 22 | minor_xkb_version: u16, 23 | flags: xkb_x11_setup_xkb_extension_flags, 24 | major_xkb_version_out: *mut u16, 25 | minor_xkb_version_out: *mut u16, 26 | base_event_out: *mut u8, 27 | base_error_out: *mut u8, 28 | ) -> c_int; 29 | 30 | pub fn xkb_x11_get_core_keyboard_device_id(connection: *mut xcb_connection_t) -> i32; 31 | 32 | pub fn xkb_x11_keymap_new_from_device( 33 | context: *mut xkb_context, 34 | connection: *mut xcb_connection_t, 35 | device_id: i32, 36 | flags: xkb_keymap_compile_flags, 37 | ) -> *mut xkb_keymap; 38 | 39 | pub fn xkb_x11_state_new_from_device( 40 | keymap: *mut xkb_keymap, 41 | connection: *mut xcb_connection_t, 42 | device_id: i32, 43 | ) -> *mut xkb_state; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/xkb/x11/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ffi; 2 | 3 | use self::ffi::*; 4 | use super::{Context, Keymap, KeymapCompileFlags, State}; 5 | use as_raw_xcb_connection::AsRawXcbConnection; 6 | use std::mem; 7 | 8 | pub const MIN_MAJOR_XKB_VERSION: u16 = 1; 9 | pub const MIN_MINOR_XKB_VERSION: u16 = 0; 10 | 11 | #[repr(C)] 12 | pub enum SetupXkbExtensionFlags { 13 | /** Do not apply any flags. */ 14 | NoFlags = 0, 15 | } 16 | 17 | pub fn setup_xkb_extension( 18 | connection: impl AsRawXcbConnection, 19 | major_xkb_version: u16, 20 | minor_xkb_version: u16, 21 | flags: SetupXkbExtensionFlags, 22 | major_xkb_version_out: &mut u16, 23 | minor_xkb_version_out: &mut u16, 24 | base_event_out: &mut u8, 25 | base_error_out: &mut u8, 26 | ) -> bool { 27 | unsafe { 28 | xkb_x11_setup_xkb_extension( 29 | connection.as_raw_xcb_connection(), 30 | major_xkb_version, 31 | minor_xkb_version, 32 | mem::transmute(flags), 33 | major_xkb_version_out, 34 | minor_xkb_version_out, 35 | base_event_out, 36 | base_error_out, 37 | ) != 0 38 | } 39 | } 40 | 41 | #[must_use] 42 | pub fn get_core_keyboard_device_id(connection: impl AsRawXcbConnection) -> i32 { 43 | unsafe { xkb_x11_get_core_keyboard_device_id(connection.as_raw_xcb_connection()) as i32 } 44 | } 45 | 46 | #[must_use] 47 | pub fn keymap_new_from_device( 48 | context: &Context, 49 | connection: impl AsRawXcbConnection, 50 | device_id: i32, 51 | flags: KeymapCompileFlags, 52 | ) -> Keymap { 53 | unsafe { 54 | Keymap::from_raw_ptr(xkb_x11_keymap_new_from_device( 55 | context.get_raw_ptr(), 56 | connection.as_raw_xcb_connection(), 57 | device_id, 58 | flags, 59 | )) 60 | } 61 | } 62 | 63 | #[must_use] 64 | pub fn state_new_from_device( 65 | keymap: &Keymap, 66 | connection: impl AsRawXcbConnection, 67 | device_id: i32, 68 | ) -> State { 69 | unsafe { 70 | State::from_raw_ptr(xkb_x11_state_new_from_device( 71 | keymap.get_raw_ptr(), 72 | connection.as_raw_xcb_connection(), 73 | device_id, 74 | )) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/quick-evdev.rs: -------------------------------------------------------------------------------- 1 | // This is a translation of the xkbcommon quick start guide: 2 | // https://xkbcommon.org/doc/current/md_doc_quick_guide.html 3 | 4 | extern crate evdev; 5 | extern crate xkbcommon; 6 | 7 | use xkbcommon::xkb; 8 | 9 | // evdev constants: 10 | const KEYCODE_OFFSET: u16 = 8; 11 | const KEY_STATE_RELEASE: i32 = 0; 12 | const KEY_STATE_REPEAT: i32 = 2; 13 | 14 | fn main() { 15 | // Open evdev device 16 | let mut device = evdev::Device::open( 17 | std::env::args() 18 | .nth(1) 19 | .unwrap_or(String::from("/dev/input/event0")), 20 | ) 21 | .unwrap(); 22 | 23 | // Create context 24 | let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); 25 | 26 | // Load keymap informations 27 | let keymap = xkb::Keymap::new_from_names( 28 | &context, 29 | "", // rules 30 | "pc105", // model 31 | "is", // layout 32 | "dvorak", // variant 33 | Some("terminate:ctrl_alt_bksp".to_string()), // options 34 | xkb::COMPILE_NO_FLAGS, 35 | ) 36 | .unwrap(); 37 | 38 | // Create the state tracker 39 | let mut state = xkb::State::new(&keymap); 40 | 41 | loop { 42 | for event in device.fetch_events().unwrap() { 43 | if let evdev::InputEventKind::Key(keycode) = event.kind() { 44 | let keycode = (keycode.0 + KEYCODE_OFFSET).into(); 45 | 46 | // Ask the keymap what to do with key-repeat event 47 | if event.value() == KEY_STATE_REPEAT && !keymap.key_repeats(keycode) { 48 | continue; 49 | } 50 | print!("keycode {:?} ", keycode); 51 | 52 | // Get keysym 53 | let keysym = state.key_get_one_sym(keycode); 54 | print!("keysym: {} ", xkb::keysym_get_name(keysym)); 55 | 56 | // Update state 57 | let _changes = if event.value() == KEY_STATE_RELEASE { 58 | state.update_key(keycode, xkb::KeyDirection::Up) 59 | } else { 60 | state.update_key(keycode, xkb::KeyDirection::Down) 61 | }; 62 | 63 | // Inspect state 64 | if state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE) { 65 | print!("Control "); 66 | } 67 | if state.led_name_is_active(xkb::LED_NAME_NUM) { 68 | print!("NumLockLED"); 69 | } 70 | 71 | println!(); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/xkb/compose.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Keysym}; 2 | use crate::xkb::ffi::compose::*; 3 | use std::borrow::Cow; 4 | use std::ffi::CStr; 5 | use std::ffi::CString; 6 | use std::ffi::OsStr; 7 | use std::mem; 8 | use std::str; 9 | 10 | pub type CompileFlags = u32; 11 | pub const COMPILE_NO_FLAGS: CompileFlags = 0; 12 | 13 | pub type Format = u32; 14 | pub const FORMAT_TEXT_V1: Format = 1; 15 | 16 | pub type StateFlags = u32; 17 | pub const STATE_NO_FLAGS: StateFlags = 0; 18 | 19 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 20 | #[repr(C)] 21 | pub enum Status { 22 | Nothing = 0, 23 | Composing = 1, 24 | Composed, 25 | Cancelled, 26 | } 27 | 28 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 29 | #[repr(C)] 30 | pub enum FeedResult { 31 | Ignored, 32 | Accepted, 33 | } 34 | 35 | pub struct Table { 36 | ptr: *mut xkb_compose_table, 37 | } 38 | 39 | impl Table { 40 | /// Build a table from a locale. 41 | /// The locale is typically obtained from environment variables. 42 | /// 43 | /// # Panics 44 | /// May panic if the locale contain inner null characters. 45 | #[allow(clippy::result_unit_err, clippy::missing_errors_doc)] 46 | pub fn new_from_locale( 47 | context: &Context, 48 | locale: &OsStr, 49 | flags: CompileFlags, 50 | ) -> Result { 51 | use std::os::unix::ffi::OsStrExt; 52 | 53 | let locale_cstr = CStr::from_bytes_with_nul(locale.as_bytes()); 54 | let locale_cstr = match locale_cstr { 55 | Ok(loc) => Cow::from(loc), 56 | Err(_) => Cow::from(CString::new(locale.as_bytes().to_vec()).unwrap()), 57 | }; 58 | 59 | let ptr = unsafe { 60 | xkb_compose_table_new_from_locale(context.get_raw_ptr(), locale_cstr.as_ptr(), flags) 61 | }; 62 | if ptr.is_null() { 63 | Err(()) 64 | } else { 65 | Ok(Table { ptr }) 66 | } 67 | } 68 | 69 | #[allow( 70 | clippy::result_unit_err, 71 | clippy::missing_panics_doc, 72 | clippy::missing_errors_doc 73 | )] 74 | pub fn new_from_buffer>( 75 | context: &Context, 76 | buffer: T, 77 | locale: &str, 78 | format: Format, 79 | flags: CompileFlags, 80 | ) -> Result { 81 | let buffer = buffer.as_ref(); 82 | let locale = CString::new(locale).unwrap(); 83 | let ptr = unsafe { 84 | xkb_compose_table_new_from_buffer( 85 | context.get_raw_ptr(), 86 | buffer.as_ptr().cast(), 87 | buffer.len() as _, 88 | locale.as_ptr(), 89 | format, 90 | flags, 91 | ) 92 | }; 93 | if ptr.is_null() { 94 | Err(()) 95 | } else { 96 | Ok(Table { ptr }) 97 | } 98 | } 99 | } 100 | 101 | impl Drop for Table { 102 | fn drop(&mut self) { 103 | unsafe { 104 | xkb_compose_table_unref(self.ptr); 105 | } 106 | } 107 | } 108 | 109 | impl Clone for Table { 110 | fn clone(&self) -> Table { 111 | Table { 112 | ptr: unsafe { xkb_compose_table_ref(self.ptr) }, 113 | } 114 | } 115 | } 116 | 117 | pub struct State { 118 | ptr: *mut xkb_compose_state, 119 | } 120 | 121 | impl State { 122 | /// # Safety 123 | /// `ptr` must be a valid pointer to `xkb_compose_state` 124 | #[must_use] 125 | pub unsafe fn from_raw_ptr(ptr: *mut xkb_compose_state) -> State { 126 | State { ptr } 127 | } 128 | 129 | pub fn get_raw_ptr(&self) -> *mut xkb_compose_state { 130 | self.ptr 131 | } 132 | 133 | #[must_use] 134 | pub fn new(table: &Table, flags: StateFlags) -> State { 135 | State { 136 | ptr: unsafe { xkb_compose_state_new(table.ptr, flags) }, 137 | } 138 | } 139 | 140 | #[must_use] 141 | pub fn compose_table(&self) -> Table { 142 | Table { 143 | ptr: unsafe { xkb_compose_table_ref(xkb_compose_state_get_compose_table(self.ptr)) }, 144 | } 145 | } 146 | 147 | pub fn feed(&mut self, keysym: Keysym) -> FeedResult { 148 | unsafe { mem::transmute(xkb_compose_state_feed(self.ptr, keysym.raw())) } 149 | } 150 | 151 | pub fn reset(&mut self) { 152 | unsafe { 153 | xkb_compose_state_reset(self.ptr); 154 | } 155 | } 156 | 157 | #[must_use] 158 | pub fn status(&self) -> Status { 159 | unsafe { mem::transmute(xkb_compose_state_get_status(self.ptr)) } 160 | } 161 | 162 | #[must_use] 163 | pub fn utf8(&self) -> Option { 164 | let mut buffer = [0_u8; 256]; 165 | 166 | unsafe { 167 | match xkb_compose_state_get_utf8(self.ptr, buffer.as_mut_ptr().cast(), buffer.len()) { 168 | 0 => None, 169 | n => Some(str::from_utf8_unchecked(&buffer[..n as usize]).into()), 170 | } 171 | } 172 | } 173 | 174 | #[must_use] 175 | pub fn keysym(&self) -> Option { 176 | unsafe { 177 | match Keysym::new(xkb_compose_state_get_one_sym(self.ptr)) { 178 | xkeysym::NO_SYMBOL => None, 179 | value => Some(value), 180 | } 181 | } 182 | } 183 | } 184 | 185 | impl Drop for State { 186 | fn drop(&mut self) { 187 | unsafe { 188 | xkb_compose_state_unref(self.ptr); 189 | } 190 | } 191 | } 192 | 193 | impl Clone for State { 194 | fn clone(&self) -> State { 195 | State { 196 | ptr: unsafe { xkb_compose_state_ref(self.ptr) }, 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /examples/how-to-type.rs: -------------------------------------------------------------------------------- 1 | //! Translation of xkbcommon's `how-to-type` tool. 2 | 3 | use std::process::ExitCode; 4 | 5 | use xkbcommon::xkb; 6 | 7 | fn main() -> ExitCode { 8 | let mut input_is_keysym = false; 9 | 10 | let mut rules = None; 11 | let mut model = None; 12 | let mut layout = None; 13 | let mut variant = None; 14 | let mut options = None; 15 | 16 | let mut disable_env_names = true; 17 | 18 | let mut input_value = None; 19 | 20 | let mut args_iter = std::env::args().skip(1); 21 | while let Some(arg) = args_iter.next() { 22 | if arg == "--keysym" { 23 | input_is_keysym = true; 24 | } else if arg == "--format" { 25 | eprintln!("--format is coming in libxkbcommon 1.11.0"); 26 | return ExitCode::FAILURE; 27 | } else if arg == "--rules" { 28 | rules = Some(args_iter.next().expect("missing argument for --rules")); 29 | } else if arg == "--model" { 30 | model = Some(args_iter.next().expect("missing argument for --model")); 31 | } else if arg == "--layout" { 32 | layout = Some(args_iter.next().expect("missing argument for --layout")); 33 | } else if arg == "--variant" { 34 | variant = Some(args_iter.next().expect("missing argument for --variant")); 35 | } else if arg == "--options" { 36 | options = Some(args_iter.next().expect("missing argument for --options")); 37 | } else if arg == "--enable-environment-names" { 38 | disable_env_names = false; 39 | } else if input_value.is_some() { 40 | eprintln!("can't have multiple input values"); 41 | return ExitCode::FAILURE; 42 | } else { 43 | input_value = Some(arg); 44 | } 45 | } 46 | 47 | let input_value = input_value.expect("missing input value"); 48 | 49 | let target_keysym = if input_is_keysym { 50 | let keysym = xkb::keysym_from_name(&input_value, xkb::KEYSYM_NO_FLAGS); 51 | 52 | if keysym == xkb::Keysym::NoSymbol { 53 | // Parse as decimal 54 | xkb::Keysym::from(input_value.parse::().unwrap()) 55 | } else { 56 | keysym 57 | } 58 | } else { 59 | let ch = if input_value.chars().nth(1).is_some() { 60 | // More than one Unicode character in input 61 | 62 | let parsed_int = if input_value.starts_with("U+") 63 | || input_value.starts_with("0x") 64 | || input_value.starts_with("0X") 65 | { 66 | u32::from_str_radix(&input_value[2..], 16) 67 | } else { 68 | input_value.parse() 69 | }; 70 | 71 | char::from_u32(parsed_int.expect("invalid number")) 72 | .expect("should be a valid Unicode scalar value") 73 | } else { 74 | // Take in a Unicode scalar value as UTF-8 75 | input_value.chars().next().expect("empty input") 76 | }; 77 | 78 | let keysym = xkb::Keysym::from_char(ch); 79 | 80 | if keysym == xkb::Keysym::NoSymbol { 81 | eprintln!("failed to convert Unicode scalar value to keysym"); 82 | return ExitCode::FAILURE; 83 | } 84 | 85 | keysym 86 | }; 87 | 88 | let context = xkb::Context::new(if disable_env_names { 89 | xkb::CONTEXT_NO_ENVIRONMENT_NAMES 90 | } else { 91 | xkb::CONTEXT_NO_FLAGS 92 | }); 93 | 94 | let keymap = xkb::Keymap::new_from_names( 95 | &context, 96 | rules.as_deref().unwrap_or_default(), 97 | model.as_deref().unwrap_or_default(), 98 | layout.as_deref().unwrap_or_default(), 99 | variant.as_deref().unwrap_or_default(), 100 | options, 101 | xkb::COMPILE_NO_FLAGS, 102 | ) 103 | .unwrap(); 104 | 105 | println!( 106 | "keysym: {} ({:#06x})", 107 | target_keysym.name().expect("failed to get name of keysym"), 108 | target_keysym.raw() 109 | ); 110 | println!( 111 | "{:<8} {:<9} {:<8} {:<20} {:<7} MODIFIERS", 112 | "KEYCODE", "KEY NAME", "LAYOUT", "LAYOUT NAME", "LEVEL#" 113 | ); 114 | 115 | let min = keymap.min_keycode().raw(); 116 | let max = keymap.max_keycode().raw(); 117 | 118 | let num_mods = keymap.num_mods(); 119 | 120 | for keycode in min..=max { 121 | let keycode = xkb::Keycode::new(keycode); 122 | 123 | // Skip unused keycodes 124 | let Some(key_name) = keymap.key_get_name(keycode) else { 125 | continue; 126 | }; 127 | 128 | let num_layouts = keymap.num_layouts_for_key(keycode); 129 | for layout_index in 0..num_layouts { 130 | let mut layout_name = keymap.layout_get_name(layout_index); 131 | if layout_name.is_empty() { 132 | layout_name = "?"; 133 | } 134 | 135 | let num_levels = keymap.num_levels_for_key(keycode, layout_index); 136 | for level_index in 0..num_levels { 137 | let syms = keymap.key_get_syms_by_level(keycode, layout_index, level_index); 138 | 139 | if syms != [target_keysym] { 140 | // Inequal or nonzero count 141 | continue; 142 | }; 143 | 144 | let mut masks = [xkb::ModMask::default(); 100]; 145 | let num_masks = 146 | keymap.key_get_mods_for_level(keycode, layout_index, level_index, &mut masks); 147 | 148 | let masks = &masks[0..num_masks]; 149 | 150 | for mod_mask in masks { 151 | print!( 152 | "{:<8} {:<9} {:<8} {:<20} {:<7} [ ", 153 | keycode.raw(), 154 | key_name, 155 | layout_index + 1, 156 | layout_name, 157 | level_index + 1 158 | ); 159 | 160 | for mod_index in 0..num_mods { 161 | // Check whether the modifier mask contains this modifier 162 | if mod_mask & (1 << mod_index) == 0 { 163 | continue; 164 | } 165 | 166 | print!("{} ", keymap.mod_get_name(mod_index)); 167 | } 168 | 169 | println!("]"); 170 | } 171 | } 172 | } 173 | } 174 | 175 | ExitCode::SUCCESS 176 | } 177 | -------------------------------------------------------------------------------- /src/xkb/ffi.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use libc::{size_t, FILE}; 4 | use std::os::raw::{c_char, c_int, c_uint, c_void}; 5 | 6 | pub enum xkb_context {} 7 | 8 | pub enum xkb_keymap {} 9 | 10 | pub enum xkb_state {} 11 | 12 | pub type xkb_keycode_t = u32; 13 | 14 | pub type xkb_keysym_t = u32; 15 | 16 | pub type xkb_layout_index_t = u32; 17 | 18 | pub type xkb_layout_mask_t = u32; 19 | 20 | pub type xkb_level_index_t = u32; 21 | 22 | pub type xkb_mod_index_t = u32; 23 | 24 | pub type xkb_mod_mask_t = u32; 25 | 26 | pub type xkb_led_index_t = u32; 27 | 28 | pub type xkb_led_mask_t = u32; 29 | 30 | pub const XKB_KEYCODE_INVALID: u32 = 0xffff_ffff; 31 | pub const XKB_LAYOUT_INVALID: u32 = 0xffff_ffff; 32 | pub const XKB_LEVEL_INVALID: u32 = 0xffff_ffff; 33 | pub const XKB_MOD_INVALID: u32 = 0xffff_ffff; 34 | pub const XKB_LED_INVALID: u32 = 0xffff_ffff; 35 | 36 | pub const XKB_KEYCODE_MAX: u32 = 0xffff_fffe; 37 | 38 | #[must_use] 39 | pub fn xkb_keycode_is_legal_ext(key: u32) -> bool { 40 | key <= XKB_KEYCODE_MAX 41 | } 42 | 43 | #[must_use] 44 | pub fn xkb_keycode_is_legal_x11(key: u32) -> bool { 45 | (8..=255).contains(&key) 46 | } 47 | 48 | #[repr(C)] 49 | pub struct xkb_rule_names { 50 | pub rules: *const c_char, 51 | pub model: *const c_char, 52 | pub layout: *const c_char, 53 | pub variant: *const c_char, 54 | pub options: *const c_char, 55 | } 56 | 57 | pub type xkb_keysym_flags = u32; 58 | pub const XKB_KEYSYM_NO_FLAGS: u32 = 0; 59 | pub const XKB_KEYSYM_CASE_INSENSITIVE: u32 = 1 << 0; 60 | 61 | pub type xkb_context_flags = u32; 62 | pub const XKB_CONTEXT_NO_FLAGS: u32 = 0; 63 | pub const XKB_CONTEXT_NO_DEFAULT_INCLUDES: u32 = 1 << 0; 64 | pub const XKB_CONTEXT_NO_ENVIRONMENT_NAMES: u32 = 1 << 1; 65 | 66 | #[repr(C)] 67 | pub enum xkb_log_level { 68 | CRITICAL = 10, 69 | ERROR = 20, 70 | WARNING = 30, 71 | INFO = 40, 72 | DEBUG = 50, 73 | } 74 | 75 | pub type xkb_keymap_compile_flags = u32; 76 | pub const XKB_KEYMAP_COMPILE_NO_FLAGS: u32 = 0; 77 | 78 | pub type xkb_keymap_format = u32; 79 | pub const XKB_KEYMAP_FORMAT_TEXT_V1: u32 = 1; 80 | pub const XKB_KEYMAP_FORMAT_USE_ORIGINAL: u32 = 0xffff_ffff; 81 | 82 | #[repr(C)] 83 | pub enum xkb_key_direction { 84 | UP, 85 | DOWN, 86 | } 87 | 88 | pub type xkb_state_component = u32; 89 | pub const XKB_STATE_MODS_DEPRESSED: u32 = 1 << 0; 90 | pub const XKB_STATE_MODS_LATCHED: u32 = 1 << 1; 91 | pub const XKB_STATE_MODS_LOCKED: u32 = 1 << 2; 92 | pub const XKB_STATE_MODS_EFFECTIVE: u32 = 1 << 3; 93 | pub const XKB_STATE_LAYOUT_DEPRESSED: u32 = 1 << 4; 94 | pub const XKB_STATE_LAYOUT_LATCHED: u32 = 1 << 5; 95 | pub const XKB_STATE_LAYOUT_LOCKED: u32 = 1 << 6; 96 | pub const XKB_STATE_LAYOUT_EFFECTIVE: u32 = 1 << 7; 97 | pub const XKB_STATE_LEDS: u32 = 1 << 8; 98 | 99 | pub type xkb_state_match = u32; 100 | pub const XKB_STATE_MATCH_ANY: u32 = 1 << 0; 101 | pub const XKB_STATE_MATCH_ALL: u32 = 1 << 1; 102 | pub const XKB_STATE_MATCH_NON_EXCLUSIVE: u32 = 1 << 16; 103 | 104 | pub type xkb_log_fn_t = unsafe extern "C" fn( 105 | context: *mut xkb_context, 106 | level: xkb_log_level, 107 | format: *const c_char, 108 | ... 109 | ); 110 | 111 | pub type xkb_keymap_key_iter_t = 112 | unsafe extern "C" fn(keymap: *mut xkb_keymap, key: xkb_keycode_t, data: *mut c_void); 113 | 114 | #[link(name = "xkbcommon")] 115 | extern "C" { 116 | 117 | pub fn xkb_keysym_get_name(keysym: xkb_keysym_t, buffer: *mut c_char, size: usize) -> c_int; 118 | 119 | pub fn xkb_keysym_from_name(name: *const c_char, flags: xkb_keysym_flags) -> xkb_keysym_t; 120 | 121 | pub fn xkb_keysym_to_utf8(keysym: xkb_keysym_t, buffer: *mut c_char, size: usize) -> c_int; 122 | 123 | pub fn xkb_keysym_to_utf32(keysym: xkb_keysym_t) -> u32; 124 | 125 | pub fn xkb_utf32_to_keysym(ucs: u32) -> xkb_keysym_t; 126 | 127 | pub fn xkb_context_new(flags: xkb_context_flags) -> *mut xkb_context; 128 | 129 | pub fn xkb_context_ref(context: *mut xkb_context) -> *mut xkb_context; 130 | 131 | pub fn xkb_context_unref(context: *mut xkb_context); 132 | 133 | pub fn xkb_context_set_user_data(context: *mut xkb_context, user_data: *mut c_void); 134 | 135 | pub fn xkb_context_get_user_data(context: *mut xkb_context) -> *mut c_void; 136 | 137 | pub fn xkb_context_include_path_append(context: *mut xkb_context, path: *const c_char) 138 | -> c_int; 139 | 140 | pub fn xkb_context_include_path_append_default(context: *mut xkb_context) -> c_int; 141 | 142 | pub fn xkb_context_include_path_reset_defaults(context: *mut xkb_context) -> c_int; 143 | 144 | pub fn xkb_context_include_path_clear(context: *mut xkb_context); 145 | 146 | pub fn xkb_context_num_include_paths(context: *mut xkb_context) -> c_uint; 147 | 148 | pub fn xkb_context_include_path_get(context: *mut xkb_context, index: c_uint) -> *const c_char; 149 | 150 | pub fn xkb_context_set_log_level(context: *mut xkb_context, level: xkb_log_level); 151 | 152 | pub fn xkb_context_get_log_level(context: *mut xkb_context) -> xkb_log_level; 153 | 154 | pub fn xkb_context_set_log_verbosity(context: *mut xkb_context, verbosity: c_int); 155 | 156 | pub fn xkb_context_get_log_verbosity(context: *mut xkb_context) -> c_int; 157 | 158 | pub fn xkb_context_set_log_fn(context: *mut xkb_context, log_fn: xkb_log_fn_t); 159 | 160 | pub fn xkb_keymap_new_from_names( 161 | context: *mut xkb_context, 162 | names: *const xkb_rule_names, 163 | flags: xkb_keymap_compile_flags, 164 | ) -> *mut xkb_keymap; 165 | 166 | pub fn xkb_keymap_new_from_file( 167 | context: *mut xkb_context, 168 | file: *mut FILE, 169 | format: xkb_keymap_format, 170 | flags: xkb_keymap_compile_flags, 171 | ) -> *mut xkb_keymap; 172 | 173 | pub fn xkb_keymap_new_from_string( 174 | context: *mut xkb_context, 175 | s: *const c_char, 176 | format: xkb_keymap_format, 177 | flags: xkb_keymap_compile_flags, 178 | ) -> *mut xkb_keymap; 179 | 180 | pub fn xkb_keymap_new_from_buffer( 181 | context: *mut xkb_context, 182 | buffer: *const c_char, 183 | length: usize, 184 | format: xkb_keymap_format, 185 | flags: xkb_keymap_compile_flags, 186 | ) -> *mut xkb_keymap; 187 | 188 | pub fn xkb_keymap_ref(keymap: *mut xkb_keymap) -> *mut xkb_keymap; 189 | 190 | pub fn xkb_keymap_unref(keymap: *mut xkb_keymap); 191 | 192 | pub fn xkb_keymap_get_as_string( 193 | keymap: *mut xkb_keymap, 194 | format: xkb_keymap_format, 195 | ) -> *mut c_char; 196 | 197 | pub fn xkb_keymap_min_keycode(keymap: *mut xkb_keymap) -> xkb_keycode_t; 198 | 199 | pub fn xkb_keymap_max_keycode(keymap: *mut xkb_keymap) -> xkb_keycode_t; 200 | 201 | pub fn xkb_keymap_key_for_each( 202 | keymap: *mut xkb_keymap, 203 | iter: xkb_keymap_key_iter_t, 204 | data: *mut c_void, 205 | ); 206 | 207 | pub fn xkb_keymap_num_mods(keymap: *mut xkb_keymap) -> xkb_mod_index_t; 208 | 209 | pub fn xkb_keymap_mod_get_name(keymap: *mut xkb_keymap, idx: xkb_mod_index_t) -> *const c_char; 210 | 211 | pub fn xkb_keymap_mod_get_index( 212 | keymap: *mut xkb_keymap, 213 | name: *const c_char, 214 | ) -> xkb_mod_index_t; 215 | 216 | pub fn xkb_keymap_num_layouts(keymap: *mut xkb_keymap) -> xkb_layout_index_t; 217 | 218 | pub fn xkb_keymap_layout_get_name( 219 | keymap: *mut xkb_keymap, 220 | idx: xkb_layout_index_t, 221 | ) -> *const c_char; 222 | 223 | pub fn xkb_keymap_layout_get_index( 224 | keymap: *mut xkb_keymap, 225 | name: *const c_char, 226 | ) -> xkb_layout_index_t; 227 | 228 | pub fn xkb_keymap_num_leds(keymap: *mut xkb_keymap) -> xkb_led_index_t; 229 | 230 | pub fn xkb_keymap_led_get_name(keymap: *mut xkb_keymap, idx: xkb_led_index_t) -> *const c_char; 231 | 232 | pub fn xkb_keymap_led_get_index( 233 | keymap: *mut xkb_keymap, 234 | name: *const c_char, 235 | ) -> xkb_led_index_t; 236 | 237 | pub fn xkb_keymap_num_layouts_for_key( 238 | keymap: *mut xkb_keymap, 239 | key: xkb_keycode_t, 240 | ) -> xkb_layout_index_t; 241 | 242 | pub fn xkb_keymap_num_levels_for_key( 243 | keymap: *mut xkb_keymap, 244 | key: xkb_keycode_t, 245 | layout: xkb_layout_index_t, 246 | ) -> xkb_level_index_t; 247 | 248 | pub fn xkb_keymap_key_get_mods_for_level( 249 | keymap: *mut xkb_keymap, 250 | key: xkb_keycode_t, 251 | layout: xkb_layout_index_t, 252 | level: xkb_level_index_t, 253 | masks_out: *mut xkb_mod_mask_t, 254 | masks_size: size_t, 255 | ) -> size_t; 256 | 257 | pub fn xkb_keymap_key_get_syms_by_level( 258 | keymap: *mut xkb_keymap, 259 | key: xkb_keycode_t, 260 | layout: xkb_layout_index_t, 261 | level: xkb_level_index_t, 262 | syms_out: *mut *const xkb_keysym_t, 263 | ) -> c_int; 264 | 265 | pub fn xkb_keymap_key_by_name(keymap: *mut xkb_keymap, name: *const c_char) -> xkb_keycode_t; 266 | 267 | pub fn xkb_keymap_key_get_name(keymap: *mut xkb_keymap, key: xkb_keycode_t) -> *const c_char; 268 | 269 | pub fn xkb_keymap_key_repeats(keymap: *mut xkb_keymap, key: xkb_keycode_t) -> c_int; 270 | 271 | pub fn xkb_state_ref(state: *mut xkb_state) -> *mut xkb_state; 272 | 273 | pub fn xkb_state_unref(state: *mut xkb_state); 274 | 275 | pub fn xkb_state_new(keymap: *mut xkb_keymap) -> *mut xkb_state; 276 | 277 | pub fn xkb_state_get_keymap(state: *mut xkb_state) -> *mut xkb_keymap; 278 | 279 | pub fn xkb_state_update_key( 280 | state: *mut xkb_state, 281 | key: xkb_keycode_t, 282 | direction: xkb_key_direction, 283 | ) -> xkb_state_component; 284 | 285 | pub fn xkb_state_update_mask( 286 | state: *mut xkb_state, 287 | depressed_mods: xkb_mod_mask_t, 288 | latched_mods: xkb_mod_mask_t, 289 | locked_mods: xkb_mod_mask_t, 290 | depressed_layout: xkb_layout_index_t, 291 | latched_layout: xkb_layout_index_t, 292 | locked_layout: xkb_layout_index_t, 293 | ) -> xkb_state_component; 294 | 295 | pub fn xkb_state_key_get_syms( 296 | state: *mut xkb_state, 297 | key: xkb_keycode_t, 298 | syms_out: *mut *const xkb_keysym_t, 299 | ) -> c_int; 300 | 301 | pub fn xkb_state_key_get_utf8( 302 | state: *mut xkb_state, 303 | key: xkb_keycode_t, 304 | buffer: *mut c_char, 305 | size: usize, 306 | ) -> c_int; 307 | 308 | pub fn xkb_state_key_get_utf32(state: *mut xkb_state, key: xkb_keycode_t) -> u32; 309 | 310 | pub fn xkb_state_key_get_one_sym(state: *mut xkb_state, key: xkb_keycode_t) -> xkb_keysym_t; 311 | 312 | pub fn xkb_state_key_get_layout( 313 | state: *mut xkb_state, 314 | key: xkb_keycode_t, 315 | ) -> xkb_layout_index_t; 316 | 317 | pub fn xkb_state_key_get_level( 318 | state: *mut xkb_state, 319 | key: xkb_keycode_t, 320 | layout: xkb_layout_index_t, 321 | ) -> xkb_level_index_t; 322 | 323 | pub fn xkb_state_serialize_mods( 324 | state: *mut xkb_state, 325 | components: xkb_state_component, 326 | ) -> xkb_mod_mask_t; 327 | 328 | pub fn xkb_state_serialize_layout( 329 | state: *mut xkb_state, 330 | components: xkb_state_component, 331 | ) -> xkb_layout_index_t; 332 | 333 | pub fn xkb_state_mod_name_is_active( 334 | state: *mut xkb_state, 335 | name: *const c_char, 336 | type_: xkb_state_component, 337 | ) -> c_int; 338 | 339 | pub fn xkb_state_mod_names_are_active( 340 | state: *mut xkb_state, 341 | type_: xkb_state_component, 342 | match_: xkb_state_match, 343 | ... 344 | ) -> c_int; 345 | 346 | pub fn xkb_state_mod_index_is_active( 347 | state: *mut xkb_state, 348 | idx: xkb_mod_index_t, 349 | type_: xkb_state_component, 350 | ) -> c_int; 351 | 352 | pub fn xkb_state_mod_index_are_active( 353 | state: *mut xkb_state, 354 | type_: xkb_state_component, 355 | match_: xkb_state_match, 356 | ... 357 | ) -> c_int; 358 | 359 | pub fn xkb_state_mod_index_is_consumed( 360 | state: *mut xkb_state, 361 | key: xkb_keycode_t, 362 | idx: xkb_mod_index_t, 363 | ) -> c_int; 364 | 365 | pub fn xkb_state_mod_mask_remove_consumed( 366 | state: *mut xkb_state, 367 | key: xkb_keycode_t, 368 | mask: xkb_mod_mask_t, 369 | ) -> xkb_mod_mask_t; 370 | 371 | pub fn xkb_state_key_get_consumed_mods( 372 | state: *mut xkb_state, 373 | key: xkb_keycode_t, 374 | ) -> xkb_mod_mask_t; 375 | 376 | pub fn xkb_state_layout_name_is_active( 377 | state: *mut xkb_state, 378 | name: *const c_char, 379 | type_: xkb_state_component, 380 | ) -> c_int; 381 | 382 | pub fn xkb_state_layout_index_is_active( 383 | state: *mut xkb_state, 384 | idx: xkb_layout_index_t, 385 | type_: xkb_state_component, 386 | ) -> c_int; 387 | 388 | pub fn xkb_state_led_name_is_active(state: *mut xkb_state, name: *const c_char) -> c_int; 389 | 390 | pub fn xkb_state_led_index_is_active(state: *mut xkb_state, idx: xkb_led_index_t) -> c_int; 391 | 392 | } 393 | 394 | pub mod compose { 395 | use super::{xkb_context, xkb_keysym_t}; 396 | use libc::{c_char, c_int, size_t, FILE}; 397 | 398 | pub enum xkb_compose_table {} 399 | 400 | pub enum xkb_compose_state {} 401 | 402 | pub type xkb_compose_compile_flags = u32; 403 | 404 | pub type xkb_compose_format = u32; 405 | 406 | pub type xkb_compose_state_flags = u32; 407 | 408 | pub type xkb_compose_status = u32; 409 | 410 | pub type xkb_compose_feed_result = u32; 411 | 412 | #[link(name = "xkbcommon")] 413 | extern "C" { 414 | 415 | pub fn xkb_compose_table_new_from_locale( 416 | context: *mut xkb_context, 417 | locale: *const c_char, 418 | flags: xkb_compose_compile_flags, 419 | ) -> *mut xkb_compose_table; 420 | 421 | pub fn xkb_compose_table_new_from_file( 422 | context: *mut xkb_context, 423 | file: *mut FILE, 424 | locale: *const c_char, 425 | format: xkb_compose_format, 426 | flags: xkb_compose_compile_flags, 427 | ) -> *mut xkb_compose_table; 428 | 429 | pub fn xkb_compose_table_new_from_buffer( 430 | context: *mut xkb_context, 431 | buffer: *const c_char, 432 | length: size_t, 433 | locale: *const c_char, 434 | format: xkb_compose_format, 435 | flags: xkb_compose_compile_flags, 436 | ) -> *mut xkb_compose_table; 437 | 438 | pub fn xkb_compose_table_ref(table: *mut xkb_compose_table) -> *mut xkb_compose_table; 439 | 440 | pub fn xkb_compose_table_unref(table: *mut xkb_compose_table); 441 | 442 | pub fn xkb_compose_state_new( 443 | table: *mut xkb_compose_table, 444 | flags: xkb_compose_state_flags, 445 | ) -> *mut xkb_compose_state; 446 | 447 | pub fn xkb_compose_state_ref(state: *mut xkb_compose_state) -> *mut xkb_compose_state; 448 | 449 | pub fn xkb_compose_state_unref(state: *mut xkb_compose_state); 450 | 451 | pub fn xkb_compose_state_get_compose_table( 452 | state: *mut xkb_compose_state, 453 | ) -> *mut xkb_compose_table; 454 | 455 | pub fn xkb_compose_state_feed( 456 | state: *mut xkb_compose_state, 457 | keysym: xkb_keysym_t, 458 | ) -> xkb_compose_feed_result; 459 | 460 | pub fn xkb_compose_state_reset(state: *mut xkb_compose_state); 461 | 462 | pub fn xkb_compose_state_get_status(state: *mut xkb_compose_state) -> xkb_compose_status; 463 | 464 | pub fn xkb_compose_state_get_utf8( 465 | state: *mut xkb_compose_state, 466 | buffer: *mut c_char, 467 | size: size_t, 468 | ) -> c_int; 469 | 470 | pub fn xkb_compose_state_get_one_sym(state: *mut xkb_compose_state) -> xkb_keysym_t; 471 | 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/xkb/mod.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::all)] 2 | #![allow( 3 | clippy::similar_names, 4 | clippy::wildcard_imports, 5 | clippy::cast_sign_loss, 6 | clippy::too_many_arguments 7 | )] 8 | pub mod compose; 9 | pub mod ffi; 10 | pub mod keysyms; 11 | 12 | #[cfg(feature = "x11")] 13 | pub mod x11; 14 | 15 | pub use self::compose::*; 16 | use crate::xkb::ffi::*; 17 | 18 | #[cfg(feature = "wayland")] 19 | use memmap2::MmapOptions; 20 | #[cfg(feature = "wayland")] 21 | use std::os::unix::io::OwnedFd; 22 | 23 | use libc::{self, c_char, c_int, c_uint}; 24 | use std::borrow::Borrow; 25 | use std::ffi::{CStr, CString}; 26 | use std::fs; 27 | use std::io::Read; 28 | use std::iter::Iterator; 29 | use std::mem; 30 | use std::os::raw; 31 | use std::path::Path; 32 | use std::ptr::{null, null_mut}; 33 | use std::slice; 34 | use std::str; 35 | 36 | /// A number used to represent a physical key on a keyboard. 37 | /// 38 | /// A standard PC-compatible keyboard might have 102 keys. An appropriate 39 | /// keymap would assign each of them a keycode, by which the user should 40 | /// refer to the key throughout the library. 41 | /// 42 | /// Historically, the X11 protocol, and consequentially the XKB protocol, 43 | /// assign only 8 bits for keycodes. This limits the number of different 44 | /// keys that can be used simultaneously in a single keymap to 256 45 | /// (disregarding other limitations). This library does not share this limit; 46 | /// keycodes beyond 255 ('extended keycodes') are not treated specially. 47 | /// Keymaps and applications which are compatible with X11 should not use 48 | /// these keycodes. 49 | /// 50 | /// The values of specific keycodes are determined by the keymap and the 51 | /// underlying input system. For example, with an X11-compatible keymap 52 | /// and Linux evdev scan codes (see linux/input.h), a fixed offset is used: 53 | /// 54 | /// ```no_run 55 | /// # use xkbcommon::xkb::keysyms::KEY_A; 56 | /// # use xkbcommon::xkb::Keycode; 57 | /// let keycode_A: Keycode = Keycode::new(KEY_A as u32 + 8); 58 | /// ``` 59 | /// 60 | /// See `xkb::keycode_is_legal_ext()` and `xkb::keycode_is_legal_x11()` 61 | pub use xkeysym::KeyCode as Keycode; 62 | 63 | /// A number used to represent the symbols generated from a key on a keyboard. 64 | /// 65 | /// A key, represented by a keycode, may generate different symbols according 66 | /// to keyboard state. For example, on a QWERTY keyboard, pressing the key 67 | /// labled \ generates the symbol 'a'. If the Shift key is held, it 68 | /// generates the symbol 'A'. If a different layout is used, say Greek, 69 | /// it generates the symbol 'α'. And so on. 70 | /// 71 | /// Each such symbol is represented by a keysym. Note that keysyms are 72 | /// somewhat more general, in that they can also represent some "function", 73 | /// such as "Left" or "Right" for the arrow keys. For more information, 74 | /// see: 75 | /// 76 | /// 77 | /// Specifically named keysyms can be found in the 78 | /// xkbcommon/xkbcommon-keysyms.h header file. Their name does not include 79 | /// the `xkb::KEY_` prefix. 80 | /// 81 | /// Besides those, any Unicode/ISO 10646 character in the range U0100 to 82 | /// U10FFFF can be represented by a keysym value in the range 0x01000100 to 83 | /// 0x0110FFFF. The name of Unicode keysyms is "`U`", e.g. "UA1B2". 84 | /// 85 | /// The name of other unnamed keysyms is the hexadecimal representation of 86 | /// their value, e.g. "0xabcd1234". 87 | /// 88 | /// Keysym names are case-sensitive. 89 | pub use xkeysym::Keysym; 90 | 91 | /// Index of a keyboard layout. 92 | /// 93 | /// The layout index is a state component which detemines which _keyboard 94 | /// layout_ active. These may be different alphabets, different key 95 | /// arrangements, etc. 96 | /// 97 | /// Layout indices are consecutive. The first layout has index 0. 98 | /// 99 | /// Each layout is not required to have a name, and the names are not 100 | /// guaranteed to be unique (though they are usually provided and unique). 101 | /// Therefore, it is not safe to use the name as a unique identifier for a 102 | /// layout. Layout names are case-sensitive. 103 | /// 104 | /// Layouts are also called "groups" by XKB. 105 | pub type LayoutIndex = u32; 106 | /// A mask of layout indices 107 | pub type LayoutMask = u32; 108 | 109 | /// Index of a shift level. 110 | /// 111 | /// Any key, in any layout, can have several _shift levels_ Each 112 | /// shift level can assign different keysyms to the key. The shift level 113 | /// to use is chosen according to the current keyboard state; for example, 114 | /// if no keys are pressed, the first level may be used; if the Left Shift 115 | /// key is pressed, the second; if Num Lock is pressed, the third; and 116 | /// many such combinations are possible (see `ModIndex`). 117 | /// 118 | /// Level indices are consecutive. The first level has index 0. 119 | pub type LevelIndex = u32; 120 | 121 | /// Index of a modifier. 122 | /// 123 | /// A modifier is a state component which changes the way keys are 124 | /// interpreted. A keymap defines a set of modifiers, such as Alt, Shift, 125 | /// Num Lock or Meta, and specifies which keys may activate which 126 | /// modifiers (in a many-to-many relationship, i.e. a key can activate 127 | /// several modifiers, and a modifier may be activated by several keys. 128 | /// Different keymaps do this differently). 129 | /// 130 | /// When retrieving the keysyms for a key, the active modifier set is 131 | /// consulted; this detemines the correct shift level to use within the 132 | /// currently active layout (see `LevelIndex`). 133 | /// 134 | /// Modifier indices are consecutive. The first modifier has index 0. 135 | /// 136 | /// Each modifier must have a name, and the names are unique. Therefore, it 137 | /// is safe to use the name as a unique identifier for a modifier. 138 | /// Modifier names are case-sensitive. 139 | pub type ModIndex = u32; 140 | /// A mask of modifier indices. 141 | pub type ModMask = u32; 142 | 143 | /// Index of a keyboard LED. 144 | /// 145 | /// LEDs are logical objects which may be active or inactive. They 146 | /// typically correspond to the lights on the keyboard. Their state is 147 | /// determined by the current keyboard state. 148 | /// 149 | /// LED indices are non-consecutive. The first LED has index 0. 150 | /// 151 | /// Each LED must have a name, and the names are unique. Therefore, 152 | /// it is safe to use the name as a unique identifier for a LED. The names 153 | /// of some common LEDs are provided in the xkbcommon/xkbcommon-names.h 154 | /// header file. LED names are case-sensitive. 155 | /// 156 | /// # Warning 157 | /// 158 | /// A given keymap may specify an exact index for a given LED. 159 | /// Therefore, LED indexing is not necessarily sequential, as opposed to 160 | /// modifiers and layouts. This means that when iterating over the LEDs 161 | /// in a keymap using e.g. `xkb_keymap_num_leds()`, some indices might be 162 | /// invalid. Given such an index, functions like `xkb_keymap_led_get_name()` 163 | /// will return `NULL`, and `xkb_state_led_index_is_active()` will return -1. 164 | /// 165 | /// LEDs are also called "indicators" by XKB. 166 | pub type LedIndex = u32; 167 | /// A mask of LED indices. 168 | pub type LedMask = u32; 169 | 170 | pub const KEYCODE_INVALID: u32 = 0xffff_ffff; 171 | pub const LAYOUT_INVALID: u32 = 0xffff_ffff; 172 | pub const LEVEL_INVALID: u32 = 0xffff_ffff; 173 | pub const MOD_INVALID: u32 = 0xffff_ffff; 174 | pub const LED_INVALID: u32 = 0xffff_ffff; 175 | 176 | pub const KEYCODE_MAX: u32 = 0xffff_fffe; 177 | 178 | pub type KeysymFlags = u32; 179 | pub const KEYSYM_NO_FLAGS: u32 = 0; 180 | pub const KEYSYM_CASE_INSENSITIVE: u32 = 1 << 0; 181 | 182 | /// Flags for context creation. 183 | pub type ContextFlags = u32; 184 | /// Do not apply any context flags. 185 | pub const CONTEXT_NO_FLAGS: u32 = 0; 186 | /// Create this context with an empty include path. 187 | pub const CONTEXT_NO_DEFAULT_INCLUDES: u32 = 1 << 0; 188 | /// Don't take RMLVO names from the environment. 189 | pub const CONTEXT_NO_ENVIRONMENT_NAMES: u32 = 1 << 1; 190 | 191 | #[repr(C)] 192 | pub enum LogLevel { 193 | Critical = 10, 194 | Error = 20, 195 | Warning = 30, 196 | Info = 40, 197 | Debug = 50, 198 | } 199 | 200 | /// Flags for keymap compilation. 201 | pub type KeymapCompileFlags = u32; 202 | /// Do not apply any flags. 203 | pub const KEYMAP_COMPILE_NO_FLAGS: u32 = 0; 204 | 205 | /// The possible keymap formats. 206 | pub type KeymapFormat = u32; 207 | /// The current/classic XKB text format, as generated by xkbcomp -xkb. 208 | pub const KEYMAP_FORMAT_TEXT_V1: u32 = 1; 209 | /// Get the keymap as a string in the format from which it was created. 210 | pub const KEYMAP_FORMAT_USE_ORIGINAL: u32 = 0xffff_ffff; 211 | 212 | /// Specifies the direction of the key (press / release). 213 | #[repr(C)] 214 | pub enum KeyDirection { 215 | /// the key was released 216 | Up, 217 | /// the key was pressed 218 | Down, 219 | } 220 | 221 | /// Modifier and layout types for state objects. This enum is bitmaskable, 222 | /// e.g. `(xkb::STATE_MODS_DEPRESSED | xkb::STATE_MODS_LATCHED)` is valid to 223 | /// exclude locked modifiers. 224 | /// 225 | /// In XKB, the DEPRESSED components are also known as 'base'. 226 | pub type StateComponent = u32; 227 | /// Depressed modifiers, i.e. a key is physically holding them. 228 | pub const STATE_MODS_DEPRESSED: u32 = 1 << 0; 229 | /// Latched modifiers, i.e. will be unset after the next non-modifier 230 | /// key press. 231 | pub const STATE_MODS_LATCHED: u32 = 1 << 1; 232 | /// Locked modifiers, i.e. will be unset after the key provoking the 233 | /// lock has been pressed again. 234 | pub const STATE_MODS_LOCKED: u32 = 1 << 2; 235 | /// Effective modifiers, i.e. currently active and affect key 236 | /// processing (derived from the other state components). 237 | /// Use this unless you explictly care how the state came about. 238 | pub const STATE_MODS_EFFECTIVE: u32 = 1 << 3; 239 | /// Depressed layout, i.e. a key is physically holding it. 240 | pub const STATE_LAYOUT_DEPRESSED: u32 = 1 << 4; 241 | /// Latched layout, i.e. will be unset after the next non-modifier 242 | /// key press. 243 | pub const STATE_LAYOUT_LATCHED: u32 = 1 << 5; 244 | /// Locked layout, i.e. will be unset after the key provoking the lock 245 | /// has been pressed again. 246 | pub const STATE_LAYOUT_LOCKED: u32 = 1 << 6; 247 | /// Effective layout, i.e. currently active and affects key processing 248 | /// (derived from the other state components). 249 | /// Use this unless you explictly care how the state came about. 250 | pub const STATE_LAYOUT_EFFECTIVE: u32 = 1 << 7; 251 | /// LEDs (derived from the other state components). 252 | pub const STATE_LEDS: u32 = 1 << 8; 253 | 254 | /// Match flags for `xkb_state_mod_indices_are_active` and 255 | /// `xkb_state_mod_names_are_active`, specifying how the conditions for a 256 | /// successful match. `xkb::STATE_MATCH_NON_EXCLUSIVE` is bitmaskable with 257 | /// the other modes. 258 | pub type StateMatch = u32; 259 | ///Returns true if any of the modifiers are active. 260 | pub const STATE_MATCH_ANY: u32 = 1 << 0; 261 | ///Returns true if all of the modifiers are active. 262 | pub const STATE_MATCH_ALL: u32 = 1 << 1; 263 | /// Makes matching non-exclusive, i.e. will not return false if a 264 | /// modifier not specified in the arguments is active. 265 | pub const STATE_MATCH_NON_EXCLUSIVE: u32 = 1 << 16; 266 | 267 | // Real modifiers names 268 | pub const MOD_NAME_SHIFT: &str = "Shift"; 269 | pub const MOD_NAME_CAPS: &str = "Lock"; 270 | pub const MOD_NAME_CTRL: &str = "Control"; 271 | pub const MOD_NAME_MOD1: &str = "Mod1"; 272 | pub const MOD_NAME_MOD2: &str = "Mod2"; 273 | pub const MOD_NAME_MOD3: &str = "Mod3"; 274 | pub const MOD_NAME_MOD4: &str = "Mod4"; 275 | pub const MOD_NAME_MOD5: &str = "Mod5"; 276 | 277 | // Virtual modifiers names 278 | pub const VMOD_NAME_ALT: &str = "Alt"; 279 | pub const VMOD_NAME_HYPER: &str = "Hyper"; 280 | pub const VMOD_NAME_LEVEL3: &str = "LevelThree"; 281 | pub const VMOD_NAME_LEVEL5: &str = "LevelFive"; 282 | pub const VMOD_NAME_META: &str = "Meta"; 283 | pub const VMOD_NAME_NUM: &str = "NumLock"; 284 | pub const VMOD_NAME_SCROLL: &str = "ScrollLock"; 285 | pub const VMOD_NAME_SUPER: &str = "Super"; 286 | 287 | /// deprecated 288 | pub const MOD_NAME_ALT: &str = "Mod1"; 289 | /// deprecated 290 | pub const MOD_NAME_LOGO: &str = "Mod4"; 291 | /// deprecated 292 | pub const MOD_NAME_NUM: &str = "Mod2"; 293 | /// deprecated 294 | pub const MOD_NAME_ISO_LEVEL3_SHIFT: &str = "Mod5"; 295 | 296 | // LEDs names 297 | pub const LED_NAME_NUM: &str = "Num Lock"; 298 | pub const LED_NAME_CAPS: &str = "Caps Lock"; 299 | pub const LED_NAME_SCROLL: &str = "Scroll Lock"; 300 | pub const LED_NAME_COMPOSE: &str = "Compose"; 301 | pub const LED_NAME_KANA: &str = "Kana"; 302 | 303 | /// Test whether a value is a valid extended keycode. 304 | /// See `xkb_keycode_t`. 305 | #[must_use] 306 | pub fn keycode_is_legal_ext(key: u32) -> bool { 307 | key <= KEYCODE_MAX 308 | } 309 | 310 | /// Names to compile a keymap with, also known as RMLVO. 311 | /// 312 | /// The names are the common configuration values by which a user picks 313 | /// a keymap. 314 | /// 315 | /// If the entire struct is NULL, then each field is taken to be NULL. 316 | /// You should prefer passing NULL instead of choosing your own defaults. 317 | #[must_use] 318 | pub fn keycode_is_legal_x11(key: u32) -> bool { 319 | (8..=255).contains(&key) 320 | } 321 | 322 | /// Get the name of a keysym. 323 | #[must_use] 324 | pub fn keysym_get_name(keysym: Keysym) -> String { 325 | unsafe { 326 | const BUF_LEN: usize = 64; 327 | let buf: &mut [c_char] = &mut [0; BUF_LEN]; 328 | let ptr = &mut buf[0] as *mut c_char; 329 | let len = xkb_keysym_get_name(keysym.raw(), ptr, BUF_LEN); 330 | if len <= 0 { 331 | return String::new(); 332 | } 333 | let slice: &[u8] = slice::from_raw_parts(ptr as *const _, (len as usize).min(BUF_LEN)); 334 | String::from_utf8_unchecked(slice.to_owned()) 335 | } 336 | } 337 | 338 | /// Get a keysym from its name. 339 | /// 340 | /// name The name of a keysym. See remarks in `xkb_keysym_get_name()`; 341 | /// this function will accept any name returned by that function. 342 | /// flags A set of flags controlling how the search is done. If 343 | /// invalid flags are passed, this will fail with `xkb::KEY_NoSymbol`. 344 | /// 345 | /// If you use the `xkb::KEYSYM_CASE_INSENSITIVE` flag and two keysym names 346 | /// differ only by case, then the lower-case keysym is returned. For 347 | /// instance, for `KEY_a` and `KEY_A`, this function would return `KEY_a` for 348 | /// the case-insensitive search. If this functionality is needed, it is 349 | /// recommended to first call this function without this flag; and if that 350 | /// fails, only then to try with this flag, while possibly warning the user 351 | /// he had misspelled the name, and might get wrong results. 352 | /// 353 | /// Returns The keysym. If the name is invalid, returns `xkb::KEY_NoSymbol`. 354 | #[allow(clippy::missing_panics_doc)] 355 | #[must_use] 356 | pub fn keysym_from_name(name: &str, flags: KeysymFlags) -> Keysym { 357 | unsafe { 358 | let cname = CString::new(name.as_bytes().to_owned()).unwrap(); 359 | Keysym::new(xkb_keysym_from_name(cname.as_ptr(), flags)) 360 | } 361 | } 362 | 363 | /// Get the Unicode/UTF-8 representation of a keysym. 364 | /// 365 | /// Prefer not to use this function on keysyms obtained from an 366 | /// `xkb_state`. In this case, use `xkb_state_key_get_utf8()` instead. 367 | #[must_use] 368 | pub fn keysym_to_utf8(keysym: Keysym) -> String { 369 | unsafe { 370 | let buf: &mut [c_char] = &mut [0; 8]; 371 | let ptr = &mut buf[0] as *mut c_char; 372 | match xkb_keysym_to_utf8(keysym.raw(), ptr, 8) { 373 | 0 => String::from(""), 374 | -1 => { 375 | panic!("Key doesn't fit in buffer") 376 | } 377 | len => { 378 | let slice: &[u8] = slice::from_raw_parts(ptr as *const _, len as usize - 1); 379 | String::from_utf8_unchecked(slice.to_owned()) 380 | } 381 | } 382 | } 383 | } 384 | 385 | /// Get the Unicode/UTF-32 representation of a keysym. 386 | /// 387 | /// Returns The Unicode/UTF-32 representation of keysym, which is also 388 | /// compatible with UCS-4. If the keysym does not have a Unicode 389 | /// representation, returns 0. 390 | /// 391 | /// Prefer not to use this function on keysyms obtained from an 392 | /// `xkb_state`. In this case, `use xkb_state_key_get_utf32()` instead. 393 | #[must_use] 394 | pub fn keysym_to_utf32(keysym: Keysym) -> u32 { 395 | unsafe { xkb_keysym_to_utf32(keysym.raw()) } 396 | } 397 | 398 | /// Get the keysym corresponding to a Unicode/UTF-32 codepoint. 399 | /// 400 | /// Returns the keysym corresponding to the specified Unicode codepoint, 401 | /// or `KEY_NoSymbol` if there is none. 402 | /// 403 | /// This function is the inverse of `keysym_to_utf32`. In cases where a 404 | /// single codepoint corresponds to multiple keysyms, returns the keysym 405 | /// with the lowest value. 406 | /// 407 | /// Unicode codepoints which do not have a special (legacy) keysym 408 | /// encoding use a direct encoding scheme. These keysyms don't usually 409 | /// have an associated keysym constant (`XKB_KEY_*`). 410 | /// 411 | /// For noncharacter Unicode codepoints and codepoints outside of the 412 | /// defined Unicode planes this function returns `KEY_NoSymbol`. 413 | #[must_use] 414 | pub fn utf32_to_keysym(ucs: u32) -> Keysym { 415 | unsafe { xkb_utf32_to_keysym(ucs) }.into() 416 | } 417 | 418 | /// Top level library context object. 419 | /// 420 | /// The context contains various general library data and state, like 421 | /// logging level and include paths. 422 | /// 423 | /// Objects are created in a specific context, and multiple contexts may 424 | /// coexist simultaneously. Objects from different contexts are completely 425 | /// separated and do not share any memory or state. 426 | pub struct Context { 427 | ptr: *mut xkb_context, 428 | } 429 | 430 | impl Context { 431 | /// contruct a context from a raw ffi pointer. This context must already been 432 | /// referenced as `xkb_context_unref` will be called at drop time 433 | #[allow(clippy::missing_safety_doc)] 434 | pub unsafe fn from_raw_ptr(ptr: *mut xkb_context) -> Context { 435 | Context { ptr } 436 | } 437 | 438 | /// get the raw pointer from this context 439 | #[must_use] 440 | pub fn get_raw_ptr(&self) -> *mut xkb_context { 441 | self.ptr 442 | } 443 | 444 | /// Create a new context. 445 | /// 446 | /// flags Optional flags for the context, or 0. 447 | /// 448 | /// The user may set some environment variables to affect default values in 449 | /// the context. 450 | #[must_use] 451 | pub fn new(flags: ContextFlags) -> Context { 452 | unsafe { 453 | Context { 454 | ptr: xkb_context_new(flags), 455 | } 456 | } 457 | } 458 | 459 | /// append a new entry to the context's include path 460 | /// returns true on success, or false if the include path could not be added 461 | /// or is inaccessible 462 | pub fn include_path_append(&mut self, path: &Path) -> bool { 463 | path.to_str().map_or(false, |s| unsafe { 464 | let cstr = CString::from_vec_unchecked(s.as_bytes().to_owned()); 465 | xkb_context_include_path_append(self.ptr, cstr.as_ptr()) == 1 466 | }) 467 | } 468 | 469 | /// Append the default include paths to the context's include path. 470 | /// 471 | /// Returns true on success. 472 | pub fn include_path_append_default(&mut self) -> bool { 473 | unsafe { xkb_context_include_path_append_default(self.ptr) == 1 } 474 | } 475 | 476 | /// Reset the context's include path to the default. 477 | /// 478 | /// Removes all entries from the context's include path, and inserts the 479 | /// default paths. 480 | /// 481 | /// Returns true on success.yy 482 | pub fn include_path_reset_defaults(&mut self) -> bool { 483 | unsafe { xkb_context_include_path_reset_defaults(self.ptr) == 1 } 484 | } 485 | 486 | /// Remove all entries from the context's include path. 487 | pub fn include_path_clear(&mut self) { 488 | unsafe { 489 | xkb_context_include_path_clear(self.ptr); 490 | } 491 | } 492 | 493 | /// get an iterator on the include paths of this context 494 | #[must_use] 495 | pub fn include_paths(&self) -> ContextIncludePaths { 496 | unsafe { 497 | ContextIncludePaths { 498 | context: self, 499 | ind: 0, 500 | len: xkb_context_num_include_paths(self.ptr), 501 | } 502 | } 503 | } 504 | 505 | /// Set the current logging level. 506 | /// 507 | /// The default level is `xkb::LogLevel::Error`. The environment variable 508 | /// `XKB_LOG_LEVEL`, if set in the time the context was created, overrides the 509 | /// default value. It may be specified as a level number or name. 510 | pub fn set_log_level(&mut self, level: LogLevel) { 511 | unsafe { 512 | xkb_context_set_log_level(self.ptr, mem::transmute(level)); 513 | } 514 | } 515 | 516 | #[must_use] 517 | pub fn get_log_level(&self) -> LogLevel { 518 | unsafe { mem::transmute(xkb_context_get_log_level(self.ptr)) } 519 | } 520 | 521 | /// Sets the current logging verbosity. 522 | /// 523 | /// The library can generate a number of warnings which are not helpful to 524 | /// ordinary users of the library. The verbosity may be increased if more 525 | /// information is desired (e.g. when developing a new keymap). 526 | /// 527 | /// The default verbosity is 0. The environment variable `XKB_LOG_VERBOSITY`, 528 | /// if set in the time the context was created, overrides the default value. 529 | /// 530 | /// verbosity can be set from 1 to 10, higher values being more verbose. 531 | /// 0 would result in no verbose messages being logged. 532 | /// 533 | /// Most verbose messages are of level `xkb::LogLevel::Warning` or lower. 534 | pub fn set_log_verbosity(&mut self, verbosity: i32) { 535 | unsafe { 536 | xkb_context_set_log_verbosity(self.ptr, verbosity as c_int); 537 | } 538 | } 539 | 540 | #[must_use] 541 | pub fn get_log_verbosity(&self) -> i32 { 542 | unsafe { xkb_context_get_log_verbosity(self.ptr) as i32 } 543 | } 544 | } 545 | 546 | impl Clone for Context { 547 | fn clone(&self) -> Context { 548 | unsafe { 549 | Context { 550 | ptr: xkb_context_ref(self.ptr), 551 | } 552 | } 553 | } 554 | } 555 | 556 | impl Drop for Context { 557 | fn drop(&mut self) { 558 | unsafe { 559 | xkb_context_unref(self.ptr); 560 | } 561 | } 562 | } 563 | 564 | /// Iterator to a Context include paths 565 | pub struct ContextIncludePaths<'a> { 566 | context: &'a Context, 567 | ind: c_uint, 568 | len: c_uint, 569 | } 570 | 571 | impl<'a> Iterator for ContextIncludePaths<'a> { 572 | type Item = &'a Path; 573 | fn next(&mut self) -> Option<&'a Path> { 574 | if self.ind == self.len { 575 | None 576 | } else { 577 | unsafe { 578 | let ptr = xkb_context_include_path_get(self.context.ptr, self.ind); 579 | self.ind += 1; 580 | let cstr = CStr::from_ptr(ptr); 581 | Some(Path::new(str::from_utf8_unchecked(cstr.to_bytes()))) 582 | } 583 | } 584 | } 585 | } 586 | 587 | #[test] 588 | fn check_include_paths() { 589 | let mut c = Context::new(CONTEXT_NO_DEFAULT_INCLUDES); 590 | let test_path = Path::new("/"); 591 | assert_eq!(true, c.include_path_append(&test_path)); 592 | assert_eq!(test_path, c.include_paths().nth(0).unwrap()); 593 | } 594 | 595 | /// Compiled keymap object. 596 | /// 597 | /// The keymap object holds all of the static keyboard information obtained 598 | /// from compiling XKB files. 599 | /// 600 | /// A keymap is immutable after it is created (besides reference counts, etc.); 601 | /// if you need to change it, you must create a new one. 602 | pub struct Keymap { 603 | ptr: *mut xkb_keymap, 604 | } 605 | 606 | impl Keymap { 607 | #[allow(clippy::missing_safety_doc)] 608 | pub unsafe fn from_raw_ptr(ptr: *mut xkb_keymap) -> Keymap { 609 | Keymap { ptr } 610 | } 611 | 612 | #[must_use] 613 | pub fn get_raw_ptr(&self) -> *mut xkb_keymap { 614 | self.ptr 615 | } 616 | 617 | /// Create a keymap from RMLVO names. 618 | /// 619 | /// The primary keymap entry point: creates a new XKB keymap from a set of 620 | /// RMLVO (Rules + Model + Layouts + Variants + Options) names. 621 | /// 622 | /// __context__ 623 | /// The context in which to create the keymap. 624 | /// 625 | /// __rules__ 626 | /// The rules file to use. The rules file describes how to interpret 627 | /// the values of the model, layout, variant and options fields. 628 | /// 629 | /// If empty string "", a default value is used. 630 | /// If the `XKB_DEFAULT_RULES` environment variable is set, it is used 631 | /// as the default. Otherwise the system default is used. 632 | /// 633 | /// __model__ 634 | /// The keyboard model by which to interpret keycodes and LEDs. 635 | /// 636 | /// If empty string "", a default value is used. 637 | /// If the `XKB_DEFAULT_MODEL` environment variable is set, it is used 638 | /// as the default. Otherwise the system default is used. 639 | /// 640 | /// __layout__ 641 | /// A comma separated list of layouts (languages) to include in the 642 | /// keymap. 643 | /// 644 | /// If empty string "", a default value is used. 645 | /// If the `XKB_DEFAULT_LAYOUT` environment variable is set, it is used 646 | /// as the default. Otherwise the system default is used. 647 | /// 648 | /// __variant__ 649 | /// A comma separated list of variants, one per layout, which may 650 | /// modify or augment the respective layout in various ways. 651 | /// 652 | /// If empty string "", and a default value is also used 653 | /// for the layout, a default value is used. Otherwise no variant is 654 | /// used. 655 | /// If the `XKB_DEFAULT_VARIANT` environment variable is set, it is used 656 | /// as the default. Otherwise the system default is used. 657 | /// 658 | /// __options__ 659 | /// A comma separated list of options, through which the user specifies 660 | /// non-layout related preferences, like which key combinations are used 661 | /// for switching layouts, or which key is the Compose key. 662 | /// 663 | /// If `None`, a default value is used. If `Some("")` (empty string), no 664 | /// options are used. 665 | /// If the `XKB_DEFAULT_OPTIONS` environment variable is set, it is used 666 | /// as the default. Otherwise the system default is used. 667 | /// 668 | /// __flags__ 669 | /// Optional flags for the keymap, or 0. 670 | /// 671 | /// Returns a keymap compiled according to the `RMLVO` names, or `None` if 672 | /// the compilation failed. 673 | #[allow(clippy::missing_panics_doc)] 674 | pub fn new_from_names + ?Sized>( 675 | context: &Context, 676 | rules: &S, 677 | model: &S, 678 | layout: &S, 679 | variant: &S, 680 | mut options: Option, 681 | flags: KeymapCompileFlags, 682 | ) -> Option { 683 | let crules = CString::new(rules.borrow().as_bytes()).unwrap(); 684 | let cmodel = CString::new(model.borrow().as_bytes()).unwrap(); 685 | let clayout = CString::new(layout.borrow().as_bytes()).unwrap(); 686 | let cvariant = CString::new(variant.borrow().as_bytes()).unwrap(); 687 | let poptions = match &mut options { 688 | None => null(), 689 | Some(s) => { 690 | s.push('\0'); 691 | s.as_ptr().cast() 692 | } 693 | }; 694 | let rule_names = xkb_rule_names { 695 | rules: crules.as_ptr(), 696 | model: cmodel.as_ptr(), 697 | layout: clayout.as_ptr(), 698 | variant: cvariant.as_ptr(), 699 | options: poptions, 700 | }; 701 | unsafe { 702 | let pkeymap = xkb_keymap_new_from_names(context.ptr, &rule_names, flags); 703 | if pkeymap.is_null() { 704 | None 705 | } else { 706 | Some(Keymap { ptr: pkeymap }) 707 | } 708 | } 709 | } 710 | 711 | /// Create a keymap from a keymap file. 712 | /// 713 | /// Returns `None` if compilation fails. 714 | /// 715 | /// The file must contain a complete keymap. For example, in the 716 | /// `XKB_KEYMAP_FORMAT_TEXT_V1` format, this means the file must contain one 717 | /// top level `%xkb_keymap` section, which in turn contains other required 718 | /// sections. 719 | /// 720 | /// bindings implementation get the content in a `String` 721 | /// and call `new_from_string()`. 722 | pub fn new_from_file( 723 | context: &Context, 724 | file: &mut fs::File, 725 | format: KeymapFormat, 726 | flags: KeymapCompileFlags, 727 | ) -> Option { 728 | let mut string = String::new(); 729 | file.read_to_string(&mut string) 730 | .ok() 731 | .and_then(|_| Keymap::new_from_string(context, string, format, flags)) 732 | } 733 | 734 | /// Create a keymap from a keymap string. 735 | /// 736 | /// This is just like `xkb_keymap_new_from_file()`, but instead of a file, gets 737 | /// the keymap as one enormous string. 738 | #[allow(clippy::missing_panics_doc)] 739 | #[must_use] 740 | pub fn new_from_string( 741 | context: &Context, 742 | string: String, 743 | format: KeymapFormat, 744 | flags: KeymapCompileFlags, 745 | ) -> Option { 746 | unsafe { 747 | let buffer = string.as_ptr() as *const c_char; 748 | let length = string.len(); 749 | let ptr = xkb_keymap_new_from_buffer(context.ptr, buffer, length, format, flags); 750 | if ptr.is_null() { 751 | None 752 | } else { 753 | Some(Keymap { ptr }) 754 | } 755 | } 756 | } 757 | 758 | #[cfg(feature = "wayland")] 759 | /// Create a keymap from a file descriptor. 760 | /// The file is mapped to memory and the keymap is created from the mapped memory buffer. 761 | /// 762 | /// # Safety 763 | /// The file descriptor must be valid and all safety concerns of mapping files to memory 764 | /// apply here. 765 | #[allow(clippy::missing_panics_doc)] 766 | pub unsafe fn new_from_fd( 767 | context: &Context, 768 | fd: OwnedFd, 769 | size: usize, 770 | format: KeymapFormat, 771 | flags: KeymapCompileFlags, 772 | ) -> std::io::Result> { 773 | let map = MmapOptions::new() 774 | .len(size as usize) 775 | // Starting in version 7 of the wl_keyboard protocol, the keymap must be mapped using MAP_PRIVATE. 776 | .map_copy_read_only(&fs::File::from(fd))?; 777 | let ptr = xkb_keymap_new_from_buffer(context.ptr, map.as_ptr().cast(), size, format, flags); 778 | if ptr.is_null() { 779 | Ok(None) 780 | } else { 781 | Ok(Some(Keymap { ptr })) 782 | } 783 | } 784 | 785 | /// Get the compiled keymap as a string. 786 | /// 787 | /// keymap The keymap to get as a string. 788 | /// format The keymap format to use for the string. You can pass 789 | /// in the special value `xkb::KEYMAP_USE_ORIGINAL_FORMAT` to use the format 790 | /// from which the keymap was originally created. 791 | /// 792 | /// Returns The keymap as a NUL-terminated string, or `NULL` if unsuccessful. 793 | /// 794 | /// The returned string may be fed back into `xkb_map_new_from_string()` to get 795 | /// the exact same keymap (possibly in another process, etc). 796 | /// 797 | /// The returned string is dynamically allocated and should be freed by the 798 | /// caller. 799 | #[must_use] 800 | pub fn get_as_string(&self, format: KeymapFormat) -> String { 801 | unsafe { 802 | let ffistr = xkb_keymap_get_as_string(self.ptr, format); 803 | let cstr = CStr::from_ptr(ffistr); 804 | let res = String::from_utf8_unchecked(cstr.to_bytes().to_owned()); 805 | libc::free(ffistr.cast()); 806 | res 807 | } 808 | } 809 | 810 | /// Get the minimum keycode in the keymap. 811 | #[must_use] 812 | pub fn min_keycode(&self) -> Keycode { 813 | Keycode::new(unsafe { xkb_keymap_min_keycode(self.ptr) }) 814 | } 815 | 816 | /// Get the maximum keycode in the keymap. 817 | #[must_use] 818 | pub fn max_keycode(&self) -> Keycode { 819 | Keycode::new(unsafe { xkb_keymap_max_keycode(self.ptr) }) 820 | } 821 | 822 | #[allow(unused_variables)] 823 | unsafe extern "C" fn callback( 824 | pkeymap: *mut ffi::xkb_keymap, 825 | key: ffi::xkb_keycode_t, 826 | data: *mut raw::c_void, 827 | ) where 828 | F: FnMut(&Keymap, Keycode), 829 | { 830 | let mut data_box: Box<(&Keymap, F)> = mem::transmute(Box::from_raw(data)); 831 | { 832 | let (keymap, ref mut closure) = *data_box; 833 | closure(keymap, key.into()); 834 | } 835 | let _ = Box::into_raw(data_box); 836 | } 837 | 838 | /// Run a specified closure for every valid keycode in the keymap. 839 | pub fn key_for_each(&self, closure: F) 840 | where 841 | F: FnMut(&Keymap, Keycode), 842 | { 843 | let data_box = Box::new((self, closure)); 844 | let data_ptr = Box::into_raw(data_box).cast(); 845 | 846 | unsafe { 847 | ffi::xkb_keymap_key_for_each(self.get_raw_ptr(), Self::callback::, data_ptr); 848 | mem::drop(Box::from_raw(data_ptr.cast::<(&Keymap, F)>())); 849 | } 850 | } 851 | 852 | /// Get an iterator to the modifiers of this keymap 853 | #[must_use] 854 | pub fn mods(&self) -> KeymapMods { 855 | unsafe { 856 | KeymapMods { 857 | keymap: self, 858 | ind: 0, 859 | len: xkb_keymap_num_mods(self.ptr), 860 | } 861 | } 862 | } 863 | 864 | /// Get the number of modifiers in the keymap. 865 | #[must_use] 866 | pub fn num_mods(&self) -> ModIndex { 867 | unsafe { xkb_keymap_num_mods(self.ptr) } 868 | } 869 | 870 | /// Get the name of a modifier by index. 871 | /// 872 | /// Returns The name. If the index is invalid, returns "". 873 | #[must_use] 874 | pub fn mod_get_name(&self, idx: ModIndex) -> &str { 875 | unsafe { 876 | let ptr = xkb_keymap_mod_get_name(self.ptr, idx); 877 | if ptr.is_null() { 878 | "" 879 | } else { 880 | let cstr = CStr::from_ptr(ptr); 881 | str::from_utf8_unchecked(cstr.to_bytes()) 882 | } 883 | } 884 | } 885 | 886 | /// Get the index of a modifier by name. 887 | /// 888 | /// Returns The index. If no modifier with this name exists, returns 889 | /// `xkb::MOD_INVALID`. 890 | #[allow(clippy::missing_panics_doc)] 891 | pub fn mod_get_index + ?Sized>(&self, name: &S) -> ModIndex { 892 | unsafe { 893 | let cstr = CString::new(name.borrow().as_bytes()).unwrap(); 894 | xkb_keymap_mod_get_index(self.ptr, cstr.as_ptr()) 895 | } 896 | } 897 | 898 | /// Returns an iterator to the layouts in this keymap 899 | #[must_use] 900 | pub fn layouts(&self) -> KeymapLayouts { 901 | unsafe { 902 | KeymapLayouts { 903 | keymap: self, 904 | ind: 0, 905 | len: xkb_keymap_num_layouts(self.ptr), 906 | } 907 | } 908 | } 909 | 910 | /// Get the number of layouts in the keymap. 911 | #[must_use] 912 | pub fn num_layouts(&self) -> LayoutIndex { 913 | unsafe { xkb_keymap_num_layouts(self.ptr) } 914 | } 915 | 916 | /// Get the name of a layout by index. 917 | /// 918 | /// Returns The name. If the index is invalid, or the layout does not have 919 | /// a name, returns "". 920 | #[must_use] 921 | pub fn layout_get_name(&self, idx: LayoutIndex) -> &str { 922 | unsafe { 923 | let ptr = xkb_keymap_layout_get_name(self.ptr, idx); 924 | if ptr.is_null() { 925 | "" 926 | } else { 927 | let cstr = CStr::from_ptr(ptr); 928 | str::from_utf8_unchecked(cstr.to_bytes()) 929 | } 930 | } 931 | } 932 | 933 | /// Find the name of the key with the given keycode. 934 | /// This function always returns the canonical name of the key (see description in [Keycode]). 935 | pub fn key_get_name(&self, key: Keycode) -> Option<&str> { 936 | unsafe { 937 | let ptr = xkb_keymap_key_get_name(self.ptr, key.into()); 938 | if ptr.is_null() { 939 | None 940 | } else { 941 | let cstr = CStr::from_ptr(ptr); 942 | Some(str::from_utf8_unchecked(cstr.to_bytes())) 943 | } 944 | } 945 | } 946 | 947 | /// Find the keycode of the key with the given name. 948 | /// The name can be either a canonical name or an alias. 949 | pub fn key_by_name + ?Sized>(&self, name: &S) -> Option { 950 | unsafe { 951 | let cstr = CString::new(name.borrow().as_bytes()).unwrap(); 952 | let code = xkb_keymap_key_by_name(self.ptr, cstr.as_ptr()); 953 | if code == XKB_KEYCODE_INVALID { 954 | None 955 | } else { 956 | Some(Keycode::new(code)) 957 | } 958 | } 959 | } 960 | 961 | /// Get the index of a layout by name. 962 | /// 963 | /// Returns The index. If no layout exists with this name, returns 964 | /// `xkb::LAYOUT_INVALID`. If more than one layout in the keymap has this name, 965 | /// returns the lowest index among them. 966 | #[allow(clippy::missing_panics_doc)] 967 | pub fn layout_get_index + ?Sized>(&self, name: &S) -> LayoutIndex { 968 | unsafe { 969 | let cstr = CString::new(name.borrow().as_bytes()).unwrap(); 970 | xkb_keymap_layout_get_index(self.ptr, cstr.as_ptr()) 971 | } 972 | } 973 | 974 | /// Returns an iterator to the leds in this keymap 975 | #[must_use] 976 | pub fn leds(&self) -> KeymapLeds { 977 | unsafe { 978 | KeymapLeds { 979 | keymap: self, 980 | ind: 0, 981 | len: xkb_keymap_num_leds(self.ptr), 982 | } 983 | } 984 | } 985 | 986 | /// Get the number of LEDs in the keymap. 987 | /// 988 | /// # warning 989 | /// The range `[0..num_leds())` includes all of the LEDs 990 | /// in the keymap, but may also contain inactive LEDs. When iterating over 991 | /// this range, you need the handle this case when calling functions such as 992 | /// `led_get_name()` or `led_index_is_active()`. 993 | #[must_use] 994 | pub fn num_leds(&self) -> LedIndex { 995 | unsafe { xkb_keymap_num_leds(self.ptr) } 996 | } 997 | 998 | /// Get the name of a LED by index. 999 | /// 1000 | /// Returns the name. If the index is invalid, returns `""`. 1001 | #[must_use] 1002 | pub fn led_get_name(&self, idx: LedIndex) -> &str { 1003 | unsafe { 1004 | let ptr = xkb_keymap_led_get_name(self.ptr, idx); 1005 | if ptr.is_null() { 1006 | "" 1007 | } else { 1008 | let cstr = CStr::from_ptr(ptr); 1009 | str::from_utf8_unchecked(cstr.to_bytes()) 1010 | } 1011 | } 1012 | } 1013 | 1014 | /// Get the index of a LED by name. 1015 | /// 1016 | /// Returns The index. If no LED with this name exists, returns 1017 | /// `xkb::LED_INVALID`. 1018 | #[allow(clippy::missing_panics_doc)] 1019 | pub fn led_get_index + ?Sized>(&self, name: &S) -> LedIndex { 1020 | unsafe { 1021 | let cstr = CString::new(name.borrow().as_bytes()).unwrap(); 1022 | xkb_keymap_led_get_index(self.ptr, cstr.as_ptr()) 1023 | } 1024 | } 1025 | 1026 | /// Get the number of layouts for a specific key. 1027 | /// 1028 | /// This number can be different `from num_layouts()`, but is always 1029 | /// smaller. It is the appropriate value to use when iterating over the 1030 | /// layouts of a key. 1031 | #[must_use] 1032 | pub fn num_layouts_for_key(&self, key: Keycode) -> LayoutIndex { 1033 | unsafe { xkb_keymap_num_layouts_for_key(self.ptr, key.raw()) } 1034 | } 1035 | 1036 | /// Get the number of shift levels for a specific key and layout. 1037 | /// 1038 | /// If layout is out of range for this key (that is, larger or equal to 1039 | /// the value returned by `num_layouts_for_key()`), it is brought 1040 | /// back into range in a manner consistent with `State::key_get_layout()`. 1041 | #[must_use] 1042 | pub fn num_levels_for_key(&self, key: Keycode, layout: LayoutIndex) -> LevelIndex { 1043 | unsafe { xkb_keymap_num_levels_for_key(self.ptr, key.into(), layout) } 1044 | } 1045 | 1046 | /// Get the keysyms obtained from pressing a key in a given layout and 1047 | /// shift level. 1048 | /// 1049 | /// This function is like `xkb_state_key_get_syms()`, only the layout and 1050 | /// shift level are not derived from the keyboard state but are instead 1051 | /// specified explicitly. 1052 | /// 1053 | /// If layout is out of range for this key (that is, larger or equal to 1054 | /// the value returned by `num_layouts_for_key()`), it is brought 1055 | /// back into range in a manner consistent with `State::key_get_layout()`. 1056 | #[must_use] 1057 | pub fn key_get_syms_by_level( 1058 | &self, 1059 | key: Keycode, 1060 | layout: LayoutIndex, 1061 | level: LevelIndex, 1062 | ) -> &[Keysym] { 1063 | unsafe { 1064 | let mut syms_out: *const Keysym = null_mut(); 1065 | let len = xkb_keymap_key_get_syms_by_level( 1066 | self.ptr, 1067 | key.raw(), 1068 | layout, 1069 | level, 1070 | &mut syms_out as *mut *const Keysym as *mut *const xkeysym::RawKeysym, 1071 | ); 1072 | if syms_out.is_null() { 1073 | &[] 1074 | } else { 1075 | slice::from_raw_parts(syms_out, len as usize) 1076 | } 1077 | } 1078 | } 1079 | 1080 | /// Determine whether a key should repeat or not. 1081 | /// 1082 | /// A keymap may specify different repeat behaviors for different keys. 1083 | /// Most keys should generally exhibit repeat behavior; for example, holding 1084 | /// the 'a' key down in a text editor should normally insert a single 'a' 1085 | /// character every few milliseconds, until the key is released. However, 1086 | /// there are keys which should not or do not need to be repeated. For 1087 | /// example, repeating modifier keys such as Left/Right Shift or Caps Lock 1088 | /// is not generally useful or desired. 1089 | #[must_use] 1090 | pub fn key_repeats(&self, key: Keycode) -> bool { 1091 | unsafe { xkb_keymap_key_repeats(self.ptr, key.into()) != 0 } 1092 | } 1093 | 1094 | /// Retrieves every possible modifier mask that produces the specified shift level for 1095 | /// a specific key and layout. 1096 | /// 1097 | /// This API is useful for inverse key transformation; i.e. finding out which modifiers 1098 | /// need to be active in order to be able to type the keysym(s) corresponding to the 1099 | /// specific key code, layout and level. 1100 | /// 1101 | /// **Warning:** If the buffer passed is too small, some of the possible modifier combinations 1102 | /// will not be returned. 1103 | /// 1104 | /// # Example 1105 | /// 1106 | /// ``` 1107 | /// use xkbcommon::xkb; 1108 | /// 1109 | /// let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); 1110 | /// let keymap = xkb::Keymap::new_from_names( 1111 | /// &context, 1112 | /// "", 1113 | /// "", 1114 | /// "us", 1115 | /// "", 1116 | /// None, 1117 | /// xkb::COMPILE_NO_FLAGS 1118 | /// ).unwrap(); 1119 | /// 1120 | /// /// Evdev keycode, from `input-event-codes.h` 1121 | /// const KEY_A: u32 = 30; 1122 | /// 1123 | /// let keycode = xkb::Keycode::new(KEY_A + 8); 1124 | /// 1125 | /// // Index of the specific layout in the keymap. 1126 | /// // For a single layout, like in this example, it can safely be hardcoded to 0. 1127 | /// let layout_index: xkb::LayoutIndex = 0; 1128 | /// 1129 | /// // The level index at which a given keysym is output for a keycode. 1130 | /// // A level index is mapped to modifier masks using the below function. 1131 | /// let level_index: xkb::LevelIndex = 0; 1132 | /// 1133 | /// let mut masks = [xkb::ModMask::default(); 100]; 1134 | /// let num_masks = keymap.key_get_mods_for_level( 1135 | /// keycode, 1136 | /// layout_index, 1137 | /// level_index, 1138 | /// &mut masks 1139 | /// ); 1140 | /// 1141 | /// let masks = &masks[0..num_masks]; 1142 | /// 1143 | /// // By convention, the lowest level corresponds to no modifiers being active. 1144 | /// assert!(masks.iter().any(|mask| *mask == 0)); 1145 | /// ``` 1146 | #[must_use] 1147 | pub fn key_get_mods_for_level( 1148 | &self, 1149 | key: Keycode, 1150 | layout: LayoutIndex, 1151 | level: LevelIndex, 1152 | masks_out: &mut [ModMask], 1153 | ) -> usize { 1154 | unsafe { 1155 | xkb_keymap_key_get_mods_for_level( 1156 | self.ptr, 1157 | key.raw(), 1158 | layout, 1159 | level, 1160 | masks_out.as_mut_ptr(), 1161 | masks_out.len(), 1162 | ) 1163 | } 1164 | } 1165 | } 1166 | 1167 | impl Clone for Keymap { 1168 | fn clone(&self) -> Keymap { 1169 | unsafe { 1170 | Keymap { 1171 | ptr: xkb_keymap_ref(self.ptr), 1172 | } 1173 | } 1174 | } 1175 | } 1176 | 1177 | impl Drop for Keymap { 1178 | fn drop(&mut self) { 1179 | unsafe { 1180 | xkb_keymap_unref(self.ptr); 1181 | } 1182 | } 1183 | } 1184 | 1185 | /// iterator to the modifiers in a Keymap 1186 | pub struct KeymapMods<'a> { 1187 | keymap: &'a Keymap, 1188 | ind: ModIndex, 1189 | len: ModIndex, 1190 | } 1191 | 1192 | impl<'a> Iterator for KeymapMods<'a> { 1193 | type Item = &'a str; 1194 | fn next(&mut self) -> Option<&'a str> { 1195 | if self.ind == self.len { 1196 | None 1197 | } else { 1198 | unsafe { 1199 | let ptr = xkb_keymap_mod_get_name(self.keymap.ptr, self.ind); 1200 | self.ind += 1; 1201 | let cstr = CStr::from_ptr(ptr); 1202 | Some(str::from_utf8_unchecked(cstr.to_bytes())) 1203 | } 1204 | } 1205 | } 1206 | } 1207 | 1208 | /// iterator to the layouts in Keymap 1209 | pub struct KeymapLayouts<'a> { 1210 | keymap: &'a Keymap, 1211 | ind: LayoutIndex, 1212 | len: LayoutIndex, 1213 | } 1214 | 1215 | impl<'a> Iterator for KeymapLayouts<'a> { 1216 | type Item = &'a str; 1217 | fn next(&mut self) -> Option<&'a str> { 1218 | if self.ind == self.len { 1219 | None 1220 | } else { 1221 | unsafe { 1222 | let ptr = xkb_keymap_layout_get_name(self.keymap.ptr, self.ind); 1223 | self.ind += 1; 1224 | let cstr = CStr::from_ptr(ptr); 1225 | Some(str::from_utf8_unchecked(cstr.to_bytes())) 1226 | } 1227 | } 1228 | } 1229 | } 1230 | 1231 | /// iterator to the leds in a Keymap 1232 | pub struct KeymapLeds<'a> { 1233 | keymap: &'a Keymap, 1234 | ind: LedIndex, 1235 | len: LedIndex, 1236 | } 1237 | 1238 | impl<'a> Iterator for KeymapLeds<'a> { 1239 | type Item = &'a str; 1240 | fn next(&mut self) -> Option<&'a str> { 1241 | if self.ind == self.len { 1242 | None 1243 | } else { 1244 | unsafe { 1245 | let ptr = xkb_keymap_led_get_name(self.keymap.ptr, self.ind); 1246 | self.ind += 1; 1247 | let cstr = CStr::from_ptr(ptr); 1248 | Some(str::from_utf8_unchecked(cstr.to_bytes())) 1249 | } 1250 | } 1251 | } 1252 | } 1253 | 1254 | /// Keyboard state object. 1255 | /// 1256 | /// State objects contain the active state of a keyboard (or keyboards), such 1257 | /// as the currently effective layout and the active modifiers. It acts as a 1258 | /// simple state machine, wherein key presses and releases are the input, and 1259 | /// key symbols (keysyms) are the output. 1260 | pub struct State { 1261 | ptr: *mut xkb_state, 1262 | } 1263 | 1264 | impl State { 1265 | #[allow(clippy::missing_safety_doc)] 1266 | pub unsafe fn from_raw_ptr(ptr: *mut xkb_state) -> State { 1267 | State { ptr } 1268 | } 1269 | 1270 | #[must_use] 1271 | pub fn get_raw_ptr(&self) -> *mut xkb_state { 1272 | self.ptr 1273 | } 1274 | 1275 | /// Create a new keyboard state object from a keymap. 1276 | #[must_use] 1277 | pub fn new(keymap: &Keymap) -> State { 1278 | unsafe { 1279 | State { 1280 | ptr: xkb_state_new(keymap.ptr), 1281 | } 1282 | } 1283 | } 1284 | 1285 | /// Get the keymap which a keyboard state object is using. 1286 | /// 1287 | /// Returns the keymap which was passed to `xkb_state_new()` when creating 1288 | /// this state object. 1289 | /// 1290 | /// This keymap can safely be used beyond the lifetime of this state 1291 | #[must_use] 1292 | pub fn get_keymap(&self) -> Keymap { 1293 | unsafe { 1294 | let keymap = xkb_state_get_keymap(self.ptr); 1295 | xkb_keymap_ref(keymap); 1296 | Keymap::from_raw_ptr(keymap) 1297 | } 1298 | } 1299 | 1300 | /// Update the keyboard state to reflect a given key being pressed or 1301 | /// released. 1302 | /// 1303 | /// This entry point is intended for programs which track the keyboard state 1304 | /// explictly (like an evdev client). If the state is serialized to you by 1305 | /// a master process (like a Wayland compositor) using functions like 1306 | /// `xkb_state_serialize_mods()`, you should use `xkb_state_update_mask()` 1307 | /// instead. The two functins should not generally be used together. 1308 | /// 1309 | /// A series of calls to this function should be consistent; that is, a call 1310 | /// with `xkb::KEY_DOWN` for a key should be matched by an `xkb::KEY_UP`; if 1311 | /// a key is pressed twice, it should be released twice; etc. Otherwise (e.g. 1312 | /// due to missed input events), situations like "stuck modifiers" may occur. 1313 | /// 1314 | /// This function is often used in conjunction with the function 1315 | /// `xkb_state_key_get_syms()` (or `xkb_state_key_get_one_sym()`), for 1316 | /// example, when handling a key event. In this case, you should prefer to 1317 | /// get the keysyms *before* updating the key, such that the keysyms reported 1318 | /// for the key event are not affected by the event itself. This is the 1319 | /// conventional behavior. 1320 | /// 1321 | /// Returns A mask of state components that have changed as a result of 1322 | /// the update. If nothing in the state has changed, returns 0. 1323 | pub fn update_key(&mut self, key: Keycode, direction: KeyDirection) -> StateComponent { 1324 | unsafe { xkb_state_update_key(self.ptr, key.into(), mem::transmute(direction)) } 1325 | } 1326 | 1327 | /// Update a keyboard state from a set of explicit masks. 1328 | /// 1329 | /// This entry point is intended for window systems and the like, where a 1330 | /// master process holds an `xkb_state`, then serializes it over a wire 1331 | /// protocol, and clients then use the serialization to feed in to their own 1332 | /// `xkb_state`. 1333 | /// 1334 | /// All parameters must always be passed, or the resulting state may be 1335 | /// incoherent. 1336 | /// 1337 | /// The serialization is lossy and will not survive round trips; it must only 1338 | /// be used to feed slave state objects, and must not be used to update the 1339 | /// master state. 1340 | /// 1341 | /// If you do not fit the description above, you should use 1342 | /// `xkb_state_update_key()` instead. The two functions should not generally be 1343 | /// used together. 1344 | /// 1345 | /// Returns a mask of state components that have changed as a result of 1346 | /// the update. If nothing in the state has changed, returns 0. 1347 | pub fn update_mask( 1348 | &mut self, 1349 | depressed_mods: ModMask, 1350 | latched_mods: ModMask, 1351 | locked_mods: ModMask, 1352 | depressed_layout: LayoutIndex, 1353 | latched_layout: LayoutIndex, 1354 | locked_layout: LayoutIndex, 1355 | ) -> StateComponent { 1356 | unsafe { 1357 | xkb_state_update_mask( 1358 | self.ptr, 1359 | depressed_mods, 1360 | latched_mods, 1361 | locked_mods, 1362 | depressed_layout, 1363 | latched_layout, 1364 | locked_layout, 1365 | ) 1366 | } 1367 | } 1368 | 1369 | /// Get the keysyms obtained from pressing a particular key in a given 1370 | /// keyboard state. 1371 | /// 1372 | /// Get the keysyms for a key according to the current active layout, 1373 | /// modifiers and shift level for the key, as determined by a keyboard 1374 | /// state. 1375 | /// 1376 | /// # Arguments 1377 | /// * `state`: The keyboard state object. 1378 | /// * `key`: The keycode of the key. 1379 | /// 1380 | /// # Return 1381 | /// * `syms_out`: An immutable array of keysyms corresponding the 1382 | /// key in the given keyboard state. 1383 | /// 1384 | /// As an extension to XKB, this function can return more than one keysym. 1385 | /// If you do not want to handle this case, you should use 1386 | /// `xkb_state_key_get_one_sym()`, which additionally performs transformations 1387 | /// which are specific to the one-keysym case. 1388 | #[must_use] 1389 | pub fn key_get_syms(&self, key: Keycode) -> &[Keysym] { 1390 | unsafe { 1391 | let mut syms_out: *const Keysym = null_mut(); 1392 | let len = xkb_state_key_get_syms( 1393 | self.ptr, 1394 | key.into(), 1395 | &mut syms_out as *mut *const Keysym as *mut *const xkeysym::RawKeysym, 1396 | ); 1397 | if syms_out.is_null() { 1398 | &[] 1399 | } else { 1400 | slice::from_raw_parts(syms_out, len as usize) 1401 | } 1402 | } 1403 | } 1404 | 1405 | /// Get the Unicode/UTF-8 string obtained from pressing a particular key 1406 | /// in a given keyboard state. 1407 | #[must_use] 1408 | pub fn key_get_utf8(&self, key: Keycode) -> String { 1409 | unsafe { 1410 | const BUF_LEN: usize = 64; 1411 | let buf: &mut [c_char] = &mut [0; BUF_LEN]; 1412 | let ptr = &mut buf[0] as *mut c_char; 1413 | let ret = xkb_state_key_get_utf8(self.ptr, key.into(), ptr, BUF_LEN); 1414 | // ret is similar to the return value of snprintf. 1415 | // it may be negative on unspecified errors, or >64 if the buffer is too small. 1416 | let len = ret.max(0).min(BUF_LEN as i32); 1417 | let slice: &[u8] = slice::from_raw_parts(ptr as *const _, len as usize); 1418 | String::from_utf8_unchecked(slice.to_owned()) 1419 | } 1420 | } 1421 | 1422 | /// Get the Unicode/UTF-32 codepoint obtained from pressing a particular 1423 | /// key in a a given keyboard state. 1424 | /// 1425 | /// Returns The UTF-32 representation for the key, if it consists of only 1426 | /// a single codepoint. Otherwise, returns 0. 1427 | #[must_use] 1428 | pub fn key_get_utf32(&self, key: Keycode) -> u32 { 1429 | unsafe { xkb_state_key_get_utf32(self.ptr, key.into()) } 1430 | } 1431 | 1432 | /// Get the single keysym obtained from pressing a particular key in a 1433 | /// given keyboard state. 1434 | /// 1435 | /// This function is similar to `xkb_state_key_get_syms()`, but intended 1436 | /// for users which cannot or do not want to handle the case where 1437 | /// multiple keysyms are returned (in which case this function is 1438 | /// preferred). 1439 | /// 1440 | /// Returns the keysym. If the key does not have exactly one keysym, 1441 | /// returns `xkb::KEY_NoSymbol`. 1442 | #[must_use] 1443 | pub fn key_get_one_sym(&self, key: Keycode) -> Keysym { 1444 | unsafe { xkb_state_key_get_one_sym(self.ptr, key.into()) }.into() 1445 | } 1446 | 1447 | /// Get the effective layout index for a key in a given keyboard state. 1448 | /// 1449 | /// Returns the layout index for the key in the given keyboard state. If 1450 | /// the given keycode is invalid, or if the key is not included in any 1451 | /// layout at all, returns `xkb::LAYOUT_INVALID`. 1452 | #[must_use] 1453 | pub fn key_get_layout(&self, key: Keycode) -> LayoutIndex { 1454 | unsafe { xkb_state_key_get_layout(self.ptr, key.into()) } 1455 | } 1456 | 1457 | /// Get the effective shift level for a key in a given keyboard state and 1458 | /// layout. 1459 | /// 1460 | /// Return the shift level index. If the key or layout are invalid, 1461 | /// returns `xkb::LEVEL_INVALID`. 1462 | #[must_use] 1463 | pub fn key_get_level(&self, key: Keycode, layout: LayoutIndex) -> LevelIndex { 1464 | unsafe { xkb_state_key_get_level(self.ptr, key.into(), layout) } 1465 | } 1466 | 1467 | /// The counterpart to `xkb_state_update_mask` for modifiers, to be used on 1468 | /// the server side of serialization. 1469 | /// 1470 | /// State components other than `xkb::STATE_MODS_*` are ignored. 1471 | /// If `xkb::STATE_MODS_EFFECTIVE` is included, all other state components are 1472 | /// ignored. 1473 | /// 1474 | /// Returns a `ModMask` representing the given components of the 1475 | /// modifier state. 1476 | /// 1477 | /// This function should not be used in regular clients; please use the 1478 | /// `xkb::State::mod_*_is_active` API instead. 1479 | #[must_use] 1480 | pub fn serialize_mods(&self, components: StateComponent) -> ModMask { 1481 | unsafe { xkb_state_serialize_mods(self.ptr, components) } 1482 | } 1483 | 1484 | #[must_use] 1485 | pub fn serialize_layout(&self, components: StateComponent) -> LayoutIndex { 1486 | unsafe { xkb_state_serialize_layout(self.ptr, components) } 1487 | } 1488 | 1489 | /// Test whether a modifier is active in a given keyboard state by name. 1490 | #[allow(clippy::missing_panics_doc)] 1491 | pub fn mod_name_is_active + ?Sized>( 1492 | &self, 1493 | name: &S, 1494 | type_: StateComponent, 1495 | ) -> bool { 1496 | unsafe { 1497 | let cname = CString::new(name.borrow().as_bytes()).unwrap(); 1498 | xkb_state_mod_name_is_active(self.ptr, cname.as_ptr(), type_) == 1 1499 | } 1500 | } 1501 | 1502 | /// Test whether a modifier is active in a given keyboard state by index. 1503 | #[must_use] 1504 | pub fn mod_index_is_active(&self, idx: ModIndex, type_: StateComponent) -> bool { 1505 | unsafe { xkb_state_mod_index_is_active(self.ptr, idx, type_) == 1 } 1506 | } 1507 | 1508 | /// Test whether a modifier is consumed by keyboard state translation for 1509 | /// a key. 1510 | /// 1511 | /// Some functions, like `xkb_state_key_get_syms()`, look at the state of 1512 | /// the modifiers in the keymap and derive from it the correct shift level 1513 | /// to use for the key. For example, in a US layout, pressing the key 1514 | /// labeled \ while the Shift modifier is active, generates the keysym 'A'. 1515 | /// In this case, the Shift modifier is said to be consumed. However, the 1516 | /// Num Lock modifier does not affect this translation at all, even if it 1517 | /// active, so it is not consumed by this translation. 1518 | /// 1519 | /// It may be desirable for some application to not reuse consumed modifiers 1520 | /// for further processing, e.g. for hotkeys or keyboard shortcuts. To 1521 | /// understand why, consider some requirements from a standard shortcut 1522 | /// mechanism, and how they are implemented: 1523 | /// 1524 | /// 1. The shortcut's modifiers must match exactly to the state. For example, 1525 | /// it is possible to bind separate actions to \\ and to 1526 | /// \\\. Further, if only \\ is bound to 1527 | /// an action, pressing \\\ should not trigger the 1528 | /// shortcut. 1529 | /// Effectively, this means that the modifiers are compared using the 1530 | /// equality operator (==). 1531 | /// 2. Only relevant modifiers are considered for the matching. For example, 1532 | /// Caps Lock and Num Lock should not generally affect the matching, e.g. 1533 | /// when matching \\ against the state, it does not matter 1534 | /// whether Num Lock is active or not. These relevant, or significant, 1535 | /// modifiers usually include Alt, Control, Shift, Super and similar. 1536 | /// Effectively, this means that non-significant modifiers are masked out, 1537 | /// before doing the comparison as described above. 1538 | /// 3. The matching must be independent of the layout/keymap. For example, 1539 | /// the \ (+) symbol is found on the first level on some layouts, 1540 | /// and requires holding Shift on others. If you simply bind the action 1541 | /// to the \ keysym, it would work for the unshifted kind, but 1542 | /// not for the others, because the match against Shift would fail. If 1543 | /// you bind the action to \\, only the shifted kind would 1544 | /// work. So what is needed is to recognize that Shift is used up in the 1545 | /// translation of the keysym itself, and therefore should not be included 1546 | /// in the matching. 1547 | /// Effectively, this means that consumed modifiers (Shift in this example) 1548 | /// are masked out as well, before doing the comparison. 1549 | /// 1550 | /// `state_modifiers` are the modifiers reported by 1551 | /// `xkb::State::mod_index_is_active()` and similar functions. 1552 | /// `consumed_modifiers` are the modifiers reported by 1553 | /// `xkb::State::mod_index_is_consumed()`. 1554 | /// `significant_modifiers` are decided upon by the application/toolkit/user; 1555 | /// it is up to them to decide whether these are configurable or hard-coded. 1556 | #[must_use] 1557 | pub fn mod_index_is_consumed(&self, key: Keycode, idx: ModIndex) -> bool { 1558 | unsafe { xkb_state_mod_index_is_consumed(self.ptr, key.into(), idx) == 1 } 1559 | } 1560 | 1561 | /// Remove consumed modifiers from a modifier mask for a key. 1562 | /// 1563 | /// Takes the given modifier mask, and removes all modifiers which are 1564 | /// consumed for that particular key (as in `xkb_state_mod_index_is_consumed()`). 1565 | #[must_use] 1566 | pub fn mod_mask_remove_consumed(&self, key: Keycode, mask: ModMask) -> ModMask { 1567 | unsafe { xkb_state_mod_mask_remove_consumed(self.ptr, key.into(), mask) } 1568 | } 1569 | 1570 | /// Get the mask of modifiers consumed by translating a given key. 1571 | /// 1572 | /// Returns a mask of the consumed modifiers. 1573 | #[must_use] 1574 | pub fn key_get_consumed_mods(&self, key: Keycode) -> ModMask { 1575 | unsafe { xkb_state_key_get_consumed_mods(self.ptr, key.into()) } 1576 | } 1577 | 1578 | /// Test whether a layout is active in a given keyboard state by name. 1579 | /// 1580 | /// If multiple layouts in the keymap have this name, the one with the lowest 1581 | /// index is tested. 1582 | #[allow(clippy::missing_panics_doc)] 1583 | pub fn layout_name_is_active + ?Sized>( 1584 | &self, 1585 | name: &S, 1586 | type_: StateComponent, 1587 | ) -> bool { 1588 | unsafe { 1589 | let cname = CString::new(name.borrow().as_bytes()).unwrap(); 1590 | xkb_state_layout_name_is_active(self.ptr, cname.as_ptr(), type_) != 0 1591 | } 1592 | } 1593 | 1594 | /// Test whether a layout is active in a given keyboard state by index. 1595 | #[must_use] 1596 | pub fn layout_index_is_active(&self, idx: LayoutIndex, type_: StateComponent) -> bool { 1597 | unsafe { xkb_state_layout_index_is_active(self.ptr, idx, type_) != 0 } 1598 | } 1599 | 1600 | /// Test whether a LED is active in a given keyboard state by name. 1601 | #[allow(clippy::missing_panics_doc)] 1602 | pub fn led_name_is_active + ?Sized>(&self, name: &S) -> bool { 1603 | unsafe { 1604 | let cname = CString::new(name.borrow().as_bytes()).unwrap(); 1605 | xkb_state_led_name_is_active(self.ptr, cname.as_ptr()) != 0 1606 | } 1607 | } 1608 | 1609 | /// Test whether a LED is active in a given keyboard state by index. 1610 | #[must_use] 1611 | pub fn led_index_is_active(&self, idx: LedIndex) -> bool { 1612 | unsafe { xkb_state_led_index_is_active(self.ptr, idx) != 0 } 1613 | } 1614 | } 1615 | 1616 | impl Clone for State { 1617 | fn clone(&self) -> State { 1618 | unsafe { 1619 | State { 1620 | ptr: xkb_state_ref(self.ptr), 1621 | } 1622 | } 1623 | } 1624 | } 1625 | 1626 | impl Drop for State { 1627 | fn drop(&mut self) { 1628 | unsafe { 1629 | xkb_state_unref(self.ptr); 1630 | } 1631 | } 1632 | } 1633 | --------------------------------------------------------------------------------