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*);
|
||||
|
||||
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;
|
||||
interceptor_instance = self;
|
||||
}
|
||||
|
||||
|
||||
int32_t initialize() {
|
||||
int32_t initialize(void * _context_instance) {
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
context_instance = _context_instance;
|
||||
|
||||
/*
|
||||
Open the 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:
|
||||
//printf ("%d %d %s\n", key_code, res, buffer.data());
|
||||
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
|
||||
keypress_callback(interceptor_instance, NULL, 0, 1, key_code);
|
||||
keypress_callback(context_instance, NULL, 0, 1, key_code);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -315,3 +316,35 @@ int32_t get_active_window_class(char * buffer, int32_t size) {
|
|||
|
||||
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>
|
||||
|
||||
extern void * context_instance;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
@ -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);
|
||||
|
||||
extern KeypressCallback keypress_callback;
|
||||
extern void * interceptor_instance;
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
* Return the active windows's executable path
|
||||
*/
|
||||
extern "C" int32_t get_active_window_executable(char * buffer, int32_t size);
|
||||
|
||||
#endif //ESPANSO_BRIDGE_H
|
||||
|
|
|
@ -3,17 +3,19 @@ use std::os::raw::{c_void, c_char};
|
|||
#[allow(improper_ctypes)]
|
||||
#[link(name="linuxbridge", kind="static")]
|
||||
extern {
|
||||
pub fn initialize(s: *const c_void);
|
||||
pub fn eventloop();
|
||||
pub fn cleanup();
|
||||
|
||||
// System
|
||||
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_executable(buffer: *mut c_char, size: i32) -> i32;
|
||||
|
||||
// Keyboard
|
||||
pub fn register_keypress_callback(s: *const c_void,
|
||||
cb: extern fn(_self: *mut c_void, *const u8,
|
||||
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u8,
|
||||
i32, i32, i32));
|
||||
pub fn initialize();
|
||||
pub fn eventloop();
|
||||
pub fn cleanup();
|
||||
|
||||
pub fn send_string(string: *const c_char);
|
||||
pub fn delete_string(count: i32);
|
||||
pub fn trigger_paste();
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
use std::process::{Command, Stdio};
|
||||
use std::io::{Write};
|
||||
|
||||
pub struct LinuxClipboardManager {
|
||||
|
||||
}
|
||||
pub struct 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> {
|
||||
let res = Command::new("xclip")
|
||||
.args(&["-o", "-sel", "clip"])
|
||||
|
@ -46,5 +40,9 @@ impl super::ClipboardManager for 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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let manager = linux::LinuxClipboardManager{};
|
||||
manager.initialize();
|
||||
manager
|
||||
linux::LinuxClipboardManager::new()
|
||||
}
|
||||
|
||||
// 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
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn new(send_channel: Sender<Event>) -> Box<dyn Context> { // TODO
|
||||
let manager = linux::LinuxUIManager{};
|
||||
manager.initialize();
|
||||
manager
|
||||
pub fn new(send_channel: Sender<Event>) -> Box<dyn Context> {
|
||||
linux::LinuxContext::new(send_channel)
|
||||
}
|
||||
|
||||
// WINDOWS IMPLEMENTATION
|
||||
|
|
|
@ -1,37 +1,8 @@
|
|||
use std::sync::mpsc;
|
||||
use std::os::raw::{c_void};
|
||||
use std::ffi::CString;
|
||||
use crate::keyboard::{KeyEvent, KeyModifier};
|
||||
use crate::keyboard::KeyModifier::*;
|
||||
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 {
|
||||
}
|
||||
|
||||
|
@ -58,36 +29,3 @@ impl super::KeyboardManager for LinuxKeyboardManager {
|
|||
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 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;
|
||||
|
||||
pub struct LinuxSystemManager {
|
||||
|
||||
}
|
||||
pub struct LinuxSystemManager {}
|
||||
|
||||
impl super::SystemManager for LinuxSystemManager {
|
||||
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> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for LinuxSystemManager {}
|
||||
|
||||
impl LinuxSystemManager {
|
||||
|
||||
pub fn new() -> LinuxSystemManager {
|
||||
LinuxSystemManager{}
|
||||
}
|
||||
}
|
|
@ -13,14 +13,10 @@ pub trait SystemManager {
|
|||
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 {
|
||||
let manager = linux::LinuxSystemManager{};
|
||||
manager.initialize();
|
||||
manager
|
||||
linux::LinuxSystemManager::new()
|
||||
}
|
||||
|
||||
// WINDOWS IMPLEMENTATION
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
use std::process::Command;
|
||||
use super::MenuItem;
|
||||
|
||||
pub struct LinuxUIManager {
|
||||
|
||||
}
|
||||
pub struct 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) {
|
||||
let res = Command::new("notify-send")
|
||||
.args(&["-t", "2000", "espanso", message])
|
||||
|
@ -18,8 +13,16 @@ impl super::UIManager for LinuxUIManager {
|
|||
// TODO: print error log
|
||||
}
|
||||
}
|
||||
|
||||
fn show_menu(&self, menu: Vec<MenuItem>) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_uimanager() -> impl UIManager {
|
||||
let manager = linux::LinuxUIManager{};
|
||||
manager.initialize();
|
||||
manager
|
||||
linux::LinuxUIManager::new()
|
||||
}
|
||||
|
||||
// WINDOWS IMPLEMENTATION
|
||||
|
|
Loading…
Reference in New Issue
Block a user