added basic xmacro support! now can record macros!
This commit is contained in:
parent
8d47ad6475
commit
0facd53b48
5 changed files with 98 additions and 28 deletions
|
@ -8,4 +8,4 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
x11 = { version = "2.19.1", features = ["xlib", "xtest"] }
|
x11 = { version = "2.19.1", features = ["xlib", "xtest"] }
|
||||||
x11-keysymdef = "0.2.0"
|
x11-keysymdef = "0.2.0"
|
||||||
clap = { version = "3.1.18", features = ["derive"] }
|
clap = { version = "3.2.4", features = ["derive"] }
|
15
README.md
15
README.md
|
@ -1,3 +1,18 @@
|
||||||
# easymacros
|
# easymacros
|
||||||
|
|
||||||
This program is inspired by xmacro, however it isn't xmacro.
|
This program is inspired by xmacro, however it isn't xmacro.
|
||||||
|
|
||||||
|
## TODOs
|
||||||
|
|
||||||
|
- [ ] Playing macros (xmacro)
|
||||||
|
- [x] Delay support
|
||||||
|
- [x] KeySym/KeyCode/KeyStr action support
|
||||||
|
- [x] MotionNotify and button support
|
||||||
|
- [ ] String typing support
|
||||||
|
- [ ] Screenshot support
|
||||||
|
- [ ] ExecBlock/ExecNoBlock support
|
||||||
|
- [ ] Recording macros (xmacro)
|
||||||
|
- [ ] Delay
|
||||||
|
- [ ] Keyboard actions
|
||||||
|
- [ ] Mouse actions
|
||||||
|
- [ ] Screenshot
|
|
@ -2,38 +2,74 @@ use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::{c_char, c_int, c_uint};
|
use std::os::raw::{c_char, c_int, c_uint};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
use std::thread;
|
use std::{fs, thread};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use x11::xlib::{Display, XCloseDisplay, XDisplayString, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
use x11::xlib::{Display, XCloseDisplay, XDisplayString, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
||||||
use easymacros::add;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use x11::keysym::{XK_d, XK_Super_L};
|
use x11::keysym::{XK_d, XK_Super_L};
|
||||||
use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension};
|
use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension};
|
||||||
use x11_keysymdef::lookup_by_name;
|
use x11_keysymdef::lookup_by_name;
|
||||||
use easymacros::x11_safe_wrapper::XDisplay;
|
use easymacros::x11_safe_wrapper::{Keysym, XDisplay};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Macro program inspired by xmacro.
|
/// Macro program inspired by xmacro.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
/// Input file
|
||||||
|
#[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)]
|
||||||
|
input_file: std::path::PathBuf,
|
||||||
/// Display
|
/// Display
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
display: String,
|
display: Option<String>,
|
||||||
/// xmacro compatibility
|
// xmacro compatibility, currently the only supported input format anyway
|
||||||
#[clap(long)]
|
// #[clap(long)]
|
||||||
xmacro: bool,
|
// xmacro: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main () {
|
fn main () {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let xmacro_mode = args.xmacro;
|
// let xmacro_mode = args.xmacro;
|
||||||
|
|
||||||
let display = get_remote(None);
|
let input_file_contents = fs::read_to_string(args.input_file).expect("couldn't read macro file");
|
||||||
|
let display = get_remote(args.display);
|
||||||
|
|
||||||
display.send_fake_keypress_from_string(b"Super_L\0");
|
for instruction in input_file_contents.lines() {
|
||||||
display.send_fake_keypress_from_string(b"d\0");
|
println!("Instruction: {}", instruction);
|
||||||
display.send_fake_keyrelease_from_string(b"d\0");
|
let command: Vec<&str> = instruction.split(' ').collect();
|
||||||
display.send_fake_keyrelease_from_string(b"Super_L\0");
|
|
||||||
|
match command[0] {
|
||||||
|
"Delay" => thread::sleep(Duration::from_millis(command[1].parse().unwrap())),
|
||||||
|
"ButtonPress" => display.send_fake_buttonpress(command[1].parse().unwrap()),
|
||||||
|
"ButtonRelease" => display.send_fake_buttonrelease(command[1].parse().unwrap()),
|
||||||
|
"MotionNotify" => display.send_fake_motion_event(command[1].parse().unwrap(), command[2].parse().unwrap()),
|
||||||
|
"KeyCodePress" => display.send_fake_keypress_from_code(command[1].parse().unwrap()),
|
||||||
|
"KeyCodeRelease" => display.send_fake_keyrelease_from_code(command[1].parse().unwrap()),
|
||||||
|
"KeySymPress" => display.send_fake_keypress_from_keysym(command[1].parse().unwrap()),
|
||||||
|
"KeySymRelease" => display.send_fake_keyrelease_from_keysym(command[1].parse().unwrap()),
|
||||||
|
"KeySym" => {
|
||||||
|
let key: Keysym = command[1].parse().unwrap();
|
||||||
|
display.send_fake_keypress_from_keysym(key);
|
||||||
|
display.send_fake_keyrelease_from_keysym(key);
|
||||||
|
},
|
||||||
|
"KeyStrPress" => display.send_fake_keypress_from_string(CString::new(command[1]).unwrap().as_bytes()),
|
||||||
|
"KeyStrRelease" => display.send_fake_keyrelease_from_string(CString::new(command[1]).unwrap().as_bytes()),
|
||||||
|
"KeyStr" => {
|
||||||
|
let keystring = CString::new(command[1]).unwrap();
|
||||||
|
display.send_fake_keypress_from_string(keystring.as_bytes());
|
||||||
|
display.send_fake_keyrelease_from_string(keystring.as_bytes());
|
||||||
|
},
|
||||||
|
"String" => {
|
||||||
|
println!("Strings are currently not supported.");
|
||||||
|
// for c in instruction[7..].chars() {
|
||||||
|
// display.send_fake_keypress_from_string(CString::new(c.to_string()).unwrap().as_bytes());
|
||||||
|
// display.send_fake_keyrelease_from_string(CString::new(c.to_string()).unwrap().as_bytes());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
c => {panic!("Unknown command {}", c)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
display.close();
|
display.close();
|
||||||
}
|
}
|
||||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -2,15 +2,21 @@ extern crate core;
|
||||||
|
|
||||||
pub mod x11_safe_wrapper;
|
pub mod x11_safe_wrapper;
|
||||||
|
|
||||||
pub fn add(a: i32, b: i32) -> i32 {
|
pub enum XMacroInstructions {
|
||||||
a + b
|
Delay,
|
||||||
}
|
ButtonPress,
|
||||||
|
ButtonRelease,
|
||||||
#[cfg(test)]
|
MotionNotify,
|
||||||
mod tests {
|
KeyCodePress,
|
||||||
#[test]
|
KeyCodeRelease,
|
||||||
fn it_works() {
|
// Screenshot,
|
||||||
let result = 2 + 2;
|
KeySymPress,
|
||||||
assert_eq!(result, 4);
|
KeySymRelease,
|
||||||
}
|
KeySym,
|
||||||
|
KeyStrPress,
|
||||||
|
KeyStrRelease,
|
||||||
|
KeyStr,
|
||||||
|
String,
|
||||||
|
// ExecBlock,
|
||||||
|
// ExecNoBlock,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@ use std::env;
|
||||||
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, XCloseDisplay, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
use x11::xlib::{Display, XCloseDisplay, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
||||||
use x11::xtest::{XTestFakeKeyEvent, XTestGrabControl, XTestQueryExtension};
|
use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension};
|
||||||
|
|
||||||
pub struct XDisplay {
|
pub struct XDisplay {
|
||||||
ptr: *mut Display,
|
ptr: *mut Display,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Keysym = c_ulong;
|
pub type Keysym = c_ulong;
|
||||||
type Keycode = c_uint;
|
pub type Keycode = c_uint;
|
||||||
|
|
||||||
const FALSE_C: c_int = 0;
|
const FALSE_C: c_int = 0;
|
||||||
const TRUE_C: c_int = 1;
|
const TRUE_C: c_int = 1;
|
||||||
|
@ -70,6 +70,14 @@ impl XDisplay {
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_fake_buttonpress(&self, button: u32) {
|
||||||
|
unsafe { XTestFakeButtonEvent(self.ptr, button, TRUE_C, 10) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_fake_buttonrelease(&self, button: u32) {
|
||||||
|
unsafe { XTestFakeButtonEvent(self.ptr, button, FALSE_C, 10) };
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_fake_keyrelease_from_string(&self, string: &[u8]) {
|
pub fn send_fake_keyrelease_from_string(&self, string: &[u8]) {
|
||||||
self.send_fake_keyrelease_from_keysym(string_to_keysym(string))
|
self.send_fake_keyrelease_from_keysym(string_to_keysym(string))
|
||||||
}
|
}
|
||||||
|
@ -83,6 +91,11 @@ impl XDisplay {
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_fake_motion_event(&self, x: c_int, y: c_int) {
|
||||||
|
unsafe { XTestFakeMotionEvent(self.ptr, -1, x, y, 10)};
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn grab_control(&self) {
|
pub fn grab_control(&self) {
|
||||||
unsafe { XTestGrabControl(self.ptr, TRUE_C); }
|
unsafe { XTestGrabControl(self.ptr, TRUE_C); }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue