feat(config): add options to configure keyboard layout on Wayland

This commit is contained in:
Federico Terzi 2021-08-09 22:53:32 +02:00
parent c68d59797e
commit 47eb2b0b69
5 changed files with 84 additions and 11 deletions

View File

@ -107,6 +107,10 @@ pub trait Config: Send + Sync {
// If false, avoid applying the built-in patches to the current config. // If false, avoid applying the built-in patches to the current config.
fn apply_patch(&self) -> bool; fn apply_patch(&self) -> bool;
// On Wayland, overrides the auto-detected keyboard configuration (RMLVO)
// which is used both for the detection and injection process.
fn keyboard_layout(&self) -> Option<RMLVOConfig>;
fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool; fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool;
fn pretty_dump(&self) -> String { fn pretty_dump(&self) -> String {
@ -192,6 +196,15 @@ pub enum ToggleKey {
LeftMeta, LeftMeta,
} }
#[derive(Debug, Clone)]
pub struct RMLVOConfig {
pub rules: Option<String>,
pub model: Option<String>,
pub layout: Option<String>,
pub variant: Option<String>,
pub options: Option<String>,
}
pub fn load_store(config_dir: &Path) -> Result<(impl ConfigStore, Vec<NonFatalErrorSet>)> { pub fn load_store(config_dir: &Path) -> Result<(impl ConfigStore, Vec<NonFatalErrorSet>)> {
store::DefaultConfigStore::load(config_dir) store::DefaultConfigStore::load(config_dir)
} }

View File

@ -18,7 +18,7 @@
*/ */
use anyhow::Result; use anyhow::Result;
use std::{convert::TryInto, path::Path}; use std::{collections::BTreeMap, convert::TryInto, path::Path};
use thiserror::Error; use thiserror::Error;
mod yaml; mod yaml;
@ -43,6 +43,7 @@ pub(crate) struct ParsedConfig {
pub paste_shortcut_event_delay: Option<usize>, pub paste_shortcut_event_delay: Option<usize>,
pub inject_delay: Option<usize>, pub inject_delay: Option<usize>,
pub key_delay: Option<usize>, pub key_delay: Option<usize>,
pub keyboard_layout: Option<BTreeMap<String, String>>,
// Includes // Includes

View File

@ -19,6 +19,7 @@
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml::Mapping;
use std::convert::TryFrom; use std::convert::TryFrom;
use crate::util::is_yaml_empty; use crate::util::is_yaml_empty;
@ -78,6 +79,9 @@ pub(crate) struct YAMLConfig {
#[serde(default)] #[serde(default)]
pub apply_patch: Option<bool>, pub apply_patch: Option<bool>,
#[serde(default)]
pub keyboard_layout: Option<Mapping>,
// Include/Exclude // Include/Exclude
#[serde(default)] #[serde(default)]
pub includes: Option<Vec<String>>, pub includes: Option<Vec<String>>,
@ -139,6 +143,18 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
word_separators: yaml_config.word_separators, word_separators: yaml_config.word_separators,
backspace_limit: yaml_config.backspace_limit, backspace_limit: yaml_config.backspace_limit,
apply_patch: yaml_config.apply_patch, apply_patch: yaml_config.apply_patch,
keyboard_layout: yaml_config.keyboard_layout.map(|mapping| {
mapping
.into_iter()
.filter_map(|(key, value)| {
if let (Some(key), Some(value)) = (key.as_str(), value.as_str()) {
Some((key.to_string(), value.to_string()))
} else {
None
}
})
.collect()
}),
pre_paste_delay: yaml_config.pre_paste_delay, pre_paste_delay: yaml_config.pre_paste_delay,
restore_clipboard_delay: yaml_config.restore_clipboard_delay, restore_clipboard_delay: yaml_config.restore_clipboard_delay,
@ -161,7 +177,7 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::convert::TryInto; use std::{collections::BTreeMap, convert::TryInto};
#[test] #[test]
fn conversion_to_parsed_config_works_correctly() { fn conversion_to_parsed_config_works_correctly() {
@ -183,6 +199,13 @@ mod tests {
backspace_delay: 30 backspace_delay: 30
word_separators: ["'", "."] word_separators: ["'", "."]
backspace_limit: 10 backspace_limit: 10
apply_patch: false
keyboard_layout:
rules: test_rule
model: test_model
layout: test_layout
variant: test_variant
options: test_options
use_standard_includes: true use_standard_includes: true
includes: ["test1"] includes: ["test1"]
@ -199,6 +222,15 @@ mod tests {
.unwrap(); .unwrap();
let parsed_config: ParsedConfig = config.try_into().unwrap(); let parsed_config: ParsedConfig = config.try_into().unwrap();
let keyboard_layout: BTreeMap<String, String> =
vec![
("rules".to_string(), "test_rule".to_string()),
("model".to_string(), "test_model".to_string()),
("layout".to_string(), "test_layout".to_string()),
("variant".to_string(), "test_variant".to_string()),
("options".to_string(), "test_options".to_string()),
].into_iter().collect();
assert_eq!( assert_eq!(
parsed_config, parsed_config,
ParsedConfig { ParsedConfig {
@ -216,6 +248,7 @@ mod tests {
key_delay: Some(20), key_delay: Some(20),
backspace_limit: Some(10), backspace_limit: Some(10),
apply_patch: Some(false), apply_patch: Some(false),
keyboard_layout: Some(keyboard_layout),
pre_paste_delay: Some(300), pre_paste_delay: Some(300),

View File

@ -25,14 +25,14 @@ use super::{
parse::ParsedConfig, parse::ParsedConfig,
path::calculate_paths, path::calculate_paths,
util::os_matches, util::os_matches,
AppProperties, Backend, Config, ToggleKey, AppProperties, Backend, Config, RMLVOConfig, ToggleKey,
}; };
use crate::{counter::next_id, merge}; use crate::{counter::next_id, merge};
use anyhow::Result; use anyhow::Result;
use log::error; use log::error;
use regex::Regex; use regex::Regex;
use std::{iter::FromIterator, path::PathBuf};
use std::{collections::HashSet, path::Path}; use std::{collections::HashSet, path::Path};
use std::{iter::FromIterator, path::PathBuf};
use thiserror::Error; use thiserror::Error;
const STANDARD_INCLUDES: &[&str] = &["../match/**/*.yml"]; const STANDARD_INCLUDES: &[&str] = &["../match/**/*.yml"];
@ -79,7 +79,7 @@ impl Config for ResolvedConfig {
if let Some(source_path) = self.source_path.as_ref() { if let Some(source_path) = self.source_path.as_ref() {
if let Some(source_path) = source_path.to_str() { if let Some(source_path) = source_path.to_str() {
return source_path return source_path;
} }
} }
@ -264,6 +264,16 @@ impl Config for ResolvedConfig {
fn apply_patch(&self) -> bool { fn apply_patch(&self) -> bool {
self.parsed.apply_patch.unwrap_or(true) self.parsed.apply_patch.unwrap_or(true)
} }
fn keyboard_layout(&self) -> Option<RMLVOConfig> {
self.parsed.keyboard_layout.as_ref().map(|layout| RMLVOConfig {
rules: layout.get("rules").map(String::from),
model: layout.get("model").map(String::from),
layout: layout.get("layout").map(String::from),
variant: layout.get("variant").map(String::from),
options: layout.get("options").map(String::from),
})
}
} }
impl ResolvedConfig { impl ResolvedConfig {
@ -336,6 +346,7 @@ impl ResolvedConfig {
key_delay, key_delay,
word_separators, word_separators,
backspace_limit, backspace_limit,
keyboard_layout,
includes, includes,
excludes, excludes,
extra_includes, extra_includes,

View File

@ -23,7 +23,13 @@ use regex::Regex;
use std::{collections::HashMap, path::Path, sync::Arc}; use std::{collections::HashMap, path::Path, sync::Arc};
use self::config::LegacyConfig; use self::config::LegacyConfig;
use crate::matches::{MatchEffect, group::loader::yaml::{parse::{YAMLMatch, YAMLVariable}, try_convert_into_match, try_convert_into_variable}}; use crate::matches::{
group::loader::yaml::{
parse::{YAMLMatch, YAMLVariable},
try_convert_into_match, try_convert_into_variable,
},
MatchEffect,
};
use crate::{config::store::DefaultConfigStore, counter::StructId}; use crate::{config::store::DefaultConfigStore, counter::StructId};
use crate::{ use crate::{
config::Config, config::Config,
@ -333,7 +339,12 @@ impl Config for LegacyInteropConfig {
} }
fn word_separators(&self) -> Vec<String> { fn word_separators(&self) -> Vec<String> {
self.config.word_separators.iter().map(|c| String::from(*c)).collect() self
.config
.word_separators
.iter()
.map(|c| String::from(*c))
.collect()
} }
fn backspace_limit(&self) -> usize { fn backspace_limit(&self) -> usize {
@ -343,6 +354,10 @@ impl Config for LegacyInteropConfig {
fn apply_patch(&self) -> bool { fn apply_patch(&self) -> bool {
true true
} }
fn keyboard_layout(&self) -> Option<crate::config::RMLVOConfig> {
None
}
} }
struct LegacyMatchGroup { struct LegacyMatchGroup {