virtual_keyboard: Submit multi-codepoint strings
This commit is contained in:
		
							
								
								
									
										151
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								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<String, u32> = 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::<String, Rc<RefCell<KeyState>>>::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)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user