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/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); 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/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() -> () { diff --git a/src/symbol.rs b/src/action.rs similarity index 84% rename from src/symbol.rs rename to src/action.rs index e2262a66..06f789d8 100644 --- a/src/symbol.rs +++ b/src/action.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, -} diff --git a/src/data.rs b/src/data.rs index 7dc08f52..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 @@ -131,11 +132,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,41 +305,73 @@ 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), - symbol: create_symbol( + let button_actions: Vec<(&str, ::action::Action)> + = button_names.iter().map(|name| {( + *name, + create_action( &self.buttons, name, self.views.keys().collect() - ), - })) - )}); + ) + )}).collect(); - let button_states = - HashMap::>>::from_iter( + 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(), + KeyState { + pressed: false, + locked: false, + keycodes, + action, + } + ) + }); + + 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(), @@ -358,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() )) @@ -380,11 +417,11 @@ impl Layout { } } -fn create_symbol( +fn create_action( button_info: &HashMap, name: &str, view_names: Vec<&String>, -) -> ::symbol::Symbol { +) -> ::action::Action { let default_meta = ButtonMeta::default(); let symbol_meta = button_info.get(name) .unwrap_or(&default_meta); @@ -410,64 +447,68 @@ fn create_symbol( 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 { - 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)) => ::action::Action::SetLevel( + filter_view_name(name, view_name.clone(), &view_names) + ), + Some(Action::Locking { + lock_view, unlock_view + }) => ::action::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) => ::action::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 => ::action::Action::Submit { + text: None, + keys: keysyms.into_iter().map(::action::KeySym).collect(), }, } } @@ -638,7 +679,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) @@ -650,7 +708,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")) ); @@ -674,7 +732,7 @@ mod tests { #[test] fn test_key_unicode() { assert_eq!( - create_symbol( + create_action( &hashmap!{ ".".into() => ButtonMeta { icon: None, @@ -687,11 +745,9 @@ mod tests { ".", Vec::new() ), - ::symbol::Symbol { - action: ::symbol::Action::Submit { - text: None, - keys: vec!(::symbol::KeySym("U002E".into())), - }, + ::action::Action::Submit { + text: None, + keys: vec!(::action::KeySym("U002E".into())), } ); } diff --git a/src/keyboard.h b/src/keyboard.h index 8011f345..6baeeb8c 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -1,26 +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; -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); +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 89369ea0..10d22747 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,13 +1,12 @@ /*! State of the emulated keyboard and keys */ -use std::cell::RefCell; use std::collections::HashMap; use std::fmt; use std::io; use std::rc::Rc; use std::string::FromUtf8Error; - -use ::symbol::{ Symbol, Action }; + +use ::action::Action; use std::io::Write; use std::iter::{ FromIterator, IntoIterator }; @@ -16,21 +15,28 @@ 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; + + use std::os::raw::c_void; pub type CKeyState = c::Wrapped; - // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers + #[repr(transparent)] + pub struct ZwpVirtualKeyboardV1(*const c_void); #[no_mangle] - pub extern "C" - fn squeek_key_free(key: CKeyState) { - unsafe { key.unwrap() }; // reference dropped + 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 #[no_mangle] pub extern "C" @@ -44,15 +50,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 { @@ -66,73 +64,45 @@ 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); - } #[no_mangle] pub extern "C" - fn squeek_key_to_keymap_entry( - key_name: *const c_char, + fn squeek_key_press( key: CKeyState, - ) -> *const c_char { - let key_name = as_cstr(&key_name) - .expect("Missing key name") - .to_str() - .expect("Bad key name"); + virtual_keyboard: *mut ZwpVirtualKeyboardV1, + press: u32, + timestamp: u32, + ) { + let key = key.clone_ref(); + let mut key = key.borrow_mut(); + key.pressed = press != 0; - 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() + let keycodes_count = key.keycodes.len(); + for keycode in key.keycodes.iter() { + let keycode = keycode - 8; + 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, _) => {}, + } + } } } @@ -140,8 +110,10 @@ pub mod c { pub struct KeyState { pub pressed: bool, pub locked: bool, - pub keycode: Option, - pub symbol: Symbol, + /// 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, } /// Generates a mapping where each key gets a keycode, starting from 8 @@ -178,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!( @@ -191,24 +163,17 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - let state = state.borrow(); - if let ::symbol::Action::Submit { text: _, keys } = &state.symbol.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 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) { + write!( + buf, + " <{}> = {};", - keys[0].0, - state.keycode.unwrap() - )?; - }, - }; + named_keysym.0, + keycode, + )?; + } } } @@ -224,8 +189,8 @@ pub fn generate_keymap( )?; for (name, state) in keystates.iter() { - if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().symbol.action { - if let Some(keysym) = keys.iter().next() { + if let Action::Submit { text: _, keys } = &state.action { + for keysym in keys.iter() { write!( buf, " @@ -255,5 +220,44 @@ 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); + } +} diff --git a/src/layout.rs b/src/layout.rs index 14abb511..a37b8875 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 { @@ -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( @@ -264,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, @@ -279,11 +270,11 @@ pub mod c { ) { let layout = unsafe { &mut *layout }; - let view_name = match key.to_owned().symbol.action { - ::symbol::Action::SetLevel(name) => { + let view_name = match key.to_owned().action { + 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(); @@ -320,9 +311,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| { @@ -495,10 +484,8 @@ pub mod c { Rc::new(RefCell::new(::keyboard::KeyState { pressed: false, locked: false, - keycode: None, - symbol: Symbol { - action: Action::SetLevel("default".into()), - } + keycodes: Vec::new(), + action: Action::SetLevel("default".into()), })) } diff --git a/src/lib.rs b/src/lib.rs index 373cca33..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; @@ -11,6 +12,6 @@ pub mod imservice; mod keyboard; mod layout; mod resources; -mod symbol; +mod action; mod util; mod xdg; 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)) + ) +} 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); +} 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 } +