keymaps: Use multiple key maps, each within the limit of what Xorg can accept.

Key maps are switched on key press whenever needed.
This commit is contained in:
Dorota Czaplejewicz
2020-09-28 17:29:59 +00:00
parent 4373cf7bc3
commit db298b0fb8
13 changed files with 326 additions and 136 deletions

View File

@ -28,19 +28,12 @@
#include <sys/random.h> // TODO: this is Linux-specific #include <sys/random.h> // TODO: this is Linux-specific
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "eek-keyboard.h" #include "eek-keyboard.h"
/// External linkage for Rust.
static void eek_key_map_deinit(struct keymap *self) { /// The corresponding deinit is implemented in vkeyboard::KeyMap::drop
if (self->fd < 0) { struct keymap squeek_key_map_from_str(const char *keymap_str) {
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) {
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) { if (!context) {
g_error("No context created"); 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) { void level_keyboard_free(LevelKeyboard *self) {
eek_key_map_deinit(&self->keymap);
squeek_layout_free(self->layout); squeek_layout_free(self->layout);
g_free(self); g_free(self);
} }
@ -104,7 +96,5 @@ level_keyboard_new (struct squeek_layout *layout)
g_error("Failed to create a keyboard"); g_error("Failed to create a keyboard");
} }
keyboard->layout = layout; keyboard->layout = layout;
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
keyboard->keymap = eek_key_map_from_str(keymap_str);
return keyboard; return keyboard;
} }

View File

@ -41,7 +41,7 @@ struct keymap {
/// Keyboard state holder /// Keyboard state holder
struct _LevelKeyboard { struct _LevelKeyboard {
struct squeek_layout *layout; // owned 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; typedef struct _LevelKeyboard LevelKeyboard;

View File

@ -159,7 +159,7 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque
// Update the keymap if necessary. // Update the keymap if necessary.
// TODO: Update submission on change event // TODO: Update submission on change event
if (context->submission) { if (context->submission) {
submission_set_keyboard(context->submission, keyboard, timestamp); submission_use_layout(context->submission, keyboard->layout, timestamp);
} }
// Update UI // Update UI
@ -345,7 +345,7 @@ void eekboard_context_service_set_submission(EekboardContextService *context, st
context->submission = submission; context->submission = submission;
if (context->submission) { if (context->submission) {
uint32_t time = gdk_event_get_time(NULL); 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);
} }
} }

View File

@ -18,7 +18,7 @@ use xkbcommon::xkb;
use ::action; use ::action;
use ::keyboard::{ use ::keyboard::{
KeyState, PressType, KeyState, PressType,
generate_keymap, generate_keycodes, FormattingError generate_keymaps, generate_keycodes, KeyCode, FormattingError
}; };
use ::layout; use ::layout;
use ::layout::ArrangementKind; use ::layout::ArrangementKind;
@ -382,7 +382,7 @@ impl Layout {
) )
)}).collect(); )}).collect();
let symbolmap: HashMap<String, u32> = generate_keycodes( let symbolmap: HashMap<String, KeyCode> = generate_keycodes(
extract_symbol_names(&button_actions) extract_symbol_names(&button_actions)
); );
@ -391,7 +391,7 @@ impl Layout {
let keycodes = match &action { let keycodes = match &action {
::action::Action::Submit { text: _, keys } => { ::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keysym| { keys.iter().map(|named_keysym| {
*symbolmap.get(named_keysym.0.as_str()) symbolmap.get(named_keysym.0.as_str())
.expect( .expect(
format!( format!(
"keysym {} in key {} missing from symbol map", "keysym {} in key {} missing from symbol map",
@ -399,11 +399,13 @@ impl Layout {
name name
).as_str() ).as_str()
) )
.clone()
}).collect() }).collect()
}, },
action::Action::Erase => vec![ action::Action::Erase => vec![
*symbolmap.get("BackSpace") symbolmap.get("BackSpace")
.expect(&format!("BackSpace missing from symbol map")), .expect(&format!("BackSpace missing from symbol map"))
.clone(),
], ],
_ => Vec::new(), _ => 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) }, Err(e) => { return (Err(e), warning_handler) },
Ok(v) => v, Ok(v) => v,
}; };
@ -482,10 +484,10 @@ impl Layout {
( (
Ok(::layout::LayoutData { Ok(::layout::LayoutData {
views: views, views: views,
keymap_str: { keymaps: keymaps.into_iter().map(|keymap_str|
CString::new(keymap_str) CString::new(keymap_str)
.expect("Invalid keymap string generated") .expect("Invalid keymap string generated")
}, ).collect(),
// FIXME: use a dedicated field // FIXME: use a dedicated field
margins: layout::Margins { margins: layout::Margins {
top: self.margins.top, top: self.margins.top,
@ -876,7 +878,7 @@ mod tests {
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"].1
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .get_buttons()[0].1
.state.borrow() .state.borrow()
.keycodes.len(), .keycodes.len(),
1 1

View File

@ -5,10 +5,13 @@ use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::mem;
use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::util;
// Traits // Traits
use std::io::Write; use std::io::Write;
@ -20,7 +23,12 @@ pub enum PressType {
Pressed = 1, 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!{ bitflags!{
/// Map to `virtual_keyboard.modifiers` modifiers values /// Map to `virtual_keyboard.modifiers` modifiers values
@ -92,12 +100,15 @@ fn sorted<'a, I: Iterator<Item=String>>(
/// which the compositor likes to discard /// which the compositor likes to discard
pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>(
key_names: C, key_names: C,
) -> HashMap<String, u32> { ) -> HashMap<String, KeyCode> {
HashMap::from_iter( HashMap::from_iter(
// Sort to remove a source of indeterminism in keycode assignment. // Sort to remove a source of indeterminism in keycode assignment.
sorted(key_names.into_iter()) sorted(key_names.into_iter())
.map(|name| name) .zip(util::cycle_count(9..255))
.zip(9..) .map(|(name, (code, keymap_idx))| (
String::from(name),
KeyCode { code, keymap_idx },
))
) )
} }
@ -122,10 +133,50 @@ impl From<io::Error> 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<String>; 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::<String, KeyCode>)
-> Result<Vec<String>, FormattingError>
{
let mut bins: Vec<SingleKeyMap> = 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. /// Generates a de-facto single level keymap.
/// Key codes must not repeat and should remain between 9 and 255. /// Key codes must not repeat and must remain between 9 and 255.
pub fn generate_keymap( fn generate_keymap(
symbolmap: HashMap::<String, KeyCode>, symbolmap: &SingleKeyMap,
) -> Result<String, FormattingError> { ) -> Result<String, FormattingError> {
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new();
writeln!( writeln!(
@ -134,14 +185,21 @@ pub fn generate_keymap(
xkb_keycodes \"squeekboard\" {{ xkb_keycodes \"squeekboard\" {{
minimum = 8; 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. // Xorg can only consume up to 255 keys, so this may not work in Xwayland.
// Two possible solutions: // Two possible solutions:
// - use levels to cram multiple characters into one key // - use levels to cram multiple characters into one key
// - swap layouts on key presses // - swap layouts on key presses
for keycode in symbolmap.values() { for (_name, keycode) in &pairs {
write!( write!(
buf, buf,
" "
@ -160,7 +218,7 @@ pub fn generate_keymap(
" "
)?; )?;
for (name, keycode) in symbolmap.iter() { for (name, keycode) in pairs {
write!( write!(
buf, buf,
" "
@ -218,13 +276,14 @@ mod tests {
use xkbcommon::xkb; use xkbcommon::xkb;
#[test] #[test]
fn test_keymap_multi() { fn test_keymap_single_resolve() {
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); 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!{ let keymap_str = generate_keymap(&key_map).unwrap();
"a".into() => 9,
"c".into() => 10, let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
}).unwrap();
let keymap = xkb::Keymap::new_from_string( let keymap = xkb::Keymap::new_from_string(
&context, &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(9), xkb::KEY_a);
assert_eq!(state.key_get_one_sym(10), xkb::KEY_c); 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);
}
} }

View File

@ -39,7 +39,6 @@ struct transformation squeek_layout_calculate_transformation(
double allocation_width, double allocation_size); double allocation_width, double allocation_size);
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type); 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 *); enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
void squeek_layout_free(struct squeek_layout*); void squeek_layout_free(struct squeek_layout*);

View File

@ -237,13 +237,6 @@ pub mod c {
}) })
} }
#[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] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_get_kind(layout: *const Layout) -> u32 { fn squeek_layout_get_kind(layout: *const Layout) -> u32 {
@ -686,8 +679,8 @@ pub struct Layout {
pub views: HashMap<String, (c::Point, View)>, pub views: HashMap<String, (c::Point, View)>,
// Non-UI stuff // Non-UI stuff
/// xkb keymap applicable to the contained keys. Unchangeable /// xkb keymaps applicable to the contained keys. Unchangeable
pub keymap_str: CString, pub keymaps: Vec<CString>,
// Changeable state // Changeable state
// a Vec would be enough, but who cares, this will be small & fast enough // 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. // TODO: turn those into per-input point *_buttons to track dragging.
@ -703,7 +696,7 @@ pub struct Layout {
pub struct LayoutData { pub struct LayoutData {
/// Point is the offset within layout /// Point is the offset within layout
pub views: HashMap<String, (c::Point, View)>, pub views: HashMap<String, (c::Point, View)>,
pub keymap_str: CString, pub keymaps: Vec<CString>,
pub margins: Margins, pub margins: Margins,
} }
@ -726,7 +719,7 @@ impl Layout {
kind, kind,
current_view: "base".to_owned(), current_view: "base".to_owned(),
views: data.views, views: data.views,
keymap_str: data.keymap_str, keymaps: data.keymaps,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
margins: data.margins, margins: data.margins,
} }
@ -1200,7 +1193,7 @@ mod test {
]); ]);
let layout = Layout { let layout = Layout {
current_view: String::new(), current_view: String::new(),
keymap_str: CString::new("").unwrap(), keymaps: Vec::new(),
kind: ArrangementKind::Base, kind: ArrangementKind::Base,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
// Lots of bottom margin // Lots of bottom margin

View File

@ -6,6 +6,7 @@
#include "eek/eek-types.h" #include "eek/eek-types.h"
struct submission; struct submission;
struct squeek_layout;
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager, struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
struct zwp_virtual_keyboard_manager_v1 *vkmanager, 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 // Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state); 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_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 #endif

View File

@ -23,8 +23,9 @@ use ::action::Modifier;
use ::imservice; use ::imservice;
use ::imservice::IMService; use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType }; use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::layout::c::LevelKeyboard; use ::layout;
use ::util::vec_remove; use ::util::vec_remove;
use ::vkeyboard;
use ::vkeyboard::VirtualKeyboard; use ::vkeyboard::VirtualKeyboard;
// traits // traits
@ -68,6 +69,8 @@ pub mod c {
modifiers_active: Vec::new(), modifiers_active: Vec::new(),
virtual_keyboard: VirtualKeyboard(vk), virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(), pressed: Vec::new(),
keymap_fds: Vec::new(),
keymap_idx: None,
} }
)) ))
} }
@ -91,16 +94,17 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn submission_set_keyboard( fn submission_use_layout(
submission: *mut Submission, submission: *mut Submission,
keyboard: LevelKeyboard, layout: *const layout::Layout,
time: u32, time: u32,
) { ) {
if submission.is_null() { if submission.is_null() {
panic!("Null submission pointer"); panic!("Null submission pointer");
} }
let submission: &mut Submission = unsafe { &mut *submission }; 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, virtual_keyboard: VirtualKeyboard,
modifiers_active: Vec<(KeyStateId, Modifier)>, modifiers_active: Vec<(KeyStateId, Modifier)>,
pressed: Vec<(KeyStateId, SubmittedAction)>, pressed: Vec<(KeyStateId, SubmittedAction)>,
keymap_fds: Vec<vkeyboard::c::KeyMap>,
keymap_idx: Option<usize>,
} }
pub enum SubmitData<'a> { pub enum SubmitData<'a> {
@ -177,11 +183,34 @@ impl Submission {
let submit_action = match was_committed_as_text { let submit_action = match was_committed_as_text {
true => SubmittedAction::IMService, true => SubmittedAction::IMService,
false => { false => {
self.virtual_keyboard.switch( let keycodes_count = keycodes.len();
keycodes, for keycode in keycodes.iter() {
PressType::Pressed, self.select_keymap(keycode.keymap_idx, time);
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()) SubmittedAction::VirtualKeyboard(keycodes.clone())
}, },
}; };
@ -199,11 +228,21 @@ impl Submission {
// no matter if the imservice got activated, // no matter if the imservice got activated,
// keys must be released // keys must be released
SubmittedAction::VirtualKeyboard(keycodes) => { SubmittedAction::VirtualKeyboard(keycodes) => {
self.virtual_keyboard.switch( let keycodes_count = keycodes.len();
&keycodes, match keycodes_count {
PressType::Released, 1 => {
time, 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. /// Changes keymap and clears pressed keys and modifiers.
/// ///
/// It's not obvious if clearing is the right thing to do, /// 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. /// Alternatively, modifiers could be restored on the new keymap.
/// That approach might be difficult /// That approach might be difficult
/// due to modifiers meaning different things in different keymaps. /// due to modifiers meaning different things in different keymaps.
pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) { fn select_keymap(&mut self, idx: usize, time: Timestamp) {
self.clear_all_modifiers(); if self.keymap_idx != Some(idx) {
self.release_all_virtual_keys(time); self.keymap_idx = Some(idx);
self.virtual_keyboard.update_keymap(keyboard); 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);
} }
} }

View File

@ -40,21 +40,29 @@ pub fn check_layout_file(path: &str) {
) )
} }
fn check_sym_presence( fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool {
state: &xkb::State,
sym_name: &str,
handler: &mut dyn logging::Handler,
) {
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS); let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
if sym == xkb::KEY_NoSymbol { if sym == xkb::KEY_NoSymbol {
panic!(format!("Entered invalid keysym: {}", sym_name)); panic!(format!("Entered invalid keysym: {}", sym_name));
} }
let map = state.get_keymap(); let map = state.get_keymap();
let range = map.min_keycode()..=map.max_keycode(); 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) .find(|s| **s == sym)
.is_some(); .is_some()
if !found { }
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( handler.handle(
logging::Level::Surprise, logging::Level::Surprise,
&format!("There's no way to input the keysym {} on this layout", sym_name), &format!("There's no way to input the keysym {} on this layout", sym_name),
@ -72,25 +80,26 @@ fn check_layout(layout: Layout, allow_missing_return: bool) {
let layout = layout.expect("layout broken"); let layout = layout.expect("layout broken");
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); let xkb_states: Vec<xkb::State> = 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 keymap_str = layout.keymap_str check_sym_presence(&xkb_states, "BackSpace", &mut handler);
.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 state = xkb::State::new(&keymap);
check_sym_presence(&state, "BackSpace", &mut handler);
let mut printer = logging::Print; let mut printer = logging::Print;
check_sym_presence( check_sym_presence(
&state, &xkb_states,
"Return", "Return",
if allow_missing_return { &mut printer } if allow_missing_return { &mut printer }
else { &mut handler }, else { &mut handler },
@ -102,10 +111,19 @@ fn check_layout(layout: Layout, allow_missing_return: bool) {
for (_x, button) in row.get_buttons() { for (_x, button) in row.get_buttons() {
let keystate = button.state.borrow(); let keystate = button.state.borrow();
for keycode in &keystate.keycodes { 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 => { xkb::KEY_NoSymbol => {
eprintln!("{}", keymap_str); eprintln!(
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name); "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,
);
}, },
_ => {}, _ => {},
} }

View File

@ -203,6 +203,23 @@ pub fn vec_remove<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, pred: F) -> Option<T>
idx.map(|idx| v.remove(idx)) 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<T, I: Clone + Iterator<Item=T>>(iter: I)
-> impl Iterator<Item=(T, usize)>
{
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -217,4 +234,12 @@ mod tests {
assert_eq!(s.insert(Pointer(Rc::new(2u32))), true); assert_eq!(s.insert(Pointer(Rc::new(2u32))), true);
assert_eq!(s.remove(&Pointer(first)), true); assert_eq!(s.remove(&Pointer(first)), true);
} }
#[test]
fn check_count() {
assert_eq!(
cycle_count(5..8).take(7).collect::<Vec<_>>(),
vec![(5, 0), (6, 0), (7, 0), (5, 1), (6, 1), (7, 1), (5, 2)]
);
}
} }

View File

@ -1,20 +1,47 @@
/*! Managing the events belonging to virtual-keyboard interface. */ /*! Managing the events belonging to virtual-keyboard interface. */
use ::keyboard::{ KeyCode, Modifiers, PressType }; use ::keyboard::{ Modifiers, PressType };
use ::layout::c::LevelKeyboard;
use ::submission::Timestamp; use ::submission::Timestamp;
/// Standard xkb keycode
type KeyCode = u32;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*; use std::ffi::CStr;
use std::os::raw::c_void; use std::os::raw::{ c_char, c_void };
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ZwpVirtualKeyboardV1(*const c_void); 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] #[no_mangle]
extern "C" { extern "C" {
// From libc, to let KeyMap get deallocated.
fn close(fd: u32);
pub fn eek_virtual_keyboard_v1_key( pub fn eek_virtual_keyboard_v1_key(
virtual_keyboard: ZwpVirtualKeyboardV1, virtual_keyboard: ZwpVirtualKeyboardV1,
timestamp: u32, timestamp: u32,
@ -24,13 +51,15 @@ pub mod c {
pub fn eek_virtual_keyboard_update_keymap( pub fn eek_virtual_keyboard_update_keymap(
virtual_keyboard: ZwpVirtualKeyboardV1, virtual_keyboard: ZwpVirtualKeyboardV1,
keyboard: LevelKeyboard, keymap: *const KeyMap,
); );
pub fn eek_virtual_keyboard_set_modifiers( pub fn eek_virtual_keyboard_set_modifiers(
virtual_keyboard: ZwpVirtualKeyboardV1, virtual_keyboard: ZwpVirtualKeyboardV1,
modifiers: u32, 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 // TODO: error out if keymap not set
pub fn switch( pub fn switch(
&self, &self,
keycodes: &[KeyCode], keycode: KeyCode,
action: PressType, action: PressType,
timestamp: Timestamp, timestamp: Timestamp,
) { ) {
let keycodes_count = keycodes.len(); let keycode = keycode - 8;
for keycode in keycodes.iter() { unsafe {
let keycode = keycode - 8; c::eek_virtual_keyboard_v1_key(
match (action, keycodes_count) { self.0, timestamp.0, keycode, action.clone() as u32
// 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, _) => {},
}
} }
} }
@ -80,9 +89,12 @@ impl VirtualKeyboard {
} }
} }
pub fn update_keymap(&self, keyboard: LevelKeyboard) { pub fn update_keymap(&self, keymap: &c::KeyMap) {
unsafe { unsafe {
c::eek_virtual_keyboard_update_keymap(self.0, keyboard); c::eek_virtual_keyboard_update_keymap(
self.0,
keymap as *const c::KeyMap,
);
} }
} }
} }

View File

@ -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, zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->keymap.fd, keyboard->keymap.fd_len); keymap->fd, keymap->fd_len);
} }
void void