Refactor Linux events and add executable filter detection
This commit is contained in:
parent
3e976784b8
commit
d0270eb99b
|
@ -59,17 +59,18 @@ xdo_t * xdo_context;
|
||||||
void event_callback (XPointer, XRecordInterceptData*);
|
void event_callback (XPointer, XRecordInterceptData*);
|
||||||
|
|
||||||
KeypressCallback keypress_callback;
|
KeypressCallback keypress_callback;
|
||||||
void * interceptor_instance;
|
void * context_instance;
|
||||||
|
|
||||||
void register_keypress_callback(void * self, KeypressCallback callback) {
|
void register_keypress_callback(KeypressCallback callback) {
|
||||||
keypress_callback = callback;
|
keypress_callback = callback;
|
||||||
interceptor_instance = self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32_t initialize() {
|
int32_t initialize(void * _context_instance) {
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
|
context_instance = _context_instance;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Open the connections to the X server.
|
Open the connections to the X server.
|
||||||
RE recommends to open 2 connections to the X server:
|
RE recommends to open 2 connections to the X server:
|
||||||
|
@ -185,9 +186,9 @@ void event_callback(XPointer p, XRecordInterceptData *hook)
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
//printf ("%d %d %s\n", key_code, res, buffer.data());
|
//printf ("%d %d %s\n", key_code, res, buffer.data());
|
||||||
if (res > 0 && key_code != 22) { // Printable character, but not backspace
|
if (res > 0 && key_code != 22) { // Printable character, but not backspace
|
||||||
keypress_callback(interceptor_instance, buffer.data(), buffer.size(), 0, key_code);
|
keypress_callback(context_instance, buffer.data(), buffer.size(), 0, key_code);
|
||||||
}else{ // Modifier key
|
}else{ // Modifier key
|
||||||
keypress_callback(interceptor_instance, NULL, 0, 1, key_code);
|
keypress_callback(context_instance, NULL, 0, 1, key_code);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -315,3 +316,35 @@ int32_t get_active_window_class(char * buffer, int32_t size) {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t get_active_window_executable(char *buffer, int32_t size) {
|
||||||
|
Display *disp = XOpenDisplay(NULL);
|
||||||
|
|
||||||
|
if (!disp) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active window
|
||||||
|
Window win;
|
||||||
|
int revert_to_return;
|
||||||
|
XGetInputFocus(disp, &win, &revert_to_return);
|
||||||
|
|
||||||
|
// Get the window process PID
|
||||||
|
char *pid_raw = (char*)get_property(disp,win, XA_CARDINAL, "_NET_WM_PID", NULL);
|
||||||
|
if (pid_raw == NULL) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pid = pid_raw[0] | pid_raw[1] << 8 | pid_raw[2] << 16 | pid_raw[3] << 24;
|
||||||
|
|
||||||
|
// Get the executable path from it
|
||||||
|
char proc_path[250];
|
||||||
|
snprintf(proc_path, 250, "/proc/%d/exe", pid);
|
||||||
|
|
||||||
|
readlink(proc_path, buffer, size);
|
||||||
|
|
||||||
|
XFree(pid_raw);
|
||||||
|
XCloseDisplay(disp);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern void * context_instance;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the X11 context and parameters
|
* Initialize the X11 context and parameters
|
||||||
*/
|
*/
|
||||||
extern "C" int32_t initialize();
|
extern "C" int32_t initialize(void * context_instance);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the event loop indefinitely. Blocking call.
|
* Start the event loop indefinitely. Blocking call.
|
||||||
|
@ -25,12 +27,11 @@ extern "C" void cleanup();
|
||||||
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t is_modifier, int32_t key_code);
|
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t is_modifier, int32_t key_code);
|
||||||
|
|
||||||
extern KeypressCallback keypress_callback;
|
extern KeypressCallback keypress_callback;
|
||||||
extern void * interceptor_instance;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register the callback that will be called when a keypress was made
|
* Register the callback that will be called when a keypress was made
|
||||||
*/
|
*/
|
||||||
extern "C" void register_keypress_callback(void *self, KeypressCallback callback);
|
extern "C" void register_keypress_callback(KeypressCallback callback);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Type the given string by simulating Key Presses
|
* Type the given string by simulating Key Presses
|
||||||
|
@ -65,4 +66,9 @@ extern "C" int32_t get_active_window_name(char * buffer, int32_t size);
|
||||||
*/
|
*/
|
||||||
extern "C" int32_t get_active_window_class(char * buffer, int32_t size);
|
extern "C" int32_t get_active_window_class(char * buffer, int32_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the active windows's executable path
|
||||||
|
*/
|
||||||
|
extern "C" int32_t get_active_window_executable(char * buffer, int32_t size);
|
||||||
|
|
||||||
#endif //ESPANSO_BRIDGE_H
|
#endif //ESPANSO_BRIDGE_H
|
||||||
|
|
|
@ -3,17 +3,19 @@ use std::os::raw::{c_void, c_char};
|
||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
#[link(name="linuxbridge", kind="static")]
|
#[link(name="linuxbridge", kind="static")]
|
||||||
extern {
|
extern {
|
||||||
|
pub fn initialize(s: *const c_void);
|
||||||
|
pub fn eventloop();
|
||||||
|
pub fn cleanup();
|
||||||
|
|
||||||
// System
|
// System
|
||||||
pub fn get_active_window_name(buffer: *mut c_char, size: i32) -> i32;
|
pub fn get_active_window_name(buffer: *mut c_char, size: i32) -> i32;
|
||||||
pub fn get_active_window_class(buffer: *mut c_char, size: i32) -> i32;
|
pub fn get_active_window_class(buffer: *mut c_char, size: i32) -> i32;
|
||||||
|
pub fn get_active_window_executable(buffer: *mut c_char, size: i32) -> i32;
|
||||||
|
|
||||||
// Keyboard
|
// Keyboard
|
||||||
pub fn register_keypress_callback(s: *const c_void,
|
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u8,
|
||||||
cb: extern fn(_self: *mut c_void, *const u8,
|
|
||||||
i32, i32, i32));
|
i32, i32, i32));
|
||||||
pub fn initialize();
|
|
||||||
pub fn eventloop();
|
|
||||||
pub fn cleanup();
|
|
||||||
pub fn send_string(string: *const c_char);
|
pub fn send_string(string: *const c_char);
|
||||||
pub fn delete_string(count: i32);
|
pub fn delete_string(count: i32);
|
||||||
pub fn trigger_paste();
|
pub fn trigger_paste();
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::io::{Write};
|
use std::io::{Write};
|
||||||
|
|
||||||
pub struct LinuxClipboardManager {
|
pub struct LinuxClipboardManager {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::ClipboardManager for LinuxClipboardManager {
|
impl super::ClipboardManager for LinuxClipboardManager {
|
||||||
fn initialize(&self) {
|
|
||||||
// TODO: check if xclip is present and log an error otherwise.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_clipboard(&self) -> Option<String> {
|
fn get_clipboard(&self) -> Option<String> {
|
||||||
let res = Command::new("xclip")
|
let res = Command::new("xclip")
|
||||||
.args(&["-o", "-sel", "clip"])
|
.args(&["-o", "-sel", "clip"])
|
||||||
|
@ -46,5 +40,9 @@ impl super::ClipboardManager for LinuxClipboardManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinuxClipboardManager {
|
impl LinuxClipboardManager {
|
||||||
|
pub fn new() -> LinuxClipboardManager {
|
||||||
|
// TODO: check if xclip is present and log an error otherwise.
|
||||||
|
|
||||||
|
LinuxClipboardManager{}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,14 +12,10 @@ pub trait ClipboardManager {
|
||||||
fn set_clipboard(&self, payload: &str);
|
fn set_clipboard(&self, payload: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change windows and linux implementations to avoid initialize() call and use constructor instead
|
|
||||||
|
|
||||||
// LINUX IMPLEMENTATION
|
// LINUX IMPLEMENTATION
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_manager() -> impl ClipboardManager {
|
pub fn get_manager() -> impl ClipboardManager {
|
||||||
let manager = linux::LinuxClipboardManager{};
|
linux::LinuxClipboardManager::new()
|
||||||
manager.initialize();
|
|
||||||
manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WINDOWS IMPLEMENTATION
|
// WINDOWS IMPLEMENTATION
|
||||||
|
|
77
src/context/linux.rs
Normal file
77
src/context/linux.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use crate::event::*;
|
||||||
|
use crate::event::KeyModifier::*;
|
||||||
|
use crate::bridge::linux::*;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LinuxContext {
|
||||||
|
pub send_channel: Sender<Event>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinuxContext {
|
||||||
|
pub fn new(send_channel: Sender<Event>) -> Box<LinuxContext> {
|
||||||
|
let context = Box::new(LinuxContext {
|
||||||
|
send_channel,
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = &*context as *const LinuxContext as *const c_void;
|
||||||
|
|
||||||
|
register_keypress_callback(keypress_callback);
|
||||||
|
|
||||||
|
initialize(context_ptr); // TODO: check initialization return codes
|
||||||
|
}
|
||||||
|
|
||||||
|
context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Context for LinuxContext {
|
||||||
|
fn eventloop(&self) {
|
||||||
|
unsafe {
|
||||||
|
eventloop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LinuxContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { cleanup(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native bridge code
|
||||||
|
|
||||||
|
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 LinuxContext;
|
||||||
|
|
||||||
|
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);
|
||||||
|
let r = String::from_utf8_lossy(buffer).chars().nth(0);
|
||||||
|
|
||||||
|
// Send the char through the channel
|
||||||
|
if let Some(c) = r {
|
||||||
|
let event = Event::Key(KeyEvent::Char(c));
|
||||||
|
(*_self).send_channel.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}else{ // Modifier event
|
||||||
|
let modifier: Option<KeyModifier> = match key_code {
|
||||||
|
133 => Some(META),
|
||||||
|
50 => Some(SHIFT),
|
||||||
|
64 => Some(ALT),
|
||||||
|
37 => Some(CTRL),
|
||||||
|
22 => Some(BACKSPACE),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(modifier) = modifier {
|
||||||
|
let event = Event::Key(KeyEvent::Modifier(modifier));
|
||||||
|
(*_self).send_channel.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,10 +22,8 @@ pub fn new(send_channel: Sender<Event>) -> Box<dyn Context> {
|
||||||
|
|
||||||
// LINUX IMPLEMENTATION
|
// LINUX IMPLEMENTATION
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn new(send_channel: Sender<Event>) -> Box<dyn Context> { // TODO
|
pub fn new(send_channel: Sender<Event>) -> Box<dyn Context> {
|
||||||
let manager = linux::LinuxUIManager{};
|
linux::LinuxContext::new(send_channel)
|
||||||
manager.initialize();
|
|
||||||
manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WINDOWS IMPLEMENTATION
|
// WINDOWS IMPLEMENTATION
|
||||||
|
|
|
@ -1,37 +1,8 @@
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::os::raw::{c_void};
|
use std::os::raw::{c_void};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use crate::keyboard::{KeyEvent, KeyModifier};
|
|
||||||
use crate::keyboard::KeyModifier::*;
|
|
||||||
use crate::bridge::linux::*;
|
use crate::bridge::linux::*;
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct LinuxKeyboardInterceptor {
|
|
||||||
pub sender: mpsc::Sender<KeyEvent>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::KeyboardInterceptor for LinuxKeyboardInterceptor {
|
|
||||||
fn initialize(&self) {
|
|
||||||
unsafe {
|
|
||||||
let self_ptr = self as *const LinuxKeyboardInterceptor as *const c_void;
|
|
||||||
register_keypress_callback( self_ptr,keypress_callback);
|
|
||||||
initialize(); // TODO: check initialization return codes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start(&self) {
|
|
||||||
unsafe {
|
|
||||||
eventloop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LinuxKeyboardInterceptor {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { cleanup(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LinuxKeyboardManager {
|
pub struct LinuxKeyboardManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,36 +29,3 @@ impl super::KeyboardManager for LinuxKeyboardManager {
|
||||||
unsafe {delete_string(count)}
|
unsafe {delete_string(count)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native bridge code
|
|
||||||
|
|
||||||
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 LinuxKeyboardInterceptor;
|
|
||||||
|
|
||||||
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);
|
|
||||||
let r = String::from_utf8_lossy(buffer).chars().nth(0);
|
|
||||||
|
|
||||||
// Send the char through the channel
|
|
||||||
if let Some(c) = r {
|
|
||||||
(*_self).sender.send(KeyEvent::Char(c)).unwrap();
|
|
||||||
}
|
|
||||||
}else{ // Modifier event
|
|
||||||
let modifier: Option<KeyModifier> = match key_code {
|
|
||||||
133 => Some(META),
|
|
||||||
50 => Some(SHIFT),
|
|
||||||
64 => Some(ALT),
|
|
||||||
37 => Some(CTRL),
|
|
||||||
22 => Some(BACKSPACE),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(modifier) = modifier {
|
|
||||||
(*_self).sender.send(KeyEvent::Modifier(modifier)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,9 @@
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
use crate::bridge::linux::{get_active_window_name, get_active_window_class};
|
use crate::bridge::linux::{get_active_window_name, get_active_window_class, get_active_window_executable};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
pub struct LinuxSystemManager {
|
pub struct LinuxSystemManager {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::SystemManager for LinuxSystemManager {
|
impl super::SystemManager for LinuxSystemManager {
|
||||||
fn get_current_window_title(&self) -> Option<String> {
|
fn get_current_window_title(&self) -> Option<String> {
|
||||||
|
@ -45,12 +43,26 @@ impl super::SystemManager for LinuxSystemManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_window_executable(&self) -> Option<String> {
|
fn get_current_window_executable(&self) -> Option<String> {
|
||||||
unimplemented!()
|
unsafe {
|
||||||
|
let mut buffer : [c_char; 100] = [0; 100];
|
||||||
|
let res = get_active_window_executable(buffer.as_mut_ptr(), buffer.len() as i32);
|
||||||
|
|
||||||
|
if res > 0 {
|
||||||
|
let c_string = CStr::from_ptr(buffer.as_ptr());
|
||||||
|
|
||||||
|
let string = c_string.to_str();
|
||||||
|
if let Ok(string) = string {
|
||||||
|
return Some((*string).to_owned());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for LinuxSystemManager {}
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LinuxSystemManager {
|
impl LinuxSystemManager {
|
||||||
|
pub fn new() -> LinuxSystemManager {
|
||||||
|
LinuxSystemManager{}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -13,14 +13,10 @@ pub trait SystemManager {
|
||||||
fn get_current_window_executable(&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
|
// LINUX IMPLEMENTATION
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_manager() -> impl SystemManager {
|
pub fn get_manager() -> impl SystemManager {
|
||||||
let manager = linux::LinuxSystemManager{};
|
linux::LinuxSystemManager::new()
|
||||||
manager.initialize();
|
|
||||||
manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WINDOWS IMPLEMENTATION
|
// WINDOWS IMPLEMENTATION
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use super::MenuItem;
|
||||||
|
|
||||||
pub struct LinuxUIManager {
|
pub struct LinuxUIManager {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::UIManager for LinuxUIManager {
|
impl super::UIManager for LinuxUIManager {
|
||||||
fn initialize(&self) {
|
|
||||||
// TODO: check if notify-send is present and log an error otherwise.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify(&self, message: &str) {
|
fn notify(&self, message: &str) {
|
||||||
let res = Command::new("notify-send")
|
let res = Command::new("notify-send")
|
||||||
.args(&["-t", "2000", "espanso", message])
|
.args(&["-t", "2000", "espanso", message])
|
||||||
|
@ -18,8 +13,16 @@ impl super::UIManager for LinuxUIManager {
|
||||||
// TODO: print error log
|
// TODO: print error log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_menu(&self, menu: Vec<MenuItem>) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinuxUIManager {
|
impl LinuxUIManager {
|
||||||
|
pub fn new() -> LinuxUIManager {
|
||||||
|
// TODO: check if notify-send is present and log an error otherwise.
|
||||||
|
|
||||||
|
LinuxUIManager{}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -32,9 +32,7 @@ pub fn get_uimanager() -> impl UIManager {
|
||||||
// LINUX IMPLEMENTATION
|
// LINUX IMPLEMENTATION
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_uimanager() -> impl UIManager {
|
pub fn get_uimanager() -> impl UIManager {
|
||||||
let manager = linux::LinuxUIManager{};
|
linux::LinuxUIManager::new()
|
||||||
manager.initialize();
|
|
||||||
manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WINDOWS IMPLEMENTATION
|
// WINDOWS IMPLEMENTATION
|
||||||
|
|
Loading…
Reference in New Issue
Block a user