<commit_before>pub mod builtins;

use std::collections::HashMap;
use std::path::PathBuf;
use std::io;
use std::io::{Read, Write};
use std::collections::hash_map::Entry;

#[derive(PartialEq, Debug, Clone)]
pub struct State {
    cwd: PathBuf,
    environment: HashMap<String, String>,
    aliases: HashMap<String, String>,
    argv: Vec<String>,
    argc: usize,
}

impl State {
    pub fn new(cwd: String) -> State {
        State {
            cwd: PathBuf::from(cwd),
            environment: HashMap::new(),
            aliases: HashMap::new(),
            argv: Vec::new(),
            argc: 0,
        }
    }
}

pub fn run(initial_state: State) {
    let mut builtins = builtins::load();
    let mut s = initial_state.clone();

    println!("Welcome to rsh! {:?}", s);

    loop {

        print!("\n");
        print!("{} -> ", s.cwd.to_str().unwrap());

        // this forces the prompt to print
        io::stdout().flush();

        // read the user input
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        s.argv = parse_args(&input);
        s.argc = s.argv.len();

        print!("\n");
        println!("Input: {}\nState: {:?}", input, s);

        let first_arg = s.argv.get(0).unwrap().clone();
        if let Entry::Occupied(f) = builtins.entry(String::from(first_arg)) {
            let bn = f.get();
            s = bn(s.clone());
        }
    }
}

#[derive(PartialEq)]
enum BuildType {
    None,
    Single,
    Double,
}

struct ParseResult {
    result: Vec<String>,
    completed: bool,
    build_string: String,
    build_type: BuildType,
}

fn parse_args(args: &String) -> Vec<String> {
    let mut parse_result: ParseResult = ParseResult {
        result: Vec::new(),
        completed: false,
        build_string: String::from(""),
        build_type: BuildType::None,
    };

    if args.len() == 0 {
        return parse_result.result;
    }

    let mut argstr = args.clone();
    loop {
        parse_string_into_vec(&argstr, &mut parse_result);

        if (parse_result.completed) {
            break;
        }

        match parse_result.build_type {
            BuildType::Single => print!("quote> "),
            BuildType::Double => print!("dquote> "),
            _ => {}
        }
        io::stdout().flush();
        argstr = String::from("");
        io::stdin().read_line(&mut argstr).unwrap();
    }

    let result: Vec<String> = parse_result.result
        .into_iter()
        .filter(|s| s.len() > 0)
        .collect();

    result
}

fn parse_string_into_vec(string: &String, parse_result: &mut ParseResult) {
    let &mut ParseResult { ref mut build_string,
                           ref mut build_type,
                           ref mut completed,
                           ref mut result } = parse_result;
    let mut iter = string.chars().peekable();

    while let Some(c) = iter.next() {
        match c {
            '\'' => {
                match *build_type {
                    BuildType::Single => {
                        *build_type = BuildType::None;
                        if iter.peek() == Some(&' ') {
                            result.push(build_string.clone());
                            *build_string = String::from("");
                        }
                    }

                    BuildType::None => {
                        *build_type = BuildType::Single;
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            '\"' => {
                match *build_type {
                    BuildType::Double => {
                        *build_type = BuildType::None;
                        if iter.peek() == Some(&' ') {
                            result.push(build_string.clone());
                            *build_string = String::from("");
                        }
                    }

                    BuildType::None => {
                        *build_type = BuildType::Double;
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            ' ' => {
                match *build_type {
                    BuildType::None => {
                        result.push(build_string.clone());
                        *build_string = String::from("");
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            '\n' => {
                match iter.peek() {
                    Some(_) => build_string.push(c),
                    None => {}
                }
            }

            _ => {
                build_string.push(c);
            }
        }
    }

    if *build_type == BuildType::None {
        *completed = true;
    }

    result.push(build_string.clone());
    *build_string = String::from("");
}

#[test]
fn parse_args_test() {
    // parse empty string
    {
        let expected: Vec<String> = Vec::new();
        let result = parse_args(&String::from(""));
        assert_eq!(result, expected);
    }

    // parse single-word string
    {
        let expected: Vec<String> = vec!["echo".to_string()];
        let result = parse_args(&String::from("echo"));
        assert_eq!(result, expected);
    }

    // parse single-word string inside quotes
    {
        let expected = vec!["echo".to_string()];
        let result = parse_args(&String::from("\"echo\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with closed quotes section
    {
        let expected = vec!["echo".to_string(), "-n".into(), "Hello Dear World".into()];
        let result = parse_args(&String::from("echo -n \"Hello Dear World\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with multiple closed quotes sections
    {
        let expected = vec!["echo".to_string(), "Hello".into(), "Dear World".into()];
        let result = parse_args(&String::from("echo \"Hello\" \"Dear World\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with no spaces around single quotes
    {
        let expected = vec!["echo".to_string(), "helloworld".into()];
        let result = parse_args(&String::from("echo 'hello'world"));
        assert_eq!(result, expected);
    }

    // parse multi-word string with no spaces around double quotes
    {
        let expected = vec!["echo".to_string(), "helloworld".into()];
        let result = parse_args(&String::from("echo \"hello\"world"));
        assert_eq!(result, expected);
    }

    // allow double quotes inside single quotes
    {
        let expected = vec!["\"".to_string()];
        let result = parse_args(&String::from("'\"'"));
        assert_eq!(result, expected);
    }

    // allow single quotes inside double quotes
    {
        let expected = vec!["\'".to_string()];
        let result = parse_args(&String::from("\"\'\""));
        assert_eq!(result, expected);
    }
}
<commit_msg>Preserve newlines in parse_args<commit_after>pub mod builtins;

use std::collections::HashMap;
use std::path::PathBuf;
use std::io;
use std::io::{Read, Write};
use std::collections::hash_map::Entry;

#[derive(PartialEq, Debug, Clone)]
pub struct State {
    cwd: PathBuf,
    environment: HashMap<String, String>,
    aliases: HashMap<String, String>,
    argv: Vec<String>,
    argc: usize,
}

impl State {
    pub fn new(cwd: String) -> State {
        State {
            cwd: PathBuf::from(cwd),
            environment: HashMap::new(),
            aliases: HashMap::new(),
            argv: Vec::new(),
            argc: 0,
        }
    }
}

pub fn run(initial_state: State) {
    let mut builtins = builtins::load();
    let mut s = initial_state.clone();

    println!("Welcome to rsh! {:?}", s);

    loop {

        print!("\n");
        print!("{} -> ", s.cwd.to_str().unwrap());

        // this forces the prompt to print
        io::stdout().flush();

        // read the user input
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        s.argv = parse_args(&input);
        s.argc = s.argv.len();

        print!("\n");
        println!("Input: {}\nState: {:?}", input, s);

        let first_arg = s.argv.get(0).unwrap().clone();
        if let Entry::Occupied(f) = builtins.entry(String::from(first_arg)) {
            let bn = f.get();
            s = bn(s.clone());
        }
    }
}

#[derive(PartialEq)]
enum BuildType {
    None,
    Single,
    Double,
}

struct ParseResult {
    result: Vec<String>,
    completed: bool,
    build_string: String,
    build_type: BuildType,
}

fn parse_args(args: &String) -> Vec<String> {
    let mut parse_result: ParseResult = ParseResult {
        result: Vec::new(),
        completed: false,
        build_string: String::from(""),
        build_type: BuildType::None,
    };

    if args.len() == 0 {
        return parse_result.result;
    }

    let mut argstr = args.clone();
    loop {
        parse_string_into_vec(&argstr, &mut parse_result);

        if (parse_result.completed) {
            break;
        }

        match parse_result.build_type {
            BuildType::Single => print!("quote> "),
            BuildType::Double => print!("dquote> "),
            _ => {}
        }
        io::stdout().flush();
        argstr = String::from("");
        io::stdin().read_line(&mut argstr).unwrap();
    }

    let result: Vec<String> = parse_result.result
        .into_iter()
        .filter(|s| s.len() > 0)
        .collect();

    result
}

fn parse_string_into_vec(string: &String, parse_result: &mut ParseResult) {
    let &mut ParseResult { ref mut build_string,
                           ref mut build_type,
                           ref mut completed,
                           ref mut result } = parse_result;
    let mut iter = string.chars().peekable();

    while let Some(c) = iter.next() {
        match c {
            '\'' => {
                match *build_type {
                    BuildType::Single => {
                        *build_type = BuildType::None;
                        if iter.peek() == Some(&' ') {
                            result.push(build_string.clone());
                            *build_string = String::from("");
                        }
                    }

                    BuildType::None => {
                        *build_type = BuildType::Single;
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            '\"' => {
                match *build_type {
                    BuildType::Double => {
                        *build_type = BuildType::None;
                        if iter.peek() == Some(&' ') {
                            result.push(build_string.clone());
                            *build_string = String::from("");
                        }
                    }

                    BuildType::None => {
                        *build_type = BuildType::Double;
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            ' ' => {
                match *build_type {
                    BuildType::None => {
                        result.push(build_string.clone());
                        *build_string = String::from("");
                    }

                    _ => {
                        build_string.push(c);
                    }
                }
            }

            '\n' => {
                match *build_type {
                    BuildType::None => {
                        match iter.peek() {
                            Some(_) => {
                                build_string.push(c);
                            }
                            None => {}
                        }
                    }
                    _ => {
                        build_string.push(c);
                    }
                }
            }

            _ => {
                build_string.push(c);
            }
        }
    }

    if *build_type == BuildType::None {
        *completed = true;
        result.push(build_string.clone());
        *build_string = String::from("");
    }
}

#[test]
fn parse_args_test() {
    // parse empty string
    {
        let expected: Vec<String> = Vec::new();
        let result = parse_args(&String::from(""));
        assert_eq!(result, expected);
    }

    // parse single-word string
    {
        let expected: Vec<String> = vec!["echo".to_string()];
        let result = parse_args(&String::from("echo"));
        assert_eq!(result, expected);
    }

    // parse single-word string inside quotes
    {
        let expected = vec!["echo".to_string()];
        let result = parse_args(&String::from("\"echo\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with closed quotes section
    {
        let expected = vec!["echo".to_string(), "-n".into(), "Hello Dear World".into()];
        let result = parse_args(&String::from("echo -n \"Hello Dear World\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with multiple closed quotes sections
    {
        let expected = vec!["echo".to_string(), "Hello".into(), "Dear World".into()];
        let result = parse_args(&String::from("echo \"Hello\" \"Dear World\""));
        assert_eq!(result, expected);
    }

    // parse multi-word string with no spaces around single quotes
    {
        let expected = vec!["echo".to_string(), "helloworld".into()];
        let result = parse_args(&String::from("echo 'hello'world"));
        assert_eq!(result, expected);
    }

    // parse multi-word string with no spaces around double quotes
    {
        let expected = vec!["echo".to_string(), "helloworld".into()];
        let result = parse_args(&String::from("echo \"hello\"world"));
        assert_eq!(result, expected);
    }

    // allow double quotes inside single quotes
    {
        let expected = vec!["\"".to_string()];
        let result = parse_args(&String::from("'\"'"));
        assert_eq!(result, expected);
    }

    // allow single quotes inside double quotes
    {
        let expected = vec!["\'".to_string()];
        let result = parse_args(&String::from("\"\'\""));
        assert_eq!(result, expected);
    }

    // handle multiple quote sections in succession
    {
        let expected = vec!["echo".to_string(), "hello world how are you".into()];
        let result = parse_args(&String::from("echo \"hello world\"\' how are you\'"));
        assert_eq!(result, expected);
    }

    // preserve newlines
    {
        let expected = vec!["echo".to_string(), "hello\nworld".into()];
        let result = parse_args(&String::from("echo \"hello\nworld\""));
        assert_eq!(result, expected);
    }

    // remove final newline
    {
        let expected = vec!["echo".to_string()];
        let result = parse_args(&String::from("echo\n"));
        assert_eq!(result, expected);
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::{path::Path, sync::Arc};

use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use pahkat_client::types::repo::Index;
use serde::{Deserialize, Serialize};
use xmlem::{Document, Element, NewElement, Selector};

use crate::build::macos::keymap::{MACOS_HARDCODED, MACOS_KEYS};
use crate::build::macos::layers::layer_attributes;
use crate::bundle::layout::macos::MacOsKbdLayer;
use crate::bundle::layout::Transform;
use crate::util::{decode_unicode_escapes, split_keys, TRANSFORM_ESCAPE};
use crate::{build::BuildStep, bundle::KbdgenBundle};

pub const KEY_LAYOUT_EXT: &str = "keylayout";

const TOP_FOLDER: &str = "Contents";
const RESOURCES_FOLDER: &str = "Resources";
const PLIST_FILENAME: &str = "Info.plist";

const PLIST_TEMPLATE: &str = include_str!("../../../resources/template-macos-plist.xml");
const LAYOUT_TEMPLATE: &str = include_str!("../../../resources/template-macos-layout.xml");

#[derive(Serialize, Deserialize)]
pub struct InfoPlist {
    #[serde(rename = "CFBundleIdentifier")]
    pub cf_bundle_identifier: String,
    #[serde(rename = "CFBundleName")]
    pub cf_bundle_name: String,
    #[serde(rename = "CFBundleVersion")]
    pub cf_bundle_version: String,
    #[serde(rename = "CFBundleShortVersionString")]
    pub cf_bundle_short_version_string: String,
}

#[derive(Debug)]
pub enum KeyTransition {
    Output(KeyOutput),
    Action(DeadKeyAction),
}

#[derive(Debug, Clone)]
pub struct KeyOutput {
    code: usize,
    output: String,
}

#[derive(Debug, Clone)]
pub struct DeadKeyOutput {
    id: DeadKeyId,
    output: String,
}

#[derive(Debug, Clone)]
pub struct DeadKeyAction {
    id: ActionId,
    code: usize,
    states: Vec<DeadKeyOutput>,
}

type DeadKeyId = String;
type ActionId = String;

struct TransformIdManager {
    dead_key_counter: usize,
    action_counter: usize,
}

impl TransformIdManager {
    fn new() -> Self {
        TransformIdManager {
            dead_key_counter: 0,
            action_counter: 0,
        }
    }

    fn next_dead_key(&mut self) -> DeadKeyId {
        let old_counter = self.dead_key_counter;

        self.dead_key_counter = self.dead_key_counter + 1;

        format!("dead_key{:03}", old_counter)
    }

    fn next_action(&mut self) -> ActionId {
        let old_counter = self.action_counter;

        self.action_counter = self.action_counter + 1;

        format!("action{:03}", old_counter)
    }
}

pub struct GenerateMacOs {}

#[async_trait(?Send)]
impl BuildStep for GenerateMacOs {
    async fn build(&self, bundle: Arc<KbdgenBundle>, output_path: &Path) {
        let contents_path = output_path.join(TOP_FOLDER);
        let cloned_contents_path = contents_path.clone();
        let resources_path = contents_path.join(RESOURCES_FOLDER);

        std::fs::create_dir_all(contents_path).unwrap();
        std::fs::create_dir_all(resources_path.clone()).unwrap();

        let mut plist: InfoPlist = plist::from_bytes(PLIST_TEMPLATE.as_bytes()).unwrap();
        println!(
            "what's my CFBundleIdentifier: {}",
            plist.cf_bundle_identifier
        );
        plist.cf_bundle_name = "MyAmazingKbdgenBundle".to_string();

        plist::to_file_xml(cloned_contents_path.join(PLIST_FILENAME), &plist).unwrap();

        // One .keylayout file in Resources folder per language with MacOS primary platform
        for (language_tag, layout) in &bundle.layouts {
            if let Some(mac_os_target) = &layout.mac_os {
                let layers = &mac_os_target.primary.layers;

                let dead_key_count = 0;
                let state_count = 0;

                let mut layered_key_transition_map: IndexMap<
                    MacOsKbdLayer,
                    IndexMap<String, KeyTransition>,
                > = IndexMap::new();
                let mut key_transition_map: IndexMap<String, KeyTransition> = IndexMap::new();
                let mut dead_keys: IndexMap<String, _> = IndexMap::new();

                let mut document = Document::from_str(LAYOUT_TEMPLATE).unwrap();

                let root = document.root();

                let selector = Selector::new("keyMapSet").unwrap();
                let key_map_set = root
                    .query_selector(&document, &selector)
                    .expect("The template document should have a 'keyMapSet' tag");

                add_layer_tags(&layers, &mut document, &key_map_set);

                initialize_key_transition_map(
                    &language_tag,
                    &layers,
                    &mut layered_key_transition_map,
                );

                if let Some(transforms) = &layout.transforms {
                    if let Some(target_dead_keys) = &mac_os_target.dead_keys {
                        process_transforms(
                            &layers,
                            transforms,
                            target_dead_keys,
                            &mut dead_keys,
                            &mut layered_key_transition_map,
                        );
                    } else {
                        tracing::warn!(
                            r#"No dead keys in {}:{}:{}"#,
                            language_tag.to_string(),
                            "MacOS",
                            "Primary",
                        )
                    }
                } else {
                    tracing::warn!(
                        r#"No transforms in {}:{}:{}"#,
                        language_tag.to_string(),
                        "MacOS",
                        "Primary",
                    )
                }

                write_key_transition_map(
                    &layers,
                    &layered_key_transition_map,
                    &mut document,
                    &key_map_set,
                );

                if dead_keys.len() > 0 {
                    let terminators = root.append_new_element(
                        &mut document,
                        NewElement {
                            name: "terminators".into(),
                            attrs: [].into(),
                        },
                    );

                    for (key, dead_key) in dead_keys {
                        append_dead_key_output_element(&terminators, &mut document, &dead_key);
                    }
                }

                let key_layout_path =
                    resources_path.join(format!("{}.{}", language_tag.to_string(), KEY_LAYOUT_EXT));
                std::fs::write(key_layout_path, document.to_string_pretty()).unwrap();
            }
        }
    }
}

fn compute_keyboard_id(language_name: &str) -> String {
    "-8045".to_string()
}

fn add_layer_tags(
    layers: &IndexMap<MacOsKbdLayer, String>,
    document: &mut Document,
    key_map_set: &Element,
) {
    let root = document.root();

    let selector = Selector::new("modifierMap").unwrap();
    let modifier_map = root
        .query_selector(&document, &selector)
        .expect("The template document should have a 'modifierMap' tag");

    for (layer_index, (layer, _)) in layers.iter().enumerate() {
        let key_map_select = modifier_map.append_new_element(
            document,
            NewElement {
                name: "keyMapSelect".into(),
                attrs: [("mapIndex".into(), layer_index.to_string())].into(),
            },
        );

        key_map_select.append_new_element(
            document,
            NewElement {
                name: "modifier".into(),
                attrs: [("keys".into(), layer_attributes(layer))].into(),
            },
        );

        key_map_set.append_new_element(
            document,
            NewElement {
                name: "keyMap".into(),
                attrs: [("index".into(), layer_index.to_string())].into(),
            },
        );
    }
}

fn initialize_key_transition_map(
    language_tag: &LanguageTag,
    layers: &IndexMap<MacOsKbdLayer, String>,
    layered_key_transition_map: &mut IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
) {
    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let mut cursor = 0;

        layered_key_transition_map.insert(*layer, IndexMap::new());

        let mut key_transition_map = layered_key_transition_map
            .get_mut(layer)
            .expect("getting back the value that was just inserted");

        for (_iso_key, key_code) in MACOS_KEYS.iter() {
            let key_map: Vec<String> = split_keys(&key_map);

            tracing::debug!(
                "iso len: {}; keymap len: {}",
                MACOS_KEYS.len(),
                key_map.len()
            );
            if MACOS_KEYS.len() > key_map.len() {
                panic!(
                    r#"Provided layer does not have enough keys, expected {} keys but`` got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                    MACOS_KEYS.len(),
                    key_map.len(),
                    language_tag.to_string(),
                    "MacOS",
                    "Primary",
                    layer,
                    key_map
                );
            }

            let key = key_map[cursor].clone();

            key_transition_map.insert(
                key.clone(),
                KeyTransition::Output(KeyOutput {
                    code: *key_code,
                    output: decode_unicode_escapes(&key),
                }),
            );

            cursor += 1;
        }
    }
}

fn process_transforms(
    layers: &IndexMap<MacOsKbdLayer, String>,
    transforms: &IndexMap<String, Transform>,
    target_dead_keys: &IndexMap<MacOsKbdLayer, Vec<String>>,
    dead_keys: &mut IndexMap<String, DeadKeyOutput>,
    layered_key_transition_map: &mut IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
) {
    let mut id_manager = TransformIdManager::new();

    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let mut cursor = 0;

        let layer_dead_keys = target_dead_keys.get(layer);

        if let Some(layer_dead_keys) = layer_dead_keys {
            let mut key_transition_map = layered_key_transition_map
                .get_mut(layer)
                .expect("this map should be prefilled by now");

            for (_iso_key, code) in MACOS_KEYS.iter() {
                let key_map: Vec<String> = split_keys(&key_map);

                for (dead_key, value) in transforms {
                    // check if dead key is even adead key on this layer lol

                    match value {
                        Transform::End(_character) => {
                            tracing::error!("Transform ended too soon for dead key {}", dead_key);
                        }
                        Transform::More(map) => {
                            let escape_transform = map.get(TRANSFORM_ESCAPE).expect(&format!(
                                "The escape transform `{}` not found for dead key `{}`",
                                TRANSFORM_ESCAPE, &dead_key
                            ));

                            match escape_transform {
                                Transform::End(end_char) => {
                                    if !dead_keys.contains_key(dead_key) {
                                        let id = id_manager.next_dead_key();

                                        dead_keys.insert(
                                            dead_key.clone(),
                                            DeadKeyOutput {
                                                id,
                                                output: end_char.clone(),
                                            },
                                        );
                                    }
                                }
                                Transform::More(_transform) => {
                                    panic!("The escape transform should be a string, not another transform");
                                }
                            };

                            let dead_key_transform = dead_keys[dead_key].clone();
                            let id = dead_key_transform.id.clone();

                            for (next_char, transform) in map {
                                //println!("next_char: {:?}, transform: {:?}", next_char, transform);

                                match transform {
                                    Transform::End(end_char) => {
                                        if next_char == TRANSFORM_ESCAPE {
                                            continue;
                                        }

                                        if next_char.to_string() == key_map[cursor] {
                                            let key_transform = DeadKeyOutput {
                                                id: id.clone(),
                                                output: end_char.to_string(),
                                            };

                                            //println!("end char: {}", end_char);
                                            update_key_transition_map_with_transform(
                                                key_transition_map,
                                                next_char,
                                                key_transform,
                                                &mut id_manager,
                                            );

                                            break;
                                        }
                                    }
                                    Transform::More(_transform) => {
                                        todo!("Recursion required ahead");
                                    }
                                };
                            }
                        }
                    };
                }

                cursor += 1;
            }
        }
    }
}

fn update_key_transition_map_with_transform(
    key_transition_map: &mut IndexMap<String, KeyTransition>,
    key: &str,
    transform: DeadKeyOutput,
    id_manager: &mut TransformIdManager,
) {
    if key_transition_map.contains_key(key) {
        let entry = key_transition_map.get_mut(key).unwrap();

        match entry {
            KeyTransition::Output(output) => {
                let code = output.code;

                let action = DeadKeyAction {
                    id: id_manager.next_action(),
                    code,
                    states: vec![transform.clone()],
                };

                key_transition_map.insert(key.to_string(), KeyTransition::Action(action));
            }
            KeyTransition::Action(ref mut action) => {
                action.states.push(transform.clone());
            }
        };
    } else {
        panic!(
            "The key_transition_map must already have Output entries for all keys by this point."
        )
    }
}

fn write_key_transition_map(
    layers: &IndexMap<MacOsKbdLayer, String>,
    layered_key_transition_map: &IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
    document: &mut Document,
    key_map_set: &Element,
) {
    let selector = Selector::new("actions").unwrap();
    let actions = document
        .root()
        .query_selector(&document, &selector)
        .expect("The template document should have an 'actions' tag");

    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let selector = Selector::new(&format!("keyMap[index=\"{}\"]", layer_index)).unwrap();
        let xml_key_map = key_map_set
            .query_selector(document, &selector)
            .expect("keymap to have right index");

        let key_transition_map = layered_key_transition_map
            .get(layer)
            .expect("this map should be prefilled by now");

        for (key, transition) in key_transition_map {
            match transition {
                KeyTransition::Output(output) => {
                    append_key_output_element(&xml_key_map, document, &output);
                }
                KeyTransition::Action(dead_key_action) => {
                    xml_key_map.append_new_element(
                        document,
                        NewElement {
                            name: "key".into(),
                            attrs: [
                                ("code".into(), dead_key_action.code.to_string()),
                                ("action".into(), dead_key_action.id.clone()),
                            ]
                            .into(),
                        },
                    );

                    // aggregate actions first, then print them
                    // instead of this
                    let action = actions.append_new_element(
                        document,
                        NewElement {
                            name: "action".into(),
                            attrs: [("id".into(), dead_key_action.id.clone())].into(),
                        },
                    );

                    for state in &dead_key_action.states {
                        append_dead_key_output_element(&action, document, &state);
                    }
                }
            };
        }

        for (key_code, output) in MACOS_HARDCODED.iter() {
            let key = KeyOutput {
                code: *key_code,
                output: output.clone(),
            };

            append_key_output_element(&xml_key_map, document, &key);
        }
    }
}

fn append_dead_key_output_element(element: &Element, document: &mut Document, key: &DeadKeyOutput) {
    element.append_new_element(
        document,
        NewElement {
            name: "when".into(),
            attrs: [
                ("state".to_string(), key.id.clone()),
                ("output".to_string(), key.output.clone()),
            ]
            .into(),
        },
    );
}

fn append_key_output_element(element: &Element, document: &mut Document, key: &KeyOutput) {
    element.append_new_element(
        document,
        NewElement {
            name: "key".into(),
            attrs: [
                ("code".into(), key.code.to_string()),
                ("output".into(), key.output.clone()),
            ]
            .into(),
        },
    );
}
<commit_msg>Unicode escapes<commit_after>use std::str::FromStr;
use std::{path::Path, sync::Arc};

use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use pahkat_client::types::repo::Index;
use serde::{Deserialize, Serialize};
use xmlem::{Document, Element, NewElement, Selector};

use crate::build::macos::keymap::{MACOS_HARDCODED, MACOS_KEYS};
use crate::build::macos::layers::layer_attributes;
use crate::bundle::layout::macos::MacOsKbdLayer;
use crate::bundle::layout::Transform;
use crate::util::{decode_unicode_escapes, split_keys, TRANSFORM_ESCAPE};
use crate::{build::BuildStep, bundle::KbdgenBundle};

pub const KEY_LAYOUT_EXT: &str = "keylayout";

const TOP_FOLDER: &str = "Contents";
const RESOURCES_FOLDER: &str = "Resources";
const PLIST_FILENAME: &str = "Info.plist";

const PLIST_TEMPLATE: &str = include_str!("../../../resources/template-macos-plist.xml");
const LAYOUT_TEMPLATE: &str = include_str!("../../../resources/template-macos-layout.xml");

#[derive(Serialize, Deserialize)]
pub struct InfoPlist {
    #[serde(rename = "CFBundleIdentifier")]
    pub cf_bundle_identifier: String,
    #[serde(rename = "CFBundleName")]
    pub cf_bundle_name: String,
    #[serde(rename = "CFBundleVersion")]
    pub cf_bundle_version: String,
    #[serde(rename = "CFBundleShortVersionString")]
    pub cf_bundle_short_version_string: String,
}

#[derive(Debug)]
pub enum KeyTransition {
    Output(KeyOutput),
    Action(DeadKeyAction),
}

#[derive(Debug, Clone)]
pub struct KeyOutput {
    code: usize,
    output: String,
}

#[derive(Debug, Clone)]
pub struct DeadKeyOutput {
    id: DeadKeyId,
    output: String,
}

#[derive(Debug, Clone)]
pub struct DeadKeyAction {
    id: ActionId,
    code: usize,
    states: Vec<DeadKeyOutput>,
}

type DeadKeyId = String;
type ActionId = String;

struct TransformIdManager {
    dead_key_counter: usize,
    action_counter: usize,
}

impl TransformIdManager {
    fn new() -> Self {
        TransformIdManager {
            dead_key_counter: 0,
            action_counter: 0,
        }
    }

    fn next_dead_key(&mut self) -> DeadKeyId {
        let old_counter = self.dead_key_counter;

        self.dead_key_counter = self.dead_key_counter + 1;

        format!("dead_key{:03}", old_counter)
    }

    fn next_action(&mut self) -> ActionId {
        let old_counter = self.action_counter;

        self.action_counter = self.action_counter + 1;

        format!("action{:03}", old_counter)
    }
}

pub struct GenerateMacOs {}

#[async_trait(?Send)]
impl BuildStep for GenerateMacOs {
    async fn build(&self, bundle: Arc<KbdgenBundle>, output_path: &Path) {
        let contents_path = output_path.join(TOP_FOLDER);
        let cloned_contents_path = contents_path.clone();
        let resources_path = contents_path.join(RESOURCES_FOLDER);

        std::fs::create_dir_all(contents_path).unwrap();
        std::fs::create_dir_all(resources_path.clone()).unwrap();

        let mut plist: InfoPlist = plist::from_bytes(PLIST_TEMPLATE.as_bytes()).unwrap();
        println!(
            "what's my CFBundleIdentifier: {}",
            plist.cf_bundle_identifier
        );
        plist.cf_bundle_name = "MyAmazingKbdgenBundle".to_string();

        plist::to_file_xml(cloned_contents_path.join(PLIST_FILENAME), &plist).unwrap();

        // One .keylayout file in Resources folder per language with MacOS primary platform
        for (language_tag, layout) in &bundle.layouts {
            if let Some(mac_os_target) = &layout.mac_os {
                let layers = &mac_os_target.primary.layers;

                let dead_key_count = 0;
                let state_count = 0;

                let mut layered_key_transition_map: IndexMap<
                    MacOsKbdLayer,
                    IndexMap<String, KeyTransition>,
                > = IndexMap::new();
                let mut key_transition_map: IndexMap<String, KeyTransition> = IndexMap::new();
                let mut dead_keys: IndexMap<String, _> = IndexMap::new();

                let mut document = Document::from_str(LAYOUT_TEMPLATE).unwrap();

                let root = document.root();

                let selector = Selector::new("keyMapSet").unwrap();
                let key_map_set = root
                    .query_selector(&document, &selector)
                    .expect("The template document should have a 'keyMapSet' tag");

                add_layer_tags(&layers, &mut document, &key_map_set);

                initialize_key_transition_map(
                    &language_tag,
                    &layers,
                    &mut layered_key_transition_map,
                );

                if let Some(transforms) = &layout.transforms {
                    if let Some(target_dead_keys) = &mac_os_target.dead_keys {
                        process_transforms(
                            &layers,
                            transforms,
                            target_dead_keys,
                            &mut dead_keys,
                            &mut layered_key_transition_map,
                        );
                    } else {
                        tracing::warn!(
                            r#"No dead keys in {}:{}:{}"#,
                            language_tag.to_string(),
                            "MacOS",
                            "Primary",
                        )
                    }
                } else {
                    tracing::warn!(
                        r#"No transforms in {}:{}:{}"#,
                        language_tag.to_string(),
                        "MacOS",
                        "Primary",
                    )
                }

                write_key_transition_map(
                    &layers,
                    &layered_key_transition_map,
                    &mut document,
                    &key_map_set,
                );

                if dead_keys.len() > 0 {
                    let terminators = root.append_new_element(
                        &mut document,
                        NewElement {
                            name: "terminators".into(),
                            attrs: [].into(),
                        },
                    );

                    for (key, dead_key) in dead_keys {
                        append_dead_key_output_element(&terminators, &mut document, &dead_key);
                    }
                }

                let key_layout_path =
                    resources_path.join(format!("{}.{}", language_tag.to_string(), KEY_LAYOUT_EXT));
                std::fs::write(key_layout_path, document.to_string_pretty()).unwrap();
            }
        }
    }
}

fn compute_keyboard_id(language_name: &str) -> String {
    "-8045".to_string()
}

fn add_layer_tags(
    layers: &IndexMap<MacOsKbdLayer, String>,
    document: &mut Document,
    key_map_set: &Element,
) {
    let root = document.root();

    let selector = Selector::new("modifierMap").unwrap();
    let modifier_map = root
        .query_selector(&document, &selector)
        .expect("The template document should have a 'modifierMap' tag");

    for (layer_index, (layer, _)) in layers.iter().enumerate() {
        let key_map_select = modifier_map.append_new_element(
            document,
            NewElement {
                name: "keyMapSelect".into(),
                attrs: [("mapIndex".into(), layer_index.to_string())].into(),
            },
        );

        key_map_select.append_new_element(
            document,
            NewElement {
                name: "modifier".into(),
                attrs: [("keys".into(), layer_attributes(layer))].into(),
            },
        );

        key_map_set.append_new_element(
            document,
            NewElement {
                name: "keyMap".into(),
                attrs: [("index".into(), layer_index.to_string())].into(),
            },
        );
    }
}

fn initialize_key_transition_map(
    language_tag: &LanguageTag,
    layers: &IndexMap<MacOsKbdLayer, String>,
    layered_key_transition_map: &mut IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
) {
    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let mut cursor = 0;

        layered_key_transition_map.insert(*layer, IndexMap::new());

        let mut key_transition_map = layered_key_transition_map
            .get_mut(layer)
            .expect("getting back the value that was just inserted");

        for (_iso_key, key_code) in MACOS_KEYS.iter() {
            let key_map: Vec<String> = split_keys(&key_map);

            tracing::debug!(
                "iso len: {}; keymap len: {}",
                MACOS_KEYS.len(),
                key_map.len()
            );
            if MACOS_KEYS.len() > key_map.len() {
                panic!(
                    r#"Provided layer does not have enough keys, expected {} keys but`` got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                    MACOS_KEYS.len(),
                    key_map.len(),
                    language_tag.to_string(),
                    "MacOS",
                    "Primary",
                    layer,
                    key_map
                );
            }

            let key = key_map[cursor].clone();

            key_transition_map.insert(
                key.clone(),
                KeyTransition::Output(KeyOutput {
                    code: *key_code,
                    output: key.clone(),
                }),
            );

            cursor += 1;
        }
    }
}

fn process_transforms(
    layers: &IndexMap<MacOsKbdLayer, String>,
    transforms: &IndexMap<String, Transform>,
    target_dead_keys: &IndexMap<MacOsKbdLayer, Vec<String>>,
    dead_keys: &mut IndexMap<String, DeadKeyOutput>,
    layered_key_transition_map: &mut IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
) {
    let mut id_manager = TransformIdManager::new();

    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let mut cursor = 0;

        let layer_dead_keys = target_dead_keys.get(layer);

        if let Some(layer_dead_keys) = layer_dead_keys {
            let mut key_transition_map = layered_key_transition_map
                .get_mut(layer)
                .expect("this map should be prefilled by now");

            for (_iso_key, code) in MACOS_KEYS.iter() {
                let key_map: Vec<String> = split_keys(&key_map);

                for (dead_key, value) in transforms {
                    // check if dead key is even adead key on this layer lol

                    match value {
                        Transform::End(_character) => {
                            tracing::error!("Transform ended too soon for dead key {}", dead_key);
                        }
                        Transform::More(map) => {
                            let escape_transform = map.get(TRANSFORM_ESCAPE).expect(&format!(
                                "The escape transform `{}` not found for dead key `{}`",
                                TRANSFORM_ESCAPE, &dead_key
                            ));

                            match escape_transform {
                                Transform::End(end_char) => {
                                    if !dead_keys.contains_key(dead_key) {
                                        let id = id_manager.next_dead_key();

                                        dead_keys.insert(
                                            dead_key.clone(),
                                            DeadKeyOutput {
                                                id,
                                                output: end_char.clone(),
                                            },
                                        );
                                    }
                                }
                                Transform::More(_transform) => {
                                    panic!("The escape transform should be a string, not another transform");
                                }
                            };

                            let dead_key_transform = dead_keys[dead_key].clone();
                            let id = dead_key_transform.id.clone();

                            for (next_char, transform) in map {
                                //println!("next_char: {:?}, transform: {:?}", next_char, transform);

                                match transform {
                                    Transform::End(end_char) => {
                                        if next_char == TRANSFORM_ESCAPE {
                                            continue;
                                        }

                                        if next_char.to_string() == key_map[cursor] {
                                            let key_transform = DeadKeyOutput {
                                                id: id.clone(),
                                                output: end_char.to_string(),
                                            };

                                            //println!("end char: {}", end_char);
                                            update_key_transition_map_with_transform(
                                                key_transition_map,
                                                next_char,
                                                key_transform,
                                                &mut id_manager,
                                            );

                                            break;
                                        }
                                    }
                                    Transform::More(_transform) => {
                                        todo!("Recursion required ahead");
                                    }
                                };
                            }
                        }
                    };
                }

                cursor += 1;
            }
        }
    }
}

fn update_key_transition_map_with_transform(
    key_transition_map: &mut IndexMap<String, KeyTransition>,
    key: &str,
    transform: DeadKeyOutput,
    id_manager: &mut TransformIdManager,
) {
    if key_transition_map.contains_key(key) {
        let entry = key_transition_map.get_mut(key).unwrap();

        match entry {
            KeyTransition::Output(output) => {
                let code = output.code;

                let action = DeadKeyAction {
                    id: id_manager.next_action(),
                    code,
                    states: vec![transform.clone()],
                };

                key_transition_map.insert(key.to_string(), KeyTransition::Action(action));
            }
            KeyTransition::Action(ref mut action) => {
                action.states.push(transform.clone());
            }
        };
    } else {
        panic!(
            "The key_transition_map must already have Output entries for all keys by this point."
        )
    }
}

fn write_key_transition_map(
    layers: &IndexMap<MacOsKbdLayer, String>,
    layered_key_transition_map: &IndexMap<MacOsKbdLayer, IndexMap<String, KeyTransition>>,
    document: &mut Document,
    key_map_set: &Element,
) {
    let selector = Selector::new("actions").unwrap();
    let actions = document
        .root()
        .query_selector(&document, &selector)
        .expect("The template document should have an 'actions' tag");

    for (layer_index, (layer, key_map)) in layers.iter().enumerate() {
        let selector = Selector::new(&format!("keyMap[index=\"{}\"]", layer_index)).unwrap();
        let xml_key_map = key_map_set
            .query_selector(document, &selector)
            .expect("keymap to have right index");

        let key_transition_map = layered_key_transition_map
            .get(layer)
            .expect("this map should be prefilled by now");

        for (key, transition) in key_transition_map {
            match transition {
                KeyTransition::Output(output) => {
                    append_key_output_element(&xml_key_map, document, &output);
                }
                KeyTransition::Action(dead_key_action) => {
                    xml_key_map.append_new_element(
                        document,
                        NewElement {
                            name: "key".into(),
                            attrs: [
                                ("code".into(), dead_key_action.code.to_string()),
                                ("action".into(), dead_key_action.id.clone()),
                            ]
                            .into(),
                        },
                    );

                    // aggregate actions first, then print them
                    // instead of this
                    let action = actions.append_new_element(
                        document,
                        NewElement {
                            name: "action".into(),
                            attrs: [("id".into(), dead_key_action.id.clone())].into(),
                        },
                    );

                    for state in &dead_key_action.states {
                        append_dead_key_output_element(&action, document, &state);
                    }
                }
            };
        }

        for (key_code, output) in MACOS_HARDCODED.iter() {
            let key = KeyOutput {
                code: *key_code,
                output: output.clone(),
            };

            append_key_output_element(&xml_key_map, document, &key);
        }
    }
}

fn append_dead_key_output_element(element: &Element, document: &mut Document, key: &DeadKeyOutput) {
    element.append_new_element(
        document,
        NewElement {
            name: "when".into(),
            attrs: [
                ("state".to_string(), key.id.clone()),
                ("output".to_string(), decode_unicode_escapes(&key.output)),
            ]
            .into(),
        },
    );
}

fn append_key_output_element(element: &Element, document: &mut Document, key: &KeyOutput) {
    element.append_new_element(
        document,
        NewElement {
            name: "key".into(),
            attrs: [
                ("code".into(), key.code.to_string()),
                ("output".into(), decode_unicode_escapes(&key.output)),
            ]
            .into(),
        },
    );
}
<|endoftext|>


<commit_before>use action::Action;
use config;
use context::base::BaseContext;
use dispatcher::Response;
use dispatcher::response::ResponseSender;
use message::{Builder, Message};
use std::cell::RefCell;
use std::rc::Rc;
use state::State;

#[derive(Clone)]
pub struct MessageAction {
    pub sender: Rc<RefCell<Box<ResponseSender<Response>>>>,
    pub action: config::action::MessageAction
}

#[derive(Debug)]
pub struct MessageResponse {
    message: Message,
}

impl Action for MessageAction {
    fn execute(&self, _state: &State, _context: &BaseContext) {
        println!("MessageAction is executed");
        let mut message = Builder::new("d6621bd6-4898-4b8c-a4ff-36d0eed7d8dc")
                                .pair(".context.uuid", &_context.uuid().to_hyphenated_string())
                                .pair(".context.len", &_state.messages().len().to_string())
                                .build();
        if let Some(name) = _context.name() {
            message.insert(".context.name", name);
        }
        let response = MessageResponse {
            message: message,
        };
        self.sender.borrow_mut().send_response(Response::Message(response));
    }
}
<commit_msg>MessageResponse: add message() method<commit_after>use action::Action;
use config;
use context::base::BaseContext;
use dispatcher::Response;
use dispatcher::response::ResponseSender;
use message::{Builder, Message};
use std::cell::RefCell;
use std::rc::Rc;
use state::State;

#[derive(Clone)]
pub struct MessageAction {
    pub sender: Rc<RefCell<Box<ResponseSender<Response>>>>,
    pub action: config::action::MessageAction
}

#[derive(Debug)]
pub struct MessageResponse {
    message: Message,
}

impl MessageResponse {
    pub fn message(&self) -> &Message {
        &self.message
    }
}

impl Action for MessageAction {
    fn execute(&self, _state: &State, _context: &BaseContext) {
        println!("MessageAction is executed");
        let mut message = Builder::new("d6621bd6-4898-4b8c-a4ff-36d0eed7d8dc")
                                .pair(".context.uuid", &_context.uuid().to_hyphenated_string())
                                .pair(".context.len", &_state.messages().len().to_string())
                                .build();
        if let Some(name) = _context.name() {
            message.insert(".context.name", name);
        }
        let response = MessageResponse {
            message: message,
        };
        self.sender.borrow_mut().send_response(Response::Message(response));
    }
}
<|endoftext|>


<commit_before>use std::io;

use tag;
use err;
use serial;
use byte::{read_byte, write_byte};

use std::cmp;

macro_rules! asn1_cereal_int {
  ($rs_type:ty, $size:expr) => (
    impl serial::traits::Asn1Info for $rs_type {
      fn asn1_type() -> tag::Type {
        tag::Type::from("INTEGER")
      }

      fn asn1_class() -> tag::Class {
        tag::Class::Universal
      }

      fn asn1_tagnum() -> tag::TagNum {
        tag::TagNum::from(2u8)
      }

      fn asn1_constructed() -> bool {
        false
      }
    }

    impl serial::traits::Asn1Serialize for $rs_type {
      fn serialize_imp<W: io::Write>(&self, writer: &mut W) -> Result<(), err::EncodeError> {
        let mut started = false;
        // Loop through bytes in int backwards, start writing when first non-zero byte is encounted.
        for offset in (0..8).rev() {
          let shifted: $rs_type = self >> (offset * 8);
          let byte: u8 = (shifted & 0xff) as u8;
          if !started {
            if byte == 0 {
              continue;
            }
            started = true;
          }
          try!(write_byte(writer, byte));
        }

        // If we haven't written anything, write a zero byte.
        if !started {
          try!(write_byte(writer, 0 as u8));
        }
        Ok(())
      }
    }

    impl serial::traits::Asn1Deserialize for $rs_type {
      fn deserialize_imp<I: Iterator<Item=io::Result<u8>>>(reader: &mut I, len: tag::Len) -> Result<Self, err::DecodeError> {
        let len_num = try!(match len {
          tag::Len::Def(l) => Ok(l),
          _ => Err(err::DecodeError::PrimIndef),
        });

        let mut int: $rs_type = 0;
        let size = cmp::min($size, len_num);

        for _ in 0..len_num {
          let byte = try!(read_byte(reader));
          int = (int << 8) + (byte as $rs_type);
        }

        Ok(int)
      }
    }
  )
}

asn1_cereal_int!(u8, 1);
asn1_cereal_int!(u16, 2);
asn1_cereal_int!(u32, 4);
asn1_cereal_int!(u64, 8);
<commit_msg>Fix error so int will compile<commit_after>use std::io;

use tag;
use err;
use serial;
use byte::{read_byte, write_byte};

use std::cmp;

macro_rules! asn1_cereal_int {
  ($rs_type:ty, $size:expr) => (
    impl serial::traits::Asn1Info for $rs_type {
      fn asn1_type() -> tag::Type {
        tag::Type::from("INTEGER")
      }

      fn asn1_class() -> tag::Class {
        tag::Class::Universal
      }

      fn asn1_tagnum() -> tag::TagNum {
        tag::TagNum::from(2u8)
      }

      fn asn1_constructed() -> bool {
        false
      }
    }

    impl serial::traits::Asn1Serialize for $rs_type {
      fn serialize_imp<W: io::Write>(&self, writer: &mut W) -> Result<(), err::EncodeError> {
        let mut started = false;
        // Loop through bytes in int backwards, start writing when first non-zero byte is encounted.
        for offset in (0..8).rev() {
          let shifted: $rs_type = self >> (offset * 8);
          let byte: u8 = (shifted & 0xff) as u8;
          if !started {
            if byte == 0 {
              continue;
            }
            started = true;
          }
          try!(write_byte(writer, byte));
        }

        // If we haven't written anything, write a zero byte.
        if !started {
          try!(write_byte(writer, 0 as u8));
        }
        Ok(())
      }
    }

    impl serial::traits::Asn1Deserialize for $rs_type {
      fn deserialize_imp<I: Iterator<Item=io::Result<u8>>>(reader: &mut I, len: tag::Len) -> Result<Self, err::DecodeError> {
        let len_num = try!(match len {
          tag::Len::Def(l) => Ok(l),
          _ => Err(err::DecodeError::PrimIndef),
        });

        let mut int: $rs_type = 0;
        let size = cmp::min($size, len_num);

        for _ in 0..len_num {
          let byte = try!(read_byte(reader));
          int = (int << 8) + (byte as $rs_type);
        }

        Ok(int)
      }
    }
  )
}

// Can't bitshift a u8 by 8 bits, need to update deserialize_imp.
// asn1_cereal_int!(u8, 1);
asn1_cereal_int!(u16, 2);
asn1_cereal_int!(u32, 4);
asn1_cereal_int!(u64, 8);
<|endoftext|>


<commit_before>//! AwesomeWM Keygrabber interface

use ::lua::LUA;
use rlua::{self, Lua, Table, Function, Value};
use rustwlc::*;
#[allow(deprecated)]
use rustwlc::xkb::Keysym;

pub const KEYGRABBER_TABLE: &str = "keygrabber";
const SECRET_CALLBACK: &str = "__callback";

/// Init the methods defined on this interface.
pub fn init(lua: &Lua) -> rlua::Result<()> {
    let keygrabber_table = lua.create_table();
    keygrabber_table.set("run", lua.create_function(run))?;
    keygrabber_table.set("stop", lua.create_function(stop))?;
    keygrabber_table.set("isrunning", lua.create_function(isrunning))?;
    let globals = lua.globals();
    globals.set(KEYGRABBER_TABLE, keygrabber_table)
}

#[allow(deprecated)]
/// Given the current input, handle calling the Lua defined callback if it is
/// defined with the input.
pub fn keygrabber_handle(mods: KeyboardModifiers, sym: Keysym, state: KeyState)
                         -> rlua::Result<()> {
    let lua = LUA.lock()
        .map_err(|err| rlua::Error::RuntimeError(
            format!("Lua lock was poisoned {:#?}", err)))?;
    let lua_state = if state == KeyState::Pressed {
        "press"
    } else {
        "release"
    }.into();
    let lua_sym = sym.get_name()
        .ok_or_else(|| rlua::Error::RuntimeError(
                 format!("Symbol did not have a name: {:#?}", sym)))?;
    let lua_mods = ::lua::mods_to_lua(&lua.0, mods.mods)?;
    let res = call_keygrabber(&lua.0, (lua_mods, lua_sym, lua_state));
    match res {
        Ok(_) | Err(rlua::Error::FromLuaConversionError { .. }) => {Ok(())},
        err => {
            err
        }
    }

}

/// Call the Lua callback function for when a key is pressed.
fn call_keygrabber(lua: &Lua,
                   (mods, key, event): (Table, String, String))
                   -> rlua::Result<()> {
    let globals = lua.globals();
    let lua_callback = globals
        .get::<_, Table>(KEYGRABBER_TABLE).expect("keygrabber table not defined")
        .get::<_, Function>(SECRET_CALLBACK)?;
    lua_callback.call((mods, key, event))
}

fn run(lua: &Lua, function: rlua::Function) -> rlua::Result<()> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    match keygrabber_table.get::<_, Value>(SECRET_CALLBACK)? {
        Value::Function(_) =>
            Err(rlua::Error::RuntimeError("keygrabber callback already set!"
                                          .into())),
        _ => keygrabber_table.set(SECRET_CALLBACK, function)
    }
}

fn stop(lua: &Lua, _: ()) -> rlua::Result<()> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    keygrabber_table.set(SECRET_CALLBACK, Value::Nil)
}

fn isrunning(lua: &Lua, _: ()) -> rlua::Result<bool> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    match keygrabber_table.get::<_, Value>(SECRET_CALLBACK)? {
        Value::Function(_) => Ok(true),
        _ => Ok(false)
    }
}
<commit_msg>Fixes #384 Implements keygrabber meta functions<commit_after>//! AwesomeWM Keygrabber interface

use ::lua::LUA;
use rlua::{self, Lua, Table, Function, Value};
use rustwlc::*;
#[allow(deprecated)]
use rustwlc::xkb::Keysym;
use super::{GLOBAL_SIGNALS, signal};

pub const KEYGRABBER_TABLE: &str = "keygrabber";
const SECRET_CALLBACK: &str = "__callback";

/// Init the methods defined on this interface.
pub fn init(lua: &Lua) -> rlua::Result<()> {
    let keygrabber_table = lua.create_table();
    let meta = lua.create_table();
    meta.set("__index", lua.create_function(index))?;
    meta.set("__newindex", lua.create_function(new_index))?;
    keygrabber_table.set("run", lua.create_function(run))?;
    keygrabber_table.set("stop", lua.create_function(stop))?;
    keygrabber_table.set("isrunning", lua.create_function(isrunning))?;
    keygrabber_table.set_metatable(Some(meta));
    let globals = lua.globals();
    globals.set(KEYGRABBER_TABLE, keygrabber_table)
}

#[allow(deprecated)]
/// Given the current input, handle calling the Lua defined callback if it is
/// defined with the input.
pub fn keygrabber_handle(mods: KeyboardModifiers, sym: Keysym, state: KeyState)
                         -> rlua::Result<()> {
    let lua = LUA.lock()
        .map_err(|err| rlua::Error::RuntimeError(
            format!("Lua lock was poisoned {:#?}", err)))?;
    let lua_state = if state == KeyState::Pressed {
        "press"
    } else {
        "release"
    }.into();
    let lua_sym = sym.get_name()
        .ok_or_else(|| rlua::Error::RuntimeError(
                 format!("Symbol did not have a name: {:#?}", sym)))?;
    let lua_mods = ::lua::mods_to_lua(&lua.0, mods.mods)?;
    let res = call_keygrabber(&lua.0, (lua_mods, lua_sym, lua_state));
    match res {
        Ok(_) | Err(rlua::Error::FromLuaConversionError { .. }) => {Ok(())},
        err => {
            err
        }
    }

}

/// Call the Lua callback function for when a key is pressed.
fn call_keygrabber(lua: &Lua,
                   (mods, key, event): (Table, String, String))
                   -> rlua::Result<()> {
    let globals = lua.globals();
    let lua_callback = globals
        .get::<_, Table>(KEYGRABBER_TABLE).expect("keygrabber table not defined")
        .get::<_, Function>(SECRET_CALLBACK)?;
    lua_callback.call((mods, key, event))
}

fn run(lua: &Lua, function: rlua::Function) -> rlua::Result<()> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    match keygrabber_table.get::<_, Value>(SECRET_CALLBACK)? {
        Value::Function(_) =>
            Err(rlua::Error::RuntimeError("keygrabber callback already set!"
                                          .into())),
        _ => keygrabber_table.set(SECRET_CALLBACK, function)
    }
}

fn stop(lua: &Lua, _: ()) -> rlua::Result<()> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    keygrabber_table.set(SECRET_CALLBACK, Value::Nil)
}

fn isrunning(lua: &Lua, _: ()) -> rlua::Result<bool> {
    let keygrabber_table = lua.globals().get::<_, Table>(KEYGRABBER_TABLE)?;
    match keygrabber_table.get::<_, Value>(SECRET_CALLBACK)? {
        Value::Function(_) => Ok(true),
        _ => Ok(false)
    }
}

fn index(lua: &Lua, args: Value) -> rlua::Result<()> {
    let global_signals = lua.globals().get::<_, Table>(GLOBAL_SIGNALS)?;
    let fake_object = lua.create_table();
    fake_object.set("signals", global_signals)?;
    signal::emit_signal(lua, fake_object.into(), "debug::index::miss".into(), args)
}

fn new_index(lua: &Lua, args: Value) -> rlua::Result<()> {
    let global_signals = lua.globals().get::<_, Table>(GLOBAL_SIGNALS)?;
    let fake_object = lua.create_table();
    fake_object.set("signals", global_signals)?;
    signal::emit_signal(lua, fake_object.into(), "debug::newindex::miss".into(), args)
}
<|endoftext|>


<commit_before>extern crate pancurses;

use backend;
use event::{Event, Key};

use self::super::find_closest;
use std::cell::Cell;
use theme::{Color, ColorStyle, Effect};
use utf8;

pub struct Concrete {
    window: pancurses::Window,
    current_style: Cell<ColorStyle>,
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        ::std::env::set_var("ESCDELAY", "25");
        let window = pancurses::initscr();
        window.keypad(true);
        pancurses::noecho();
        pancurses::cbreak();
        pancurses::start_color();
        pancurses::curs_set(0);
        window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as u32));

        Concrete {
            window: window,
            current_style: Cell::new(ColorStyle::Background),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let (y, x) = self.window.get_max_yx();
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        pancurses::has_colors()
    }

    fn finish(&mut self) {
        pancurses::endwin();
    }

    fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
                        background: &Color) {
        pancurses::init_pair(style.id(),
                             find_closest(foreground) as i16,
                             find_closest(background) as i16);
    }

    fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
        // TODO: pancurses doesn't have an `attr_get` equivalent
        // let mut current_style: pancurses::attr_t = 0;
        // let mut current_color: i16 = 0;
        // pancurses::attr_get(&mut current_style, &mut current_color);
        let current_style = self.current_style.get();

        let style = pancurses::COLOR_PAIR(color.id() as u32);
        self.window.attron(style);

        self.current_style.set(color);
        f();
        self.current_style.set(current_style);

        // self.window.attroff(style);
        self.window.attron(pancurses::COLOR_PAIR(current_style.id() as u32));
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            // A_REVERSE, taken from ncurses
            Effect::Reverse => 1 << (10 + 8u32),
            Effect::Simple => pancurses::A_NORMAL,
        };
        self.window.attron(style);
        f();
        self.window.attroff(style);
    }

    fn clear(&self) {
        self.window.clear();
    }

    fn refresh(&mut self) {
        self.window.refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        self.window.mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        // TODO: there seems to not be any indication
        // of Ctrl/Alt/Shift in these :v
        if let Some(ev) = self.window.getch() {
            match ev {
                pancurses::Input::Character('\n') => Event::Key(Key::Enter),
                // TODO: wait for a very short delay. If more keys are
                // pipelined, it may be an escape sequence.
                pancurses::Input::Character('\u{7f}') |
                pancurses::Input::Character('\u{8}') => Event::Key(Key::Backspace),
                pancurses::Input::Character('\u{9}') => Event::Key(Key::Tab),
                pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
                pancurses::Input::Character(c) if 32 <= (c as u32) &&
                                                  (c as u32) <= 255 => {
                    Event::Char(utf8::read_char(c as u8, || {
                            self.window.getch().and_then(|i| match i {
                                pancurses::Input::Character(c) => {
                                    Some(c as u8)
                                }
                                _ => None,
                            })
                        })
                        .unwrap())
                }
                pancurses::Input::Character(c) => Event::Unknown(c as i32),
                // TODO: Some key combos are not recognized by pancurses,
                // but are sent as Unknown. We could still parse them here.
                pancurses::Input::Unknown(i) => Event::Unknown(i),
                // TODO: I honestly have no fucking idea what KeyCodeYes is
                pancurses::Input::KeyCodeYes => Event::Refresh,
                pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),
                pancurses::Input::KeyDown => Event::Key(Key::Down),
                pancurses::Input::KeyUp => Event::Key(Key::Up),
                pancurses::Input::KeyLeft => Event::Key(Key::Left),
                pancurses::Input::KeyRight => Event::Key(Key::Right),
                pancurses::Input::KeyHome => Event::Key(Key::Home),
                pancurses::Input::KeyBackspace => Event::Key(Key::Backspace),
                pancurses::Input::KeyF0 => Event::Key(Key::F0),
                pancurses::Input::KeyF1 => Event::Key(Key::F1),
                pancurses::Input::KeyF2 => Event::Key(Key::F2),
                pancurses::Input::KeyF3 => Event::Key(Key::F3),
                pancurses::Input::KeyF4 => Event::Key(Key::F4),
                pancurses::Input::KeyF5 => Event::Key(Key::F5),
                pancurses::Input::KeyF6 => Event::Key(Key::F6),
                pancurses::Input::KeyF7 => Event::Key(Key::F7),
                pancurses::Input::KeyF8 => Event::Key(Key::F8),
                pancurses::Input::KeyF9 => Event::Key(Key::F9),
                pancurses::Input::KeyF10 => Event::Key(Key::F10),
                pancurses::Input::KeyF11 => Event::Key(Key::F11),
                pancurses::Input::KeyF12 => Event::Key(Key::F12),
                pancurses::Input::KeyF13 => Event::Shift(Key::F1),
                pancurses::Input::KeyF14 => Event::Shift(Key::F2),
                pancurses::Input::KeyF15 => Event::Shift(Key::F3),
                pancurses::Input::KeyDL => Event::Refresh,
                pancurses::Input::KeyIL => Event::Refresh,
                pancurses::Input::KeyDC => Event::Key(Key::Del),
                pancurses::Input::KeyIC => Event::Key(Key::Ins),
                pancurses::Input::KeyEIC => Event::Refresh,
                pancurses::Input::KeyClear => Event::Refresh,
                pancurses::Input::KeyEOS => Event::Refresh,
                pancurses::Input::KeyEOL => Event::Refresh,
                pancurses::Input::KeySF => Event::Shift(Key::Down),
                pancurses::Input::KeySR => Event::Shift(Key::Up),
                pancurses::Input::KeyNPage => Event::Key(Key::PageDown),
                pancurses::Input::KeyPPage => Event::Key(Key::PageUp),
                pancurses::Input::KeySTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyCTab => Event::Ctrl(Key::Tab),
                pancurses::Input::KeyCATab => Event::CtrlAlt(Key::Tab),
                pancurses::Input::KeyEnter => Event::Key(Key::Enter),
                pancurses::Input::KeySReset => Event::Refresh,
                pancurses::Input::KeyReset => Event::Refresh,
                pancurses::Input::KeyPrint => Event::Refresh,
                pancurses::Input::KeyLL => Event::Refresh,
                pancurses::Input::KeyAbort => Event::Refresh,
                pancurses::Input::KeySHelp => Event::Refresh,
                pancurses::Input::KeyLHelp => Event::Refresh,
                pancurses::Input::KeyBTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyBeg => Event::Refresh,
                pancurses::Input::KeyCancel => Event::Refresh,
                pancurses::Input::KeyClose => Event::Refresh,
                pancurses::Input::KeyCommand => Event::Refresh,
                pancurses::Input::KeyCopy => Event::Refresh,
                pancurses::Input::KeyCreate => Event::Refresh,
                pancurses::Input::KeyEnd => Event::Key(Key::End),
                pancurses::Input::KeyExit => Event::Refresh,
                pancurses::Input::KeyFind => Event::Refresh,
                pancurses::Input::KeyHelp => Event::Refresh,
                pancurses::Input::KeyMark => Event::Refresh,
                pancurses::Input::KeyMessage => Event::Refresh,
                pancurses::Input::KeyMove => Event::Refresh,
                pancurses::Input::KeyNext => Event::Refresh,
                pancurses::Input::KeyOpen => Event::Refresh,
                pancurses::Input::KeyOptions => Event::Refresh,
                pancurses::Input::KeyPrevious => Event::Refresh,
                pancurses::Input::KeyRedo => Event::Refresh,
                pancurses::Input::KeyReference => Event::Refresh,
                pancurses::Input::KeyRefresh => Event::Refresh,
                pancurses::Input::KeyReplace => Event::Refresh,
                pancurses::Input::KeyRestart => Event::Refresh,
                pancurses::Input::KeyResume => Event::Refresh,
                pancurses::Input::KeySave => Event::Refresh,
                pancurses::Input::KeySBeg => Event::Refresh,
                pancurses::Input::KeySCancel => Event::Refresh,
                pancurses::Input::KeySCommand => Event::Refresh,
                pancurses::Input::KeySCopy => Event::Refresh,
                pancurses::Input::KeySCreate => Event::Refresh,
                pancurses::Input::KeySDC => Event::Shift(Key::Del),
                pancurses::Input::KeySDL => Event::Refresh,
                pancurses::Input::KeySelect => Event::Refresh,
                pancurses::Input::KeySEnd => Event::Shift(Key::End),
                pancurses::Input::KeySEOL => Event::Refresh,
                pancurses::Input::KeySExit => Event::Refresh,
                pancurses::Input::KeySFind => Event::Refresh,
                pancurses::Input::KeySHome => Event::Shift(Key::Home),
                pancurses::Input::KeySIC => Event::Shift(Key::Ins),
                pancurses::Input::KeySLeft => Event::Shift(Key::Left),
                pancurses::Input::KeySMessage => Event::Refresh,
                pancurses::Input::KeySMove => Event::Refresh,
                pancurses::Input::KeySNext => Event::Shift(Key::PageDown),
                pancurses::Input::KeySOptions => Event::Refresh,
                pancurses::Input::KeySPrevious => Event::Shift(Key::PageUp),
                pancurses::Input::KeySPrint => Event::Refresh,
                pancurses::Input::KeySRedo => Event::Refresh,
                pancurses::Input::KeySReplace => Event::Refresh,
                pancurses::Input::KeySRight => Event::Shift(Key::Right),
                pancurses::Input::KeySResume => Event::Refresh,
                pancurses::Input::KeySSave => Event::Refresh,
                pancurses::Input::KeySSuspend => Event::Refresh,
                pancurses::Input::KeySUndo => Event::Refresh,
                pancurses::Input::KeySuspend => Event::Refresh,
                pancurses::Input::KeyUndo => Event::Refresh,
                pancurses::Input::KeyResize => Event::WindowResize,
                pancurses::Input::KeyEvent => Event::Refresh,
                // TODO: mouse support
                pancurses::Input::KeyMouse => Event::Refresh,
                pancurses::Input::KeyA1 => Event::Refresh,
                pancurses::Input::KeyA3 => Event::Refresh,
                pancurses::Input::KeyB2 => Event::Key(Key::NumpadCenter),
                pancurses::Input::KeyC1 => Event::Refresh,
                pancurses::Input::KeyC3 => Event::Refresh,
            }
        } else {
            Event::Refresh
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            self.window.timeout(-1);
        } else {
            self.window.timeout(1000 / fps as i32);
        }
    }
}
<commit_msg>Fix type cast in pancurses backend<commit_after>extern crate pancurses;

use backend;
use event::{Event, Key};

use self::super::find_closest;
use std::cell::Cell;
use theme::{Color, ColorStyle, Effect};
use utf8;

pub struct Concrete {
    window: pancurses::Window,
    current_style: Cell<ColorStyle>,
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        ::std::env::set_var("ESCDELAY", "25");
        let window = pancurses::initscr();
        window.keypad(true);
        pancurses::noecho();
        pancurses::cbreak();
        pancurses::start_color();
        pancurses::curs_set(0);
        window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as pancurses::chtype));

        Concrete {
            window: window,
            current_style: Cell::new(ColorStyle::Background),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let (y, x) = self.window.get_max_yx();
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        pancurses::has_colors()
    }

    fn finish(&mut self) {
        pancurses::endwin();
    }

    fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
                        background: &Color) {
        pancurses::init_pair(style.id(),
                             find_closest(foreground) as i16,
                             find_closest(background) as i16);
    }

    fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
        // TODO: pancurses doesn't have an `attr_get` equivalent
        // let mut current_style: pancurses::attr_t = 0;
        // let mut current_color: i16 = 0;
        // pancurses::attr_get(&mut current_style, &mut current_color);
        let current_style = self.current_style.get();

        let style = pancurses::COLOR_PAIR(color.id() as pancurses::chtype);
        self.window.attron(style);

        self.current_style.set(color);
        f();
        self.current_style.set(current_style);

        // self.window.attroff(style);
        self.window.attron(pancurses::COLOR_PAIR(current_style.id() as pancurses::chtype));
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            // A_REVERSE, taken from ncurses
            Effect::Reverse => 1 << (10 + 8u32),
            Effect::Simple => pancurses::A_NORMAL,
        };
        self.window.attron(style);
        f();
        self.window.attroff(style);
    }

    fn clear(&self) {
        self.window.clear();
    }

    fn refresh(&mut self) {
        self.window.refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        self.window.mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        // TODO: there seems to not be any indication
        // of Ctrl/Alt/Shift in these :v
        if let Some(ev) = self.window.getch() {
            match ev {
                pancurses::Input::Character('\n') => Event::Key(Key::Enter),
                // TODO: wait for a very short delay. If more keys are
                // pipelined, it may be an escape sequence.
                pancurses::Input::Character('\u{7f}') |
                pancurses::Input::Character('\u{8}') => Event::Key(Key::Backspace),
                pancurses::Input::Character('\u{9}') => Event::Key(Key::Tab),
                pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
                pancurses::Input::Character(c) if 32 <= (c as u32) &&
                                                  (c as u32) <= 255 => {
                    Event::Char(utf8::read_char(c as u8, || {
                            self.window.getch().and_then(|i| match i {
                                pancurses::Input::Character(c) => {
                                    Some(c as u8)
                                }
                                _ => None,
                            })
                        })
                        .unwrap())
                }
                pancurses::Input::Character(c) => Event::Unknown(c as i32),
                // TODO: Some key combos are not recognized by pancurses,
                // but are sent as Unknown. We could still parse them here.
                pancurses::Input::Unknown(i) => Event::Unknown(i),
                // TODO: I honestly have no fucking idea what KeyCodeYes is
                pancurses::Input::KeyCodeYes => Event::Refresh,
                pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),
                pancurses::Input::KeyDown => Event::Key(Key::Down),
                pancurses::Input::KeyUp => Event::Key(Key::Up),
                pancurses::Input::KeyLeft => Event::Key(Key::Left),
                pancurses::Input::KeyRight => Event::Key(Key::Right),
                pancurses::Input::KeyHome => Event::Key(Key::Home),
                pancurses::Input::KeyBackspace => Event::Key(Key::Backspace),
                pancurses::Input::KeyF0 => Event::Key(Key::F0),
                pancurses::Input::KeyF1 => Event::Key(Key::F1),
                pancurses::Input::KeyF2 => Event::Key(Key::F2),
                pancurses::Input::KeyF3 => Event::Key(Key::F3),
                pancurses::Input::KeyF4 => Event::Key(Key::F4),
                pancurses::Input::KeyF5 => Event::Key(Key::F5),
                pancurses::Input::KeyF6 => Event::Key(Key::F6),
                pancurses::Input::KeyF7 => Event::Key(Key::F7),
                pancurses::Input::KeyF8 => Event::Key(Key::F8),
                pancurses::Input::KeyF9 => Event::Key(Key::F9),
                pancurses::Input::KeyF10 => Event::Key(Key::F10),
                pancurses::Input::KeyF11 => Event::Key(Key::F11),
                pancurses::Input::KeyF12 => Event::Key(Key::F12),
                pancurses::Input::KeyF13 => Event::Shift(Key::F1),
                pancurses::Input::KeyF14 => Event::Shift(Key::F2),
                pancurses::Input::KeyF15 => Event::Shift(Key::F3),
                pancurses::Input::KeyDL => Event::Refresh,
                pancurses::Input::KeyIL => Event::Refresh,
                pancurses::Input::KeyDC => Event::Key(Key::Del),
                pancurses::Input::KeyIC => Event::Key(Key::Ins),
                pancurses::Input::KeyEIC => Event::Refresh,
                pancurses::Input::KeyClear => Event::Refresh,
                pancurses::Input::KeyEOS => Event::Refresh,
                pancurses::Input::KeyEOL => Event::Refresh,
                pancurses::Input::KeySF => Event::Shift(Key::Down),
                pancurses::Input::KeySR => Event::Shift(Key::Up),
                pancurses::Input::KeyNPage => Event::Key(Key::PageDown),
                pancurses::Input::KeyPPage => Event::Key(Key::PageUp),
                pancurses::Input::KeySTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyCTab => Event::Ctrl(Key::Tab),
                pancurses::Input::KeyCATab => Event::CtrlAlt(Key::Tab),
                pancurses::Input::KeyEnter => Event::Key(Key::Enter),
                pancurses::Input::KeySReset => Event::Refresh,
                pancurses::Input::KeyReset => Event::Refresh,
                pancurses::Input::KeyPrint => Event::Refresh,
                pancurses::Input::KeyLL => Event::Refresh,
                pancurses::Input::KeyAbort => Event::Refresh,
                pancurses::Input::KeySHelp => Event::Refresh,
                pancurses::Input::KeyLHelp => Event::Refresh,
                pancurses::Input::KeyBTab => Event::Shift(Key::Tab),
                pancurses::Input::KeyBeg => Event::Refresh,
                pancurses::Input::KeyCancel => Event::Refresh,
                pancurses::Input::KeyClose => Event::Refresh,
                pancurses::Input::KeyCommand => Event::Refresh,
                pancurses::Input::KeyCopy => Event::Refresh,
                pancurses::Input::KeyCreate => Event::Refresh,
                pancurses::Input::KeyEnd => Event::Key(Key::End),
                pancurses::Input::KeyExit => Event::Refresh,
                pancurses::Input::KeyFind => Event::Refresh,
                pancurses::Input::KeyHelp => Event::Refresh,
                pancurses::Input::KeyMark => Event::Refresh,
                pancurses::Input::KeyMessage => Event::Refresh,
                pancurses::Input::KeyMove => Event::Refresh,
                pancurses::Input::KeyNext => Event::Refresh,
                pancurses::Input::KeyOpen => Event::Refresh,
                pancurses::Input::KeyOptions => Event::Refresh,
                pancurses::Input::KeyPrevious => Event::Refresh,
                pancurses::Input::KeyRedo => Event::Refresh,
                pancurses::Input::KeyReference => Event::Refresh,
                pancurses::Input::KeyRefresh => Event::Refresh,
                pancurses::Input::KeyReplace => Event::Refresh,
                pancurses::Input::KeyRestart => Event::Refresh,
                pancurses::Input::KeyResume => Event::Refresh,
                pancurses::Input::KeySave => Event::Refresh,
                pancurses::Input::KeySBeg => Event::Refresh,
                pancurses::Input::KeySCancel => Event::Refresh,
                pancurses::Input::KeySCommand => Event::Refresh,
                pancurses::Input::KeySCopy => Event::Refresh,
                pancurses::Input::KeySCreate => Event::Refresh,
                pancurses::Input::KeySDC => Event::Shift(Key::Del),
                pancurses::Input::KeySDL => Event::Refresh,
                pancurses::Input::KeySelect => Event::Refresh,
                pancurses::Input::KeySEnd => Event::Shift(Key::End),
                pancurses::Input::KeySEOL => Event::Refresh,
                pancurses::Input::KeySExit => Event::Refresh,
                pancurses::Input::KeySFind => Event::Refresh,
                pancurses::Input::KeySHome => Event::Shift(Key::Home),
                pancurses::Input::KeySIC => Event::Shift(Key::Ins),
                pancurses::Input::KeySLeft => Event::Shift(Key::Left),
                pancurses::Input::KeySMessage => Event::Refresh,
                pancurses::Input::KeySMove => Event::Refresh,
                pancurses::Input::KeySNext => Event::Shift(Key::PageDown),
                pancurses::Input::KeySOptions => Event::Refresh,
                pancurses::Input::KeySPrevious => Event::Shift(Key::PageUp),
                pancurses::Input::KeySPrint => Event::Refresh,
                pancurses::Input::KeySRedo => Event::Refresh,
                pancurses::Input::KeySReplace => Event::Refresh,
                pancurses::Input::KeySRight => Event::Shift(Key::Right),
                pancurses::Input::KeySResume => Event::Refresh,
                pancurses::Input::KeySSave => Event::Refresh,
                pancurses::Input::KeySSuspend => Event::Refresh,
                pancurses::Input::KeySUndo => Event::Refresh,
                pancurses::Input::KeySuspend => Event::Refresh,
                pancurses::Input::KeyUndo => Event::Refresh,
                pancurses::Input::KeyResize => Event::WindowResize,
                pancurses::Input::KeyEvent => Event::Refresh,
                // TODO: mouse support
                pancurses::Input::KeyMouse => Event::Refresh,
                pancurses::Input::KeyA1 => Event::Refresh,
                pancurses::Input::KeyA3 => Event::Refresh,
                pancurses::Input::KeyB2 => Event::Key(Key::NumpadCenter),
                pancurses::Input::KeyC1 => Event::Refresh,
                pancurses::Input::KeyC3 => Event::Refresh,
            }
        } else {
            Event::Refresh
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            self.window.timeout(-1);
        } else {
            self.window.timeout(1000 / fps as i32);
        }
    }
}
<|endoftext|>