Merge branch 'multicodepoint' into 'master'
Support submitting multi-codepoint sequences Closes #96 See merge request Librem5/squeekboard!207
This commit is contained in:
		@ -31,6 +31,7 @@
 | 
				
			|||||||
#include <glib/gprintf.h>
 | 
					#include <glib/gprintf.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "eek-enumtypes.h"
 | 
					#include "eek-enumtypes.h"
 | 
				
			||||||
 | 
					#include "eekboard/eekboard-context-service.h"
 | 
				
			||||||
#include "eekboard/key-emitter.h"
 | 
					#include "eekboard/key-emitter.h"
 | 
				
			||||||
#include "keymap.h"
 | 
					#include "keymap.h"
 | 
				
			||||||
#include "src/keyboard.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) {
 | 
					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);
 | 
					    keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
 | 
				
			||||||
 | 
					    squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_PRESS, timestamp);
 | 
				
			||||||
    // 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);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
					void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
				
			||||||
@ -118,12 +110,7 @@ void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    set_level_from_press (keyboard, key);
 | 
					    set_level_from_press (keyboard, key);
 | 
				
			||||||
 | 
					    squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_RELEASE, timestamp);
 | 
				
			||||||
    // "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);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void level_keyboard_deinit(LevelKeyboard *self) {
 | 
					void level_keyboard_deinit(LevelKeyboard *self) {
 | 
				
			||||||
 | 
				
			|||||||
@ -193,12 +193,7 @@ static void render_button_in_context(EekRenderer *self,
 | 
				
			|||||||
    /* render outline */
 | 
					    /* render outline */
 | 
				
			||||||
    EekBounds bounds = squeek_button_get_bounds(place->button);
 | 
					    EekBounds bounds = squeek_button_get_bounds(place->button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (active)
 | 
					    outline_surface = NULL;
 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!outline_surface) {
 | 
					    if (!outline_surface) {
 | 
				
			||||||
        cairo_t *cr;
 | 
					        cairo_t *cr;
 | 
				
			||||||
@ -221,10 +216,6 @@ static void render_button_in_context(EekRenderer *self,
 | 
				
			|||||||
        cairo_restore (cr);
 | 
					        cairo_restore (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cairo_destroy (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_set_source_surface (cr, outline_surface, 0.0, 0.0);
 | 
				
			||||||
    cairo_paint (cr);
 | 
					    cairo_paint (cr);
 | 
				
			||||||
 | 
				
			|||||||
@ -131,7 +131,6 @@ emit_key_activated (EekboardContextService *manager,
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
    SeatEmitter emitter = {0};
 | 
					    SeatEmitter emitter = {0};
 | 
				
			||||||
    emitter.virtual_keyboard = manager->virtual_keyboard;
 | 
					    emitter.virtual_keyboard = manager->virtual_keyboard;
 | 
				
			||||||
    emitter.keymap = keyboard->keymap;
 | 
					 | 
				
			||||||
    update_modifier_info (&emitter);
 | 
					    update_modifier_info (&emitter);
 | 
				
			||||||
    send_fake_key (&emitter, keyboard, keycode, pressed, timestamp);
 | 
					    send_fake_key (&emitter, keyboard, keycode, pressed, timestamp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,14 +14,37 @@ fn check_layout(name: &str) {
 | 
				
			|||||||
        .expect("layout broken");
 | 
					        .expect("layout broken");
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
 | 
					    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,
 | 
					        &context,
 | 
				
			||||||
        layout.keymap_str
 | 
					        keymap_str.clone(),
 | 
				
			||||||
            .clone()
 | 
					 | 
				
			||||||
            .into_string().expect("Failed to decode keymap string"),
 | 
					 | 
				
			||||||
        xkb::KEYMAP_FORMAT_TEXT_V1,
 | 
					        xkb::KEYMAP_FORMAT_TEXT_V1,
 | 
				
			||||||
        xkb::KEYMAP_COMPILE_NO_FLAGS,
 | 
					        xkb::KEYMAP_COMPILE_NO_FLAGS,
 | 
				
			||||||
    ).expect("Failed to create keymap");
 | 
					    ).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() -> () {
 | 
					fn main() -> () {
 | 
				
			||||||
 | 
				
			|||||||
@ -37,10 +37,3 @@ pub enum Action {
 | 
				
			|||||||
        keys: Vec<KeySym>,
 | 
					        keys: Vec<KeySym>,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/// 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,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										218
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								src/data.rs
									
									
									
									
									
								
							@ -19,6 +19,7 @@ use ::keyboard::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
use ::resources;
 | 
					use ::resources;
 | 
				
			||||||
use ::util::c::as_str;
 | 
					use ::util::c::as_str;
 | 
				
			||||||
 | 
					use ::util::hash_map_map;
 | 
				
			||||||
use ::xdg;
 | 
					use ::xdg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// traits, derives
 | 
					// traits, derives
 | 
				
			||||||
@ -131,11 +132,15 @@ fn load_layout(
 | 
				
			|||||||
fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) {
 | 
					fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) {
 | 
				
			||||||
    match attempt {
 | 
					    match attempt {
 | 
				
			||||||
        Some((
 | 
					        Some((
 | 
				
			||||||
            LoadError::BadData(Error::Missing(_e)),
 | 
					            LoadError::BadData(Error::Missing(e)),
 | 
				
			||||||
            DataSource::File(_file)
 | 
					            DataSource::File(file)
 | 
				
			||||||
        )) => {
 | 
					        )) => {
 | 
				
			||||||
 | 
					            eprintln!(
 | 
				
			||||||
 | 
					                "Tried file {:?}, but it's missing: {}",
 | 
				
			||||||
 | 
					                file, e
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            // Missing file, not to worry. TODO: print in debug logging level
 | 
					            // Missing file, not to worry. TODO: print in debug logging level
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
        Some((e, source)) => {
 | 
					        Some((e, source)) => {
 | 
				
			||||||
            eprintln!(
 | 
					            eprintln!(
 | 
				
			||||||
                "Failed to load layout from {}: {}, trying builtin",
 | 
					                "Failed to load layout from {}: {}, trying builtin",
 | 
				
			||||||
@ -300,41 +305,73 @@ impl Layout {
 | 
				
			|||||||
        let button_names: HashSet<&str>
 | 
					        let button_names: HashSet<&str>
 | 
				
			||||||
            = HashSet::from_iter(button_names);
 | 
					            = HashSet::from_iter(button_names);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let keycodes = generate_keycodes(
 | 
					        let button_actions: Vec<(&str, ::action::Action)>
 | 
				
			||||||
            button_names.iter()
 | 
					            = button_names.iter().map(|name| {(
 | 
				
			||||||
                .map(|name| *name)
 | 
					                *name,
 | 
				
			||||||
                .filter(|name| {
 | 
					                create_action(
 | 
				
			||||||
                    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(
 | 
					 | 
				
			||||||
                    &self.buttons,
 | 
					                    &self.buttons,
 | 
				
			||||||
                    name,
 | 
					                    name,
 | 
				
			||||||
                    self.views.keys().collect()
 | 
					                    self.views.keys().collect()
 | 
				
			||||||
                ),
 | 
					                )
 | 
				
			||||||
            }))
 | 
					            )}).collect();
 | 
				
			||||||
        )});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let button_states =
 | 
					        let keymap: HashMap<String, u32> = generate_keycodes(
 | 
				
			||||||
            HashMap::<String, Rc<RefCell<KeyState>>>::from_iter(
 | 
					            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::<String, KeyState>::from_iter(
 | 
				
			||||||
                button_states
 | 
					                button_states
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO: generate from symbols
 | 
					        // TODO: generate from symbols
 | 
				
			||||||
        let keymap_str = generate_keymap(&button_states)?;
 | 
					        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(
 | 
					        let views = HashMap::from_iter(
 | 
				
			||||||
            self.views.iter().map(|(name, view)| {(
 | 
					            self.views.iter().map(|(name, view)| {(
 | 
				
			||||||
                name.clone(),
 | 
					                name.clone(),
 | 
				
			||||||
@ -358,7 +395,7 @@ impl Layout {
 | 
				
			|||||||
                                    &self.buttons,
 | 
					                                    &self.buttons,
 | 
				
			||||||
                                    &self.outlines,
 | 
					                                    &self.outlines,
 | 
				
			||||||
                                    name,
 | 
					                                    name,
 | 
				
			||||||
                                    button_states.get(name.into())
 | 
					                                    button_states_cache.get(name.into())
 | 
				
			||||||
                                        .expect("Button state not created")
 | 
					                                        .expect("Button state not created")
 | 
				
			||||||
                                        .clone()
 | 
					                                        .clone()
 | 
				
			||||||
                                ))
 | 
					                                ))
 | 
				
			||||||
@ -380,11 +417,11 @@ impl Layout {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn create_symbol(
 | 
					fn create_action(
 | 
				
			||||||
    button_info: &HashMap<String, ButtonMeta>,
 | 
					    button_info: &HashMap<String, ButtonMeta>,
 | 
				
			||||||
    name: &str,
 | 
					    name: &str,
 | 
				
			||||||
    view_names: Vec<&String>,
 | 
					    view_names: Vec<&String>,
 | 
				
			||||||
) -> ::symbol::Symbol {
 | 
					) -> ::action::Action {
 | 
				
			||||||
    let default_meta = ButtonMeta::default();
 | 
					    let default_meta = ButtonMeta::default();
 | 
				
			||||||
    let symbol_meta = button_info.get(name)
 | 
					    let symbol_meta = button_info.get(name)
 | 
				
			||||||
        .unwrap_or(&default_meta);
 | 
					        .unwrap_or(&default_meta);
 | 
				
			||||||
@ -410,64 +447,68 @@ fn create_symbol(
 | 
				
			|||||||
        xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
 | 
					        xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let keysym = match &symbol_meta.action {
 | 
					    let keysyms = match &symbol_meta.action {
 | 
				
			||||||
        Some(_) => None,
 | 
					        // Non-submit action
 | 
				
			||||||
        None => Some(match &symbol_meta.keysym {
 | 
					        Some(_) => Vec::new(),
 | 
				
			||||||
            Some(keysym) => match keysym_valid(keysym.as_str()) {
 | 
					        // Submit action
 | 
				
			||||||
 | 
					        None => match &symbol_meta.keysym {
 | 
				
			||||||
 | 
					            // Keysym given explicitly
 | 
				
			||||||
 | 
					            Some(keysym) => vec!(match keysym_valid(keysym.as_str()) {
 | 
				
			||||||
                true => keysym.clone(),
 | 
					                true => keysym.clone(),
 | 
				
			||||||
                false => {
 | 
					                false => {
 | 
				
			||||||
                    eprintln!("Keysym name invalid: {}", keysym);
 | 
					                    eprintln!("Keysym name invalid: {}", keysym);
 | 
				
			||||||
                    "space".into() // placeholder
 | 
					                    "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) {
 | 
					            None => match keysym_valid(name) {
 | 
				
			||||||
                true => String::from(name),
 | 
					                // Button name is actually a valid xkb name
 | 
				
			||||||
                false => match name.chars().count() {
 | 
					                true => vec!(String::from(name)),
 | 
				
			||||||
                    1 => format!("U{:04X}", name.chars().next().unwrap() as u32),
 | 
					                // Button name is not a valid xkb name,
 | 
				
			||||||
                    // If the name is longer than 1 char,
 | 
					                // so assume it's a literal string to be submitted
 | 
				
			||||||
                    // then it's not a single Unicode char,
 | 
					                false => {
 | 
				
			||||||
                    // but was trying to be an identifier
 | 
					                    if name.chars().count() == 0 {
 | 
				
			||||||
                    _ => {
 | 
					                        // A name read from yaml with no valid Unicode.
 | 
				
			||||||
                        eprintln!(
 | 
					                        // Highly improbable, but let's be safe.
 | 
				
			||||||
                            "Could not derive a valid keysym for key {}",
 | 
					                        eprintln!("Key {} doesn't have any characters", name);
 | 
				
			||||||
                            name
 | 
					                        vec!("space".into()) // placeholder
 | 
				
			||||||
                        );
 | 
					                    } else {
 | 
				
			||||||
                        "space".into() // placeholder
 | 
					                        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 {
 | 
					    match &symbol_meta.action {
 | 
				
			||||||
        Some(Action::SetView(view_name)) => ::symbol::Symbol {
 | 
					        Some(Action::SetView(view_name)) => ::action::Action::SetLevel(
 | 
				
			||||||
            action: ::symbol::Action::SetLevel(
 | 
					            filter_view_name(name, view_name.clone(), &view_names)
 | 
				
			||||||
                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 {
 | 
					        Some(Action::ShowPrefs) => ::action::Action::Submit {
 | 
				
			||||||
            action: ::symbol::Action::LockLevel {
 | 
					            text: None,
 | 
				
			||||||
                lock: filter_view_name(name, lock_view.clone(), &view_names),
 | 
					            keys: Vec::new(),
 | 
				
			||||||
                unlock: filter_view_name(
 | 
					 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    unlock_view.clone(),
 | 
					 | 
				
			||||||
                    &view_names
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        Some(Action::ShowPrefs) => ::symbol::Symbol {
 | 
					        None => ::action::Action::Submit {
 | 
				
			||||||
            action: ::symbol::Action::Submit {
 | 
					            text: None,
 | 
				
			||||||
                text: None,
 | 
					            keys: keysyms.into_iter().map(::action::KeySym).collect(),
 | 
				
			||||||
                keys: Vec::new(),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        None => ::symbol::Symbol {
 | 
					 | 
				
			||||||
            action: ::symbol::Action::Submit {
 | 
					 | 
				
			||||||
                text: None,
 | 
					 | 
				
			||||||
                keys: vec!(
 | 
					 | 
				
			||||||
                    ::symbol::KeySym(keysym.unwrap()),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -639,6 +680,23 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// 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]
 | 
					    #[test]
 | 
				
			||||||
    fn parsing_fallback() {
 | 
					    fn parsing_fallback() {
 | 
				
			||||||
        assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
 | 
					        assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
 | 
				
			||||||
@ -650,7 +708,7 @@ mod tests {
 | 
				
			|||||||
    /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
 | 
					    /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn fallbacks_order() {
 | 
					    fn fallbacks_order() {
 | 
				
			||||||
        let (layout, source, _failure) = load_layout(
 | 
					        let (_layout, source, _failure) = load_layout(
 | 
				
			||||||
            "nb",
 | 
					            "nb",
 | 
				
			||||||
            Some(PathBuf::from("tests"))
 | 
					            Some(PathBuf::from("tests"))
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -674,7 +732,7 @@ mod tests {
 | 
				
			|||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_key_unicode() {
 | 
					    fn test_key_unicode() {
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            create_symbol(
 | 
					            create_action(
 | 
				
			||||||
                &hashmap!{
 | 
					                &hashmap!{
 | 
				
			||||||
                    ".".into() => ButtonMeta {
 | 
					                    ".".into() => ButtonMeta {
 | 
				
			||||||
                        icon: None,
 | 
					                        icon: None,
 | 
				
			||||||
@ -687,11 +745,9 @@ mod tests {
 | 
				
			|||||||
                ".",
 | 
					                ".",
 | 
				
			||||||
                Vec::new()
 | 
					                Vec::new()
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            ::symbol::Symbol {
 | 
					            ::action::Action::Submit {
 | 
				
			||||||
                action: ::symbol::Action::Submit {
 | 
					                text: None,
 | 
				
			||||||
                    text: None,
 | 
					                keys: vec!(::action::KeySym("U002E".into())),
 | 
				
			||||||
                    keys: vec!(::symbol::KeySym("U002E".into())),
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,26 +1,21 @@
 | 
				
			|||||||
#ifndef __KEYBOARD_H
 | 
					#ifndef __KEYBOARD_H
 | 
				
			||||||
#define __KEYBOARD_H
 | 
					#define __KEYBOARD_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "stdbool.h"
 | 
					 | 
				
			||||||
#include "inttypes.h"
 | 
					#include "inttypes.h"
 | 
				
			||||||
 | 
					#include "stdbool.h"
 | 
				
			||||||
 | 
					#include "virtual-keyboard-unstable-v1-client-protocol.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_key;
 | 
					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);
 | 
					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);
 | 
					uint32_t squeek_key_is_locked(struct squeek_key *key);
 | 
				
			||||||
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
 | 
					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);
 | 
					uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key);
 | 
					enum key_press {
 | 
				
			||||||
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);
 | 
					    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
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										206
									
								
								src/keyboard.rs
									
									
									
									
									
								
							
							
						
						
									
										206
									
								
								src/keyboard.rs
									
									
									
									
									
								
							@ -1,13 +1,12 @@
 | 
				
			|||||||
/*! State of the emulated keyboard and keys */
 | 
					/*! State of the emulated keyboard and keys */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
use std::string::FromUtf8Error;
 | 
					use std::string::FromUtf8Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::symbol::{ Symbol, Action };
 | 
					use ::action::Action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::io::Write;
 | 
					use std::io::Write;
 | 
				
			||||||
use std::iter::{ FromIterator, IntoIterator };
 | 
					use std::iter::{ FromIterator, IntoIterator };
 | 
				
			||||||
@ -16,21 +15,28 @@ use std::iter::{ FromIterator, IntoIterator };
 | 
				
			|||||||
pub mod c {
 | 
					pub mod c {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    use ::util::c;
 | 
					    use ::util::c;
 | 
				
			||||||
    use ::util::c::as_cstr;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use std::ffi::CString;
 | 
					    use std::os::raw::c_void;
 | 
				
			||||||
    use std::os::raw::c_char;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub type CKeyState = c::Wrapped<KeyState>;
 | 
					    pub type CKeyState = c::Wrapped<KeyState>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 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]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    extern "C" {
 | 
				
			||||||
    fn squeek_key_free(key: CKeyState) {
 | 
					        /// Checks if point falls within bounds,
 | 
				
			||||||
        unsafe { key.unwrap() }; // reference dropped
 | 
					        /// 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
 | 
					    /// Compares pointers to the data
 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
@ -45,14 +51,6 @@ pub mod c {
 | 
				
			|||||||
        return key.to_owned().pressed as u32;
 | 
					        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]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_key_is_locked(key: CKeyState) -> u32 {
 | 
					    fn squeek_key_is_locked(key: CKeyState) -> u32 {
 | 
				
			||||||
@ -69,70 +67,42 @@ pub mod c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_key_get_keycode(key: CKeyState) -> u32 {
 | 
					    fn squeek_key_press(
 | 
				
			||||||
        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,
 | 
					        key: CKeyState,
 | 
				
			||||||
    ) -> *const c_char {
 | 
					        virtual_keyboard: *mut ZwpVirtualKeyboardV1,
 | 
				
			||||||
        let key_name = as_cstr(&key_name)
 | 
					        press: u32,
 | 
				
			||||||
            .expect("Missing key name")
 | 
					        timestamp: u32,
 | 
				
			||||||
            .to_str()
 | 
					    ) {
 | 
				
			||||||
            .expect("Bad key name");
 | 
					        let key = key.clone_ref();
 | 
				
			||||||
 | 
					        let mut key = key.borrow_mut();
 | 
				
			||||||
 | 
					        key.pressed = press != 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let symbol_name = match key.to_owned().symbol.action {
 | 
					        let keycodes_count = key.keycodes.len();
 | 
				
			||||||
            Action::Submit { text: Some(text), .. } => {
 | 
					        for keycode in key.keycodes.iter() {
 | 
				
			||||||
                Some(
 | 
					            let keycode = keycode - 8;
 | 
				
			||||||
                    text.clone()
 | 
					            match (key.pressed, keycodes_count) {
 | 
				
			||||||
                        .into_string().expect("Bad symbol")
 | 
					                // Pressing a key made out of a single keycode is simple:
 | 
				
			||||||
                )
 | 
					                // press on press, release on release.
 | 
				
			||||||
            },
 | 
					                (_, 1) => unsafe {
 | 
				
			||||||
            _ => None,
 | 
					                    eek_virtual_keyboard_v1_key(
 | 
				
			||||||
        };
 | 
					                        virtual_keyboard, timestamp, keycode, press
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
        let inner = match symbol_name {
 | 
					                },
 | 
				
			||||||
            Some(name) => format!("[ {} ]", name),
 | 
					                // A key made of multiple keycodes
 | 
				
			||||||
            _ => format!("[ ]"),
 | 
					                // has to submit them one after the other
 | 
				
			||||||
        };
 | 
					                (true, _) => unsafe {
 | 
				
			||||||
 | 
					                    eek_virtual_keyboard_v1_key(
 | 
				
			||||||
        CString::new(format!("        key <{}> {{ {} }};\n", key_name, inner))
 | 
					                        virtual_keyboard, timestamp, keycode, 1
 | 
				
			||||||
            .expect("Couldn't convert string")
 | 
					                    );
 | 
				
			||||||
            .into_raw()
 | 
					                    eek_virtual_keyboard_v1_key(
 | 
				
			||||||
    }
 | 
					                        virtual_keyboard, timestamp, keycode, 0
 | 
				
			||||||
    
 | 
					                    );
 | 
				
			||||||
    #[no_mangle]
 | 
					                },
 | 
				
			||||||
    pub extern "C"
 | 
					                // Design choice here: submit multiple all at press time
 | 
				
			||||||
    fn squeek_key_get_action_name(
 | 
					                // and do nothing at release time
 | 
				
			||||||
        key_name: *const c_char,
 | 
					                (false, _) => {},
 | 
				
			||||||
        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()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -140,8 +110,10 @@ pub mod c {
 | 
				
			|||||||
pub struct KeyState {
 | 
					pub struct KeyState {
 | 
				
			||||||
    pub pressed: bool,
 | 
					    pub pressed: bool,
 | 
				
			||||||
    pub locked: bool,
 | 
					    pub locked: bool,
 | 
				
			||||||
    pub keycode: Option<u32>,
 | 
					    /// A cache of raw keycodes derived from Action::Sumbit given a keymap
 | 
				
			||||||
    pub symbol: Symbol,
 | 
					    pub keycodes: Vec<u32>,
 | 
				
			||||||
 | 
					    /// 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
 | 
					/// Generates a mapping where each key gets a keycode, starting from 8
 | 
				
			||||||
@ -178,7 +150,7 @@ impl From<io::Error> for FormattingError {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Generates a de-facto single level keymap. TODO: actually drop second level
 | 
					/// Generates a de-facto single level keymap. TODO: actually drop second level
 | 
				
			||||||
pub fn generate_keymap(
 | 
					pub fn generate_keymap(
 | 
				
			||||||
    keystates: &HashMap::<String, Rc<RefCell<KeyState>>>
 | 
					    keystates: &HashMap::<String, KeyState>
 | 
				
			||||||
) -> Result<String, FormattingError> {
 | 
					) -> Result<String, FormattingError> {
 | 
				
			||||||
    let mut buf: Vec<u8> = Vec::new();
 | 
					    let mut buf: Vec<u8> = Vec::new();
 | 
				
			||||||
    writeln!(
 | 
					    writeln!(
 | 
				
			||||||
@ -191,24 +163,17 @@ pub fn generate_keymap(
 | 
				
			|||||||
    )?;
 | 
					    )?;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    for (name, state) in keystates.iter() {
 | 
					    for (name, state) in keystates.iter() {
 | 
				
			||||||
        let state = state.borrow();
 | 
					        if let Action::Submit { text: _, keys } = &state.action {
 | 
				
			||||||
        if let ::symbol::Action::Submit { text: _, keys } = &state.symbol.action {
 | 
					            if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); };
 | 
				
			||||||
            match keys.len() {
 | 
					            for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
 | 
				
			||||||
                0 => eprintln!("Key {} has no keysyms", name),
 | 
					                write!(
 | 
				
			||||||
                a => {
 | 
					                    buf,
 | 
				
			||||||
                    // TODO: don't ignore any keysyms
 | 
					                    "
 | 
				
			||||||
                    if a > 1 {
 | 
					 | 
				
			||||||
                        eprintln!("Key {} multiple keysyms", name);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    write!(
 | 
					 | 
				
			||||||
                        buf,
 | 
					 | 
				
			||||||
                        "
 | 
					 | 
				
			||||||
        <{}> = {};",
 | 
					        <{}> = {};",
 | 
				
			||||||
                        keys[0].0,
 | 
					                    named_keysym.0,
 | 
				
			||||||
                        state.keycode.unwrap()
 | 
					                    keycode,
 | 
				
			||||||
                    )?;
 | 
					                )?;
 | 
				
			||||||
                },
 | 
					            }
 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -224,8 +189,8 @@ pub fn generate_keymap(
 | 
				
			|||||||
    )?;
 | 
					    )?;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    for (name, state) in keystates.iter() {
 | 
					    for (name, state) in keystates.iter() {
 | 
				
			||||||
        if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().symbol.action {
 | 
					        if let Action::Submit { text: _, keys } = &state.action {
 | 
				
			||||||
            if let Some(keysym) = keys.iter().next() {
 | 
					            for keysym in keys.iter() {
 | 
				
			||||||
                write!(
 | 
					                write!(
 | 
				
			||||||
                    buf,
 | 
					                    buf,
 | 
				
			||||||
                    "
 | 
					                    "
 | 
				
			||||||
@ -255,5 +220,44 @@ pub fn generate_keymap(
 | 
				
			|||||||
}};"
 | 
					}};"
 | 
				
			||||||
    )?;
 | 
					    )?;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    //println!("{}", String::from_utf8(buf.clone()).unwrap());
 | 
				
			||||||
    String::from_utf8(buf).map_err(FormattingError::Utf)
 | 
					    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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,9 @@ use std::ffi::CString;
 | 
				
			|||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
use std::vec::Vec;
 | 
					use std::vec::Vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::keyboard::*;
 | 
					use ::action::Action;
 | 
				
			||||||
use ::float_ord::FloatOrd;
 | 
					use ::float_ord::FloatOrd;
 | 
				
			||||||
use ::symbol::*;
 | 
					use ::keyboard::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Gathers stuff defined in C or called by C
 | 
					/// Gathers stuff defined in C or called by C
 | 
				
			||||||
pub mod c {
 | 
					pub mod c {
 | 
				
			||||||
@ -145,17 +145,6 @@ pub mod c {
 | 
				
			|||||||
        ::keyboard::c::CKeyState::wrap(button.state.clone())
 | 
					        ::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]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_button_get_label(
 | 
					    fn squeek_button_get_label(
 | 
				
			||||||
@ -264,6 +253,8 @@ pub mod c {
 | 
				
			|||||||
                angle: i32
 | 
					                angle: i32
 | 
				
			||||||
            ) -> u32;
 | 
					            ) -> u32;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            // CKeyState is safe to pass to C as long as nothing dereferences it
 | 
				
			||||||
 | 
					            #[allow(improper_ctypes)]
 | 
				
			||||||
            pub fn eek_keyboard_set_key_locked(
 | 
					            pub fn eek_keyboard_set_key_locked(
 | 
				
			||||||
                keyboard: *mut LevelKeyboard,
 | 
					                keyboard: *mut LevelKeyboard,
 | 
				
			||||||
                key: ::keyboard::c::CKeyState,
 | 
					                key: ::keyboard::c::CKeyState,
 | 
				
			||||||
@ -279,11 +270,11 @@ pub mod c {
 | 
				
			|||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            let layout = unsafe { &mut *layout };
 | 
					            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) => {
 | 
					                Action::SetLevel(name) => {
 | 
				
			||||||
                    Some(name.clone())
 | 
					                    Some(name.clone())
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                ::symbol::Action::LockLevel { lock, unlock } => {
 | 
					                Action::LockLevel { lock, unlock } => {
 | 
				
			||||||
                    let locked = {
 | 
					                    let locked = {
 | 
				
			||||||
                        let key = key.clone_ref();
 | 
					                        let key = key.clone_ref();
 | 
				
			||||||
                        let mut key = key.borrow_mut();
 | 
					                        let mut key = key.borrow_mut();
 | 
				
			||||||
@ -320,9 +311,7 @@ pub mod c {
 | 
				
			|||||||
        /// Sets button and row sizes according to their contents.
 | 
					        /// Sets button and row sizes according to their contents.
 | 
				
			||||||
        #[no_mangle]
 | 
					        #[no_mangle]
 | 
				
			||||||
        pub extern "C"
 | 
					        pub extern "C"
 | 
				
			||||||
        fn squeek_layout_place_contents(
 | 
					        fn squeek_layout_place_contents(layout: *mut Layout) {
 | 
				
			||||||
            layout: *mut Layout,
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            let layout = unsafe { &mut *layout };
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
            for view in layout.views.values_mut() {
 | 
					            for view in layout.views.values_mut() {
 | 
				
			||||||
                let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
 | 
					                let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
 | 
				
			||||||
@ -495,10 +484,8 @@ pub mod c {
 | 
				
			|||||||
            Rc::new(RefCell::new(::keyboard::KeyState {
 | 
					            Rc::new(RefCell::new(::keyboard::KeyState {
 | 
				
			||||||
                pressed: false,
 | 
					                pressed: false,
 | 
				
			||||||
                locked: false,
 | 
					                locked: false,
 | 
				
			||||||
                keycode: None,
 | 
					                keycodes: Vec::new(),
 | 
				
			||||||
                symbol: Symbol {
 | 
					                action: Action::SetLevel("default".into()),
 | 
				
			||||||
                    action: Action::SetLevel("default".into()),
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
#[macro_use]
 | 
					#[macro_use]
 | 
				
			||||||
extern crate bitflags;
 | 
					extern crate bitflags;
 | 
				
			||||||
#[macro_use]
 | 
					#[allow(unused_imports)]
 | 
				
			||||||
 | 
					#[macro_use] // only for tests
 | 
				
			||||||
extern crate maplit;
 | 
					extern crate maplit;
 | 
				
			||||||
extern crate serde;
 | 
					extern crate serde;
 | 
				
			||||||
extern crate xkbcommon;
 | 
					extern crate xkbcommon;
 | 
				
			||||||
@ -11,6 +12,6 @@ pub mod imservice;
 | 
				
			|||||||
mod keyboard;
 | 
					mod keyboard;
 | 
				
			||||||
mod layout;
 | 
					mod layout;
 | 
				
			||||||
mod resources;
 | 
					mod resources;
 | 
				
			||||||
mod symbol;
 | 
					mod action;
 | 
				
			||||||
mod util;
 | 
					mod util;
 | 
				
			||||||
mod xdg;
 | 
					mod xdg;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								src/util.rs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/util.rs
									
									
									
									
									
								
							@ -1,3 +1,8 @@
 | 
				
			|||||||
 | 
					/*! Assorted helpers */
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod c {
 | 
					pub mod c {
 | 
				
			||||||
    use std::cell::RefCell;
 | 
					    use std::cell::RefCell;
 | 
				
			||||||
    use std::ffi::{ CStr, CString };
 | 
					    use std::ffi::{ CStr, CString };
 | 
				
			||||||
@ -101,3 +106,13 @@ pub mod c {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn hash_map_map<K, V, F, K1, V1>(map: HashMap<K, V>, mut f: F)
 | 
				
			||||||
 | 
					    -> HashMap<K1, V1>
 | 
				
			||||||
 | 
					    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))
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,12 @@
 | 
				
			|||||||
#include "wayland.h"
 | 
					#include "wayland.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_wayland *squeek_wayland = NULL;
 | 
					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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								tests/layout_key3.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/layout_key3.yaml
									
									
									
									
									
										Normal file
									
								
							@ -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 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user