From db298b0fb8956359ac605d622ca83f249d4fdd13 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Mon, 28 Sep 2020 17:29:59 +0000 Subject: [PATCH] keymaps: Use multiple key maps, each within the limit of what Xorg can accept. Key maps are switched on key press whenever needed. --- eek/eek-keyboard.c | 18 +--- eek/eek-keyboard.h | 2 +- eekboard/eekboard-context-service.c | 4 +- src/data.rs | 20 +++-- src/keyboard.rs | 123 ++++++++++++++++++++++++---- src/layout.h | 1 - src/layout.rs | 17 ++-- src/submission.h | 3 +- src/submission.rs | 95 +++++++++++++++++---- src/tests.rs | 72 ++++++++++------ src/util.rs | 25 ++++++ src/vkeyboard.rs | 78 ++++++++++-------- src/wayland.c | 4 +- 13 files changed, 326 insertions(+), 136 deletions(-) diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 8afe1057..3295da2d 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -28,19 +28,12 @@ #include // TODO: this is Linux-specific #include + #include "eek-keyboard.h" - -static void eek_key_map_deinit(struct keymap *self) { - if (self->fd < 0) { - g_error("Deinit called multiple times on KeyMap"); - } else { - close(self->fd); - } - self->fd = -1; -} - -static struct keymap eek_key_map_from_str(const char *keymap_str) { +/// External linkage for Rust. +/// The corresponding deinit is implemented in vkeyboard::KeyMap::drop +struct keymap squeek_key_map_from_str(const char *keymap_str) { struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { g_error("No context created"); @@ -91,7 +84,6 @@ static struct keymap eek_key_map_from_str(const char *keymap_str) { } void level_keyboard_free(LevelKeyboard *self) { - eek_key_map_deinit(&self->keymap); squeek_layout_free(self->layout); g_free(self); } @@ -104,7 +96,5 @@ level_keyboard_new (struct squeek_layout *layout) g_error("Failed to create a keyboard"); } keyboard->layout = layout; - const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout); - keyboard->keymap = eek_key_map_from_str(keymap_str); return keyboard; } diff --git a/eek/eek-keyboard.h b/eek/eek-keyboard.h index f70a268a..be13b0b9 100644 --- a/eek/eek-keyboard.h +++ b/eek/eek-keyboard.h @@ -41,7 +41,7 @@ struct keymap { /// Keyboard state holder struct _LevelKeyboard { struct squeek_layout *layout; // owned - struct keymap keymap; // owned +// FIXME: This no longer needs to exist, keymap was folded into layout. }; typedef struct _LevelKeyboard LevelKeyboard; diff --git a/eekboard/eekboard-context-service.c b/eekboard/eekboard-context-service.c index a360c2b6..537be830 100644 --- a/eekboard/eekboard-context-service.c +++ b/eekboard/eekboard-context-service.c @@ -159,7 +159,7 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque // Update the keymap if necessary. // TODO: Update submission on change event if (context->submission) { - submission_set_keyboard(context->submission, keyboard, timestamp); + submission_use_layout(context->submission, keyboard->layout, timestamp); } // Update UI @@ -345,7 +345,7 @@ void eekboard_context_service_set_submission(EekboardContextService *context, st context->submission = submission; if (context->submission) { uint32_t time = gdk_event_get_time(NULL); - submission_set_keyboard(context->submission, context->keyboard, time); + submission_use_layout(context->submission, context->keyboard->layout, time); } } diff --git a/src/data.rs b/src/data.rs index 4197ed24..ab0c4d5a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -18,7 +18,7 @@ use xkbcommon::xkb; use ::action; use ::keyboard::{ KeyState, PressType, - generate_keymap, generate_keycodes, FormattingError + generate_keymaps, generate_keycodes, KeyCode, FormattingError }; use ::layout; use ::layout::ArrangementKind; @@ -382,7 +382,7 @@ impl Layout { ) )}).collect(); - let symbolmap: HashMap = generate_keycodes( + let symbolmap: HashMap = generate_keycodes( extract_symbol_names(&button_actions) ); @@ -391,7 +391,7 @@ impl Layout { let keycodes = match &action { ::action::Action::Submit { text: _, keys } => { keys.iter().map(|named_keysym| { - *symbolmap.get(named_keysym.0.as_str()) + symbolmap.get(named_keysym.0.as_str()) .expect( format!( "keysym {} in key {} missing from symbol map", @@ -399,11 +399,13 @@ impl Layout { name ).as_str() ) + .clone() }).collect() }, action::Action::Erase => vec![ - *symbolmap.get("BackSpace") - .expect(&format!("BackSpace missing from symbol map")), + symbolmap.get("BackSpace") + .expect(&format!("BackSpace missing from symbol map")) + .clone(), ], _ => Vec::new(), }; @@ -418,7 +420,7 @@ impl Layout { }) ); - let keymap_str = match generate_keymap(symbolmap) { + let keymaps = match generate_keymaps(symbolmap) { Err(e) => { return (Err(e), warning_handler) }, Ok(v) => v, }; @@ -482,10 +484,10 @@ impl Layout { ( Ok(::layout::LayoutData { views: views, - keymap_str: { + keymaps: keymaps.into_iter().map(|keymap_str| CString::new(keymap_str) .expect("Invalid keymap string generated") - }, + ).collect(), // FIXME: use a dedicated field margins: layout::Margins { top: self.margins.top, @@ -876,7 +878,7 @@ mod tests { assert_eq!( out.views["base"].1 .get_rows()[0].1 - .buttons[0].1 + .get_buttons()[0].1 .state.borrow() .keycodes.len(), 1 diff --git a/src/keyboard.rs b/src/keyboard.rs index 26680045..1801215b 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -5,10 +5,13 @@ use std::cell::RefCell; use std::collections::HashMap; use std::fmt; use std::io; +use std::mem; +use std::ptr; use std::rc::Rc; use std::string::FromUtf8Error; use ::action::Action; +use ::util; // Traits use std::io::Write; @@ -20,7 +23,12 @@ pub enum PressType { Pressed = 1, } -pub type KeyCode = u32; +/// The extended, unambiguous layout-keycode +#[derive(Debug, Clone)] +pub struct KeyCode { + pub code: u32, + pub keymap_idx: usize, +} bitflags!{ /// Map to `virtual_keyboard.modifiers` modifiers values @@ -92,12 +100,15 @@ fn sorted<'a, I: Iterator>( /// which the compositor likes to discard pub fn generate_keycodes<'a, C: IntoIterator>( key_names: C, -) -> HashMap { +) -> HashMap { HashMap::from_iter( // Sort to remove a source of indeterminism in keycode assignment. sorted(key_names.into_iter()) - .map(|name| name) - .zip(9..) + .zip(util::cycle_count(9..255)) + .map(|(name, (code, keymap_idx))| ( + String::from(name), + KeyCode { code, keymap_idx }, + )) ) } @@ -122,10 +133,50 @@ impl From for FormattingError { } } +/// Index is the key code, String is the occupant. +/// Starts all empty. +/// https://gitlab.freedesktop.org/xorg/xserver/-/issues/260 +type SingleKeyMap = [Option; 256]; + +fn single_key_map_new() -> SingleKeyMap { + // Why can't we just initialize arrays without tricks -_- ? + unsafe { + // Inspired by + // https://www.reddit.com/r/rust/comments/5n7bh1/how_to_create_an_array_of_a_type_with_clone_but/ + let mut array: SingleKeyMap = mem::MaybeUninit::uninit().assume_init(); + for element in array.iter_mut() { + ptr::write(element, None); + } + array + } +} + +pub fn generate_keymaps(symbolmap: HashMap::) + -> Result, FormattingError> +{ + let mut bins: Vec = Vec::new(); + + for (name, KeyCode { code, keymap_idx }) in symbolmap.into_iter() { + if keymap_idx >= bins.len() { + bins.resize_with( + keymap_idx + 1, + || single_key_map_new(), + ); + } + bins[keymap_idx][code as usize] = Some(name); + } + + let mut out = Vec::new(); + for bin in bins { + out.push(generate_keymap(&bin)?); + } + Ok(out) +} + /// Generates a de-facto single level keymap. -/// Key codes must not repeat and should remain between 9 and 255. -pub fn generate_keymap( - symbolmap: HashMap::, +/// Key codes must not repeat and must remain between 9 and 255. +fn generate_keymap( + symbolmap: &SingleKeyMap, ) -> Result { let mut buf: Vec = Vec::new(); writeln!( @@ -134,14 +185,21 @@ pub fn generate_keymap( xkb_keycodes \"squeekboard\" {{ minimum = 8; - maximum = 999;" + maximum = 255;" )?; + + let pairs: Vec<(&String, usize)> = symbolmap.iter() + // Attach a key code to each cell. + .enumerate() + // Get rid of empty keycodes. + .filter_map(|(code, name)| name.as_ref().map(|n| (n, code))) + .collect(); // Xorg can only consume up to 255 keys, so this may not work in Xwayland. // Two possible solutions: // - use levels to cram multiple characters into one key // - swap layouts on key presses - for keycode in symbolmap.values() { + for (_name, keycode) in &pairs { write!( buf, " @@ -160,7 +218,7 @@ pub fn generate_keymap( " )?; - for (name, keycode) in symbolmap.iter() { + for (name, keycode) in pairs { write!( buf, " @@ -218,13 +276,14 @@ mod tests { use xkbcommon::xkb; #[test] - fn test_keymap_multi() { - let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + fn test_keymap_single_resolve() { + let mut key_map = single_key_map_new(); + key_map[9] = Some("a".into()); + key_map[10] = Some("c".into()); - let keymap_str = generate_keymap(hashmap!{ - "a".into() => 9, - "c".into() => 10, - }).unwrap(); + let keymap_str = generate_keymap(&key_map).unwrap(); + + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); let keymap = xkb::Keymap::new_from_string( &context, @@ -238,4 +297,36 @@ mod tests { assert_eq!(state.key_get_one_sym(9), xkb::KEY_a); assert_eq!(state.key_get_one_sym(10), xkb::KEY_c); } + + #[test] + fn test_keymap_second_resolve() { + let keymaps = generate_keymaps(hashmap!( + "a".into() => KeyCode { keymap_idx: 1, code: 9 }, + )).unwrap(); + + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + + let keymap = xkb::Keymap::new_from_string( + &context, + keymaps[1].clone(), // this index is part of the test + xkb::KEYMAP_FORMAT_TEXT_V1, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ).expect("Failed to create keymap"); + + let state = xkb::State::new(&keymap); + + assert_eq!(state.key_get_one_sym(9), xkb::KEY_a); + } + + #[test] + fn test_symbolmap_overflow() { + // The 257th key (U1101) is interesting. + // Use Unicode encoding for being able to use in xkb keymaps. + let keynames = (0..258).map(|num| format!("U{:04X}", 0x1000 + num)); + let keycodes = generate_keycodes(keynames); + + // test now + let code = keycodes.get("U1101").expect("Did not find the tested keysym"); + assert_eq!(code.keymap_idx, 1); + } } diff --git a/src/layout.h b/src/layout.h index 756f90d5..53318bc4 100644 --- a/src/layout.h +++ b/src/layout.h @@ -39,7 +39,6 @@ struct transformation squeek_layout_calculate_transformation( double allocation_width, double allocation_size); struct squeek_layout *squeek_load_layout(const char *name, uint32_t type); -const char *squeek_layout_get_keymap(const struct squeek_layout*); enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *); void squeek_layout_free(struct squeek_layout*); diff --git a/src/layout.rs b/src/layout.rs index 2fa09112..03b20039 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -236,13 +236,6 @@ pub mod c { height: allocation_height, }) } - - #[no_mangle] - pub extern "C" - fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char { - let layout = unsafe { &*layout }; - layout.keymap_str.as_ptr() - } #[no_mangle] pub extern "C" @@ -686,8 +679,8 @@ pub struct Layout { pub views: HashMap, // Non-UI stuff - /// xkb keymap applicable to the contained keys. Unchangeable - pub keymap_str: CString, + /// xkb keymaps applicable to the contained keys. Unchangeable + pub keymaps: Vec, // Changeable state // a Vec would be enough, but who cares, this will be small & fast enough // TODO: turn those into per-input point *_buttons to track dragging. @@ -703,7 +696,7 @@ pub struct Layout { pub struct LayoutData { /// Point is the offset within layout pub views: HashMap, - pub keymap_str: CString, + pub keymaps: Vec, pub margins: Margins, } @@ -726,7 +719,7 @@ impl Layout { kind, current_view: "base".to_owned(), views: data.views, - keymap_str: data.keymap_str, + keymaps: data.keymaps, pressed_keys: HashSet::new(), margins: data.margins, } @@ -1200,7 +1193,7 @@ mod test { ]); let layout = Layout { current_view: String::new(), - keymap_str: CString::new("").unwrap(), + keymaps: Vec::new(), kind: ArrangementKind::Base, pressed_keys: HashSet::new(), // Lots of bottom margin diff --git a/src/submission.h b/src/submission.h index ed904212..acb20323 100644 --- a/src/submission.h +++ b/src/submission.h @@ -6,6 +6,7 @@ #include "eek/eek-types.h" struct submission; +struct squeek_layout; struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager, struct zwp_virtual_keyboard_manager_v1 *vkmanager, @@ -15,5 +16,5 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager, // Defined in Rust struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state); void submission_set_ui(struct submission *self, ServerContextService *ui_context); -void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard, uint32_t time); +void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time); #endif diff --git a/src/submission.rs b/src/submission.rs index 4c8c9c41..bfe8d00d 100644 --- a/src/submission.rs +++ b/src/submission.rs @@ -23,8 +23,9 @@ use ::action::Modifier; use ::imservice; use ::imservice::IMService; use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType }; -use ::layout::c::LevelKeyboard; +use ::layout; use ::util::vec_remove; +use ::vkeyboard; use ::vkeyboard::VirtualKeyboard; // traits @@ -68,6 +69,8 @@ pub mod c { modifiers_active: Vec::new(), virtual_keyboard: VirtualKeyboard(vk), pressed: Vec::new(), + keymap_fds: Vec::new(), + keymap_idx: None, } )) } @@ -91,16 +94,17 @@ pub mod c { #[no_mangle] pub extern "C" - fn submission_set_keyboard( + fn submission_use_layout( submission: *mut Submission, - keyboard: LevelKeyboard, + layout: *const layout::Layout, time: u32, ) { if submission.is_null() { panic!("Null submission pointer"); } let submission: &mut Submission = unsafe { &mut *submission }; - submission.update_keymap(keyboard, Timestamp(time)); + let layout = unsafe { &*layout }; + submission.use_layout(layout, Timestamp(time)); } } @@ -119,6 +123,8 @@ pub struct Submission { virtual_keyboard: VirtualKeyboard, modifiers_active: Vec<(KeyStateId, Modifier)>, pressed: Vec<(KeyStateId, SubmittedAction)>, + keymap_fds: Vec, + keymap_idx: Option, } pub enum SubmitData<'a> { @@ -177,11 +183,34 @@ impl Submission { let submit_action = match was_committed_as_text { true => SubmittedAction::IMService, false => { - self.virtual_keyboard.switch( - keycodes, - PressType::Pressed, - time, - ); + let keycodes_count = keycodes.len(); + for keycode in keycodes.iter() { + self.select_keymap(keycode.keymap_idx, time); + let keycode = keycode.code; + match keycodes_count { + // Pressing a key made out of a single keycode is simple: + // press on press, release on release. + 1 => self.virtual_keyboard.switch( + keycode, + PressType::Pressed, + time, + ), + // A key made of multiple keycodes + // has to submit them one after the other. + _ => { + self.virtual_keyboard.switch( + keycode.clone(), + PressType::Pressed, + time, + ); + self.virtual_keyboard.switch( + keycode.clone(), + PressType::Released, + time, + ); + }, + }; + } SubmittedAction::VirtualKeyboard(keycodes.clone()) }, }; @@ -199,11 +228,21 @@ impl Submission { // no matter if the imservice got activated, // keys must be released SubmittedAction::VirtualKeyboard(keycodes) => { - self.virtual_keyboard.switch( - &keycodes, - PressType::Released, - time, - ) + let keycodes_count = keycodes.len(); + match keycodes_count { + 1 => { + let keycode = &keycodes[0]; + self.select_keymap(keycode.keymap_idx, time); + self.virtual_keyboard.switch( + keycode.code, + PressType::Released, + time, + ); + }, + // Design choice here: submit multiple all at press time + // and do nothing at release time. + _ => {}, + }; }, } }; @@ -274,6 +313,7 @@ impl Submission { } } + /// Changes keymap and clears pressed keys and modifiers. /// /// It's not obvious if clearing is the right thing to do, @@ -283,9 +323,28 @@ impl Submission { /// Alternatively, modifiers could be restored on the new keymap. /// That approach might be difficult /// due to modifiers meaning different things in different keymaps. - pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) { - self.clear_all_modifiers(); - self.release_all_virtual_keys(time); - self.virtual_keyboard.update_keymap(keyboard); + fn select_keymap(&mut self, idx: usize, time: Timestamp) { + if self.keymap_idx != Some(idx) { + self.keymap_idx = Some(idx); + self.clear_all_modifiers(); + self.release_all_virtual_keys(time); + let keymap = &self.keymap_fds[idx]; + self.virtual_keyboard.update_keymap(keymap); + } + } + + pub fn use_layout(&mut self, layout: &layout::Layout, time: Timestamp) { + self.keymap_fds = layout.keymaps.iter() + .map(|keymap_str| vkeyboard::c::KeyMap::from_cstr( + keymap_str.as_c_str() + )) + .collect(); + self.keymap_idx = None; + + // This can probably be eliminated, + // because key presses can trigger an update anyway. + // However, self.keymap_idx needs to become Option<> + // in order to force update on new layouts. + self.select_keymap(0, time); } } diff --git a/src/tests.rs b/src/tests.rs index c59a741e..619ad321 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -40,21 +40,29 @@ pub fn check_layout_file(path: &str) { ) } -fn check_sym_presence( - state: &xkb::State, - sym_name: &str, - handler: &mut dyn logging::Handler, -) { +fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool { let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS); if sym == xkb::KEY_NoSymbol { panic!(format!("Entered invalid keysym: {}", sym_name)); } let map = state.get_keymap(); let range = map.min_keycode()..=map.max_keycode(); - let found = range.flat_map(|code| state.key_get_syms(code)) + range.flat_map(|code| state.key_get_syms(code)) .find(|s| **s == sym) - .is_some(); - if !found { + .is_some() +} + +fn check_sym_presence( + states: &[xkb::State], + sym_name: &str, + handler: &mut dyn logging::Handler, +) { + let found = states.iter() + .position(|state| { + check_sym_in_keymap(&state, sym_name) + }); + + if let None = found { handler.handle( logging::Level::Surprise, &format!("There's no way to input the keysym {} on this layout", sym_name), @@ -71,26 +79,27 @@ fn check_layout(layout: Layout, allow_missing_return: bool) { } let layout = layout.expect("layout broken"); - - let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); - let keymap_str = layout.keymap_str - .clone() - .into_string().expect("Failed to decode keymap string"); - - let keymap = xkb::Keymap::new_from_string( - &context, - keymap_str.clone(), - xkb::KEYMAP_FORMAT_TEXT_V1, - xkb::KEYMAP_COMPILE_NO_FLAGS, - ).expect("Failed to create keymap"); + let xkb_states: Vec = layout.keymaps.iter() + .map(|keymap_str| { + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let keymap_str = keymap_str + .clone() + .into_string().expect("Failed to decode keymap string"); + let keymap = xkb::Keymap::new_from_string( + &context, + keymap_str.clone(), + xkb::KEYMAP_FORMAT_TEXT_V1, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ).expect("Failed to create keymap"); + xkb::State::new(&keymap) + }) + .collect(); - let state = xkb::State::new(&keymap); - - check_sym_presence(&state, "BackSpace", &mut handler); + check_sym_presence(&xkb_states, "BackSpace", &mut handler); let mut printer = logging::Print; check_sym_presence( - &state, + &xkb_states, "Return", if allow_missing_return { &mut printer } else { &mut handler }, @@ -102,10 +111,19 @@ fn check_layout(layout: Layout, allow_missing_return: bool) { for (_x, button) in row.get_buttons() { let keystate = button.state.borrow(); for keycode in &keystate.keycodes { - match state.key_get_one_sym(*keycode) { + match xkb_states[keycode.keymap_idx].key_get_one_sym(keycode.code) { xkb::KEY_NoSymbol => { - eprintln!("{}", keymap_str); - panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name); + eprintln!( + "keymap {}: {}", + keycode.keymap_idx, + layout.keymaps[keycode.keymap_idx].to_str().unwrap(), + ); + panic!( + "Keysym for code {:?} on key {} ({:?}) can't be resolved", + keycode, + button.name.to_string_lossy(), + button.name, + ); }, _ => {}, } diff --git a/src/util.rs b/src/util.rs index 767c32ea..d5109066 100644 --- a/src/util.rs +++ b/src/util.rs @@ -203,6 +203,23 @@ pub fn vec_remove bool>(v: &mut Vec, pred: F) -> Option idx.map(|idx| v.remove(idx)) } +/// Repeats all the items of the iterator forever, +/// but returns the cycle number alongside. +/// Inefficient due to all the vectors, but doesn't have to be fast. +pub fn cycle_count>(iter: I) + -> impl Iterator +{ + let numbered_copies = vec![iter].into_iter() + .cycle() + .enumerate(); + numbered_copies.flat_map(|(idx, cycle)| + // Pair each element from the cycle with a copy of the index. + cycle.zip( + vec![idx].into_iter().cycle() // Repeat the index forever. + ) + ) +} + #[cfg(test)] mod tests { use super::*; @@ -217,4 +234,12 @@ mod tests { assert_eq!(s.insert(Pointer(Rc::new(2u32))), true); assert_eq!(s.remove(&Pointer(first)), true); } + + #[test] + fn check_count() { + assert_eq!( + cycle_count(5..8).take(7).collect::>(), + vec![(5, 0), (6, 0), (7, 0), (5, 1), (6, 1), (7, 1), (5, 2)] + ); + } } diff --git a/src/vkeyboard.rs b/src/vkeyboard.rs index 23eba7dd..54f3142f 100644 --- a/src/vkeyboard.rs +++ b/src/vkeyboard.rs @@ -1,20 +1,47 @@ /*! Managing the events belonging to virtual-keyboard interface. */ -use ::keyboard::{ KeyCode, Modifiers, PressType }; -use ::layout::c::LevelKeyboard; +use ::keyboard::{ Modifiers, PressType }; use ::submission::Timestamp; +/// Standard xkb keycode +type KeyCode = u32; + /// Gathers stuff defined in C or called by C pub mod c { - use super::*; - use std::os::raw::c_void; + use std::ffi::CStr; + use std::os::raw::{ c_char, c_void }; #[repr(transparent)] #[derive(Clone, Copy)] pub struct ZwpVirtualKeyboardV1(*const c_void); + #[repr(C)] + pub struct KeyMap { + fd: u32, + fd_len: usize, + } + + impl KeyMap { + pub fn from_cstr(s: &CStr) -> KeyMap { + unsafe { + squeek_key_map_from_str(s.as_ptr()) + } + } + } + + impl Drop for KeyMap { + fn drop(&mut self) { + unsafe { + close(self.fd as u32); + } + } + } + #[no_mangle] extern "C" { + // From libc, to let KeyMap get deallocated. + fn close(fd: u32); + pub fn eek_virtual_keyboard_v1_key( virtual_keyboard: ZwpVirtualKeyboardV1, timestamp: u32, @@ -24,13 +51,15 @@ pub mod c { pub fn eek_virtual_keyboard_update_keymap( virtual_keyboard: ZwpVirtualKeyboardV1, - keyboard: LevelKeyboard, + keymap: *const KeyMap, ); pub fn eek_virtual_keyboard_set_modifiers( virtual_keyboard: ZwpVirtualKeyboardV1, modifiers: u32, ); + + pub fn squeek_key_map_from_str(keymap_str: *const c_char) -> KeyMap; } } @@ -41,35 +70,15 @@ impl VirtualKeyboard { // TODO: error out if keymap not set pub fn switch( &self, - keycodes: &[KeyCode], + keycode: KeyCode, action: PressType, timestamp: Timestamp, ) { - let keycodes_count = keycodes.len(); - for keycode in keycodes.iter() { - let keycode = keycode - 8; - match (action, keycodes_count) { - // Pressing a key made out of a single keycode is simple: - // press on press, release on release. - (_, 1) => unsafe { - c::eek_virtual_keyboard_v1_key( - self.0, timestamp.0, keycode, action.clone() as u32 - ); - }, - // A key made of multiple keycodes - // has to submit them one after the other - (PressType::Pressed, _) => unsafe { - c::eek_virtual_keyboard_v1_key( - self.0, timestamp.0, keycode, PressType::Pressed as u32 - ); - c::eek_virtual_keyboard_v1_key( - self.0, timestamp.0, keycode, PressType::Released as u32 - ); - }, - // Design choice here: submit multiple all at press time - // and do nothing at release time - (PressType::Released, _) => {}, - } + let keycode = keycode - 8; + unsafe { + c::eek_virtual_keyboard_v1_key( + self.0, timestamp.0, keycode, action.clone() as u32 + ); } } @@ -80,9 +89,12 @@ impl VirtualKeyboard { } } - pub fn update_keymap(&self, keyboard: LevelKeyboard) { + pub fn update_keymap(&self, keymap: &c::KeyMap) { unsafe { - c::eek_virtual_keyboard_update_keymap(self.0, keyboard); + c::eek_virtual_keyboard_update_keymap( + self.0, + keymap as *const c::KeyMap, + ); } } } diff --git a/src/wayland.c b/src/wayland.c index 30955810..3c9fce93 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -14,10 +14,10 @@ eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard } -void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, const LevelKeyboard *keyboard) { +void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, struct keymap *keymap) { zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - keyboard->keymap.fd, keyboard->keymap.fd_len); + keymap->fd, keymap->fd_len); } void