Add license header and formatting

This commit is contained in:
Federico Terzi 2021-03-09 16:06:50 +01:00
parent 4143caff3d
commit e8881d0faf
42 changed files with 711 additions and 263 deletions

View File

@ -1,12 +1,31 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result;
use std::{collections::HashSet, path::Path}; use std::{collections::HashSet, path::Path};
use thiserror::Error; use thiserror::Error;
use anyhow::Result;
mod path;
mod parse; mod parse;
mod util; mod path;
mod resolve; mod resolve;
mod store; mod store;
mod util;
pub trait Config { pub trait Config {
fn label(&self) -> &str; fn label(&self) -> &str;

View File

@ -1,6 +1,25 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result; use anyhow::Result;
use thiserror::Error;
use std::{convert::TryInto, path::Path}; use std::{convert::TryInto, path::Path};
use thiserror::Error;
mod yaml; mod yaml;
@ -26,12 +45,8 @@ impl ParsedConfig {
pub fn load(path: &Path) -> Result<Self> { pub fn load(path: &Path) -> Result<Self> {
let content = std::fs::read_to_string(path)?; let content = std::fs::read_to_string(path)?;
match yaml::YAMLConfig::parse_from_str(&content) { match yaml::YAMLConfig::parse_from_str(&content) {
Ok(config) => { Ok(config) => Ok(config.try_into()?),
Ok(config.try_into()?) Err(err) => Err(ParsedConfigError::LoadFailed(err).into()),
}
Err(err) => {
Err(ParsedConfigError::LoadFailed(err).into())
}
} }
} }
} }

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryFrom; use std::convert::TryFrom;

View File

@ -1,7 +1,23 @@
use std::{ /*
collections::HashSet, * This file is part of espanso.
path::{Path}, *
}; * Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{collections::HashSet, path::Path};
use glob::glob; use glob::glob;
use log::error; use log::error;
@ -36,7 +52,10 @@ pub fn calculate_paths<'a>(
path_set.insert(canonical_path.to_string_lossy().to_string()); path_set.insert(canonical_path.to_string_lossy().to_string());
} }
Err(err) => { Err(err) => {
error!("unable to canonicalize path from glob: {:?}, with error: {}", path, err); error!(
"unable to canonicalize path from glob: {:?}, with error: {}",
path, err
);
} }
} }
} }

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use super::{parse::ParsedConfig, path::calculate_paths, util::os_matches, AppProperties, Config}; use super::{parse::ParsedConfig, path::calculate_paths, util::os_matches, AppProperties, Config};
use crate::merge; use crate::merge;
use anyhow::Result; use anyhow::Result;
@ -14,7 +33,6 @@ pub(crate) struct ResolvedConfig {
parsed: ParsedConfig, parsed: ParsedConfig,
// Generated properties // Generated properties
match_paths: Vec<String>, match_paths: Vec<String>,
filter_title: Option<Regex>, filter_title: Option<Regex>,
@ -58,8 +76,7 @@ impl Config for ResolvedConfig {
true true
}; };
let is_title_match = let is_title_match = if let Some(title_regex) = self.filter_title.as_ref() {
if let Some(title_regex) = self.filter_title.as_ref() {
if let Some(title) = app.title { if let Some(title) = app.title {
title_regex.is_match(title) title_regex.is_match(title)
} else { } else {
@ -69,8 +86,7 @@ impl Config for ResolvedConfig {
true true
}; };
let is_exec_match = let is_exec_match = if let Some(exec_regex) = self.filter_exec.as_ref() {
if let Some(exec_regex) = self.filter_exec.as_ref() {
if let Some(exec) = app.exec { if let Some(exec) = app.exec {
exec_regex.is_match(exec) exec_regex.is_match(exec)
} else { } else {
@ -80,8 +96,7 @@ impl Config for ResolvedConfig {
true true
}; };
let is_class_match = let is_class_match = if let Some(class_regex) = self.filter_class.as_ref() {
if let Some(class_regex) = self.filter_class.as_ref() {
if let Some(class) = app.class { if let Some(class) = app.class {
class_regex.is_match(class) class_regex.is_match(class)
} else { } else {
@ -110,7 +125,9 @@ impl ResolvedConfig {
.parent() .parent()
.ok_or_else(ResolveError::ParentResolveFailed)?; .ok_or_else(ResolveError::ParentResolveFailed)?;
let match_paths = Self::generate_match_paths(&config, base_dir).into_iter().collect(); let match_paths = Self::generate_match_paths(&config, base_dir)
.into_iter()
.collect();
let filter_title = if let Some(filter_title) = config.filter_title.as_deref() { let filter_title = if let Some(filter_title) = config.filter_title.as_deref() {
Some(Regex::new(filter_title)?) Some(Regex::new(filter_title)?)
@ -491,9 +508,7 @@ mod tests {
result.sort(); result.sort();
assert_eq!(result, expected.as_slice()); assert_eq!(result, expected.as_slice());
let expected = vec![ let expected = vec![base_file.to_string_lossy().to_string()];
base_file.to_string_lossy().to_string()
];
assert_eq!(parent.match_paths(), expected.as_slice()); assert_eq!(parent.match_paths(), expected.as_slice());
}); });

View File

@ -1,4 +1,23 @@
use super::{Config, ConfigStore, ConfigStoreError, resolve::ResolvedConfig}; /*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use super::{resolve::ResolvedConfig, Config, ConfigStore, ConfigStoreError};
use anyhow::Result; use anyhow::Result;
use log::{debug, error}; use log::{debug, error};
use std::{collections::HashSet, path::Path}; use std::{collections::HashSet, path::Path};
@ -56,7 +75,11 @@ impl DefaultConfigStore {
for entry in std::fs::read_dir(config_dir).map_err(ConfigStoreError::IOError)? { for entry in std::fs::read_dir(config_dir).map_err(ConfigStoreError::IOError)? {
let entry = entry?; let entry = entry?;
let config_file = entry.path(); let config_file = entry.path();
let extension = config_file.extension().unwrap_or_default().to_string_lossy().to_lowercase(); let extension = config_file
.extension()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase();
// Additional config files are loaded best-effort // Additional config files are loaded best-effort
if config_file.is_file() if config_file.is_file()
@ -69,7 +92,10 @@ impl DefaultConfigStore {
debug!("loaded config at path: {:?}", config_file); debug!("loaded config at path: {:?}", config_file);
} }
Err(err) => { Err(err) => {
error!("unable to load config at path: {:?}, with error: {}", config_file, err); error!(
"unable to load config at path: {:?}, with error: {}",
config_file, err
);
} }
} }
} }

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
#[macro_export] #[macro_export]
macro_rules! merge { macro_rules! merge {
( $t:ident, $child:expr, $parent:expr, $( $x:ident ),* ) => { ( $t:ident, $child:expr, $parent:expr, $( $x:ident ),* ) => {
@ -41,7 +60,6 @@ mod tests {
assert!(!os_matches("invalid")); assert!(!os_matches("invalid"));
} }
#[test] #[test]
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn os_matches_macos() { fn os_matches_macos() {

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
static STRUCT_COUNTER: AtomicUsize = AtomicUsize::new(0); static STRUCT_COUNTER: AtomicUsize = AtomicUsize::new(0);

View File

@ -17,24 +17,24 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::path::Path; use anyhow::Result;
use config::ConfigStore; use config::ConfigStore;
use matches::store::MatchStore; use matches::store::MatchStore;
use anyhow::Result; use std::path::Path;
use thiserror::Error; use thiserror::Error;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
mod util;
mod counter;
pub mod config; pub mod config;
mod counter;
pub mod matches; pub mod matches;
mod util;
pub fn load(base_path: &Path) -> Result<(impl ConfigStore, impl MatchStore)> { pub fn load(base_path: &Path) -> Result<(impl ConfigStore, impl MatchStore)> {
let config_dir = base_path.join("config"); let config_dir = base_path.join("config");
if !config_dir.exists() || !config_dir.is_dir() { if !config_dir.exists() || !config_dir.is_dir() {
return Err(ConfigError::MissingConfigDir().into()) return Err(ConfigError::MissingConfigDir().into());
} }
let config_store = config::load_store(&config_dir)?; let config_store = config::load_store(&config_dir)?;
@ -55,62 +55,100 @@ pub enum ConfigError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use config::{AppProperties, ConfigStore};
use crate::util::tests::use_test_directory; use crate::util::tests::use_test_directory;
use config::{AppProperties, ConfigStore};
#[test] #[test]
fn load_works_correctly() { fn load_works_correctly() {
use_test_directory(|base, match_dir, config_dir| { use_test_directory(|base, match_dir, config_dir| {
let base_file = match_dir.join("base.yml"); let base_file = match_dir.join("base.yml");
std::fs::write(&base_file, r#" std::fs::write(
&base_file,
r#"
matches: matches:
- trigger: "hello" - trigger: "hello"
replace: "world" replace: "world"
"#).unwrap(); "#,
)
.unwrap();
let another_file = match_dir.join("another.yml"); let another_file = match_dir.join("another.yml");
std::fs::write(&another_file, r#" std::fs::write(
&another_file,
r#"
imports: imports:
- "_sub.yml" - "_sub.yml"
matches: matches:
- trigger: "hello2" - trigger: "hello2"
replace: "world2" replace: "world2"
"#).unwrap(); "#,
)
.unwrap();
let under_file = match_dir.join("_sub.yml"); let under_file = match_dir.join("_sub.yml");
std::fs::write(&under_file, r#" std::fs::write(
&under_file,
r#"
matches: matches:
- trigger: "hello3" - trigger: "hello3"
replace: "world3" replace: "world3"
"#).unwrap(); "#,
)
.unwrap();
let config_file = config_dir.join("default.yml"); let config_file = config_dir.join("default.yml");
std::fs::write(&config_file, "").unwrap(); std::fs::write(&config_file, "").unwrap();
let custom_config_file = config_dir.join("custom.yml"); let custom_config_file = config_dir.join("custom.yml");
std::fs::write(&custom_config_file, r#" std::fs::write(
&custom_config_file,
r#"
filter_title: "Chrome" filter_title: "Chrome"
use_standard_includes: false use_standard_includes: false
includes: ["../match/another.yml"] includes: ["../match/another.yml"]
"#).unwrap(); "#,
)
.unwrap();
let (config_store, match_store) = load(&base).unwrap(); let (config_store, match_store) = load(&base).unwrap();
assert_eq!(config_store.default().match_paths().len(), 2); assert_eq!(config_store.default().match_paths().len(), 2);
assert_eq!(config_store.active(&AppProperties { assert_eq!(
config_store
.active(&AppProperties {
title: Some("Google Chrome"), title: Some("Google Chrome"),
class: None, class: None,
exec: None, exec: None,
}).match_paths().len(), 1); })
.match_paths()
.len(),
1
);
assert_eq!(match_store.query(config_store.default().match_paths()).matches.len(), 3); assert_eq!(
assert_eq!(match_store.query(config_store.active(&AppProperties { match_store
.query(config_store.default().match_paths())
.matches
.len(),
3
);
assert_eq!(
match_store
.query(
config_store
.active(&AppProperties {
title: Some("Chrome"), title: Some("Chrome"),
class: None, class: None,
exec: None, exec: None,
}).match_paths()).matches.len(), 2); })
.match_paths()
)
.matches
.len(),
2
);
}); });
} }

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result; use anyhow::Result;
use std::path::Path; use std::path::Path;
use thiserror::Error; use thiserror::Error;
@ -14,9 +33,7 @@ trait Importer {
} }
lazy_static! { lazy_static! {
static ref IMPORTERS: Vec<Box<dyn Importer + Sync + Send>> = vec![ static ref IMPORTERS: Vec<Box<dyn Importer + Sync + Send>> = vec![Box::new(YAMLImporter::new()),];
Box::new(YAMLImporter::new()),
];
} }
pub(crate) fn load_match_group(path: &Path) -> Result<MatchGroup> { pub(crate) fn load_match_group(path: &Path) -> Result<MatchGroup> {
@ -62,7 +79,13 @@ mod tests {
let file = match_dir.join("base.invalid"); let file = match_dir.join("base.invalid");
std::fs::write(&file, "test").unwrap(); std::fs::write(&file, "test").unwrap();
assert!(matches!(load_match_group(&file).unwrap_err().downcast::<LoadError>().unwrap(), LoadError::InvalidFormat())); assert!(matches!(
load_match_group(&file)
.unwrap_err()
.downcast::<LoadError>()
.unwrap(),
LoadError::InvalidFormat()
));
}); });
} }
@ -72,7 +95,13 @@ mod tests {
let file = match_dir.join("base"); let file = match_dir.join("base");
std::fs::write(&file, "test").unwrap(); std::fs::write(&file, "test").unwrap();
assert!(matches!(load_match_group(&file).unwrap_err().downcast::<LoadError>().unwrap(), LoadError::MissingExtension())); assert!(matches!(
load_match_group(&file)
.unwrap_err()
.downcast::<LoadError>()
.unwrap(),
LoadError::MissingExtension()
));
}); });
} }
@ -82,7 +111,13 @@ mod tests {
let file = match_dir.join("base.yml"); let file = match_dir.join("base.yml");
std::fs::write(&file, "test").unwrap(); std::fs::write(&file, "test").unwrap();
assert!(matches!(load_match_group(&file).unwrap_err().downcast::<LoadError>().unwrap(), LoadError::ParsingError(_))); assert!(matches!(
load_match_group(&file)
.unwrap_err()
.downcast::<LoadError>()
.unwrap(),
LoadError::ParsingError(_)
));
}); });
} }
@ -90,11 +125,15 @@ mod tests {
fn load_group_yaml_format() { fn load_group_yaml_format() {
use_test_directory(|_, match_dir, _| { use_test_directory(|_, match_dir, _| {
let file = match_dir.join("base.yml"); let file = match_dir.join("base.yml");
std::fs::write(&file, r#" std::fs::write(
&file,
r#"
matches: matches:
- trigger: "hello" - trigger: "hello"
replace: "world" replace: "world"
"#).unwrap(); "#,
)
.unwrap();
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1); assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
}); });
@ -104,11 +143,15 @@ mod tests {
fn load_group_yaml_format_2() { fn load_group_yaml_format_2() {
use_test_directory(|_, match_dir, _| { use_test_directory(|_, match_dir, _| {
let file = match_dir.join("base.yaml"); let file = match_dir.join("base.yaml");
std::fs::write(&file, r#" std::fs::write(
&file,
r#"
matches: matches:
- trigger: "hello" - trigger: "hello"
replace: "world" replace: "world"
"#).unwrap(); "#,
)
.unwrap();
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1); assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
}); });
@ -118,11 +161,15 @@ mod tests {
fn load_group_yaml_format_casing() { fn load_group_yaml_format_casing() {
use_test_directory(|_, match_dir, _| { use_test_directory(|_, match_dir, _| {
let file = match_dir.join("base.YML"); let file = match_dir.join("base.YML");
std::fs::write(&file, r#" std::fs::write(
&file,
r#"
matches: matches:
- trigger: "hello" - trigger: "hello"
replace: "world" replace: "world"
"#).unwrap(); "#,
)
.unwrap();
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1); assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
}); });

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::matches::{ use crate::matches::{
group::{path::resolve_imports, MatchGroup}, group::{path::resolve_imports, MatchGroup},
Match, Variable, Match, Variable,
@ -140,9 +159,9 @@ impl TryFrom<YAMLVariable> for Variable {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::fs::create_dir_all;
use serde_yaml::{Mapping, Value};
use crate::{matches::Match, util::tests::use_test_directory}; use crate::{matches::Match, util::tests::use_test_directory};
use serde_yaml::{Mapping, Value};
use std::fs::create_dir_all;
fn create_match(yaml: &str) -> Result<Match> { fn create_match(yaml: &str) -> Result<Match> {
let yaml_match: YAMLMatch = serde_yaml::from_str(yaml)?; let yaml_match: YAMLMatch = serde_yaml::from_str(yaml)?;
@ -388,7 +407,9 @@ mod tests {
create_dir_all(&sub_dir).unwrap(); create_dir_all(&sub_dir).unwrap();
let base_file = match_dir.join("base.yml"); let base_file = match_dir.join("base.yml");
std::fs::write(&base_file, r#" std::fs::write(
&base_file,
r#"
imports: imports:
- "sub/sub.yml" - "sub/sub.yml"
- "invalid/import.yml" # This should be discarded - "invalid/import.yml" # This should be discarded
@ -400,7 +421,9 @@ mod tests {
matches: matches:
- trigger: "hello" - trigger: "hello"
replace: "world" replace: "world"
"#).unwrap(); "#,
)
.unwrap();
let sub_file = sub_dir.join("sub.yml"); let sub_file = sub_dir.join("sub.yml");
std::fs::write(&sub_file, "").unwrap(); std::fs::write(&sub_file, "").unwrap();
@ -418,12 +441,9 @@ mod tests {
assert_eq!( assert_eq!(
group, group,
MatchGroup { MatchGroup {
imports: vec![ imports: vec![sub_file.to_string_lossy().to_string(),],
sub_file.to_string_lossy().to_string(),
],
global_vars: vars, global_vars: vars,
matches: vec![ matches: vec![Match {
Match {
cause: MatchCause::Trigger(TriggerCause { cause: MatchCause::Trigger(TriggerCause {
triggers: vec!["hello".to_string()], triggers: vec!["hello".to_string()],
..Default::default() ..Default::default()
@ -433,8 +453,7 @@ mod tests {
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
} }],
],
} }
) )
}); });

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{collections::HashMap, path::Path}; use std::{collections::HashMap, path::Path};
use anyhow::Result; use anyhow::Result;

View File

@ -1,7 +1,24 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result; use anyhow::Result;
use std::{ use std::path::Path;
path::{Path},
};
use super::{Match, Variable}; use super::{Match, Variable};

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result; use anyhow::Result;
use log::error; use log::error;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use serde_yaml::Mapping; use serde_yaml::Mapping;
use crate::counter::{next_id, StructId}; use crate::counter::{next_id, StructId};

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use log::error; use log::error;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use super::{Match, Variable}; use super::{Match, Variable};
mod default; mod default;

View File

@ -1,3 +1,22 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
/// Check if the given string represents an empty YAML. /// Check if the given string represents an empty YAML.
/// In other words, it checks if the document is only composed /// In other words, it checks if the document is only composed
/// of spaces and/or comments /// of spaces and/or comments
@ -5,7 +24,7 @@ pub fn is_yaml_empty(yaml: &str) -> bool {
for line in yaml.lines() { for line in yaml.lines() {
let trimmed_line = line.trim(); let trimmed_line = line.trim();
if !trimmed_line.starts_with("#") && !trimmed_line.is_empty() { if !trimmed_line.starts_with("#") && !trimmed_line.is_empty() {
return false return false;
} }
} }

View File

@ -10,7 +10,13 @@ use thiserror::Error;
use crate::KeyboardConfig; use crate::KeyboardConfig;
use super::{context::Context, ffi::{XKB_KEYMAP_COMPILE_NO_FLAGS, xkb_keymap, xkb_keymap_new_from_names, xkb_keymap_unref, xkb_rule_names}}; use super::{
context::Context,
ffi::{
xkb_keymap, xkb_keymap_new_from_names, xkb_keymap_unref, xkb_rule_names,
XKB_KEYMAP_COMPILE_NO_FLAGS,
},
};
pub struct Keymap { pub struct Keymap {
keymap: *mut xkb_keymap, keymap: *mut xkb_keymap,
@ -18,17 +24,11 @@ pub struct Keymap {
impl Keymap { impl Keymap {
pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> { pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
let names = rmlvo.map(|rmlvo| { let names = rmlvo.map(|rmlvo| Self::generate_names(rmlvo));
Self::generate_names(rmlvo)
});
let names_ptr = names.map_or(std::ptr::null(), |names| &names); let names_ptr = names.map_or(std::ptr::null(), |names| &names);
let raw_keymap = unsafe { let raw_keymap = unsafe {
xkb_keymap_new_from_names( xkb_keymap_new_from_names(context.get_handle(), names_ptr, XKB_KEYMAP_COMPILE_NO_FLAGS)
context.get_handle(),
names_ptr,
XKB_KEYMAP_COMPILE_NO_FLAGS,
)
}; };
let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe { let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe {
xkb_keymap_unref(raw_keymap); xkb_keymap_unref(raw_keymap);
@ -48,11 +48,21 @@ impl Keymap {
} }
fn generate_names(rmlvo: KeyboardConfig) -> xkb_rule_names { fn generate_names(rmlvo: KeyboardConfig) -> xkb_rule_names {
let rules = rmlvo.rules.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); let rules = rmlvo
let model = rmlvo.model.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .rules
let layout = rmlvo.layout.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .map(|s| CString::new(s).expect("unable to create CString for keymap"));
let variant = rmlvo.variant.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); let model = rmlvo
let options = rmlvo.options.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .model
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let layout = rmlvo
.layout
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let variant = rmlvo
.variant
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let options = rmlvo
.options
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
xkb_rule_names { xkb_rule_names {
rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()), rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()),

View File

@ -36,10 +36,10 @@ use libc::{
use log::{error, trace}; use log::{error, trace};
use thiserror::Error; use thiserror::Error;
use crate::{KeyboardConfig, Source, SourceCallback, SourceCreationOptions, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
use crate::{event::Status::*, KeyboardConfig, Source, SourceCallback, SourceCreationOptions};
use self::device::{DeviceError, RawInputEvent}; use self::device::{DeviceError, RawInputEvent};
@ -72,7 +72,8 @@ impl EVDEVSource {
impl Source for EVDEVSource { impl Source for EVDEVSource {
fn initialize(&mut self) -> Result<()> { fn initialize(&mut self) -> Result<()> {
let context = Context::new().expect("unable to obtain xkb context"); let context = Context::new().expect("unable to obtain xkb context");
let keymap = Keymap::new(&context, self._keyboard_rmlvo.clone()).expect("unable to create xkb keymap"); let keymap =
Keymap::new(&context, self._keyboard_rmlvo.clone()).expect("unable to create xkb keymap");
match get_devices(&keymap) { match get_devices(&keymap) {
Ok(devices) => self.devices = devices, Ok(devices) => self.devices = devices,
@ -140,9 +141,7 @@ impl Source for EVDEVSource {
if unsafe { *errno_ptr } == EINTR { if unsafe { *errno_ptr } == EINTR {
continue; continue;
} else { } else {
error!("Could not poll for events, {}", unsafe { error!("Could not poll for events, {}", unsafe { *errno_ptr });
*errno_ptr
});
return Err(EVDEVSourceError::Internal().into()); return Err(EVDEVSourceError::Internal().into());
} }
} }

View File

@ -107,4 +107,3 @@ pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
info!("using EVDEVSource"); info!("using EVDEVSource");
Ok(Box::new(evdev::EVDEVSource::new(options))) Ok(Box::new(evdev::EVDEVSource::new(options)))
} }

View File

@ -31,10 +31,10 @@ use log::{error, trace, warn};
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{Source, SourceCallback, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
use crate::{event::Status::*, Source, SourceCallback};
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1; const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
const INPUT_EVENT_TYPE_MOUSE: i32 = 2; const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
@ -97,8 +97,6 @@ impl CocoaSource {
receiver: LazyCell::new(), receiver: LazyCell::new(),
} }
} }
} }
impl Source for CocoaSource { impl Source for CocoaSource {
@ -139,7 +137,7 @@ impl Source for CocoaSource {
} }
} else { } else {
error!("Unable to start event loop if CocoaSource receiver is null"); error!("Unable to start event loop if CocoaSource receiver is null");
return Err(CocoaSourceError::Unknown().into()) return Err(CocoaSourceError::Unknown().into());
} }
Ok(()) Ok(())

View File

@ -26,10 +26,10 @@ use widestring::U16CStr;
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{Source, SourceCallback, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
use crate::{event::Status::*, Source, SourceCallback};
const INPUT_LEFT_VARIANT: i32 = 1; const INPUT_LEFT_VARIANT: i32 = 1;
const INPUT_RIGHT_VARIANT: i32 = 2; const INPUT_RIGHT_VARIANT: i32 = 2;
@ -116,7 +116,7 @@ impl Source for Win32Source {
if self.callback.fill(event_callback).is_err() { if self.callback.fill(event_callback).is_err() {
error!("Unable to set Win32Source event callback"); error!("Unable to set Win32Source event callback");
return Err(Win32SourceError::Unknown().into()) return Err(Win32SourceError::Unknown().into());
} }
extern "C" fn callback(_self: *mut Win32Source, event: RawInputEvent) { extern "C" fn callback(_self: *mut Win32Source, event: RawInputEvent) {
@ -134,7 +134,7 @@ impl Source for Win32Source {
if error_code <= 0 { if error_code <= 0 {
error!("Win32Source eventloop returned a negative error code"); error!("Win32Source eventloop returned a negative error code");
return Err(Win32SourceError::Unknown().into()) return Err(Win32SourceError::Unknown().into());
} }
Ok(()) Ok(())

View File

@ -25,10 +25,10 @@ use log::{error, trace, warn};
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{Source, SourceCallback, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
use crate::{event::Status::*, Source, SourceCallback};
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1; const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
const INPUT_EVENT_TYPE_MOUSE: i32 = 2; const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
@ -87,8 +87,6 @@ impl X11Source {
pub fn is_compatible() -> bool { pub fn is_compatible() -> bool {
unsafe { detect_check_x11() != 0 } unsafe { detect_check_x11() != 0 }
} }
} }
impl Source for X11Source { impl Source for X11Source {

View File

@ -24,18 +24,12 @@ pub struct Keymap {
impl Keymap { impl Keymap {
pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> { pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
let names = rmlvo.map(|rmlvo| { let names = rmlvo.map(|rmlvo| Self::generate_names(rmlvo));
Self::generate_names(rmlvo)
});
let names_ptr = names.map_or(std::ptr::null(), |names| &names); let names_ptr = names.map_or(std::ptr::null(), |names| &names);
let raw_keymap = unsafe { let raw_keymap = unsafe {
xkb_keymap_new_from_names( xkb_keymap_new_from_names(context.get_handle(), names_ptr, XKB_KEYMAP_COMPILE_NO_FLAGS)
context.get_handle(),
names_ptr,
XKB_KEYMAP_COMPILE_NO_FLAGS,
)
}; };
let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe { let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe {
xkb_keymap_unref(raw_keymap); xkb_keymap_unref(raw_keymap);
@ -55,11 +49,21 @@ impl Keymap {
} }
fn generate_names(rmlvo: KeyboardConfig) -> xkb_rule_names { fn generate_names(rmlvo: KeyboardConfig) -> xkb_rule_names {
let rules = rmlvo.rules.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); let rules = rmlvo
let model = rmlvo.model.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .rules
let layout = rmlvo.layout.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .map(|s| CString::new(s).expect("unable to create CString for keymap"));
let variant = rmlvo.variant.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); let model = rmlvo
let options = rmlvo.options.map(|s| { CString::new(s).expect("unable to create CString for keymap") }); .model
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let layout = rmlvo
.layout
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let variant = rmlvo
.variant
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
let options = rmlvo
.options
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
xkb_rule_names { xkb_rule_names {
rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()), rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()),

View File

@ -25,7 +25,7 @@ mod uinput;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
ffi::{CString}, ffi::CString,
}; };
use context::Context; use context::Context;
@ -34,10 +34,7 @@ use log::error;
use std::iter::FromIterator; use std::iter::FromIterator;
use uinput::UInputDevice; use uinput::UInputDevice;
use crate::{ use crate::{linux::raw_keys::convert_to_sym_array, InjectorCreationOptions};
linux::raw_keys::{convert_to_sym_array},
InjectorCreationOptions,
};
use anyhow::Result; use anyhow::Result;
use itertools::Itertools; use itertools::Itertools;
use thiserror::Error; use thiserror::Error;
@ -120,7 +117,8 @@ impl EVDEVInjector {
} }
let context = Context::new().expect("unable to obtain xkb context"); let context = Context::new().expect("unable to obtain xkb context");
let keymap = Keymap::new(&context, options.evdev_keyboard_rmlvo).expect("unable to create xkb keymap"); let keymap =
Keymap::new(&context, options.evdev_keyboard_rmlvo).expect("unable to create xkb keymap");
let (char_map, sym_map) = let (char_map, sym_map) =
Self::generate_maps(&modifiers, max_modifier_combination_len, &keymap)?; Self::generate_maps(&modifiers, max_modifier_combination_len, &keymap)?;

View File

@ -8,7 +8,13 @@ use scopeguard::ScopeGuard;
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use super::{ffi::{xkb_state, xkb_state_key_get_one_sym, xkb_state_key_get_utf8, xkb_state_new, xkb_state_unref, xkb_state_update_key}, keymap::Keymap}; use super::{
ffi::{
xkb_state, xkb_state_key_get_one_sym, xkb_state_key_get_utf8, xkb_state_new, xkb_state_unref,
xkb_state_update_key,
},
keymap::Keymap,
};
pub struct State { pub struct State {
state: *mut xkb_state, state: *mut xkb_state,

View File

@ -151,4 +151,3 @@ pub fn get_injector(options: InjectorCreationOptions) -> Result<Box<dyn Injector
info!("using EVDEVInjector"); info!("using EVDEVInjector");
Ok(Box::new(evdev::EVDEVInjector::new(options)?)) Ok(Box::new(evdev::EVDEVInjector::new(options)?))
} }

View File

@ -27,7 +27,7 @@ use raw_keys::convert_key_to_vkey;
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{InjectionOptions, Injector, keys}; use crate::{keys, InjectionOptions, Injector};
#[allow(improper_ctypes)] #[allow(improper_ctypes)]
#[link(name = "espansoinject", kind = "static")] #[link(name = "espansoinject", kind = "static")]
@ -72,7 +72,11 @@ impl Injector for MacInjector {
let virtual_keys = Self::convert_to_vk_array(keys)?; let virtual_keys = Self::convert_to_vk_array(keys)?;
unsafe { unsafe {
inject_separate_vkeys(virtual_keys.as_ptr(), virtual_keys.len() as i32, options.delay); inject_separate_vkeys(
virtual_keys.as_ptr(),
virtual_keys.len() as i32,
options.delay,
);
} }
Ok(()) Ok(())
@ -82,7 +86,11 @@ impl Injector for MacInjector {
let virtual_keys = Self::convert_to_vk_array(keys)?; let virtual_keys = Self::convert_to_vk_array(keys)?;
unsafe { unsafe {
inject_vkeys_combination(virtual_keys.as_ptr(), virtual_keys.len() as i32, options.delay); inject_vkeys_combination(
virtual_keys.as_ptr(),
virtual_keys.len() as i32,
options.delay,
);
} }
Ok(()) Ok(())

View File

@ -19,13 +19,13 @@
mod raw_keys; mod raw_keys;
use log::{error}; use log::error;
use raw_keys::convert_key_to_vkey; use raw_keys::convert_key_to_vkey;
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{InjectionOptions, Injector, keys}; use crate::{keys, InjectionOptions, Injector};
#[allow(improper_ctypes)] #[allow(improper_ctypes)]
#[link(name = "espansoinject", kind = "static")] #[link(name = "espansoinject", kind = "static")]
@ -77,7 +77,11 @@ impl Injector for Win32Injector {
} }
} else { } else {
unsafe { unsafe {
inject_separate_vkeys_with_delay(virtual_keys.as_ptr(), virtual_keys.len() as i32, options.delay); inject_separate_vkeys_with_delay(
virtual_keys.as_ptr(),
virtual_keys.len() as i32,
options.delay,
);
} }
} }
@ -93,7 +97,11 @@ impl Injector for Win32Injector {
} }
} else { } else {
unsafe { unsafe {
inject_vkeys_combination_with_delay(virtual_keys.as_ptr(), virtual_keys.len() as i32, options.delay); inject_vkeys_combination_with_delay(
virtual_keys.as_ptr(),
virtual_keys.len() as i32,
options.delay,
);
} }
} }
@ -113,6 +121,9 @@ mod tests {
#[test] #[test]
fn convert_raw_to_virtual_key_array() { fn convert_raw_to_virtual_key_array() {
assert_eq!(Win32Injector::convert_to_vk_array(&[keys::Key::Alt, keys::Key::V]).unwrap(), vec![0x12, 0x56]); assert_eq!(
Win32Injector::convert_to_vk_array(&[keys::Key::Alt, keys::Key::V]).unwrap(),
vec![0x12, 0x56]
);
} }
} }

View File

@ -26,11 +26,15 @@ use std::{
slice, slice,
}; };
use ffi::{Display, KeyCode, KeyPress, KeyRelease, KeySym, Window, XCloseDisplay, XDefaultRootWindow, XFlush, XFreeModifiermap, XGetInputFocus, XGetModifierMapping, XKeyEvent, XLookupString, XQueryKeymap, XSendEvent, XSync, XTestFakeKeyEvent}; use ffi::{
Display, KeyCode, KeyPress, KeyRelease, KeySym, Window, XCloseDisplay, XDefaultRootWindow,
XFlush, XFreeModifiermap, XGetInputFocus, XGetModifierMapping, XKeyEvent, XLookupString,
XQueryKeymap, XSendEvent, XSync, XTestFakeKeyEvent,
};
use log::error; use log::error;
use crate::linux::raw_keys::convert_to_sym_array;
use anyhow::Result; use anyhow::Result;
use crate::linux::raw_keys::{convert_to_sym_array};
use thiserror::Error; use thiserror::Error;
use crate::{keys, InjectionOptions, Injector}; use crate::{keys, InjectionOptions, Injector};
@ -174,7 +178,6 @@ impl X11Injector {
modifiers.modifiermap, modifiers.modifiermap,
(8 * modifiers.max_keypermod) as usize, (8 * modifiers.max_keypermod) as usize,
) )
}; };
let keycode = modifier_map[(mod_index * modifiers.max_keypermod + mod_key) as usize]; let keycode = modifier_map[(mod_index * modifiers.max_keypermod + mod_key) as usize];
if keycode != 0 { if keycode != 0 {

View File

@ -17,11 +17,11 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::path::Path;
use anyhow::Result; use anyhow::Result;
use serde::{Serialize, de::DeserializeOwned}; use crossbeam::channel::{unbounded, Receiver};
use serde::{de::DeserializeOwned, Serialize};
use std::path::Path;
use thiserror::Error; use thiserror::Error;
use crossbeam::channel::{Receiver, unbounded};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub mod windows; pub mod windows;
@ -39,7 +39,10 @@ pub trait IPCClient<Event> {
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn server<Event: Send + Sync + DeserializeOwned>(id: &str, parent_dir: &Path) -> Result<(impl IPCServer<Event>, Receiver<Event>)> { pub fn server<Event: Send + Sync + DeserializeOwned>(
id: &str,
parent_dir: &Path,
) -> Result<(impl IPCServer<Event>, Receiver<Event>)> {
let (sender, receiver) = unbounded(); let (sender, receiver) = unbounded();
let server = unix::UnixIPCServer::new(id, parent_dir, sender)?; let server = unix::UnixIPCServer::new(id, parent_dir, sender)?;
Ok((server, receiver)) Ok((server, receiver))
@ -52,7 +55,10 @@ pub fn client<Event: Serialize>(id: &str, parent_dir: &Path) -> Result<impl IPCC
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn server<Event: Send + Sync + DeserializeOwned>(id: &str, _: &Path) -> Result<(impl IPCServer<Event>, Receiver<Event>)> { pub fn server<Event: Send + Sync + DeserializeOwned>(
id: &str,
_: &Path,
) -> Result<(impl IPCServer<Event>, Receiver<Event>)> {
let (sender, receiver) = unbounded(); let (sender, receiver) = unbounded();
let server = windows::WinIPCServer::new(id, sender)?; let server = windows::WinIPCServer::new(id, sender)?;
Ok((server, receiver)) Ok((server, receiver))
@ -76,7 +82,7 @@ pub enum IPCServerError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
enum Event { enum Event {

View File

@ -22,9 +22,7 @@ use crossbeam::channel::Sender;
use log::{error, info}; use log::{error, info};
use named_pipe::{PipeClient, PipeOptions}; use named_pipe::{PipeClient, PipeOptions};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use std::{ use std::io::{BufReader, Read, Write};
io::{BufReader, Read, Write},
};
use crate::{IPCClient, IPCServer, IPCServerError}; use crate::{IPCClient, IPCServer, IPCServerError};
@ -41,10 +39,7 @@ impl<Event> WinIPCServer<Event> {
let options = PipeOptions::new(&pipe_name); let options = PipeOptions::new(&pipe_name);
info!( info!("binded to named pipe: {}", pipe_name);
"binded to named pipe: {}",
pipe_name
);
Ok(Self { options, sender }) Ok(Self { options, sender })
} }
@ -116,4 +111,3 @@ impl<Event: Serialize> IPCClient<Event> for WinIPCClient {
Ok(()) Ok(())
} }
} }

View File

@ -1,5 +1,5 @@
use icons::TrayIcon;
use anyhow::Result; use anyhow::Result;
use icons::TrayIcon;
use thiserror::Error; use thiserror::Error;
pub mod event; pub mod event;
@ -48,14 +48,15 @@ pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEve
let (remote, eventloop) = win32::create(win32::Win32UIOptions { let (remote, eventloop) = win32::create(win32::Win32UIOptions {
show_icon: options.show_icon, show_icon: options.show_icon,
icon_paths: &options.icon_paths, icon_paths: &options.icon_paths,
notification_icon_path: options.notification_icon_path.ok_or_else(|| UIError::MissingOption("notification icon".to_string()))?, notification_icon_path: options
.notification_icon_path
.ok_or_else(|| UIError::MissingOption("notification icon".to_string()))?,
})?; })?;
Ok((Box::new(remote), Box::new(eventloop))) Ok((Box::new(remote), Box::new(eventloop)))
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEventLoop>)> {
UIEventLoop>)> {
let (remote, eventloop) = mac::create(mac::MacUIOptions { let (remote, eventloop) = mac::create(mac::MacUIOptions {
show_icon: options.show_icon, show_icon: options.show_icon,
icon_paths: &options.icon_paths, icon_paths: &options.icon_paths,
@ -66,7 +67,9 @@ pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEventLoop>)> { pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEventLoop>)> {
let (remote, eventloop) = linux::create(linux::LinuxUIOptions { let (remote, eventloop) = linux::create(linux::LinuxUIOptions {
notification_icon_path: options.notification_icon_path.ok_or_else(|| UIError::MissingOption("notification icon".to_string()))?, notification_icon_path: options
.notification_icon_path
.ok_or_else(|| UIError::MissingOption("notification icon".to_string()))?,
}); });
Ok((Box::new(remote), Box::new(eventloop))) Ok((Box::new(remote), Box::new(eventloop)))
} }

View File

@ -1,5 +1,5 @@
use log::error;
use anyhow::Result; use anyhow::Result;
use log::error;
use notify_rust::Notification; use notify_rust::Notification;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};

View File

@ -20,11 +20,11 @@
use std::{cmp::min, collections::HashMap, ffi::CString, os::raw::c_char, thread::ThreadId}; use std::{cmp::min, collections::HashMap, ffi::CString, os::raw::c_char, thread::ThreadId};
use anyhow::Result; use anyhow::Result;
use thiserror::Error;
use lazycell::LazyCell; use lazycell::LazyCell;
use log::{error, trace}; use log::{error, trace};
use thiserror::Error;
use crate::{UIEventLoop, UIRemote, event::UIEvent, icons::TrayIcon, menu::Menu}; use crate::{event::UIEvent, icons::TrayIcon, menu::Menu, UIEventLoop, UIRemote};
// IMPORTANT: if you change these, also edit the native.h file. // IMPORTANT: if you change these, also edit the native.h file.
const MAX_FILE_PATH: usize = 1024; const MAX_FILE_PATH: usize = 1024;
@ -105,8 +105,6 @@ impl MacEventLoop {
_init_thread_id: LazyCell::new(), _init_thread_id: LazyCell::new(),
} }
} }
} }
impl UIEventLoop for MacEventLoop { impl UIEventLoop for MacEventLoop {
@ -147,7 +145,7 @@ impl UIEventLoop for MacEventLoop {
if self._event_callback.fill(event_callback).is_err() { if self._event_callback.fill(event_callback).is_err() {
error!("Unable to set MacEventLoop callback"); error!("Unable to set MacEventLoop callback");
return Err(MacUIError::InternalError().into()) return Err(MacUIError::InternalError().into());
} }
extern "C" fn callback(_self: *mut MacEventLoop, event: RawUIEvent) { extern "C" fn callback(_self: *mut MacEventLoop, event: RawUIEvent) {
@ -165,7 +163,7 @@ impl UIEventLoop for MacEventLoop {
if error_code <= 0 { if error_code <= 0 {
error!("MacEventLoop exited with <= 0 code"); error!("MacEventLoop exited with <= 0 code");
return Err(MacUIError::InternalError().into()) return Err(MacUIError::InternalError().into());
} }
Ok(()) Ok(())

View File

@ -29,13 +29,13 @@ use std::{
thread::ThreadId, thread::ThreadId,
}; };
use anyhow::Result;
use lazycell::LazyCell; use lazycell::LazyCell;
use log::{error, trace}; use log::{error, trace};
use widestring::WideCString;
use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use widestring::WideCString;
use crate::{UIEventCallback, UIEventLoop, UIRemote, event::UIEvent, icons::TrayIcon, menu::Menu}; use crate::{event::UIEvent, icons::TrayIcon, menu::Menu, UIEventCallback, UIEventLoop, UIRemote};
// IMPORTANT: if you change these, also edit the native.h file. // IMPORTANT: if you change these, also edit the native.h file.
const MAX_FILE_PATH: usize = 260; const MAX_FILE_PATH: usize = 260;
@ -154,8 +154,7 @@ impl UIEventLoop for Win32EventLoop {
let mut icon_paths: [[u16; MAX_FILE_PATH]; MAX_ICON_COUNT] = let mut icon_paths: [[u16; MAX_FILE_PATH]; MAX_ICON_COUNT] =
[[0; MAX_FILE_PATH]; MAX_ICON_COUNT]; [[0; MAX_FILE_PATH]; MAX_ICON_COUNT];
for (i, icon_path) in icon_paths.iter_mut().enumerate().take(self.icons.len()) { for (i, icon_path) in icon_paths.iter_mut().enumerate().take(self.icons.len()) {
let wide_path = let wide_path = WideCString::from_str(&self.icons[i])?;
WideCString::from_str(&self.icons[i])?;
let len = min(wide_path.len(), MAX_FILE_PATH - 1); let len = min(wide_path.len(), MAX_FILE_PATH - 1);
icon_path[0..len].clone_from_slice(&wide_path.as_slice()[..len]); icon_path[0..len].clone_from_slice(&wide_path.as_slice()[..len]);
} }
@ -178,11 +177,31 @@ impl UIEventLoop for Win32EventLoop {
if handle.is_null() { if handle.is_null() {
return match error_code { return match error_code {
-1 => Err(Win32UIError::EventLoopInitError("Unable to initialize Win32EventLoop, error registering window class".to_string()).into()), -1 => Err(
-2 => Err(Win32UIError::EventLoopInitError("Unable to initialize Win32EventLoop, error creating window".to_string()).into()), Win32UIError::EventLoopInitError(
-3 => Err(Win32UIError::EventLoopInitError("Unable to initialize Win32EventLoop, initializing notifications".to_string()).into()), "Unable to initialize Win32EventLoop, error registering window class".to_string(),
_ => Err(Win32UIError::EventLoopInitError("Unable to initialize Win32EventLoop, unknown error".to_string()).into()), )
} .into(),
),
-2 => Err(
Win32UIError::EventLoopInitError(
"Unable to initialize Win32EventLoop, error creating window".to_string(),
)
.into(),
),
-3 => Err(
Win32UIError::EventLoopInitError(
"Unable to initialize Win32EventLoop, initializing notifications".to_string(),
)
.into(),
),
_ => Err(
Win32UIError::EventLoopInitError(
"Unable to initialize Win32EventLoop, unknown error".to_string(),
)
.into(),
),
};
} }
self.handle.store(handle, Ordering::Release); self.handle.store(handle, Ordering::Release);
@ -207,12 +226,12 @@ impl UIEventLoop for Win32EventLoop {
let window_handle = self.handle.load(Ordering::Acquire); let window_handle = self.handle.load(Ordering::Acquire);
if window_handle.is_null() { if window_handle.is_null() {
error!("Attempt to run Win32EventLoop on a null window handle"); error!("Attempt to run Win32EventLoop on a null window handle");
return Err(Win32UIError::InvalidHandle().into()) return Err(Win32UIError::InvalidHandle().into());
} }
if self._event_callback.fill(event_callback).is_err() { if self._event_callback.fill(event_callback).is_err() {
error!("Unable to set Win32EventLoop callback"); error!("Unable to set Win32EventLoop callback");
return Err(Win32UIError::InternalError().into()) return Err(Win32UIError::InternalError().into());
} }
extern "C" fn callback(_self: *mut Win32EventLoop, event: RawUIEvent) { extern "C" fn callback(_self: *mut Win32EventLoop, event: RawUIEvent) {
@ -230,7 +249,7 @@ impl UIEventLoop for Win32EventLoop {
if error_code <= 0 { if error_code <= 0 {
error!("Win32EventLoop exited with <= 0 code"); error!("Win32EventLoop exited with <= 0 code");
return Err(Win32UIError::InternalError().into()) return Err(Win32UIError::InternalError().into());
} }
Ok(()) Ok(())

View File

@ -1,7 +1,10 @@
use std::time::Duration; use std::time::Duration;
use espanso_detect::{event::{InputEvent, Status}, get_source}; use espanso_detect::{
use espanso_inject::{get_injector, Injector, keys}; event::{InputEvent, Status},
get_source,
};
use espanso_inject::{get_injector, keys, Injector};
use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*}; use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*};
use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode}; use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
@ -49,9 +52,12 @@ fn main() {
// icon_paths: &icon_paths, // icon_paths: &icon_paths,
// }); // });
let (remote, mut eventloop) = espanso_ui::create_ui(espanso_ui::UIOptions { let (remote, mut eventloop) = espanso_ui::create_ui(espanso_ui::UIOptions {
notification_icon_path: Some(r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png".to_string()), notification_icon_path: Some(
r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png".to_string(),
),
..Default::default() ..Default::default()
}).unwrap(); })
.unwrap();
eventloop.initialize().unwrap(); eventloop.initialize().unwrap();
@ -59,7 +65,8 @@ fn main() {
let injector = get_injector(Default::default()).unwrap(); let injector = get_injector(Default::default()).unwrap();
let mut source = get_source(Default::default()).unwrap(); let mut source = get_source(Default::default()).unwrap();
source.initialize().unwrap(); source.initialize().unwrap();
source.eventloop(Box::new(move |event: InputEvent| { source
.eventloop(Box::new(move |event: InputEvent| {
println!("ev {:?}", event); println!("ev {:?}", event);
match event { match event {
InputEvent::Mouse(_) => {} InputEvent::Mouse(_) => {}
@ -67,13 +74,16 @@ fn main() {
if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released { if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released {
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled); //remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
//remote.show_notification("Espanso is running!"); //remote.show_notification("Espanso is running!");
injector.send_string("Hey guys! @", Default::default()).expect("error"); injector
.send_string("Hey guys! @", Default::default())
.expect("error");
//std::thread::sleep(std::time::Duration::from_secs(2)); //std::thread::sleep(std::time::Duration::from_secs(2));
//injector.send_key_combination(&[keys::Key::Control, keys::Key::V], Default::default()).unwrap(); //injector.send_key_combination(&[keys::Key::Control, keys::Key::V], Default::default()).unwrap();
} }
} }
} }
})).unwrap(); }))
.unwrap();
}); });
eventloop.run(Box::new(move |event| { eventloop.run(Box::new(move |event| {