Add license header and formatting
This commit is contained in:
parent
4143caff3d
commit
e8881d0faf
|
@ -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 thiserror::Error;
|
||||
use anyhow::Result;
|
||||
|
||||
mod path;
|
||||
mod parse;
|
||||
mod util;
|
||||
mod path;
|
||||
mod resolve;
|
||||
mod store;
|
||||
mod util;
|
||||
|
||||
pub trait Config {
|
||||
fn label(&self) -> &str;
|
||||
|
@ -42,4 +61,4 @@ pub enum ConfigStoreError {
|
|||
|
||||
#[error("io error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 thiserror::Error;
|
||||
use std::{convert::TryInto, path::Path};
|
||||
use thiserror::Error;
|
||||
|
||||
mod yaml;
|
||||
|
||||
|
@ -26,12 +45,8 @@ impl ParsedConfig {
|
|||
pub fn load(path: &Path) -> Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
match yaml::YAMLConfig::parse_from_str(&content) {
|
||||
Ok(config) => {
|
||||
Ok(config.try_into()?)
|
||||
}
|
||||
Err(err) => {
|
||||
Err(ParsedConfigError::LoadFailed(err).into())
|
||||
}
|
||||
Ok(config) => Ok(config.try_into()?),
|
||||
Err(err) => Err(ParsedConfigError::LoadFailed(err).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,4 +55,4 @@ impl ParsedConfig {
|
|||
pub enum ParsedConfigError {
|
||||
#[error("can't load config `{0}`")]
|
||||
LoadFailed(#[from] anyhow::Error),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
|
|
|
@ -1,7 +1,23 @@
|
|||
use std::{
|
||||
collections::HashSet,
|
||||
path::{Path},
|
||||
};
|
||||
/*
|
||||
* 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::HashSet, path::Path};
|
||||
|
||||
use glob::glob;
|
||||
use log::error;
|
||||
|
@ -36,7 +52,10 @@ pub fn calculate_paths<'a>(
|
|||
path_set.insert(canonical_path.to_string_lossy().to_string());
|
||||
}
|
||||
Err(err) => {
|
||||
error!("unable to canonicalize path from glob: {:?}, with error: {}", path, err);
|
||||
error!(
|
||||
"unable to canonicalize path from glob: {:?}, with error: {}",
|
||||
path, err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 crate::merge;
|
||||
use anyhow::Result;
|
||||
|
@ -14,7 +33,6 @@ pub(crate) struct ResolvedConfig {
|
|||
parsed: ParsedConfig,
|
||||
|
||||
// Generated properties
|
||||
|
||||
match_paths: Vec<String>,
|
||||
|
||||
filter_title: Option<Regex>,
|
||||
|
@ -58,38 +76,35 @@ impl Config for ResolvedConfig {
|
|||
true
|
||||
};
|
||||
|
||||
let is_title_match =
|
||||
if let Some(title_regex) = self.filter_title.as_ref() {
|
||||
if let Some(title) = app.title {
|
||||
title_regex.is_match(title)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let is_title_match = if let Some(title_regex) = self.filter_title.as_ref() {
|
||||
if let Some(title) = app.title {
|
||||
title_regex.is_match(title)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let is_exec_match =
|
||||
if let Some(exec_regex) = self.filter_exec.as_ref() {
|
||||
if let Some(exec) = app.exec {
|
||||
exec_regex.is_match(exec)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let is_exec_match = if let Some(exec_regex) = self.filter_exec.as_ref() {
|
||||
if let Some(exec) = app.exec {
|
||||
exec_regex.is_match(exec)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let is_class_match =
|
||||
if let Some(class_regex) = self.filter_class.as_ref() {
|
||||
if let Some(class) = app.class {
|
||||
class_regex.is_match(class)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let is_class_match = if let Some(class_regex) = self.filter_class.as_ref() {
|
||||
if let Some(class) = app.class {
|
||||
class_regex.is_match(class)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
// All the filters that have been specified must be true to define a match
|
||||
is_os_match && is_exec_match && is_title_match && is_class_match
|
||||
|
@ -110,7 +125,9 @@ impl ResolvedConfig {
|
|||
.parent()
|
||||
.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() {
|
||||
Some(Regex::new(filter_title)?)
|
||||
|
@ -431,7 +448,7 @@ mod tests {
|
|||
sub_file.to_string_lossy().to_string(),
|
||||
];
|
||||
expected.sort();
|
||||
|
||||
|
||||
let mut result = config.match_paths().to_vec();
|
||||
result.sort();
|
||||
|
||||
|
@ -491,9 +508,7 @@ mod tests {
|
|||
result.sort();
|
||||
assert_eq!(result, expected.as_slice());
|
||||
|
||||
let expected = vec![
|
||||
base_file.to_string_lossy().to_string()
|
||||
];
|
||||
let expected = vec![base_file.to_string_lossy().to_string()];
|
||||
|
||||
assert_eq!(parent.match_paths(), expected.as_slice());
|
||||
});
|
||||
|
|
|
@ -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 log::{debug, error};
|
||||
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)? {
|
||||
let entry = entry?;
|
||||
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
|
||||
if config_file.is_file()
|
||||
|
@ -69,7 +92,10 @@ impl DefaultConfigStore {
|
|||
debug!("loaded config at path: {:?}", config_file);
|
||||
}
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_rules! merge {
|
||||
( $t:ident, $child:expr, $parent:expr, $( $x:ident ),* ) => {
|
||||
|
@ -7,7 +26,7 @@ macro_rules! merge {
|
|||
$child.$x = $parent.$x.clone();
|
||||
}
|
||||
)*
|
||||
|
||||
|
||||
// Build a temporary object to verify that all fields
|
||||
// are being used at compile time
|
||||
$t {
|
||||
|
@ -41,7 +60,6 @@ mod tests {
|
|||
assert!(!os_matches("invalid"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn os_matches_macos() {
|
||||
|
@ -59,4 +77,4 @@ mod tests {
|
|||
assert!(!os_matches("linux"));
|
||||
assert!(!os_matches("invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
static STRUCT_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
@ -10,4 +29,4 @@ pub type StructId = usize;
|
|||
/// that is incremented for each struct.
|
||||
pub fn next_id() -> StructId {
|
||||
STRUCT_COUNTER.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,24 +17,24 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::path::Path;
|
||||
use anyhow::Result;
|
||||
use config::ConfigStore;
|
||||
use matches::store::MatchStore;
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
mod util;
|
||||
mod counter;
|
||||
pub mod config;
|
||||
mod counter;
|
||||
pub mod matches;
|
||||
mod util;
|
||||
|
||||
pub fn load(base_path: &Path) -> Result<(impl ConfigStore, impl MatchStore)> {
|
||||
let config_dir = base_path.join("config");
|
||||
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)?;
|
||||
|
@ -55,62 +55,100 @@ pub enum ConfigError {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use config::{AppProperties, ConfigStore};
|
||||
use crate::util::tests::use_test_directory;
|
||||
use config::{AppProperties, ConfigStore};
|
||||
|
||||
#[test]
|
||||
fn load_works_correctly() {
|
||||
use_test_directory(|base, match_dir, config_dir| {
|
||||
let base_file = match_dir.join("base.yml");
|
||||
std::fs::write(&base_file, r#"
|
||||
std::fs::write(
|
||||
&base_file,
|
||||
r#"
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let another_file = match_dir.join("another.yml");
|
||||
std::fs::write(&another_file, r#"
|
||||
std::fs::write(
|
||||
&another_file,
|
||||
r#"
|
||||
imports:
|
||||
- "_sub.yml"
|
||||
|
||||
matches:
|
||||
- trigger: "hello2"
|
||||
replace: "world2"
|
||||
"#).unwrap();
|
||||
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let under_file = match_dir.join("_sub.yml");
|
||||
std::fs::write(&under_file, r#"
|
||||
std::fs::write(
|
||||
&under_file,
|
||||
r#"
|
||||
matches:
|
||||
- trigger: "hello3"
|
||||
replace: "world3"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let config_file = config_dir.join("default.yml");
|
||||
std::fs::write(&config_file, "").unwrap();
|
||||
|
||||
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"
|
||||
|
||||
use_standard_includes: false
|
||||
includes: ["../match/another.yml"]
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (config_store, match_store) = load(&base).unwrap();
|
||||
|
||||
assert_eq!(config_store.default().match_paths().len(), 2);
|
||||
assert_eq!(config_store.active(&AppProperties {
|
||||
title: Some("Google Chrome"),
|
||||
class: None,
|
||||
exec: None,
|
||||
}).match_paths().len(), 1);
|
||||
assert_eq!(
|
||||
config_store
|
||||
.active(&AppProperties {
|
||||
title: Some("Google Chrome"),
|
||||
class: None,
|
||||
exec: None,
|
||||
})
|
||||
.match_paths()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
|
||||
assert_eq!(match_store.query(config_store.default().match_paths()).matches.len(), 3);
|
||||
assert_eq!(match_store.query(config_store.active(&AppProperties {
|
||||
title: Some("Chrome"),
|
||||
class: None,
|
||||
exec: None,
|
||||
}).match_paths()).matches.len(), 2);
|
||||
assert_eq!(
|
||||
match_store
|
||||
.query(config_store.default().match_paths())
|
||||
.matches
|
||||
.len(),
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
match_store
|
||||
.query(
|
||||
config_store
|
||||
.active(&AppProperties {
|
||||
title: Some("Chrome"),
|
||||
class: None,
|
||||
exec: None,
|
||||
})
|
||||
.match_paths()
|
||||
)
|
||||
.matches
|
||||
.len(),
|
||||
2
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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 std::path::Path;
|
||||
use thiserror::Error;
|
||||
|
@ -14,9 +33,7 @@ trait Importer {
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref IMPORTERS: Vec<Box<dyn Importer + Sync + Send>> = vec![
|
||||
Box::new(YAMLImporter::new()),
|
||||
];
|
||||
static ref IMPORTERS: Vec<Box<dyn Importer + Sync + Send>> = vec![Box::new(YAMLImporter::new()),];
|
||||
}
|
||||
|
||||
pub(crate) fn load_match_group(path: &Path) -> Result<MatchGroup> {
|
||||
|
@ -26,7 +43,7 @@ pub(crate) fn load_match_group(path: &Path) -> Result<MatchGroup> {
|
|||
let importer = IMPORTERS
|
||||
.iter()
|
||||
.find(|importer| importer.is_supported(&extension));
|
||||
|
||||
|
||||
match importer {
|
||||
Some(importer) => match importer.load_group(path) {
|
||||
Ok(group) => Ok(group),
|
||||
|
@ -62,7 +79,13 @@ mod tests {
|
|||
let file = match_dir.join("base.invalid");
|
||||
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");
|
||||
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");
|
||||
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() {
|
||||
use_test_directory(|_, match_dir, _| {
|
||||
let file = match_dir.join("base.yml");
|
||||
std::fs::write(&file, r#"
|
||||
std::fs::write(
|
||||
&file,
|
||||
r#"
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
|
||||
});
|
||||
|
@ -104,11 +143,15 @@ mod tests {
|
|||
fn load_group_yaml_format_2() {
|
||||
use_test_directory(|_, match_dir, _| {
|
||||
let file = match_dir.join("base.yaml");
|
||||
std::fs::write(&file, r#"
|
||||
std::fs::write(
|
||||
&file,
|
||||
r#"
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
|
||||
});
|
||||
|
@ -118,11 +161,15 @@ mod tests {
|
|||
fn load_group_yaml_format_casing() {
|
||||
use_test_directory(|_, match_dir, _| {
|
||||
let file = match_dir.join("base.YML");
|
||||
std::fs::write(&file, r#"
|
||||
std::fs::write(
|
||||
&file,
|
||||
r#"
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(load_match_group(&file).unwrap().matches.len(), 1);
|
||||
});
|
||||
|
|
|
@ -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::{
|
||||
group::{path::resolve_imports, MatchGroup},
|
||||
Match, Variable,
|
||||
|
@ -140,9 +159,9 @@ impl TryFrom<YAMLVariable> for Variable {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::create_dir_all;
|
||||
use serde_yaml::{Mapping, Value};
|
||||
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> {
|
||||
let yaml_match: YAMLMatch = serde_yaml::from_str(yaml)?;
|
||||
|
@ -388,7 +407,9 @@ mod tests {
|
|||
create_dir_all(&sub_dir).unwrap();
|
||||
|
||||
let base_file = match_dir.join("base.yml");
|
||||
std::fs::write(&base_file, r#"
|
||||
std::fs::write(
|
||||
&base_file,
|
||||
r#"
|
||||
imports:
|
||||
- "sub/sub.yml"
|
||||
- "invalid/import.yml" # This should be discarded
|
||||
|
@ -400,14 +421,16 @@ mod tests {
|
|||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"#).unwrap();
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sub_file = sub_dir.join("sub.yml");
|
||||
std::fs::write(&sub_file, "").unwrap();
|
||||
|
||||
std::fs::write(&sub_file, "").unwrap();
|
||||
|
||||
let importer = YAMLImporter::new();
|
||||
let group = importer.load_group(&base_file).unwrap();
|
||||
|
||||
|
||||
let vars = vec![Variable {
|
||||
name: "var1".to_string(),
|
||||
var_type: "test".to_string(),
|
||||
|
@ -418,23 +441,19 @@ mod tests {
|
|||
assert_eq!(
|
||||
group,
|
||||
MatchGroup {
|
||||
imports: vec![
|
||||
sub_file.to_string_lossy().to_string(),
|
||||
],
|
||||
imports: vec![sub_file.to_string_lossy().to_string(),],
|
||||
global_vars: vars,
|
||||
matches: vec![
|
||||
Match {
|
||||
cause: MatchCause::Trigger(TriggerCause {
|
||||
triggers: vec!["hello".to_string()],
|
||||
..Default::default()
|
||||
}),
|
||||
effect: MatchEffect::Text(TextEffect {
|
||||
replace: "world".to_string(),
|
||||
..Default::default()
|
||||
}),
|
||||
matches: vec![Match {
|
||||
cause: MatchCause::Trigger(TriggerCause {
|
||||
triggers: vec!["hello".to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
],
|
||||
}),
|
||||
effect: MatchEffect::Text(TextEffect {
|
||||
replace: "world".to_string(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}],
|
||||
}
|
||||
)
|
||||
});
|
||||
|
|
|
@ -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 anyhow::Result;
|
||||
|
@ -98,4 +117,4 @@ pub struct YAMLVariable {
|
|||
|
||||
fn default_params() -> Mapping {
|
||||
Mapping::new()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 std::{
|
||||
path::{Path},
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
use super::{Match, Variable};
|
||||
|
||||
|
|
|
@ -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 log::error;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
|
@ -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 crate::counter::{next_id, StructId};
|
||||
|
|
|
@ -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 std::{
|
||||
collections::{HashMap, HashSet},
|
||||
|
@ -629,7 +648,7 @@ mod tests {
|
|||
.cloned()
|
||||
.collect::<Vec<Match>>(),
|
||||
create_matches(&[
|
||||
("hello", "world3"), // This appears only once, though it appears 2 times
|
||||
("hello", "world3"), // This appears only once, though it appears 2 times
|
||||
("hello", "world2"),
|
||||
("foo", "bar"),
|
||||
("hello", "world"),
|
||||
|
|
|
@ -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};
|
||||
|
||||
mod default;
|
||||
|
@ -17,4 +36,4 @@ pub fn new() -> impl MatchStore {
|
|||
// TODO: here we can replace the DefaultMatchStore with a caching wrapper
|
||||
// that returns the same response for the given "paths" query
|
||||
default::DefaultMatchStore::new()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/// In other words, it checks if the document is only composed
|
||||
/// of spaces and/or comments
|
||||
|
@ -5,7 +24,7 @@ pub fn is_yaml_empty(yaml: &str) -> bool {
|
|||
for line in yaml.lines() {
|
||||
let trimmed_line = line.trim();
|
||||
if !trimmed_line.starts_with("#") && !trimmed_line.is_empty() {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,4 +67,4 @@ pub mod tests {
|
|||
fn is_yaml_empty_document_with_content() {
|
||||
assert_eq!(is_yaml_empty("\nfield: true\n"), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,13 @@ use thiserror::Error;
|
|||
|
||||
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 {
|
||||
keymap: *mut xkb_keymap,
|
||||
|
@ -18,17 +24,11 @@ pub struct Keymap {
|
|||
|
||||
impl Keymap {
|
||||
pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
|
||||
let names = rmlvo.map(|rmlvo| {
|
||||
Self::generate_names(rmlvo)
|
||||
});
|
||||
let names = rmlvo.map(|rmlvo| Self::generate_names(rmlvo));
|
||||
|
||||
let names_ptr = names.map_or(std::ptr::null(), |names| &names);
|
||||
let raw_keymap = unsafe {
|
||||
xkb_keymap_new_from_names(
|
||||
context.get_handle(),
|
||||
names_ptr,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
xkb_keymap_new_from_names(context.get_handle(), names_ptr, XKB_KEYMAP_COMPILE_NO_FLAGS)
|
||||
};
|
||||
let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe {
|
||||
xkb_keymap_unref(raw_keymap);
|
||||
|
@ -48,11 +48,21 @@ impl Keymap {
|
|||
}
|
||||
|
||||
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 model = rmlvo.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") });
|
||||
let rules = rmlvo
|
||||
.rules
|
||||
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
|
||||
let model = rmlvo
|
||||
.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 {
|
||||
rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
|
|
|
@ -36,10 +36,10 @@ use libc::{
|
|||
use log::{error, trace};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{KeyboardConfig, Source, SourceCallback, SourceCreationOptions, event::Status::*};
|
||||
use crate::event::Variant::*;
|
||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||
use crate::event::{Key::*, MouseButton, MouseEvent};
|
||||
use crate::{event::Status::*, KeyboardConfig, Source, SourceCallback, SourceCreationOptions};
|
||||
|
||||
use self::device::{DeviceError, RawInputEvent};
|
||||
|
||||
|
@ -72,7 +72,8 @@ impl EVDEVSource {
|
|||
impl Source for EVDEVSource {
|
||||
fn initialize(&mut self) -> Result<()> {
|
||||
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) {
|
||||
Ok(devices) => self.devices = devices,
|
||||
|
@ -140,9 +141,7 @@ impl Source for EVDEVSource {
|
|||
if unsafe { *errno_ptr } == EINTR {
|
||||
continue;
|
||||
} else {
|
||||
error!("Could not poll for events, {}", unsafe {
|
||||
*errno_ptr
|
||||
});
|
||||
error!("Could not poll for events, {}", unsafe { *errno_ptr });
|
||||
return Err(EVDEVSourceError::Internal().into());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub trait Source {
|
|||
pub struct SourceCreationOptions {
|
||||
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
||||
use_evdev: bool,
|
||||
|
||||
|
||||
// Can be used to overwrite the keymap configuration
|
||||
// used by espanso to inject key presses.
|
||||
evdev_keyboard_rmlvo: Option<KeyboardConfig>,
|
||||
|
@ -107,4 +107,3 @@ pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
|
|||
info!("using EVDEVSource");
|
||||
Ok(Box::new(evdev::EVDEVSource::new(options)))
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ use log::{error, trace, warn};
|
|||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{Source, SourceCallback, event::Status::*};
|
||||
use crate::event::Variant::*;
|
||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||
use crate::event::{Key::*, MouseButton, MouseEvent};
|
||||
use crate::{event::Status::*, Source, SourceCallback};
|
||||
|
||||
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
|
||||
const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
|
||||
|
@ -97,8 +97,6 @@ impl CocoaSource {
|
|||
receiver: LazyCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Source for CocoaSource {
|
||||
|
@ -139,7 +137,7 @@ impl Source for CocoaSource {
|
|||
}
|
||||
} else {
|
||||
error!("Unable to start event loop if CocoaSource receiver is null");
|
||||
return Err(CocoaSourceError::Unknown().into())
|
||||
return Err(CocoaSourceError::Unknown().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -26,10 +26,10 @@ use widestring::U16CStr;
|
|||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{Source, SourceCallback, event::Status::*};
|
||||
use crate::event::Variant::*;
|
||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||
use crate::event::{Key::*, MouseButton, MouseEvent};
|
||||
use crate::{event::Status::*, Source, SourceCallback};
|
||||
|
||||
const INPUT_LEFT_VARIANT: i32 = 1;
|
||||
const INPUT_RIGHT_VARIANT: i32 = 2;
|
||||
|
@ -116,7 +116,7 @@ impl Source for Win32Source {
|
|||
|
||||
if self.callback.fill(event_callback).is_err() {
|
||||
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) {
|
||||
|
@ -134,7 +134,7 @@ impl Source for Win32Source {
|
|||
|
||||
if error_code <= 0 {
|
||||
error!("Win32Source eventloop returned a negative error code");
|
||||
return Err(Win32SourceError::Unknown().into())
|
||||
return Err(Win32SourceError::Unknown().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -25,10 +25,10 @@ use log::{error, trace, warn};
|
|||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{Source, SourceCallback, event::Status::*};
|
||||
use crate::event::Variant::*;
|
||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||
use crate::event::{Key::*, MouseButton, MouseEvent};
|
||||
use crate::{event::Status::*, Source, SourceCallback};
|
||||
|
||||
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
|
||||
const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
|
||||
|
@ -87,8 +87,6 @@ impl X11Source {
|
|||
pub fn is_compatible() -> bool {
|
||||
unsafe { detect_check_x11() != 0 }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Source for X11Source {
|
||||
|
|
|
@ -81,4 +81,4 @@ extern "C" {
|
|||
|
||||
pub fn setup_uinput_device(fd: c_int) -> c_int;
|
||||
pub fn uinput_emit(fd: c_int, code: c_uint, pressed: c_int);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,18 +24,12 @@ pub struct Keymap {
|
|||
|
||||
impl Keymap {
|
||||
pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
|
||||
let names = rmlvo.map(|rmlvo| {
|
||||
Self::generate_names(rmlvo)
|
||||
});
|
||||
let names = rmlvo.map(|rmlvo| Self::generate_names(rmlvo));
|
||||
|
||||
let names_ptr = names.map_or(std::ptr::null(), |names| &names);
|
||||
|
||||
let raw_keymap = unsafe {
|
||||
xkb_keymap_new_from_names(
|
||||
context.get_handle(),
|
||||
names_ptr,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
xkb_keymap_new_from_names(context.get_handle(), names_ptr, XKB_KEYMAP_COMPILE_NO_FLAGS)
|
||||
};
|
||||
let keymap = scopeguard::guard(raw_keymap, |raw_keymap| unsafe {
|
||||
xkb_keymap_unref(raw_keymap);
|
||||
|
@ -55,11 +49,21 @@ impl Keymap {
|
|||
}
|
||||
|
||||
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 model = rmlvo.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") });
|
||||
let rules = rmlvo
|
||||
.rules
|
||||
.map(|s| CString::new(s).expect("unable to create CString for keymap"));
|
||||
let model = rmlvo
|
||||
.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 {
|
||||
rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()),
|
||||
|
|
|
@ -25,7 +25,7 @@ mod uinput;
|
|||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ffi::{CString},
|
||||
ffi::CString,
|
||||
};
|
||||
|
||||
use context::Context;
|
||||
|
@ -34,10 +34,7 @@ use log::error;
|
|||
use std::iter::FromIterator;
|
||||
use uinput::UInputDevice;
|
||||
|
||||
use crate::{
|
||||
linux::raw_keys::{convert_to_sym_array},
|
||||
InjectorCreationOptions,
|
||||
};
|
||||
use crate::{linux::raw_keys::convert_to_sym_array, InjectorCreationOptions};
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
@ -120,7 +117,8 @@ impl EVDEVInjector {
|
|||
}
|
||||
|
||||
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) =
|
||||
Self::generate_maps(&modifiers, max_modifier_combination_len, &keymap)?;
|
||||
|
@ -294,7 +292,7 @@ impl Injector for EVDEVInjector {
|
|||
// Compute all the key record sequence first to make sure a mapping is available
|
||||
let syms = convert_to_sym_array(keys)?;
|
||||
let records = self.convert_to_record_array(&syms)?;
|
||||
|
||||
|
||||
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
||||
|
||||
// First press the keys
|
||||
|
|
|
@ -8,7 +8,13 @@ use scopeguard::ScopeGuard;
|
|||
use anyhow::Result;
|
||||
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 {
|
||||
state: *mut xkb_state,
|
||||
|
|
|
@ -356,4 +356,4 @@ mod tests {
|
|||
assert!(Key::parse("INVALID").is_none());
|
||||
assert!(Key::parse("RAW(a)").is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ pub struct InjectorCreationOptions {
|
|||
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
||||
use_evdev: bool,
|
||||
|
||||
// Overwrite the list of modifiers to be scanned when
|
||||
// Overwrite the list of modifiers to be scanned when
|
||||
// populating the evdev injector lookup maps
|
||||
evdev_modifiers: Option<Vec<u32>>,
|
||||
|
||||
|
@ -151,4 +151,3 @@ pub fn get_injector(options: InjectorCreationOptions) -> Result<Box<dyn Injector
|
|||
info!("using EVDEVInjector");
|
||||
Ok(Box::new(evdev::EVDEVInjector::new(options)?))
|
||||
}
|
||||
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod raw_keys;
|
||||
pub mod raw_keys;
|
||||
|
|
|
@ -143,4 +143,4 @@ pub fn convert_to_sym_array(keys: &[Key]) -> Result<Vec<u64>> {
|
|||
pub enum LinuxRawKeyError {
|
||||
#[error("missing mapping for key `{0}`")]
|
||||
MappingFailure(Key),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use raw_keys::convert_key_to_vkey;
|
|||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{InjectionOptions, Injector, keys};
|
||||
use crate::{keys, InjectionOptions, Injector};
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
#[link(name = "espansoinject", kind = "static")]
|
||||
|
@ -72,7 +72,11 @@ impl Injector for MacInjector {
|
|||
let virtual_keys = Self::convert_to_vk_array(keys)?;
|
||||
|
||||
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(())
|
||||
|
@ -82,7 +86,11 @@ impl Injector for MacInjector {
|
|||
let virtual_keys = Self::convert_to_vk_array(keys)?;
|
||||
|
||||
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(())
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
|
||||
mod raw_keys;
|
||||
|
||||
use log::{error};
|
||||
use log::error;
|
||||
use raw_keys::convert_key_to_vkey;
|
||||
|
||||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{InjectionOptions, Injector, keys};
|
||||
use crate::{keys, InjectionOptions, Injector};
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
#[link(name = "espansoinject", kind = "static")]
|
||||
|
@ -77,7 +77,11 @@ impl Injector for Win32Injector {
|
|||
}
|
||||
} else {
|
||||
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 {
|
||||
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]
|
||||
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]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,15 @@ use std::{
|
|||
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 crate::linux::raw_keys::convert_to_sym_array;
|
||||
use anyhow::Result;
|
||||
use crate::linux::raw_keys::{convert_to_sym_array};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{keys, InjectionOptions, Injector};
|
||||
|
@ -174,7 +178,6 @@ impl X11Injector {
|
|||
modifiers.modifiermap,
|
||||
(8 * modifiers.max_keypermod) as usize,
|
||||
)
|
||||
|
||||
};
|
||||
let keycode = modifier_map[(mod_index * modifiers.max_keypermod + mod_key) as usize];
|
||||
if keycode != 0 {
|
||||
|
@ -355,7 +358,7 @@ impl Injector for X11Injector {
|
|||
|
||||
fn send_keys(&self, keys: &[keys::Key], options: InjectionOptions) -> Result<()> {
|
||||
let focused_window = self.get_focused_window();
|
||||
|
||||
|
||||
// Compute all the key record sequence first to make sure a mapping is available
|
||||
let syms = convert_to_sym_array(keys)?;
|
||||
let records = self.convert_to_record_array(&syms)?;
|
||||
|
@ -385,7 +388,7 @@ impl Injector for X11Injector {
|
|||
// Compute all the key record sequence first to make sure a mapping is available
|
||||
let syms = convert_to_sym_array(keys)?;
|
||||
let records = self.convert_to_record_array(&syms)?;
|
||||
|
||||
|
||||
// Render the correct modifier mask for the given sequence
|
||||
let records = self.render_key_combination(&records);
|
||||
|
||||
|
@ -427,4 +430,4 @@ pub enum X11InjectorError {
|
|||
|
||||
#[error("missing record mapping for sym `{0}`")]
|
||||
SymMappingFailure(u64),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::path::Path;
|
||||
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 crossbeam::channel::{Receiver, unbounded};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
|
@ -39,7 +39,10 @@ pub trait IPCClient<Event> {
|
|||
}
|
||||
|
||||
#[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 server = unix::UnixIPCServer::new(id, parent_dir, sender)?;
|
||||
Ok((server, receiver))
|
||||
|
@ -52,7 +55,10 @@ pub fn client<Event: Serialize>(id: &str, parent_dir: &Path) -> Result<impl IPCC
|
|||
}
|
||||
|
||||
#[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 server = windows::WinIPCServer::new(id, sender)?;
|
||||
Ok((server, receiver))
|
||||
|
@ -76,7 +82,7 @@ pub enum IPCServerError {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
enum Event {
|
||||
|
@ -93,10 +99,10 @@ mod tests {
|
|||
|
||||
// TODO: avoid delay and change the IPC code so that we can wait for the IPC
|
||||
//std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
|
||||
|
||||
let client = client::<Event>("testespansoipc", &std::env::temp_dir()).unwrap();
|
||||
client.send(Event::Foo("hello".to_string())).unwrap();
|
||||
|
||||
|
||||
let event = receiver.recv().unwrap();
|
||||
assert!(matches!(event, Event::Foo(x) if x == "hello"));
|
||||
|
||||
|
@ -108,4 +114,4 @@ mod tests {
|
|||
let client = client::<Event>("testespansoipc", &std::env::temp_dir()).unwrap();
|
||||
assert!(client.send(Event::Foo("hello".to_string())).is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,7 @@ use crossbeam::channel::Sender;
|
|||
use log::{error, info};
|
||||
use named_pipe::{PipeClient, PipeOptions};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::{
|
||||
io::{BufReader, Read, Write},
|
||||
};
|
||||
use std::io::{BufReader, Read, Write};
|
||||
|
||||
use crate::{IPCClient, IPCServer, IPCServerError};
|
||||
|
||||
|
@ -41,10 +39,7 @@ impl<Event> WinIPCServer<Event> {
|
|||
|
||||
let options = PipeOptions::new(&pipe_name);
|
||||
|
||||
info!(
|
||||
"binded to named pipe: {}",
|
||||
pipe_name
|
||||
);
|
||||
info!("binded to named pipe: {}", pipe_name);
|
||||
|
||||
Ok(Self { options, sender })
|
||||
}
|
||||
|
@ -116,4 +111,3 @@ impl<Event: Serialize> IPCClient<Event> for WinIPCClient {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use icons::TrayIcon;
|
||||
use anyhow::Result;
|
||||
use icons::TrayIcon;
|
||||
use thiserror::Error;
|
||||
|
||||
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 {
|
||||
show_icon: options.show_icon,
|
||||
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)))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
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) = mac::create(mac::MacUIOptions {
|
||||
show_icon: options.show_icon,
|
||||
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")]
|
||||
pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEventLoop>)> {
|
||||
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)))
|
||||
}
|
||||
|
@ -75,4 +78,4 @@ pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEve
|
|||
pub enum UIError {
|
||||
#[error("missing required option for ui: `{0}`")]
|
||||
MissingOption(String),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use log::error;
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
use notify_rust::Notification;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
use std::{cmp::min, collections::HashMap, ffi::CString, os::raw::c_char, thread::ThreadId};
|
||||
|
||||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
use lazycell::LazyCell;
|
||||
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.
|
||||
const MAX_FILE_PATH: usize = 1024;
|
||||
|
@ -105,8 +105,6 @@ impl MacEventLoop {
|
|||
_init_thread_id: LazyCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl UIEventLoop for MacEventLoop {
|
||||
|
@ -133,7 +131,7 @@ impl UIEventLoop for MacEventLoop {
|
|||
._init_thread_id
|
||||
.fill(std::thread::current().id())
|
||||
.expect("Unable to set initialization thread id");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -147,7 +145,7 @@ impl UIEventLoop for MacEventLoop {
|
|||
|
||||
if self._event_callback.fill(event_callback).is_err() {
|
||||
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) {
|
||||
|
@ -165,7 +163,7 @@ impl UIEventLoop for MacEventLoop {
|
|||
|
||||
if error_code <= 0 {
|
||||
error!("MacEventLoop exited with <= 0 code");
|
||||
return Err(MacUIError::InternalError().into())
|
||||
return Err(MacUIError::InternalError().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -180,7 +178,7 @@ pub struct MacRemote {
|
|||
impl MacRemote {
|
||||
pub(crate) fn new(icon_indexes: HashMap<TrayIcon, usize>) -> Self {
|
||||
Self { icon_indexes }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UIRemote for MacRemote {
|
||||
|
@ -242,4 +240,4 @@ impl From<RawUIEvent> for Option<UIEvent> {
|
|||
pub enum MacUIError {
|
||||
#[error("internal error")]
|
||||
InternalError(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ use std::{
|
|||
thread::ThreadId,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use lazycell::LazyCell;
|
||||
use log::{error, trace};
|
||||
use widestring::WideCString;
|
||||
use anyhow::Result;
|
||||
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.
|
||||
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] =
|
||||
[[0; MAX_FILE_PATH]; MAX_ICON_COUNT];
|
||||
for (i, icon_path) in icon_paths.iter_mut().enumerate().take(self.icons.len()) {
|
||||
let wide_path =
|
||||
WideCString::from_str(&self.icons[i])?;
|
||||
let wide_path = WideCString::from_str(&self.icons[i])?;
|
||||
let len = min(wide_path.len(), MAX_FILE_PATH - 1);
|
||||
icon_path[0..len].clone_from_slice(&wide_path.as_slice()[..len]);
|
||||
}
|
||||
|
@ -178,11 +177,31 @@ impl UIEventLoop for Win32EventLoop {
|
|||
|
||||
if handle.is_null() {
|
||||
return match error_code {
|
||||
-1 => Err(Win32UIError::EventLoopInitError("Unable to initialize Win32EventLoop, error registering window class".to_string()).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()),
|
||||
}
|
||||
-1 => Err(
|
||||
Win32UIError::EventLoopInitError(
|
||||
"Unable to initialize Win32EventLoop, error registering window class".to_string(),
|
||||
)
|
||||
.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);
|
||||
|
@ -192,7 +211,7 @@ impl UIEventLoop for Win32EventLoop {
|
|||
._init_thread_id
|
||||
.fill(std::thread::current().id())
|
||||
.expect("Unable to set initialization thread id");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -207,12 +226,12 @@ impl UIEventLoop for Win32EventLoop {
|
|||
let window_handle = self.handle.load(Ordering::Acquire);
|
||||
if window_handle.is_null() {
|
||||
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() {
|
||||
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) {
|
||||
|
@ -230,7 +249,7 @@ impl UIEventLoop for Win32EventLoop {
|
|||
|
||||
if error_code <= 0 {
|
||||
error!("Win32EventLoop exited with <= 0 code");
|
||||
return Err(Win32UIError::InternalError().into())
|
||||
return Err(Win32UIError::InternalError().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use espanso_detect::{event::{InputEvent, Status}, get_source};
|
||||
use espanso_inject::{get_injector, Injector, keys};
|
||||
use espanso_detect::{
|
||||
event::{InputEvent, Status},
|
||||
get_source,
|
||||
};
|
||||
use espanso_inject::{get_injector, keys, Injector};
|
||||
use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*};
|
||||
use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
|
||||
|
||||
|
@ -49,9 +52,12 @@ fn main() {
|
|||
// icon_paths: &icon_paths,
|
||||
// });
|
||||
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()
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
eventloop.initialize().unwrap();
|
||||
|
||||
|
@ -59,21 +65,25 @@ fn main() {
|
|||
let injector = get_injector(Default::default()).unwrap();
|
||||
let mut source = get_source(Default::default()).unwrap();
|
||||
source.initialize().unwrap();
|
||||
source.eventloop(Box::new(move |event: InputEvent| {
|
||||
println!("ev {:?}", event);
|
||||
match event {
|
||||
InputEvent::Mouse(_) => {}
|
||||
InputEvent::Keyboard(evt) => {
|
||||
if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released {
|
||||
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
||||
//remote.show_notification("Espanso is running!");
|
||||
injector.send_string("Hey guys! @", Default::default()).expect("error");
|
||||
//std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
//injector.send_key_combination(&[keys::Key::Control, keys::Key::V], Default::default()).unwrap();
|
||||
source
|
||||
.eventloop(Box::new(move |event: InputEvent| {
|
||||
println!("ev {:?}", event);
|
||||
match event {
|
||||
InputEvent::Mouse(_) => {}
|
||||
InputEvent::Keyboard(evt) => {
|
||||
if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released {
|
||||
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
||||
//remote.show_notification("Espanso is running!");
|
||||
injector
|
||||
.send_string("Hey guys! @", Default::default())
|
||||
.expect("error");
|
||||
//std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
//injector.send_key_combination(&[keys::Key::Control, keys::Key::V], Default::default()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})).unwrap();
|
||||
}))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
eventloop.run(Box::new(move |event| {
|
||||
|
|
Loading…
Reference in New Issue
Block a user