337 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			337 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
/*! State of the emulated keyboard and keys.
 | 
						|
 * Regards the keyboard as if it was composed of switches. */
 | 
						|
 | 
						|
use std::cell::RefCell;
 | 
						|
use std::collections::HashMap;
 | 
						|
use std::fmt;
 | 
						|
use std::io;
 | 
						|
use std::mem;
 | 
						|
use std::ptr;
 | 
						|
use std::rc::Rc;
 | 
						|
use std::string::FromUtf8Error;
 | 
						|
 | 
						|
use ::action::Action;
 | 
						|
use ::util;
 | 
						|
 | 
						|
// Traits
 | 
						|
use std::io::Write;
 | 
						|
use std::iter::{ FromIterator, IntoIterator };
 | 
						|
 | 
						|
#[derive(Debug, Clone, Copy, PartialEq)]
 | 
						|
pub enum PressType {
 | 
						|
    Released = 0,
 | 
						|
    Pressed = 1,
 | 
						|
}
 | 
						|
 | 
						|
/// The extended, unambiguous layout-keycode
 | 
						|
#[derive(Debug, Clone)]
 | 
						|
pub struct KeyCode {
 | 
						|
    pub code: u32,
 | 
						|
    pub keymap_idx: usize,
 | 
						|
}
 | 
						|
 | 
						|
bitflags!{
 | 
						|
    /// Map to `virtual_keyboard.modifiers` modifiers values
 | 
						|
    /// From https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Keyboard_State
 | 
						|
    pub struct Modifiers: u8 {
 | 
						|
        const SHIFT = 0x1;
 | 
						|
        const LOCK = 0x2;
 | 
						|
        const CONTROL = 0x4;
 | 
						|
        /// Alt
 | 
						|
        const MOD1 = 0x8;
 | 
						|
        const MOD2 = 0x10;
 | 
						|
        const MOD3 = 0x20;
 | 
						|
        /// Meta
 | 
						|
        const MOD4 = 0x40;
 | 
						|
        /// AltGr
 | 
						|
        const MOD5 = 0x80;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// When the submitted actions of keys need to be tracked,
 | 
						|
/// they need a stable, comparable ID
 | 
						|
#[derive(Clone, PartialEq)]
 | 
						|
pub struct KeyStateId(*const KeyState);
 | 
						|
 | 
						|
#[derive(Debug, Clone)]
 | 
						|
pub struct KeyState {
 | 
						|
    pub pressed: PressType,
 | 
						|
    /// A cache of raw keycodes derived from Action::Submit given a keymap
 | 
						|
    pub keycodes: Vec<KeyCode>,
 | 
						|
    /// Static description of what the key does when pressed or released
 | 
						|
    pub action: Action,
 | 
						|
}
 | 
						|
 | 
						|
impl KeyState {
 | 
						|
    #[must_use]
 | 
						|
    pub fn into_released(self) -> KeyState {
 | 
						|
        KeyState {
 | 
						|
            pressed: PressType::Released,
 | 
						|
            ..self
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[must_use]
 | 
						|
    pub fn into_pressed(self) -> KeyState {
 | 
						|
        KeyState {
 | 
						|
            pressed: PressType::Pressed,
 | 
						|
            ..self
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// KeyStates instances are the unique identifiers of pressed keys,
 | 
						|
    /// and the actions submitted with them.
 | 
						|
    pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
 | 
						|
        KeyStateId(keystate.as_ptr() as *const KeyState)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Sorts an iterator by converting it to a Vector and back
 | 
						|
fn sorted<'a, I: Iterator<Item=String>>(
 | 
						|
    iter: I
 | 
						|
) -> impl Iterator<Item=String> {
 | 
						|
    let mut v: Vec<String> = iter.collect();
 | 
						|
    v.sort();
 | 
						|
    v.into_iter()
 | 
						|
}
 | 
						|
 | 
						|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
 | 
						|
/// HACK: starting from 9, because 8 results in keycode 0,
 | 
						|
/// which the compositor likes to discard
 | 
						|
pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>(
 | 
						|
    key_names: C,
 | 
						|
) -> HashMap<String, KeyCode> {
 | 
						|
    HashMap::from_iter(
 | 
						|
        // Sort to remove a source of indeterminism in keycode assignment.
 | 
						|
        sorted(key_names.into_iter())
 | 
						|
            .zip(util::cycle_count(9..255))
 | 
						|
            .map(|(name, (code, keymap_idx))| (
 | 
						|
                String::from(name),
 | 
						|
                KeyCode { code, keymap_idx },
 | 
						|
            ))
 | 
						|
    )
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Debug)]
 | 
						|
pub enum FormattingError {
 | 
						|
    Utf(FromUtf8Error),
 | 
						|
    Format(io::Error),
 | 
						|
}
 | 
						|
 | 
						|
impl fmt::Display for FormattingError {
 | 
						|
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
						|
        match self {
 | 
						|
            FormattingError::Utf(e) => write!(f, "UTF: {}", e),
 | 
						|
            FormattingError::Format(e) => write!(f, "Format: {}", e),
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl From<io::Error> for FormattingError {
 | 
						|
    fn from(e: io::Error) -> Self {
 | 
						|
        FormattingError::Format(e)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Index is the key code, String is the occupant.
 | 
						|
/// Starts all empty.
 | 
						|
/// https://gitlab.freedesktop.org/xorg/xserver/-/issues/260
 | 
						|
type SingleKeyMap = [Option<String>; 256];
 | 
						|
 | 
						|
fn single_key_map_new() -> SingleKeyMap {
 | 
						|
    // Why can't we just initialize arrays without tricks -_- ?
 | 
						|
    unsafe {
 | 
						|
        // Inspired by
 | 
						|
        // https://www.reddit.com/r/rust/comments/5n7bh1/how_to_create_an_array_of_a_type_with_clone_but/
 | 
						|
        #[cfg(feature = "rustc_less_1_36")]
 | 
						|
        let mut array: SingleKeyMap = mem::uninitialized();
 | 
						|
        #[cfg(not(feature = "rustc_less_1_36"))]
 | 
						|
        let mut array: SingleKeyMap = mem::MaybeUninit::uninit().assume_init();
 | 
						|
 | 
						|
        for element in array.iter_mut() {
 | 
						|
            ptr::write(element, None);
 | 
						|
        }
 | 
						|
        array
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pub fn generate_keymaps(symbolmap: HashMap::<String, KeyCode>)
 | 
						|
    -> Result<Vec<String>, FormattingError>
 | 
						|
{
 | 
						|
    let mut bins: Vec<SingleKeyMap> = Vec::new();
 | 
						|
    
 | 
						|
    for (name, KeyCode { code, keymap_idx }) in symbolmap.into_iter() {
 | 
						|
        if keymap_idx >= bins.len() {
 | 
						|
            bins.resize_with(
 | 
						|
                keymap_idx + 1,
 | 
						|
                || single_key_map_new(),
 | 
						|
            );
 | 
						|
        }
 | 
						|
        bins[keymap_idx][code as usize] = Some(name);
 | 
						|
    }
 | 
						|
 | 
						|
    let mut out = Vec::new();
 | 
						|
    for bin in bins {
 | 
						|
        out.push(generate_keymap(&bin)?);
 | 
						|
    }
 | 
						|
    Ok(out)
 | 
						|
}
 | 
						|
 | 
						|
/// Generates a de-facto single level keymap.
 | 
						|
/// Key codes must not repeat and must remain between 9 and 255.
 | 
						|
fn generate_keymap(
 | 
						|
    symbolmap: &SingleKeyMap,
 | 
						|
) -> Result<String, FormattingError> {
 | 
						|
    let mut buf: Vec<u8> = Vec::new();
 | 
						|
    writeln!(
 | 
						|
        buf,
 | 
						|
        "xkb_keymap {{
 | 
						|
 | 
						|
    xkb_keycodes \"squeekboard\" {{
 | 
						|
        minimum = 8;
 | 
						|
        maximum = 255;"
 | 
						|
    )?;
 | 
						|
 | 
						|
    let pairs: Vec<(&String, usize)> = symbolmap.iter()
 | 
						|
        // Attach a key code to each cell.
 | 
						|
        .enumerate()
 | 
						|
        // Get rid of empty keycodes.
 | 
						|
        .filter_map(|(code, name)| name.as_ref().map(|n| (n, code)))
 | 
						|
        .collect();
 | 
						|
    
 | 
						|
    // Xorg can only consume up to 255 keys, so this may not work in Xwayland.
 | 
						|
    // Two possible solutions:
 | 
						|
    // - use levels to cram multiple characters into one key
 | 
						|
    // - swap layouts on key presses
 | 
						|
    for (_name, keycode) in &pairs {
 | 
						|
        write!(
 | 
						|
            buf,
 | 
						|
            "
 | 
						|
        <I{}> = {0};",
 | 
						|
            keycode,
 | 
						|
        )?;
 | 
						|
    }
 | 
						|
 | 
						|
    writeln!(
 | 
						|
        buf,
 | 
						|
        "
 | 
						|
        indicator 1 = \"Caps Lock\"; // Xwayland won't accept without it.
 | 
						|
    }};
 | 
						|
    
 | 
						|
    xkb_symbols \"squeekboard\" {{
 | 
						|
"
 | 
						|
    )?;
 | 
						|
    
 | 
						|
    for (name, keycode) in pairs {
 | 
						|
        write!(
 | 
						|
            buf,
 | 
						|
            "
 | 
						|
key <I{}> {{ [ {} ] }};",
 | 
						|
            keycode,
 | 
						|
            name,
 | 
						|
        )?;
 | 
						|
    }
 | 
						|
 | 
						|
    writeln!(
 | 
						|
        buf,
 | 
						|
        "
 | 
						|
    }};
 | 
						|
 | 
						|
    xkb_types \"squeekboard\" {{
 | 
						|
        virtual_modifiers Squeekboard; // No modifiers! Needed for Xorg for some reason.
 | 
						|
    
 | 
						|
        // Those names are needed for Xwayland.
 | 
						|
        type \"ONE_LEVEL\" {{
 | 
						|
            modifiers= none;
 | 
						|
            level_name[Level1]= \"Any\";
 | 
						|
        }};
 | 
						|
        type \"TWO_LEVEL\" {{
 | 
						|
            level_name[Level1]= \"Base\";
 | 
						|
        }};
 | 
						|
        type \"ALPHABETIC\" {{
 | 
						|
            level_name[Level1]= \"Base\";
 | 
						|
        }};
 | 
						|
        type \"KEYPAD\" {{
 | 
						|
            level_name[Level1]= \"Base\";
 | 
						|
        }};
 | 
						|
        type \"SHIFT+ALT\" {{
 | 
						|
            level_name[Level1]= \"Base\";
 | 
						|
        }};
 | 
						|
 | 
						|
    }};
 | 
						|
 | 
						|
    xkb_compatibility \"squeekboard\" {{
 | 
						|
        // Needed for Xwayland again.
 | 
						|
        interpret Any+AnyOf(all) {{
 | 
						|
            action= SetMods(modifiers=modMapMods,clearLocks);
 | 
						|
        }};
 | 
						|
    }};
 | 
						|
}};"
 | 
						|
    )?;
 | 
						|
    
 | 
						|
    //println!("{}", String::from_utf8(buf.clone()).unwrap());
 | 
						|
    String::from_utf8(buf).map_err(FormattingError::Utf)
 | 
						|
}
 | 
						|
 | 
						|
#[cfg(test)]
 | 
						|
mod tests {
 | 
						|
    use super::*;
 | 
						|
    
 | 
						|
    use xkbcommon::xkb;
 | 
						|
 | 
						|
    #[test]
 | 
						|
    fn test_keymap_single_resolve() {
 | 
						|
        let mut key_map = single_key_map_new();
 | 
						|
        key_map[9] = Some("a".into());
 | 
						|
        key_map[10] = Some("c".into());
 | 
						|
 | 
						|
        let keymap_str = generate_keymap(&key_map).unwrap();
 | 
						|
 | 
						|
        let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
 | 
						|
 | 
						|
        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);
 | 
						|
    }
 | 
						|
 | 
						|
    #[test]
 | 
						|
    fn test_keymap_second_resolve() {
 | 
						|
        let keymaps = generate_keymaps(hashmap!(
 | 
						|
            "a".into() => KeyCode { keymap_idx: 1, code: 9 },
 | 
						|
        )).unwrap();
 | 
						|
 | 
						|
        let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
 | 
						|
 | 
						|
        let keymap = xkb::Keymap::new_from_string(
 | 
						|
            &context,
 | 
						|
            keymaps[1].clone(), // this index is part of the test
 | 
						|
            xkb::KEYMAP_FORMAT_TEXT_V1,
 | 
						|
            xkb::KEYMAP_COMPILE_NO_FLAGS,
 | 
						|
        ).expect("Failed to create keymap");
 | 
						|
 | 
						|
        let state = xkb::State::new(&keymap);
 | 
						|
 | 
						|
        assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
 | 
						|
    }
 | 
						|
 | 
						|
    #[test]
 | 
						|
    fn test_symbolmap_overflow() {
 | 
						|
        // The 257th key (U1101) is interesting.
 | 
						|
        // Use Unicode encoding for being able to use in xkb keymaps.
 | 
						|
        let keynames = (0..258).map(|num| format!("U{:04X}", 0x1000 + num));
 | 
						|
        let keycodes = generate_keycodes(keynames);
 | 
						|
        
 | 
						|
        // test now
 | 
						|
        let code = keycodes.get("U1101").expect("Did not find the tested keysym");
 | 
						|
        assert_eq!(code.keymap_idx, 1);
 | 
						|
    }
 | 
						|
}
 |