Implement notification on MacOS

This commit is contained in:
Federico Terzi 2019-09-09 15:15:01 +02:00
parent 68fd2fccc1
commit fbd053f67d
16 changed files with 244 additions and 44 deletions

75
Cargo.lock generated
View File

@ -1,5 +1,10 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "adler32"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aho-corasick"
version = "0.7.6"
@ -91,6 +96,24 @@ name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bzip2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bzip2-sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.41"
@ -147,6 +170,14 @@ name = "constant_time_eq"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crc32fast"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.6.6"
@ -194,6 +225,7 @@ dependencies = [
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
"simplelog 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -216,6 +248,16 @@ dependencies = [
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "flate2"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@ -249,6 +291,14 @@ name = "memchr"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "miniz_oxide"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nodrop"
version = "0.1.13"
@ -271,6 +321,11 @@ dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "podio"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.30"
@ -546,7 +601,20 @@ dependencies = [
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zip"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
@ -559,6 +627,8 @@ dependencies = [
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum blake2b_simd 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bf775a81bb2d464e20ff170ac20316c7b08a43d11dbc72f0f82e8e8d3d6d0499"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f"
"checksum cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
@ -566,21 +636,25 @@ dependencies = [
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62"
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
@ -616,3 +690,4 @@ dependencies = [
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6"

View File

@ -14,6 +14,7 @@ clap = "2.33.0"
regex = "1.3.1"
log = "0.4.8"
simplelog = "0.7.1"
zip = "0.5.3"
[build-dependencies]
cmake = "0.1.31"

View File

@ -22,10 +22,12 @@
NSString *title = @"Title";
NSString *desc = @"Description";
double delay = 1.5;
if ([args count] > 2) {
if ([args count] > 3) {
title = args[1];
desc = args[2];
delay = [args[3] doubleValue];
}
NSUserNotification *notification = [[NSUserNotification alloc] init];
@ -35,6 +37,8 @@
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
[[NSUserNotificationCenter defaultUserNotificationCenter] performSelector:@selector(removeDeliveredNotification:) withObject:notification afterDelay:delay];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSRunningApplication *app = [NSRunningApplication currentApplication];
[app terminate];

14
src/bridge/macos.rs Normal file
View File

@ -0,0 +1,14 @@
use std::os::raw::{c_void, c_char};
#[allow(improper_ctypes)]
#[link(name="macbridge", kind="static")]
extern {
pub fn register_keypress_callback(s: *const c_void,
cb: extern fn(_self: *mut c_void, *const u8,
i32, i32, i32));
pub fn initialize();
pub fn eventloop();
pub fn send_string(string: *const c_char);
pub fn send_vkey(vk: i32);
pub fn delete_string(count: i32);
}

19
src/clipboard/macos.rs Normal file
View File

@ -0,0 +1,19 @@
pub struct MacClipboardManager {
}
impl super::ClipboardManager for MacClipboardManager {
fn get_clipboard(&self) -> Option<String> {
unimplemented!();
}
fn set_clipboard(&self, payload: &str) {
unimplemented!();
}
}
impl MacClipboardManager {
pub fn new() -> MacClipboardManager {
MacClipboardManager{}
}
}

View File

@ -8,11 +8,12 @@ mod linux;
mod macos;
pub trait ClipboardManager {
fn initialize(&self);
fn get_clipboard(&self) -> Option<String>;
fn set_clipboard(&self, payload: &str);
}
// TODO: change windows and linux implementations to avoid initialize() call and use constructor instead
// LINUX IMPLEMENTATION
#[cfg(target_os = "linux")]
pub fn get_manager() -> impl ClipboardManager {
@ -28,3 +29,9 @@ pub fn get_manager() -> impl ClipboardManager {
manager.initialize();
manager
}
// MAC IMPLEMENTATION
#[cfg(target_os = "macos")]
pub fn get_manager() -> impl ClipboardManager {
macos::MacClipboardManager::new()
}

View File

@ -1,8 +1,9 @@
use std::sync::mpsc;
use std::os::raw::c_char;
use std::os::raw::{c_char, c_void};
use std::ffi::CString;
use crate::keyboard::{KeyEvent, KeyModifier};
use crate::keyboard::KeyModifier::*;
use crate::bridge::macos::*;
#[repr(C)]
pub struct MacKeyboardInterceptor {
@ -12,7 +13,8 @@ pub struct MacKeyboardInterceptor {
impl super::KeyboardInterceptor for MacKeyboardInterceptor {
fn initialize(&self) {
unsafe {
register_keypress_callback(self,keypress_callback);
let self_ptr = self as *const MacKeyboardInterceptor as *const c_void;
register_keypress_callback(self_ptr,keypress_callback);
initialize();
} // TODO: check initialization return codes
}
@ -41,6 +43,10 @@ impl super::KeyboardSender for MacKeyboardSender {
}
}
fn trigger_paste(&self) {
unimplemented!()
}
fn delete_string(&self, count: i32) {
unsafe {delete_string(count)}
}
@ -48,9 +54,11 @@ impl super::KeyboardSender for MacKeyboardSender {
// Native bridge code
extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *const u8, len: i32,
extern fn keypress_callback(_self: *mut c_void, raw_buffer: *const u8, len: i32,
is_modifier: i32, key_code: i32) {
unsafe {
let _self = _self as *mut MacKeyboardInterceptor;
if is_modifier == 0 { // Char event
// Convert the received buffer to a character
let buffer = std::slice::from_raw_parts(raw_buffer, len as usize);
@ -76,16 +84,3 @@ extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *con
}
}
}
#[allow(improper_ctypes)]
#[link(name="macbridge", kind="static")]
extern {
fn register_keypress_callback(s: *const MacKeyboardInterceptor,
cb: extern fn(_self: *mut MacKeyboardInterceptor, *const u8,
i32, i32, i32));
fn initialize();
fn eventloop();
fn send_string(string: *const c_char);
fn send_vkey(vk: i32);
fn delete_string(count: i32);
}

View File

@ -95,6 +95,7 @@ fn espanso_background(rxc: Receiver<KeyEvent>, config_set: ConfigSet) {
let config_manager = RuntimeConfigManager::new(config_set, system_manager);
let ui_manager = ui::get_uimanager();
ui_manager.notify("espanso is running!");
let clipboard_manager = clipboard::get_manager();

Binary file not shown.

View File

@ -8,10 +8,6 @@ pub struct LinuxSystemManager {
}
impl super::SystemManager for LinuxSystemManager {
fn initialize(&self) {
}
fn get_current_window_title(&self) -> Option<String> {
unsafe {
let mut buffer : [c_char; 100] = [0; 100];

29
src/system/macos.rs Normal file
View File

@ -0,0 +1,29 @@
use std::os::raw::c_char;
use std::ffi::CStr;
pub struct MacSystemManager {
}
impl super::SystemManager for MacSystemManager {
fn get_current_window_title(&self) -> Option<String> {
unimplemented!()
}
fn get_current_window_class(&self) -> Option<String> {
unimplemented!();
}
fn get_current_window_executable(&self) -> Option<String> {
unimplemented!()
}
}
impl MacSystemManager {
pub fn new() -> MacSystemManager {
MacSystemManager{
}
}
}

View File

@ -8,12 +8,13 @@ mod linux;
mod macos;
pub trait SystemManager {
fn initialize(&self);
fn get_current_window_title(&self) -> Option<String>;
fn get_current_window_class(&self) -> Option<String>;
fn get_current_window_executable(&self) -> Option<String>;
}
// TODO: change windows and linux implementations to avoid initialize() call and use constructor instead
// LINUX IMPLEMENTATION
#[cfg(target_os = "linux")]
pub fn get_manager() -> impl SystemManager {
@ -29,3 +30,9 @@ pub fn get_manager() -> impl SystemManager {
manager.initialize();
manager
}
// MAC IMPLEMENTATION
#[cfg(target_os = "macos")]
pub fn get_manager() -> impl SystemManager {
macos::MacSystemManager::new()
}

View File

@ -6,10 +6,6 @@ pub struct WindowsSystemManager {
}
impl super::SystemManager for WindowsSystemManager {
fn initialize(&self) {
}
fn get_current_window_title(&self) -> Option<String> {
unsafe {
let mut buffer : [u16; 100] = [0; 100];

View File

@ -1,35 +1,93 @@
use std::fs::create_dir_all;
use std::{fs, io};
use std::io::{BufReader, Cursor};
use zip::ZipArchive;
use log::{info, debug, error};
use std::path::PathBuf;
use std::process::Command;
const NOTIFY_HELPER_BINARY : &'static [u8] = include_bytes!("res/mac/EspansoNotifyHelper.zip");
const NOTIFY_HELPER_BINARY : &'static [u8] = include_bytes!("../res/mac/EspansoNotifyHelper.zip");
const DEFAULT_NOTIFICATION_DELAY : f64 = 1.5;
pub struct MacUIManager {
notify_helper_path: PathBuf
}
impl super::UIManager for MacUIManager {
fn initialize(&self) {
self.initialize_notify_helper();
}
fn notify(&self, message: &str) {
unimplemented!()
let executable_path = self.notify_helper_path.join("Contents");
let executable_path = executable_path.join("MacOS");
let executable_path = executable_path.join("EspansoNotifyHelper");
let res = Command::new(executable_path)
.args(&["espanso", message, &DEFAULT_NOTIFICATION_DELAY.to_string()])
.spawn();
}
}
impl MacUIManager {
fn initialize_notify_helper(&self) {
let res = dirs::data_dir();
if let Some(data_dir) = res {
pub fn new() -> MacUIManager {
let notify_helper_path = MacUIManager::initialize_notify_helper();
MacUIManager{
notify_helper_path
}
}
fn initialize_notify_helper() -> PathBuf {
let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating.");
let espanso_dir = data_dir.join("espanso");
let res = create_dir_all(espanso_dir);
let res = create_dir_all(&espanso_dir);
info!("Initializing EspansoNotifyHelper in {}", espanso_dir.as_path().display());
let espanso_target = espanso_dir.join("EspansoNotifyHelper.app");
if espanso_target.exists() {
info!("EspansoNotifyHelper already initialized, skipping.");
}else{
if let Ok(_) = res {
// TODO: extract zip file
// Extract zip file
let reader = Cursor::new(NOTIFY_HELPER_BINARY);
let mut archive = zip::ZipArchive::new(reader).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let outpath = espanso_dir.join(file.sanitized_name());
{
let comment = file.comment();
if !comment.is_empty() {
debug!("File {} comment: {}", i, comment);
}
}
// TODO: print error message
if (&*file.name()).ends_with('/') {
debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
fs::create_dir_all(&outpath).unwrap();
} else {
debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
if let Some(p) = outpath.parent() {
if !p.exists() {
fs::create_dir_all(&p).unwrap();
}
}
let mut outfile = fs::File::create(&outpath).unwrap();
io::copy(&mut file, &mut outfile).unwrap();
}
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = file.unix_mode() {
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
}
}
}
}
espanso_target
}
}

View File

@ -14,9 +14,7 @@ pub trait UIManager {
// MAC IMPLEMENTATION
#[cfg(target_os = "macos")]
pub fn get_uimanager() -> impl UIManager {
let manager = macos::MacUIManager{};
manager.initialize();
manager
macos::MacUIManager::new()
}
// LINUX IMPLEMENTATION