diff --git a/Cargo.toml b/Cargo.toml index 3c9b4e0..d7f0a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] x11 = { version = "2.19.1", features = ["xlib", "xtest"] } x11-keysymdef = "0.2.0" -clap = { version = "3.1.18", features = ["derive"] } \ No newline at end of file +clap = { version = "3.2.4", features = ["derive"] } \ No newline at end of file diff --git a/README.md b/README.md index 3e83d1b..c76327c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ # easymacros 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 \ No newline at end of file diff --git a/src/bin/easymacroplay.rs b/src/bin/easymacroplay.rs index bf05ccb..fa2669c 100644 --- a/src/bin/easymacroplay.rs +++ b/src/bin/easymacroplay.rs @@ -2,38 +2,74 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int, c_uint}; use std::process::exit; use std::slice::from_raw_parts; -use std::thread; +use std::{fs, thread}; use std::time::Duration; use x11::xlib::{Display, XCloseDisplay, XDisplayString, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync}; -use easymacros::add; use clap::Parser; use x11::keysym::{XK_d, XK_Super_L}; use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension}; use x11_keysymdef::lookup_by_name; -use easymacros::x11_safe_wrapper::XDisplay; +use easymacros::x11_safe_wrapper::{Keysym, XDisplay}; + + /// Macro program inspired by xmacro. #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { + /// Input file + #[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)] + input_file: std::path::PathBuf, /// Display #[clap(short, long)] - display: String, - /// xmacro compatibility - #[clap(long)] - xmacro: bool, + display: Option, + // xmacro compatibility, currently the only supported input format anyway + // #[clap(long)] + // xmacro: bool, } fn main () { 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"); - display.send_fake_keypress_from_string(b"d\0"); - display.send_fake_keyrelease_from_string(b"d\0"); - display.send_fake_keyrelease_from_string(b"Super_L\0"); + for instruction in input_file_contents.lines() { + println!("Instruction: {}", instruction); + let command: Vec<&str> = instruction.split(' ').collect(); + + 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(); } diff --git a/src/lib.rs b/src/lib.rs index 1761e8b..869945a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,21 @@ extern crate core; pub mod x11_safe_wrapper; -pub fn add(a: i32, b: i32) -> i32 { - a + b -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } +pub enum XMacroInstructions { + Delay, + ButtonPress, + ButtonRelease, + MotionNotify, + KeyCodePress, + KeyCodeRelease, + // Screenshot, + KeySymPress, + KeySymRelease, + KeySym, + KeyStrPress, + KeyStrRelease, + KeyStr, + String, + // ExecBlock, + // ExecNoBlock, } diff --git a/src/x11_safe_wrapper.rs b/src/x11_safe_wrapper.rs index dc9b6c4..3c142ab 100644 --- a/src/x11_safe_wrapper.rs +++ b/src/x11_safe_wrapper.rs @@ -2,14 +2,14 @@ use std::env; use std::ffi::{CStr, CString}; 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::xtest::{XTestFakeKeyEvent, XTestGrabControl, XTestQueryExtension}; +use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension}; pub struct XDisplay { ptr: *mut Display, } -type Keysym = c_ulong; -type Keycode = c_uint; +pub type Keysym = c_ulong; +pub type Keycode = c_uint; const FALSE_C: c_int = 0; const TRUE_C: c_int = 1; @@ -70,6 +70,14 @@ impl XDisplay { 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]) { self.send_fake_keyrelease_from_keysym(string_to_keysym(string)) } @@ -83,6 +91,11 @@ impl XDisplay { 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) { unsafe { XTestGrabControl(self.ptr, TRUE_C); } }