Implemented "String" playing! Should now be compatible with xmacro yay
This commit is contained in:
parent
c695d803e0
commit
e28a69d29e
5 changed files with 347 additions and 24 deletions
|
@ -9,11 +9,11 @@ This program is inspired by [**xmacro**](https://github.com/Ortega-Dan/xmacroInc
|
||||||
<details>
|
<details>
|
||||||
<summary><h2>:pen_ballpoint: TODOs :notepad_spiral:</h2></summary>
|
<summary><h2>:pen_ballpoint: TODOs :notepad_spiral:</h2></summary>
|
||||||
|
|
||||||
- [ ] Playing macros (xmacro like)
|
- [x] Playing macros (xmacro like)
|
||||||
- [x] Delay support
|
- [x] Delay support
|
||||||
- [x] KeySym/KeyCode/KeyStr action support
|
- [x] KeySym/KeyCode/KeyStr action support
|
||||||
- [x] MotionNotify and button support
|
- [x] MotionNotify and button support
|
||||||
- [ ] String typing support (Not too high priority, but I'll add it some time probably)
|
- [x] String typing support (Not too high priority, but I'll add it some time probably)
|
||||||
- [x] ExecBlock/ExecNoBlock support (not high priority)
|
- [x] ExecBlock/ExecNoBlock support (not high priority)
|
||||||
- [x] ExecBlock
|
- [x] ExecBlock
|
||||||
- [x] ExecNoBlock
|
- [x] ExecNoBlock
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use easymacros::x11_safe_wrapper::{Keysym, XDisplay};
|
use easymacros::x11_safe_wrapper::{Keysym, string_to_keysym, XDisplay};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::process::{Command, exit};
|
use std::process::{Command, exit};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
|
use x11::keysym::XK_Shift_L;
|
||||||
|
use easymacros::chartbl::CHARTBL;
|
||||||
|
|
||||||
/// Macro player module for easymacros. It's partially compatible with xmacro macros, with aim for full compatibility.
|
/// Macro player module for easymacros. It's partially compatible with xmacro macros, with aim for full compatibility.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -64,51 +66,84 @@ fn get_remote(display_name: Option<String>) -> XDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_instruction(instruction: &str, dpy: &XDisplay) {
|
fn run_instruction(instruction: &str, dpy: &XDisplay) {
|
||||||
let instruction: Vec<&str> = instruction.split(' ').collect();
|
let instruction_split: Vec<&str> = instruction.split(' ').collect();
|
||||||
|
|
||||||
match instruction[0] {
|
match instruction_split[0] {
|
||||||
"Delay" => thread::sleep(Duration::from_millis(instruction[1].parse().unwrap())),
|
"Delay" => thread::sleep(Duration::from_millis(instruction_split[1].parse().unwrap())),
|
||||||
"ButtonPress" => dpy.send_fake_buttonpress(instruction[1].parse().unwrap()),
|
"ButtonPress" => dpy.send_fake_buttonpress(instruction_split[1].parse().unwrap()),
|
||||||
"ButtonRelease" => dpy.send_fake_buttonrelease(instruction[1].parse().unwrap()),
|
"ButtonRelease" => dpy.send_fake_buttonrelease(instruction_split[1].parse().unwrap()),
|
||||||
"MotionNotify" => dpy
|
"MotionNotify" => dpy
|
||||||
.send_fake_motion_event(instruction[1].parse().unwrap(), instruction[2].parse().unwrap()),
|
.send_fake_motion_event(instruction_split[1].parse().unwrap(), instruction_split[2].parse().unwrap()),
|
||||||
"KeyCodePress" => dpy.send_fake_keypress_from_code(instruction[1].parse().unwrap()),
|
"KeyCodePress" => dpy.send_fake_keypress_from_code(instruction_split[1].parse().unwrap()),
|
||||||
"KeyCodeRelease" => dpy.send_fake_keyrelease_from_code(instruction[1].parse().unwrap()),
|
"KeyCodeRelease" => dpy.send_fake_keyrelease_from_code(instruction_split[1].parse().unwrap()),
|
||||||
"KeySymPress" => dpy.send_fake_keypress_from_keysym(instruction[1].parse().unwrap()),
|
"KeySymPress" => dpy.send_fake_keypress_from_keysym(instruction_split[1].parse().unwrap()),
|
||||||
"KeySymRelease" => {
|
"KeySymRelease" => {
|
||||||
dpy.send_fake_keyrelease_from_keysym(instruction[1].parse().unwrap())
|
dpy.send_fake_keyrelease_from_keysym(instruction_split[1].parse().unwrap())
|
||||||
}
|
}
|
||||||
"KeySym" => {
|
"KeySym" => {
|
||||||
let key: Keysym = instruction[1].parse().unwrap();
|
let key: Keysym = instruction_split[1].parse().unwrap();
|
||||||
dpy.send_fake_keypress_from_keysym(key);
|
dpy.send_fake_keypress_from_keysym(key);
|
||||||
dpy.send_fake_keyrelease_from_keysym(key);
|
dpy.send_fake_keyrelease_from_keysym(key);
|
||||||
}
|
}
|
||||||
"KeyStrPress" => {
|
"KeyStrPress" => {
|
||||||
dpy.send_fake_keypress_from_string(CString::new(instruction[1]).unwrap().as_bytes())
|
dpy.send_fake_keypress_from_string(CString::new(instruction_split[1]).unwrap().as_bytes())
|
||||||
}
|
}
|
||||||
"KeyStrRelease" => dpy
|
"KeyStrRelease" => dpy
|
||||||
.send_fake_keyrelease_from_string(CString::new(instruction[1]).unwrap().as_bytes()),
|
.send_fake_keyrelease_from_string(CString::new(instruction_split[1]).unwrap().as_bytes()),
|
||||||
"KeyStr" => {
|
"KeyStr" => {
|
||||||
let keystring = CString::new(instruction[1]).unwrap();
|
let keystring = CString::new(instruction_split[1]).unwrap();
|
||||||
dpy.send_fake_keypress_from_string(keystring.as_bytes());
|
dpy.send_fake_keypress_from_string(keystring.as_bytes());
|
||||||
dpy.send_fake_keyrelease_from_string(keystring.as_bytes());
|
dpy.send_fake_keyrelease_from_string(keystring.as_bytes());
|
||||||
}
|
}
|
||||||
"String" => {
|
"String" => {
|
||||||
println!("Strings are currently not supported.");
|
for c in instruction["String".len()+1..].chars() {
|
||||||
|
send_char(dpy, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ExecBlock" | "ExecNoBlock" => {
|
"ExecBlock" | "ExecNoBlock" => {
|
||||||
let mut command = Command::new(instruction[1]);
|
let mut command = Command::new(instruction_split[1]);
|
||||||
for arg in &instruction[2..] {
|
for arg in &instruction_split[2..] {
|
||||||
command.arg(arg);
|
command.arg(arg);
|
||||||
}
|
}
|
||||||
if instruction[0] == "ExecBlock" {
|
if instruction_split[0] == "ExecBlock" {
|
||||||
command.status();
|
command.status();
|
||||||
} else {
|
} else {
|
||||||
command.spawn();
|
command.spawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c => {
|
c => {
|
||||||
panic!("Unknown command {:?}", instruction)
|
panic!("Unknown command {:?}", instruction_split)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_char(dpy: &XDisplay, c: char) {
|
||||||
|
let keysym = string_to_keysym(CHARTBL[c as usize].as_ref());
|
||||||
|
let keycode = dpy.keysym_to_keycode(keysym);
|
||||||
|
|
||||||
|
if keycode == 0 {
|
||||||
|
eprintln!("No keycode found for character '{}'", c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let map_ks = dpy.get_keyboard_mapping(keycode, 1);
|
||||||
|
if unsafe { map_ks[0] } == 0 {
|
||||||
|
eprintln!("XGetKeyboardMapping failed (keycode: {})", keycode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ks_lower, ks_upper) = dpy.convert_case(keysym);
|
||||||
|
|
||||||
|
let mut shift_needed = true;
|
||||||
|
if keysym == map_ks[0] && (keysym == ks_lower && keysym == ks_upper) {
|
||||||
|
shift_needed = false;
|
||||||
|
}
|
||||||
|
if keysym == ks_lower && keysym != ks_upper {
|
||||||
|
shift_needed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if shift_needed { dpy.send_fake_keypress_from_keysym(XK_Shift_L as Keysym); }
|
||||||
|
dpy.send_fake_keypress_from_code(keycode);
|
||||||
|
dpy.send_fake_keyrelease_from_code(keycode);
|
||||||
|
if shift_needed { dpy.send_fake_keyrelease_from_keysym(XK_Shift_L as Keysym); }
|
||||||
|
}
|
259
src/chartbl.rs
Normal file
259
src/chartbl.rs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
// the list is copied from https://github.com/Ortega-Dan/xmacroIncludingDelayCapturing/blob/1767fd310227dd13ae488b7d2821cc4bbf3847e0/chartbl.h#L27
|
||||||
|
pub const CHARTBL: [&str; 256] = [
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"BackSpace\0",
|
||||||
|
"Tab\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"Return\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"Escape\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"space\0",
|
||||||
|
"exclam\0",
|
||||||
|
"quotedbl\0",
|
||||||
|
"numbersign\0",
|
||||||
|
"dollar\0",
|
||||||
|
"percent\0",
|
||||||
|
"ampersand\0",
|
||||||
|
"apostrophe\0",
|
||||||
|
"parenleft\0",
|
||||||
|
"parenright\0",
|
||||||
|
"asterisk\0",
|
||||||
|
"plus\0",
|
||||||
|
"comma\0",
|
||||||
|
"minus\0",
|
||||||
|
"period\0",
|
||||||
|
"slash\0",
|
||||||
|
"0\0",
|
||||||
|
"1\0",
|
||||||
|
"2\0",
|
||||||
|
"3\0",
|
||||||
|
"4\0",
|
||||||
|
"5\0",
|
||||||
|
"6\0",
|
||||||
|
"7\0",
|
||||||
|
"8\0",
|
||||||
|
"9\0",
|
||||||
|
"colon\0",
|
||||||
|
"semicolon\0",
|
||||||
|
"less\0",
|
||||||
|
"equal\0",
|
||||||
|
"greater\0",
|
||||||
|
"question\0",
|
||||||
|
"at\0",
|
||||||
|
"A\0",
|
||||||
|
"B\0",
|
||||||
|
"C\0",
|
||||||
|
"D\0",
|
||||||
|
"E\0",
|
||||||
|
"F\0",
|
||||||
|
"G\0",
|
||||||
|
"H\0",
|
||||||
|
"I\0",
|
||||||
|
"J\0",
|
||||||
|
"K\0",
|
||||||
|
"L\0",
|
||||||
|
"M\0",
|
||||||
|
"N\0",
|
||||||
|
"O\0",
|
||||||
|
"P\0",
|
||||||
|
"Q\0",
|
||||||
|
"R\0",
|
||||||
|
"S\0",
|
||||||
|
"T\0",
|
||||||
|
"U\0",
|
||||||
|
"V\0",
|
||||||
|
"W\0",
|
||||||
|
"X\0",
|
||||||
|
"Y\0",
|
||||||
|
"Z\0",
|
||||||
|
"bracketleft\0",
|
||||||
|
"backslash\0",
|
||||||
|
"bracketright\0",
|
||||||
|
"asciicircum\0",
|
||||||
|
"underscore\0",
|
||||||
|
"grave\0",
|
||||||
|
"a\0",
|
||||||
|
"b\0",
|
||||||
|
"c\0",
|
||||||
|
"d\0",
|
||||||
|
"e\0",
|
||||||
|
"f\0",
|
||||||
|
"g\0",
|
||||||
|
"h\0",
|
||||||
|
"i\0",
|
||||||
|
"j\0",
|
||||||
|
"k\0",
|
||||||
|
"l\0",
|
||||||
|
"m\0",
|
||||||
|
"n\0",
|
||||||
|
"o\0",
|
||||||
|
"p\0",
|
||||||
|
"q\0",
|
||||||
|
"r\0",
|
||||||
|
"s\0",
|
||||||
|
"t\0",
|
||||||
|
"u\0",
|
||||||
|
"v\0",
|
||||||
|
"w\0",
|
||||||
|
"x\0",
|
||||||
|
"y\0",
|
||||||
|
"z\0",
|
||||||
|
"braceleft\0",
|
||||||
|
"bar\0",
|
||||||
|
"braceright\0",
|
||||||
|
"asciitilde\0",
|
||||||
|
"Delete\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"\0",
|
||||||
|
"nobreakspace\0",
|
||||||
|
"exclamdown\0",
|
||||||
|
"cent\0",
|
||||||
|
"sterling\0",
|
||||||
|
"currency\0",
|
||||||
|
"yen\0",
|
||||||
|
"brokenbar\0",
|
||||||
|
"section\0",
|
||||||
|
"diaeresis\0",
|
||||||
|
"copyright\0",
|
||||||
|
"ordfeminine\0",
|
||||||
|
"guillemotleft\0",
|
||||||
|
"notsign\0",
|
||||||
|
"hyphen\0",
|
||||||
|
"registered\0",
|
||||||
|
"macron\0",
|
||||||
|
"degree\0",
|
||||||
|
"plusminus\0",
|
||||||
|
"twosuperior\0",
|
||||||
|
"threesuperior\0",
|
||||||
|
"acute\0",
|
||||||
|
"mu\0",
|
||||||
|
"paragraph\0",
|
||||||
|
"periodcentered\0",
|
||||||
|
"cedilla\0",
|
||||||
|
"onesuperior\0",
|
||||||
|
"masculine\0",
|
||||||
|
"guillemotright\0",
|
||||||
|
"onequarter\0",
|
||||||
|
"onehalf\0",
|
||||||
|
"threequarters\0",
|
||||||
|
"questiondown\0",
|
||||||
|
"Agrave\0",
|
||||||
|
"Aacute\0",
|
||||||
|
"Acircumflex\0",
|
||||||
|
"Atilde\0",
|
||||||
|
"Adiaeresis\0",
|
||||||
|
"Aring\0",
|
||||||
|
"AE\0",
|
||||||
|
"Ccedilla\0",
|
||||||
|
"Egrave\0",
|
||||||
|
"Eacute\0",
|
||||||
|
"Ecircumflex\0",
|
||||||
|
"Ediaeresis\0",
|
||||||
|
"Igrave\0",
|
||||||
|
"Iacute\0",
|
||||||
|
"Icircumflex\0",
|
||||||
|
"Idiaeresis\0",
|
||||||
|
"ETH\0",
|
||||||
|
"Ntilde\0",
|
||||||
|
"Ograve\0",
|
||||||
|
"Oacute\0",
|
||||||
|
"Ocircumflex\0",
|
||||||
|
"Otilde\0",
|
||||||
|
"Odiaeresis\0",
|
||||||
|
"multiply\0",
|
||||||
|
"Ooblique\0",
|
||||||
|
"Ugrave\0",
|
||||||
|
"Uacute\0",
|
||||||
|
"Ucircumflex\0",
|
||||||
|
"Udiaeresis\0",
|
||||||
|
"Yacute\0",
|
||||||
|
"THORN\0",
|
||||||
|
"ssharp\0",
|
||||||
|
"agrave\0",
|
||||||
|
"aacute\0",
|
||||||
|
"acircumflex\0",
|
||||||
|
"atilde\0",
|
||||||
|
"adiaeresis\0",
|
||||||
|
"aring\0",
|
||||||
|
"ae\0",
|
||||||
|
"ccedilla\0",
|
||||||
|
"egrave\0",
|
||||||
|
"eacute\0",
|
||||||
|
"ecircumflex\0",
|
||||||
|
"ediaeresis\0",
|
||||||
|
"igrave\0",
|
||||||
|
"iacute\0",
|
||||||
|
"icircumflex\0",
|
||||||
|
"idiaeresis\0",
|
||||||
|
"eth\0",
|
||||||
|
"ntilde\0",
|
||||||
|
"ograve\0",
|
||||||
|
"oacute\0",
|
||||||
|
"ocircumflex\0",
|
||||||
|
"otilde\0",
|
||||||
|
"odiaeresis\0",
|
||||||
|
"division\0",
|
||||||
|
"oslash\0",
|
||||||
|
"ugrave\0",
|
||||||
|
"uacute\0",
|
||||||
|
"ucircumflex\0",
|
||||||
|
"udiaeresis\0",
|
||||||
|
"yacute\0",
|
||||||
|
"thorn\0",
|
||||||
|
"ydiaeresis\0",
|
||||||
|
];
|
|
@ -1,6 +1,7 @@
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
pub mod x11_safe_wrapper;
|
pub mod x11_safe_wrapper;
|
||||||
|
pub mod chartbl;
|
||||||
|
|
||||||
pub enum XMacroInstructions {
|
pub enum XMacroInstructions {
|
||||||
Delay,
|
Delay,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::env;
|
use std::{env, slice};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ulong};
|
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ulong};
|
||||||
use x11::xlib::{Display, GenericEvent, KeyPress, MotionNotify, Time, Window, XAllowEvents, XCloseDisplay, XDefaultScreen, XEvent, XFlush, XGrabKeyboard, XKeycodeToKeysym, XKeysymToKeycode, XKeysymToString, XOpenDisplay, XQueryPointer, XRootWindow, XStringToKeysym, XSync, XUngrabKeyboard, XUngrabPointer, XWindowEvent};
|
use x11::xlib::{Display, GenericEvent, KeyPress, MotionNotify, Time, Window, XAllowEvents, XCloseDisplay, XConvertCase, XDefaultScreen, XEvent, XFlush, XGetKeyboardMapping, XGrabKeyboard, XKeycodeToKeysym, XKeysymToKeycode, XKeysymToString, XOpenDisplay, XQueryPointer, XRootWindow, XStringToKeysym, XSync, XUngrabKeyboard, XUngrabPointer, XWindowEvent};
|
||||||
use x11::xrecord::{XRecordAllClients, XRecordClientSpec, XRecordContext, XRecordCreateContext, XRecordDisableContext, XRecordEnableContext, XRecordEnableContextAsync, XRecordFreeContext, XRecordInterceptData, XRecordProcessReplies, XRecordQueryVersion, XRecordRange};
|
use x11::xrecord::{XRecordAllClients, XRecordClientSpec, XRecordContext, XRecordCreateContext, XRecordDisableContext, XRecordEnableContext, XRecordEnableContextAsync, XRecordFreeContext, XRecordInterceptData, XRecordProcessReplies, XRecordQueryVersion, XRecordRange};
|
||||||
use x11::xtest::{
|
use x11::xtest::{
|
||||||
XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl,
|
XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl,
|
||||||
XTestQueryExtension,
|
XTestQueryExtension,
|
||||||
};
|
};
|
||||||
|
use crate::XMacroInstructions::KeySym;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct XDisplay {
|
pub struct XDisplay {
|
||||||
|
@ -76,6 +77,33 @@ impl XDisplay {
|
||||||
unsafe { XRootWindow(self.ptr, screen_nr) }
|
unsafe { XRootWindow(self.ptr, screen_nr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_keyboard_mapping(&self, keycode: Keycode, keycode_count: i32) -> &[Keysym] {
|
||||||
|
let mut keysyms_per_keycode = 0;
|
||||||
|
let r = unsafe {
|
||||||
|
let ptr = XGetKeyboardMapping(
|
||||||
|
self.ptr,
|
||||||
|
keycode as c_uchar,
|
||||||
|
keycode_count,
|
||||||
|
&mut keysyms_per_keycode
|
||||||
|
);
|
||||||
|
|
||||||
|
slice::from_raw_parts::<Keysym>(ptr, keysyms_per_keycode as usize)
|
||||||
|
};
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_case(&self, keysym: Keysym) -> (Keysym, Keysym) {
|
||||||
|
let mut keysym_lower: Keysym = Keysym::default();
|
||||||
|
let mut keysym_upper: Keysym = Keysym::default();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
XConvertCase(keysym, &mut keysym_lower, &mut keysym_upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
(keysym_lower, keysym_upper)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn keysym_to_keycode(&self, keysym: Keysym) -> Keycode {
|
pub fn keysym_to_keycode(&self, keysym: Keysym) -> Keycode {
|
||||||
unsafe { XKeysymToKeycode(self.ptr, keysym) as Keycode }
|
unsafe { XKeysymToKeycode(self.ptr, keysym) as Keycode }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue