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 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;
|
||||||
|
@ -42,4 +61,4 @@ pub enum ConfigStoreError {
|
||||||
|
|
||||||
#[error("io error")]
|
#[error("io error")]
|
||||||
IOError(#[from] std::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 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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,4 +55,4 @@ impl ParsedConfig {
|
||||||
pub enum ParsedConfigError {
|
pub enum ParsedConfigError {
|
||||||
#[error("can't load config `{0}`")]
|
#[error("can't load config `{0}`")]
|
||||||
LoadFailed(#[from] anyhow::Error),
|
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 anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,38 +76,35 @@ 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 {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
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 {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
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 {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
// All the filters that have been specified must be true to define a match
|
// 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
|
is_os_match && is_exec_match && is_title_match && is_class_match
|
||||||
|
@ -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)?)
|
||||||
|
@ -431,7 +448,7 @@ mod tests {
|
||||||
sub_file.to_string_lossy().to_string(),
|
sub_file.to_string_lossy().to_string(),
|
||||||
];
|
];
|
||||||
expected.sort();
|
expected.sort();
|
||||||
|
|
||||||
let mut result = config.match_paths().to_vec();
|
let mut result = config.match_paths().to_vec();
|
||||||
result.sort();
|
result.sort();
|
||||||
|
|
||||||
|
@ -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());
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ),* ) => {
|
||||||
|
@ -7,7 +26,7 @@ macro_rules! merge {
|
||||||
$child.$x = $parent.$x.clone();
|
$child.$x = $parent.$x.clone();
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
||||||
// Build a temporary object to verify that all fields
|
// Build a temporary object to verify that all fields
|
||||||
// are being used at compile time
|
// are being used at compile time
|
||||||
$t {
|
$t {
|
||||||
|
@ -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() {
|
||||||
|
@ -59,4 +77,4 @@ mod tests {
|
||||||
assert!(!os_matches("linux"));
|
assert!(!os_matches("linux"));
|
||||||
assert!(!os_matches("invalid"));
|
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};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
static STRUCT_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static STRUCT_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
@ -10,4 +29,4 @@ pub type StructId = usize;
|
||||||
/// that is incremented for each struct.
|
/// that is incremented for each struct.
|
||||||
pub fn next_id() -> StructId {
|
pub fn next_id() -> StructId {
|
||||||
STRUCT_COUNTER.fetch_add(1, Ordering::SeqCst)
|
STRUCT_COUNTER.fetch_add(1, Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
||||||
title: Some("Google Chrome"),
|
config_store
|
||||||
class: None,
|
.active(&AppProperties {
|
||||||
exec: None,
|
title: Some("Google Chrome"),
|
||||||
}).match_paths().len(), 1);
|
class: None,
|
||||||
|
exec: None,
|
||||||
|
})
|
||||||
|
.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
|
||||||
title: Some("Chrome"),
|
.query(config_store.default().match_paths())
|
||||||
class: None,
|
.matches
|
||||||
exec: None,
|
.len(),
|
||||||
}).match_paths()).matches.len(), 2);
|
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 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> {
|
||||||
|
@ -26,7 +43,7 @@ pub(crate) fn load_match_group(path: &Path) -> Result<MatchGroup> {
|
||||||
let importer = IMPORTERS
|
let importer = IMPORTERS
|
||||||
.iter()
|
.iter()
|
||||||
.find(|importer| importer.is_supported(&extension));
|
.find(|importer| importer.is_supported(&extension));
|
||||||
|
|
||||||
match importer {
|
match importer {
|
||||||
Some(importer) => match importer.load_group(path) {
|
Some(importer) => match importer.load_group(path) {
|
||||||
Ok(group) => Ok(group),
|
Ok(group) => Ok(group),
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,14 +421,16 @@ 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();
|
||||||
|
|
||||||
let importer = YAMLImporter::new();
|
let importer = YAMLImporter::new();
|
||||||
let group = importer.load_group(&base_file).unwrap();
|
let group = importer.load_group(&base_file).unwrap();
|
||||||
|
|
||||||
let vars = vec![Variable {
|
let vars = vec![Variable {
|
||||||
name: "var1".to_string(),
|
name: "var1".to_string(),
|
||||||
var_type: "test".to_string(),
|
var_type: "test".to_string(),
|
||||||
|
@ -418,23 +441,19 @@ 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()
|
|
||||||
}),
|
|
||||||
effect: MatchEffect::Text(TextEffect {
|
|
||||||
replace: "world".to_string(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..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 std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -98,4 +117,4 @@ pub struct YAMLVariable {
|
||||||
|
|
||||||
fn default_params() -> Mapping {
|
fn default_params() -> Mapping {
|
||||||
Mapping::new()
|
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 anyhow::Result;
|
||||||
use std::{
|
use std::path::Path;
|
||||||
path::{Path},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Match, Variable};
|
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 anyhow::Result;
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::path::{Path, PathBuf};
|
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 serde_yaml::Mapping;
|
||||||
|
|
||||||
use crate::counter::{next_id, StructId};
|
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 log::error;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
@ -629,7 +648,7 @@ mod tests {
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<Match>>(),
|
.collect::<Vec<Match>>(),
|
||||||
create_matches(&[
|
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"),
|
("hello", "world2"),
|
||||||
("foo", "bar"),
|
("foo", "bar"),
|
||||||
("hello", "world"),
|
("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};
|
use super::{Match, Variable};
|
||||||
|
|
||||||
mod default;
|
mod default;
|
||||||
|
@ -17,4 +36,4 @@ pub fn new() -> impl MatchStore {
|
||||||
// TODO: here we can replace the DefaultMatchStore with a caching wrapper
|
// TODO: here we can replace the DefaultMatchStore with a caching wrapper
|
||||||
// that returns the same response for the given "paths" query
|
// that returns the same response for the given "paths" query
|
||||||
default::DefaultMatchStore::new()
|
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.
|
/// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,4 +67,4 @@ pub mod tests {
|
||||||
fn is_yaml_empty_document_with_content() {
|
fn is_yaml_empty_document_with_content() {
|
||||||
assert_eq!(is_yaml_empty("\nfield: true\n"), false);
|
assert_eq!(is_yaml_empty("\nfield: true\n"), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub trait Source {
|
||||||
pub struct SourceCreationOptions {
|
pub struct SourceCreationOptions {
|
||||||
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
||||||
use_evdev: bool,
|
use_evdev: bool,
|
||||||
|
|
||||||
// Can be used to overwrite the keymap configuration
|
// Can be used to overwrite the keymap configuration
|
||||||
// used by espanso to inject key presses.
|
// used by espanso to inject key presses.
|
||||||
evdev_keyboard_rmlvo: Option<KeyboardConfig>,
|
evdev_keyboard_rmlvo: Option<KeyboardConfig>,
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -81,4 +81,4 @@ extern "C" {
|
||||||
|
|
||||||
pub fn setup_uinput_device(fd: c_int) -> c_int;
|
pub fn setup_uinput_device(fd: c_int) -> c_int;
|
||||||
pub fn uinput_emit(fd: c_int, code: c_uint, pressed: c_int);
|
pub fn uinput_emit(fd: c_int, code: c_uint, pressed: c_int);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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)?;
|
||||||
|
@ -294,7 +292,7 @@ impl Injector for EVDEVInjector {
|
||||||
// Compute all the key record sequence first to make sure a mapping is available
|
// Compute all the key record sequence first to make sure a mapping is available
|
||||||
let syms = convert_to_sym_array(keys)?;
|
let syms = convert_to_sym_array(keys)?;
|
||||||
let records = self.convert_to_record_array(&syms)?;
|
let records = self.convert_to_record_array(&syms)?;
|
||||||
|
|
||||||
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
||||||
|
|
||||||
// First press the keys
|
// First press the keys
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -356,4 +356,4 @@ mod tests {
|
||||||
assert!(Key::parse("INVALID").is_none());
|
assert!(Key::parse("INVALID").is_none());
|
||||||
assert!(Key::parse("RAW(a)").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.
|
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
||||||
use_evdev: bool,
|
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
|
// populating the evdev injector lookup maps
|
||||||
evdev_modifiers: Option<Vec<u32>>,
|
evdev_modifiers: Option<Vec<u32>>,
|
||||||
|
|
||||||
|
@ -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)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,4 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* 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 {
|
pub enum LinuxRawKeyError {
|
||||||
#[error("missing mapping for key `{0}`")]
|
#[error("missing mapping for key `{0}`")]
|
||||||
MappingFailure(Key),
|
MappingFailure(Key),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -355,7 +358,7 @@ impl Injector for X11Injector {
|
||||||
|
|
||||||
fn send_keys(&self, keys: &[keys::Key], options: InjectionOptions) -> Result<()> {
|
fn send_keys(&self, keys: &[keys::Key], options: InjectionOptions) -> Result<()> {
|
||||||
let focused_window = self.get_focused_window();
|
let focused_window = self.get_focused_window();
|
||||||
|
|
||||||
// Compute all the key record sequence first to make sure a mapping is available
|
// Compute all the key record sequence first to make sure a mapping is available
|
||||||
let syms = convert_to_sym_array(keys)?;
|
let syms = convert_to_sym_array(keys)?;
|
||||||
let records = self.convert_to_record_array(&syms)?;
|
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
|
// Compute all the key record sequence first to make sure a mapping is available
|
||||||
let syms = convert_to_sym_array(keys)?;
|
let syms = convert_to_sym_array(keys)?;
|
||||||
let records = self.convert_to_record_array(&syms)?;
|
let records = self.convert_to_record_array(&syms)?;
|
||||||
|
|
||||||
// Render the correct modifier mask for the given sequence
|
// Render the correct modifier mask for the given sequence
|
||||||
let records = self.render_key_combination(&records);
|
let records = self.render_key_combination(&records);
|
||||||
|
|
||||||
|
@ -427,4 +430,4 @@ pub enum X11InjectorError {
|
||||||
|
|
||||||
#[error("missing record mapping for sym `{0}`")]
|
#[error("missing record mapping for sym `{0}`")]
|
||||||
SymMappingFailure(u64),
|
SymMappingFailure(u64),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -93,10 +99,10 @@ mod tests {
|
||||||
|
|
||||||
// TODO: avoid delay and change the IPC code so that we can wait for the IPC
|
// 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));
|
//std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
|
||||||
let client = client::<Event>("testespansoipc", &std::env::temp_dir()).unwrap();
|
let client = client::<Event>("testespansoipc", &std::env::temp_dir()).unwrap();
|
||||||
client.send(Event::Foo("hello".to_string())).unwrap();
|
client.send(Event::Foo("hello".to_string())).unwrap();
|
||||||
|
|
||||||
let event = receiver.recv().unwrap();
|
let event = receiver.recv().unwrap();
|
||||||
assert!(matches!(event, Event::Foo(x) if x == "hello"));
|
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();
|
let client = client::<Event>("testespansoipc", &std::env::temp_dir()).unwrap();
|
||||||
assert!(client.send(Event::Foo("hello".to_string())).is_err());
|
assert!(client.send(Event::Foo("hello".to_string())).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
@ -75,4 +78,4 @@ pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEve
|
||||||
pub enum UIError {
|
pub enum UIError {
|
||||||
#[error("missing required option for ui: `{0}`")]
|
#[error("missing required option for ui: `{0}`")]
|
||||||
MissingOption(String),
|
MissingOption(String),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -133,7 +131,7 @@ impl UIEventLoop for MacEventLoop {
|
||||||
._init_thread_id
|
._init_thread_id
|
||||||
.fill(std::thread::current().id())
|
.fill(std::thread::current().id())
|
||||||
.expect("Unable to set initialization thread id");
|
.expect("Unable to set initialization thread id");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
|
@ -180,7 +178,7 @@ pub struct MacRemote {
|
||||||
impl MacRemote {
|
impl MacRemote {
|
||||||
pub(crate) fn new(icon_indexes: HashMap<TrayIcon, usize>) -> Self {
|
pub(crate) fn new(icon_indexes: HashMap<TrayIcon, usize>) -> Self {
|
||||||
Self { icon_indexes }
|
Self { icon_indexes }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UIRemote for MacRemote {
|
impl UIRemote for MacRemote {
|
||||||
|
@ -242,4 +240,4 @@ impl From<RawUIEvent> for Option<UIEvent> {
|
||||||
pub enum MacUIError {
|
pub enum MacUIError {
|
||||||
#[error("internal error")]
|
#[error("internal error")]
|
||||||
InternalError(),
|
InternalError(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -192,7 +211,7 @@ impl UIEventLoop for Win32EventLoop {
|
||||||
._init_thread_id
|
._init_thread_id
|
||||||
.fill(std::thread::current().id())
|
.fill(std::thread::current().id())
|
||||||
.expect("Unable to set initialization thread id");
|
.expect("Unable to set initialization thread id");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
|
|
|
@ -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,21 +65,25 @@ 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
|
||||||
println!("ev {:?}", event);
|
.eventloop(Box::new(move |event: InputEvent| {
|
||||||
match event {
|
println!("ev {:?}", event);
|
||||||
InputEvent::Mouse(_) => {}
|
match event {
|
||||||
InputEvent::Keyboard(evt) => {
|
InputEvent::Mouse(_) => {}
|
||||||
if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released {
|
InputEvent::Keyboard(evt) => {
|
||||||
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
if evt.key == espanso_detect::event::Key::Escape && evt.status == Status::Released {
|
||||||
//remote.show_notification("Espanso is running!");
|
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
||||||
injector.send_string("Hey guys! @", Default::default()).expect("error");
|
//remote.show_notification("Espanso is running!");
|
||||||
//std::thread::sleep(std::time::Duration::from_secs(2));
|
injector
|
||||||
//injector.send_key_combination(&[keys::Key::Control, keys::Key::V], Default::default()).unwrap();
|
.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| {
|
eventloop.run(Box::new(move |event| {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user