From 46d3312c860003c1bece12a2f97060a1a186d320 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 07:56:56 +0000 Subject: [PATCH 01/10] cleanup: Unused symbol functions --- src/keyboard.h | 11 -------- src/keyboard.rs | 68 ------------------------------------------------- src/layout.rs | 13 +--------- 3 files changed, 1 insertion(+), 91 deletions(-) diff --git a/src/keyboard.h b/src/keyboard.h index 8011f345..f806e262 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -6,21 +6,10 @@ struct squeek_key; -struct squeek_key *squeek_key_new(uint32_t keycode); -void squeek_key_free(struct squeek_key *key); -void squeek_key_add_symbol(struct squeek_key* key, - const char *element_name, - const char *text, uint32_t keyval, - const char *label, const char *icon, - const char *tooltip); uint32_t squeek_key_is_pressed(struct squeek_key *key); void squeek_key_set_pressed(struct squeek_key *key, uint32_t pressed); uint32_t squeek_key_is_locked(struct squeek_key *key); void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed); uint32_t squeek_key_get_keycode(struct squeek_key *key); -void squeek_key_set_keycode(struct squeek_key *key, uint32_t keycode); uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1); - -struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key); -const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key); #endif diff --git a/src/keyboard.rs b/src/keyboard.rs index 89369ea0..13edf2f5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -25,12 +25,6 @@ pub mod c { // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers - #[no_mangle] - pub extern "C" - fn squeek_key_free(key: CKeyState) { - unsafe { key.unwrap() }; // reference dropped - } - /// Compares pointers to the data #[no_mangle] pub extern "C" @@ -72,68 +66,6 @@ pub mod c { fn squeek_key_get_keycode(key: CKeyState) -> u32 { return key.to_owned().keycode.unwrap_or(0u32); } - - #[no_mangle] - pub extern "C" - fn squeek_key_to_keymap_entry( - key_name: *const c_char, - key: CKeyState, - ) -> *const c_char { - let key_name = as_cstr(&key_name) - .expect("Missing key name") - .to_str() - .expect("Bad key name"); - - let symbol_name = match key.to_owned().symbol.action { - Action::Submit { text: Some(text), .. } => { - Some( - text.clone() - .into_string().expect("Bad symbol") - ) - }, - _ => None, - }; - - let inner = match symbol_name { - Some(name) => format!("[ {} ]", name), - _ => format!("[ ]"), - }; - - CString::new(format!(" key <{}> {{ {} }};\n", key_name, inner)) - .expect("Couldn't convert string") - .into_raw() - } - - #[no_mangle] - pub extern "C" - fn squeek_key_get_action_name( - key_name: *const c_char, - key: CKeyState, - ) -> *const c_char { - let key_name = as_cstr(&key_name) - .expect("Missing key name") - .to_str() - .expect("Bad key name"); - - let symbol_name = match key.to_owned().symbol.action { - Action::Submit { text: Some(text), .. } => { - Some( - text.clone() - .into_string().expect("Bad symbol text") - ) - }, - _ => None - }; - - let inner = match symbol_name { - Some(name) => format!("[ {} ]", name), - _ => format!("[ ]"), - }; - - CString::new(format!(" key <{}> {{ {} }};\n", key_name, inner)) - .expect("Couldn't convert string") - .into_raw() - } } #[derive(Debug, Clone)] diff --git a/src/layout.rs b/src/layout.rs index 14abb511..44192890 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -144,18 +144,7 @@ pub mod c { let button = unsafe { &*button }; ::keyboard::c::CKeyState::wrap(button.state.clone()) } - - /// Really should just return the label - #[no_mangle] - pub extern "C" - fn squeek_button_get_symbol( - button: *const ::layout::Button, - ) -> *const Symbol { - let button = unsafe { &*button }; - let state = button.state.borrow(); - &state.symbol as *const Symbol - } - + #[no_mangle] pub extern "C" fn squeek_button_get_label( From ef38fd3e640487d3e618983c9ac14f9978d45261 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 08:06:17 +0000 Subject: [PATCH 02/10] symbol: Eliminate in favor of Action --- src/data.rs | 60 +++++++++++++++++++++---------------------------- src/keyboard.rs | 9 ++++---- src/layout.rs | 6 ++--- src/symbol.rs | 7 ------ 4 files changed, 33 insertions(+), 49 deletions(-) diff --git a/src/data.rs b/src/data.rs index 7dc08f52..29fa8065 100644 --- a/src/data.rs +++ b/src/data.rs @@ -319,7 +319,7 @@ impl Layout { pressed: false, locked: false, keycode: keycodes.get(*name).map(|k| *k), - symbol: create_symbol( + action: create_action( &self.buttons, name, self.views.keys().collect() @@ -380,11 +380,11 @@ impl Layout { } } -fn create_symbol( +fn create_action( button_info: &HashMap, name: &str, view_names: Vec<&String>, -) -> ::symbol::Symbol { +) -> ::symbol::Action { let default_meta = ButtonMeta::default(); let symbol_meta = button_info.get(name) .unwrap_or(&default_meta); @@ -440,34 +440,28 @@ fn create_symbol( }; match &symbol_meta.action { - Some(Action::SetView(view_name)) => ::symbol::Symbol { - action: ::symbol::Action::SetLevel( - filter_view_name(name, view_name.clone(), &view_names) + Some(Action::SetView(view_name)) => ::symbol::Action::SetLevel( + filter_view_name(name, view_name.clone(), &view_names) + ), + Some(Action::Locking { + lock_view, unlock_view + }) => ::symbol::Action::LockLevel { + lock: filter_view_name(name, lock_view.clone(), &view_names), + unlock: filter_view_name( + name, + unlock_view.clone(), + &view_names ), }, - Some(Action::Locking { lock_view, unlock_view }) => ::symbol::Symbol { - action: ::symbol::Action::LockLevel { - lock: filter_view_name(name, lock_view.clone(), &view_names), - unlock: filter_view_name( - name, - unlock_view.clone(), - &view_names - ), - }, + Some(Action::ShowPrefs) => ::symbol::Action::Submit { + text: None, + keys: Vec::new(), }, - Some(Action::ShowPrefs) => ::symbol::Symbol { - action: ::symbol::Action::Submit { - text: None, - keys: Vec::new(), - }, - }, - None => ::symbol::Symbol { - action: ::symbol::Action::Submit { - text: None, - keys: vec!( - ::symbol::KeySym(keysym.unwrap()), - ), - }, + None => ::symbol::Action::Submit { + text: None, + keys: vec!( + ::symbol::KeySym(keysym.unwrap()), + ), }, } } @@ -674,7 +668,7 @@ mod tests { #[test] fn test_key_unicode() { assert_eq!( - create_symbol( + create_action( &hashmap!{ ".".into() => ButtonMeta { icon: None, @@ -687,11 +681,9 @@ mod tests { ".", Vec::new() ), - ::symbol::Symbol { - action: ::symbol::Action::Submit { - text: None, - keys: vec!(::symbol::KeySym("U002E".into())), - }, + ::symbol::Action::Submit { + text: None, + keys: vec!(::symbol::KeySym("U002E".into())), } ); } diff --git a/src/keyboard.rs b/src/keyboard.rs index 13edf2f5..4248c34f 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -7,7 +7,7 @@ use std::io; use std::rc::Rc; use std::string::FromUtf8Error; -use ::symbol::{ Symbol, Action }; +use ::symbol::Action; use std::io::Write; use std::iter::{ FromIterator, IntoIterator }; @@ -73,7 +73,8 @@ pub struct KeyState { pub pressed: bool, pub locked: bool, pub keycode: Option, - pub symbol: Symbol, + /// Static description of what the key does when pressed or released + pub action: Action, } /// Generates a mapping where each key gets a keycode, starting from 8 @@ -124,7 +125,7 @@ pub fn generate_keymap( for (name, state) in keystates.iter() { let state = state.borrow(); - if let ::symbol::Action::Submit { text: _, keys } = &state.symbol.action { + if let ::symbol::Action::Submit { text: _, keys } = &state.action { match keys.len() { 0 => eprintln!("Key {} has no keysyms", name), a => { @@ -156,7 +157,7 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().symbol.action { + if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().action { if let Some(keysym) = keys.iter().next() { write!( buf, diff --git a/src/layout.rs b/src/layout.rs index 44192890..da18d9e5 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -268,7 +268,7 @@ pub mod c { ) { let layout = unsafe { &mut *layout }; - let view_name = match key.to_owned().symbol.action { + let view_name = match key.to_owned().action { ::symbol::Action::SetLevel(name) => { Some(name.clone()) }, @@ -485,9 +485,7 @@ pub mod c { pressed: false, locked: false, keycode: None, - symbol: Symbol { - action: Action::SetLevel("default".into()), - } + action: Action::SetLevel("default".into()), })) } diff --git a/src/symbol.rs b/src/symbol.rs index e2262a66..06f789d8 100644 --- a/src/symbol.rs +++ b/src/symbol.rs @@ -37,10 +37,3 @@ pub enum Action { keys: Vec, }, } - -/// Contains a static description of a particular key's actions -#[derive(Debug, Clone, PartialEq)] -pub struct Symbol { - /// The action that this key performs - pub action: Action, -} From da5a83f367e9e34ed34762c6bf228d61ff5dcac4 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 08:11:28 +0000 Subject: [PATCH 03/10] action: Rename file to action.rs --- src/{symbol.rs => action.rs} | 0 src/data.rs | 16 ++++++++-------- src/keyboard.rs | 6 +++--- src/layout.rs | 12 +++++------- src/lib.rs | 2 +- 5 files changed, 17 insertions(+), 19 deletions(-) rename src/{symbol.rs => action.rs} (100%) diff --git a/src/symbol.rs b/src/action.rs similarity index 100% rename from src/symbol.rs rename to src/action.rs diff --git a/src/data.rs b/src/data.rs index 29fa8065..95a2b7ee 100644 --- a/src/data.rs +++ b/src/data.rs @@ -384,7 +384,7 @@ fn create_action( button_info: &HashMap, name: &str, view_names: Vec<&String>, -) -> ::symbol::Action { +) -> ::action::Action { let default_meta = ButtonMeta::default(); let symbol_meta = button_info.get(name) .unwrap_or(&default_meta); @@ -440,12 +440,12 @@ fn create_action( }; match &symbol_meta.action { - Some(Action::SetView(view_name)) => ::symbol::Action::SetLevel( + Some(Action::SetView(view_name)) => ::action::Action::SetLevel( filter_view_name(name, view_name.clone(), &view_names) ), Some(Action::Locking { lock_view, unlock_view - }) => ::symbol::Action::LockLevel { + }) => ::action::Action::LockLevel { lock: filter_view_name(name, lock_view.clone(), &view_names), unlock: filter_view_name( name, @@ -453,14 +453,14 @@ fn create_action( &view_names ), }, - Some(Action::ShowPrefs) => ::symbol::Action::Submit { + Some(Action::ShowPrefs) => ::action::Action::Submit { text: None, keys: Vec::new(), }, - None => ::symbol::Action::Submit { + None => ::action::Action::Submit { text: None, keys: vec!( - ::symbol::KeySym(keysym.unwrap()), + ::action::KeySym(keysym.unwrap()), ), }, } @@ -681,9 +681,9 @@ mod tests { ".", Vec::new() ), - ::symbol::Action::Submit { + ::action::Action::Submit { text: None, - keys: vec!(::symbol::KeySym("U002E".into())), + keys: vec!(::action::KeySym("U002E".into())), } ); } diff --git a/src/keyboard.rs b/src/keyboard.rs index 4248c34f..16e7c035 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -7,7 +7,7 @@ use std::io; use std::rc::Rc; use std::string::FromUtf8Error; -use ::symbol::Action; +use ::action::Action; use std::io::Write; use std::iter::{ FromIterator, IntoIterator }; @@ -125,7 +125,7 @@ pub fn generate_keymap( for (name, state) in keystates.iter() { let state = state.borrow(); - if let ::symbol::Action::Submit { text: _, keys } = &state.action { + if let Action::Submit { text: _, keys } = &state.action { match keys.len() { 0 => eprintln!("Key {} has no keysyms", name), a => { @@ -157,7 +157,7 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().action { + if let Action::Submit { text: _, keys } = &state.borrow().action { if let Some(keysym) = keys.iter().next() { write!( buf, diff --git a/src/layout.rs b/src/layout.rs index da18d9e5..775491b9 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -23,9 +23,9 @@ use std::ffi::CString; use std::rc::Rc; use std::vec::Vec; -use ::keyboard::*; +use ::action::Action; use ::float_ord::FloatOrd; -use ::symbol::*; +use ::keyboard::*; /// Gathers stuff defined in C or called by C pub mod c { @@ -269,10 +269,10 @@ pub mod c { let layout = unsafe { &mut *layout }; let view_name = match key.to_owned().action { - ::symbol::Action::SetLevel(name) => { + Action::SetLevel(name) => { Some(name.clone()) }, - ::symbol::Action::LockLevel { lock, unlock } => { + Action::LockLevel { lock, unlock } => { let locked = { let key = key.clone_ref(); let mut key = key.borrow_mut(); @@ -309,9 +309,7 @@ pub mod c { /// Sets button and row sizes according to their contents. #[no_mangle] pub extern "C" - fn squeek_layout_place_contents( - layout: *mut Layout, - ) { + fn squeek_layout_place_contents(layout: *mut Layout) { let layout = unsafe { &mut *layout }; for view in layout.views.values_mut() { let sizes: Vec> = view.rows.iter().map(|row| { diff --git a/src/lib.rs b/src/lib.rs index 373cca33..003b1c6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,6 @@ pub mod imservice; mod keyboard; mod layout; mod resources; -mod symbol; +mod action; mod util; mod xdg; From 10bad4ebe383b1ba08f8f947726490dbb5040807 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 08:15:03 +0000 Subject: [PATCH 04/10] warnings: Fix and silence --- src/data.rs | 2 +- src/keyboard.rs | 4 ---- src/layout.rs | 2 ++ src/lib.rs | 3 ++- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/data.rs b/src/data.rs index 95a2b7ee..98266c90 100644 --- a/src/data.rs +++ b/src/data.rs @@ -644,7 +644,7 @@ mod tests { /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME #[test] fn fallbacks_order() { - let (layout, source, _failure) = load_layout( + let (_layout, source, _failure) = load_layout( "nb", Some(PathBuf::from("tests")) ); diff --git a/src/keyboard.rs b/src/keyboard.rs index 16e7c035..9b7c7e9b 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -16,10 +16,6 @@ use std::iter::{ FromIterator, IntoIterator }; pub mod c { use super::*; use ::util::c; - use ::util::c::as_cstr; - - use std::ffi::CString; - use std::os::raw::c_char; pub type CKeyState = c::Wrapped; diff --git a/src/layout.rs b/src/layout.rs index 775491b9..bbbf3263 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -253,6 +253,8 @@ pub mod c { angle: i32 ) -> u32; + // CKeyState is safe to pass to C as long as nothing dereferences it + #[allow(improper_ctypes)] pub fn eek_keyboard_set_key_locked( keyboard: *mut LevelKeyboard, key: ::keyboard::c::CKeyState, diff --git a/src/lib.rs b/src/lib.rs index 003b1c6b..0425601c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #[macro_use] extern crate bitflags; -#[macro_use] +#[allow(unused_imports)] +#[macro_use] // only for tests extern crate maplit; extern crate serde; extern crate xkbcommon; From 31fdde4da980b6134e52e64358b1c799ff345ee7 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 08:52:10 +0000 Subject: [PATCH 05/10] keycodes: Moved submission to Rust --- eek/eek-keyboard.c | 19 +++------------- eekboard/key-emitter.c | 1 - src/keyboard.h | 12 ++++++++--- src/keyboard.rs | 49 +++++++++++++++++++++++++++++++----------- src/wayland.c | 9 ++++++++ 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index bf50c751..794f3e99 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -31,6 +31,7 @@ #include #include "eek-enumtypes.h" +#include "eekboard/eekboard-context-service.h" #include "eekboard/key-emitter.h" #include "keymap.h" #include "src/keyboard.h" @@ -93,17 +94,8 @@ set_level_from_press (LevelKeyboard *keyboard, struct squeek_key *key) } void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) { - squeek_key_set_pressed(key, TRUE); keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key); - - // Only take action about setting level *after* the key has taken effect, i.e. on release - //set_level_from_press (keyboard, key); - - // "Borrowed" from eek-context-service; doesn't influence the state but forwards the event - - guint keycode = squeek_key_get_keycode (key); - - emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp); + squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_PRESS, timestamp); } void eek_keyboard_release_key(LevelKeyboard *keyboard, @@ -118,12 +110,7 @@ void eek_keyboard_release_key(LevelKeyboard *keyboard, } set_level_from_press (keyboard, key); - - // "Borrowed" from eek-context-service; doesn't influence the state but forwards the event - - guint keycode = squeek_key_get_keycode (key); - - emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp); + squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_RELEASE, timestamp); } void level_keyboard_deinit(LevelKeyboard *self) { diff --git a/eekboard/key-emitter.c b/eekboard/key-emitter.c index dcd5131d..b652441e 100644 --- a/eekboard/key-emitter.c +++ b/eekboard/key-emitter.c @@ -131,7 +131,6 @@ emit_key_activated (EekboardContextService *manager, */ SeatEmitter emitter = {0}; emitter.virtual_keyboard = manager->virtual_keyboard; - emitter.keymap = keyboard->keymap; update_modifier_info (&emitter); send_fake_key (&emitter, keyboard, keycode, pressed, timestamp); } diff --git a/src/keyboard.h b/src/keyboard.h index f806e262..6baeeb8c 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -1,15 +1,21 @@ #ifndef __KEYBOARD_H #define __KEYBOARD_H -#include "stdbool.h" #include "inttypes.h" +#include "stdbool.h" +#include "virtual-keyboard-unstable-v1-client-protocol.h" struct squeek_key; uint32_t squeek_key_is_pressed(struct squeek_key *key); -void squeek_key_set_pressed(struct squeek_key *key, uint32_t pressed); uint32_t squeek_key_is_locked(struct squeek_key *key); void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed); -uint32_t squeek_key_get_keycode(struct squeek_key *key); uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1); + +enum key_press { + KEY_RELEASE = 0, + KEY_PRESS = 1, +}; + +void squeek_key_press(struct squeek_key *key, struct zwp_virtual_keyboard_v1*, enum key_press press, uint32_t timestamp); #endif diff --git a/src/keyboard.rs b/src/keyboard.rs index 9b7c7e9b..025f9563 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -17,8 +17,25 @@ pub mod c { use super::*; use ::util::c; + use std::os::raw::c_void; + pub type CKeyState = c::Wrapped; + #[repr(transparent)] + pub struct ZwpVirtualKeyboardV1(*const c_void); + + #[no_mangle] + extern "C" { + /// Checks if point falls within bounds, + /// which are relative to origin and rotated by angle (I think) + pub fn eek_virtual_keyboard_v1_key( + virtual_keyboard: *mut ZwpVirtualKeyboardV1, + timestamp: u32, + keycode: u32, + press: u32, + ); + } + // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers /// Compares pointers to the data @@ -34,15 +51,7 @@ pub mod c { //let key = unsafe { Rc::from_raw(key.0) }; return key.to_owned().pressed as u32; } - - #[no_mangle] - pub extern "C" - fn squeek_key_set_pressed(key: CKeyState, pressed: u32) { - let key = key.clone_ref(); - let mut key = key.borrow_mut(); - key.pressed = pressed != 0; - } - + #[no_mangle] pub extern "C" fn squeek_key_is_locked(key: CKeyState) -> u32 { @@ -56,11 +65,27 @@ pub mod c { let mut key = key.borrow_mut(); key.locked = locked != 0; } - + #[no_mangle] pub extern "C" - fn squeek_key_get_keycode(key: CKeyState) -> u32 { - return key.to_owned().keycode.unwrap_or(0u32); + fn squeek_key_press( + key: CKeyState, + virtual_keyboard: *mut ZwpVirtualKeyboardV1, + press: u32, + timestamp: u32, + ) { + let key = key.clone_ref(); + let mut key = key.borrow_mut(); + key.pressed = press != 0; + + if let Some(keycode) = key.keycode { + let keycode = keycode - 8; + unsafe { + eek_virtual_keyboard_v1_key( + virtual_keyboard, timestamp, keycode, press + ); + } + } } } diff --git a/src/wayland.c b/src/wayland.c index e441e2e7..cd10609e 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -1,3 +1,12 @@ #include "wayland.h" struct squeek_wayland *squeek_wayland = NULL; + +// The following functions only exist +// to create linkable symbols out of inline functions, +// because those are not directly callable from Rust + +void +eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t time, uint32_t key, uint32_t state) { + zwp_virtual_keyboard_v1_key(zwp_virtual_keyboard_v1, time, key, state); +} From 212e55d7cf9675d55976dd213995b715adfe91e6 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 09:23:50 +0000 Subject: [PATCH 06/10] caches: Stop using due to bugginness The button cache is storing only button outlines (not useful to us), and it only cares about active/inactive states (there are more). This will have to be rewritten sanely, so for the time being, disabling it is not going to deal a big performance penalty. --- eek/eek-renderer.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 5dceb655..8659bfba 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -193,12 +193,7 @@ static void render_button_in_context(EekRenderer *self, /* render outline */ EekBounds bounds = squeek_button_get_bounds(place->button); - if (active) - outline_surface_cache = priv->active_outline_surface_cache; - else - outline_surface_cache = priv->outline_surface_cache; - - outline_surface = g_hash_table_lookup (outline_surface_cache, place->button); + outline_surface = NULL; if (!outline_surface) { cairo_t *cr; @@ -221,10 +216,6 @@ static void render_button_in_context(EekRenderer *self, cairo_restore (cr); cairo_destroy (cr); - - g_hash_table_insert (outline_surface_cache, - (gpointer)place->button, - outline_surface); } cairo_set_source_surface (cr, outline_surface, 0.0, 0.0); cairo_paint (cr); From 9cd439767eb0bc358215e39292b87e8b54effcf8 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 15:08:41 +0000 Subject: [PATCH 07/10] virtual_keyboard: Submit multi-codepoint strings --- src/data.rs | 151 ++++++++++++++++++++++++++++------------- src/keyboard.rs | 58 ++++++++++------ src/layout.rs | 2 +- tests/layout_key3.yaml | 17 +++++ 4 files changed, 157 insertions(+), 71 deletions(-) create mode 100644 tests/layout_key3.yaml diff --git a/src/data.rs b/src/data.rs index 98266c90..44cf2268 100644 --- a/src/data.rs +++ b/src/data.rs @@ -131,11 +131,15 @@ fn load_layout( fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) { match attempt { Some(( - LoadError::BadData(Error::Missing(_e)), - DataSource::File(_file) + LoadError::BadData(Error::Missing(e)), + DataSource::File(file) )) => { + eprintln!( + "Tried file {:?}, but it's missing: {}", + file, e + ); // Missing file, not to worry. TODO: print in debug logging level - } + }, Some((e, source)) => { eprintln!( "Failed to load layout from {}: {}, trying builtin", @@ -300,32 +304,56 @@ impl Layout { let button_names: HashSet<&str> = HashSet::from_iter(button_names); - let keycodes = generate_keycodes( - button_names.iter() - .map(|name| *name) - .filter(|name| { - match self.buttons.get(*name) { - // buttons with defined action can't emit keysyms - // and so don't need keycodes - Some(ButtonMeta { action: Some(_), .. }) => false, - _ => true, - } - }) - ); - - let button_states = button_names.iter().map(|name| {( - String::from(*name), - Rc::new(RefCell::new(KeyState { - pressed: false, - locked: false, - keycode: keycodes.get(*name).map(|k| *k), - action: create_action( + let button_actions: Vec<(&str, ::action::Action)> + = button_names.iter().map(|name| {( + *name, + create_action( &self.buttons, name, self.views.keys().collect() - ), - })) - )}); + ) + )}).collect(); + + let keymap: HashMap = generate_keycodes( + button_actions.iter() + .filter_map(|(_name, action)| { + match action { + ::action::Action::Submit { + text: _, keys, + } => Some(keys), + _ => None, + } + }) + .flatten() + .map(|named_keysym| named_keysym.0.as_str()) + ); + + let button_states = button_actions.into_iter().map(|(name, action)| { + let keycodes = match &action { + ::action::Action::Submit { text: _, keys } => { + keys.iter().map(|named_keycode| { + *keymap.get(named_keycode.0.as_str()) + .expect( + format!( + "keycode {} in key {} missing from keymap", + named_keycode.0, + name + ).as_str() + ) + }).collect() + }, + _ => Vec::new(), + }; + ( + name.into(), + Rc::new(RefCell::new(KeyState { + pressed: false, + locked: false, + keycodes, + action, + })) + ) + }); let button_states = HashMap::>>::from_iter( @@ -410,33 +438,45 @@ fn create_action( xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol } - let keysym = match &symbol_meta.action { - Some(_) => None, - None => Some(match &symbol_meta.keysym { - Some(keysym) => match keysym_valid(keysym.as_str()) { + let keysyms = match &symbol_meta.action { + // Non-submit action + Some(_) => Vec::new(), + // Submit action + None => match &symbol_meta.keysym { + // Keysym given explicitly + Some(keysym) => vec!(match keysym_valid(keysym.as_str()) { true => keysym.clone(), false => { eprintln!("Keysym name invalid: {}", keysym); "space".into() // placeholder }, - }, + }), + // Keysyms left open to derive + // TODO: when button name is meant diretly as xkb keysym name, + // mark it so, e.g. with a "#" None => match keysym_valid(name) { - true => String::from(name), - false => match name.chars().count() { - 1 => format!("U{:04X}", name.chars().next().unwrap() as u32), - // If the name is longer than 1 char, - // then it's not a single Unicode char, - // but was trying to be an identifier - _ => { - eprintln!( - "Could not derive a valid keysym for key {}", - name - ); - "space".into() // placeholder + // Button name is actually a valid xkb name + true => vec!(String::from(name)), + // Button name is not a valid xkb name, + // so assume it's a literal string to be submitted + false => { + if name.chars().count() == 0 { + // A name read from yaml with no valid Unicode. + // Highly improbable, but let's be safe. + eprintln!("Key {} doesn't have any characters", name); + vec!("space".into()) // placeholder + } else { + name.chars().map(|codepoint| { + let codepoint_string = codepoint.to_string(); + match keysym_valid(codepoint_string.as_str()) { + true => codepoint_string, + false => format!("U{:04X}", codepoint as u32), + } + }).collect() } }, }, - }), + }, }; match &symbol_meta.action { @@ -459,9 +499,7 @@ fn create_action( }, None => ::action::Action::Submit { text: None, - keys: vec!( - ::action::KeySym(keysym.unwrap()), - ), + keys: keysyms.into_iter().map(::action::KeySym).collect(), }, } } @@ -632,7 +670,24 @@ mod tests { ::layout::Label::Text(CString::new("test").unwrap()) ); } - + + /// Test multiple codepoints + #[test] + fn test_layout_unicode_multi() { + let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml")) + .unwrap() + .build() + .unwrap(); + assert_eq!( + out.views["base"] + .rows[0] + .buttons[0] + .state.borrow() + .keycodes.len(), + 2 + ); + } + #[test] fn parsing_fallback() { assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) diff --git a/src/keyboard.rs b/src/keyboard.rs index 025f9563..1041b8fb 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -78,12 +78,30 @@ pub mod c { let mut key = key.borrow_mut(); key.pressed = press != 0; - if let Some(keycode) = key.keycode { + let keycodes_count = key.keycodes.len(); + for keycode in key.keycodes.iter() { let keycode = keycode - 8; - unsafe { - eek_virtual_keyboard_v1_key( - virtual_keyboard, timestamp, keycode, press - ); + match (key.pressed, keycodes_count) { + // Pressing a key made out of a single keycode is simple: + // press on press, release on release. + (_, 1) => unsafe { + eek_virtual_keyboard_v1_key( + virtual_keyboard, timestamp, keycode, press + ); + }, + // A key made of multiple keycodes + // has to submit them one after the other + (true, _) => unsafe { + eek_virtual_keyboard_v1_key( + virtual_keyboard, timestamp, keycode, 1 + ); + eek_virtual_keyboard_v1_key( + virtual_keyboard, timestamp, keycode, 0 + ); + }, + // Design choice here: submit multiple all at press time + // and do nothing at release time + (false, _) => {}, } } } @@ -93,7 +111,8 @@ pub mod c { pub struct KeyState { pub pressed: bool, pub locked: bool, - pub keycode: Option, + /// A cache of raw keycodes derived from Action::Sumbit given a keymap + pub keycodes: Vec, /// Static description of what the key does when pressed or released pub action: Action, } @@ -147,22 +166,16 @@ pub fn generate_keymap( for (name, state) in keystates.iter() { let state = state.borrow(); if let Action::Submit { text: _, keys } = &state.action { - match keys.len() { - 0 => eprintln!("Key {} has no keysyms", name), - a => { - // TODO: don't ignore any keysyms - if a > 1 { - eprintln!("Key {} multiple keysyms", name); - } - write!( - buf, - " + if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); }; + for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) { + write!( + buf, + " <{}> = {};", - keys[0].0, - state.keycode.unwrap() - )?; - }, - }; + named_keysym.0, + keycode, + )?; + } } } @@ -179,7 +192,7 @@ pub fn generate_keymap( for (name, state) in keystates.iter() { if let Action::Submit { text: _, keys } = &state.borrow().action { - if let Some(keysym) = keys.iter().next() { + for keysym in keys.iter() { write!( buf, " @@ -209,5 +222,6 @@ pub fn generate_keymap( }};" )?; + //println!("{}", String::from_utf8(buf.clone()).unwrap()); String::from_utf8(buf).map_err(FormattingError::Utf) } diff --git a/src/layout.rs b/src/layout.rs index bbbf3263..a37b8875 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -484,7 +484,7 @@ pub mod c { Rc::new(RefCell::new(::keyboard::KeyState { pressed: false, locked: false, - keycode: None, + keycodes: Vec::new(), action: Action::SetLevel("default".into()), })) } diff --git a/tests/layout_key3.yaml b/tests/layout_key3.yaml new file mode 100644 index 00000000..7336830c --- /dev/null +++ b/tests/layout_key3.yaml @@ -0,0 +1,17 @@ +--- +# punctuation +row_spacing: 0 +button_spacing: 0 + +bounds: + x: 0 + y: 0 + width: 0 + height: 0 +views: + base: + - "か゚" # 2 codepoints +outlines: + default: + bounds: { x: 0, y: 0, width: 0, height: 0 } + From dc2bc46167cfabcf59f3a8abf20fcb548d4ea24c Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 15:38:21 +0000 Subject: [PATCH 08/10] keymap: Simplified key state passing There's no need to treat states as a shared resource before they are placed inside buttons. --- src/data.rs | 19 ++++++++++++++----- src/keyboard.rs | 6 ++---- src/util.rs | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/data.rs b/src/data.rs index 44cf2268..d0631621 100644 --- a/src/data.rs +++ b/src/data.rs @@ -19,6 +19,7 @@ use ::keyboard::{ }; use ::resources; use ::util::c::as_str; +use ::util::hash_map_map; use ::xdg; // traits, derives @@ -346,23 +347,31 @@ impl Layout { }; ( name.into(), - Rc::new(RefCell::new(KeyState { + KeyState { pressed: false, locked: false, keycodes, action, - })) + } ) }); - let button_states = - HashMap::>>::from_iter( + let button_states + = HashMap::::from_iter( button_states ); // TODO: generate from symbols let keymap_str = generate_keymap(&button_states)?; + let button_states_cache = hash_map_map( + button_states, + |name, state| {( + name, + Rc::new(RefCell::new(state)) + )} + ); + let views = HashMap::from_iter( self.views.iter().map(|(name, view)| {( name.clone(), @@ -386,7 +395,7 @@ impl Layout { &self.buttons, &self.outlines, name, - button_states.get(name.into()) + button_states_cache.get(name.into()) .expect("Button state not created") .clone() )) diff --git a/src/keyboard.rs b/src/keyboard.rs index 1041b8fb..9150e0c1 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,6 +1,5 @@ /*! State of the emulated keyboard and keys */ -use std::cell::RefCell; use std::collections::HashMap; use std::fmt; use std::io; @@ -151,7 +150,7 @@ impl From for FormattingError { /// Generates a de-facto single level keymap. TODO: actually drop second level pub fn generate_keymap( - keystates: &HashMap::>> + keystates: &HashMap:: ) -> Result { let mut buf: Vec = Vec::new(); writeln!( @@ -164,7 +163,6 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - let state = state.borrow(); if let Action::Submit { text: _, keys } = &state.action { if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); }; for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) { @@ -191,7 +189,7 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - if let Action::Submit { text: _, keys } = &state.borrow().action { + if let Action::Submit { text: _, keys } = &state.action { for keysym in keys.iter() { write!( buf, diff --git a/src/util.rs b/src/util.rs index 9289a40d..59b9f51b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,8 @@ +/*! Assorted helpers */ +use std::collections::HashMap; + +use std::iter::FromIterator; + pub mod c { use std::cell::RefCell; use std::ffi::{ CStr, CString }; @@ -101,3 +106,13 @@ pub mod c { } } } + +pub fn hash_map_map(map: HashMap, mut f: F) + -> HashMap + where F: FnMut(K, V) -> (K1, V1), + K1: std::cmp::Eq + std::hash::Hash +{ + HashMap::from_iter( + map.into_iter().map(|(key, value)| f(key, value)) + ) +} From b45edcd4fe673b2c622b4a30094851285974ca62 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 15:54:06 +0000 Subject: [PATCH 09/10] tests: Emulate clicking all submitting buttons --- examples/test_layout.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/examples/test_layout.rs b/examples/test_layout.rs index 95659545..b8fe0a1b 100644 --- a/examples/test_layout.rs +++ b/examples/test_layout.rs @@ -14,14 +14,37 @@ fn check_layout(name: &str) { .expect("layout broken"); let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); - xkb::Keymap::new_from_string( + + let keymap_str = layout.keymap_str + .clone() + .into_string().expect("Failed to decode keymap string"); + + let keymap = xkb::Keymap::new_from_string( &context, - layout.keymap_str - .clone() - .into_string().expect("Failed to decode keymap string"), + keymap_str.clone(), xkb::KEYMAP_FORMAT_TEXT_V1, xkb::KEYMAP_COMPILE_NO_FLAGS, ).expect("Failed to create keymap"); + + let state = xkb::State::new(&keymap); + + // "Press" each button with keysyms + for view in layout.views.values() { + for row in &view.rows { + for button in &row.buttons { + let keystate = button.state.borrow(); + for keycode in &keystate.keycodes { + match state.key_get_one_sym(*keycode) { + xkb::KEY_NoSymbol => { + eprintln!("{}", keymap_str); + panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name); + }, + _ => {}, + } + } + } + } + } } fn main() -> () { From ac68d5d0a8a4292fb65bb28a5470816c24904abd Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Wed, 9 Oct 2019 16:08:50 +0000 Subject: [PATCH 10/10] tests: Check keymap generation results --- src/keyboard.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 9150e0c1..10d22747 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -5,7 +5,7 @@ use std::fmt; use std::io; use std::rc::Rc; use std::string::FromUtf8Error; - + use ::action::Action; use std::io::Write; @@ -223,3 +223,41 @@ pub fn generate_keymap( //println!("{}", String::from_utf8(buf.clone()).unwrap()); String::from_utf8(buf).map_err(FormattingError::Utf) } + +#[cfg(test)] +mod tests { + use super::*; + + use xkbcommon::xkb; + + use ::action::KeySym; + + #[test] + fn test_keymap_multi() { + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + + let keymap_str = generate_keymap(&hashmap!{ + "ac".into() => KeyState { + action: Action::Submit { + text: None, + keys: vec!(KeySym("a".into()), KeySym("c".into())), + }, + keycodes: vec!(9, 10), + locked: false, + pressed: false, + }, + }).unwrap(); + + 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); + + assert_eq!(state.key_get_one_sym(9), xkb::KEY_a); + assert_eq!(state.key_get_one_sym(10), xkb::KEY_c); + } +}