diff --git a/README.md b/README.md index b0a0431..b1bbc1e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ This program is inspired by [**xmacro**](https://github.com/Ortega-Dan/xmacroInc - [x] MotionNotify and button support - [ ] String typing support (Not too high priority, but I'll add it some time probably) - [ ] ExecBlock/ExecNoBlock support (not high priority) + - [x] ExecBlock + - [ ] ExecNoBlock - [x] Recording macros (xmacro like) - [x] Delay - [x] Keyboard actions diff --git a/src/bin/easymacroplay.rs b/src/bin/easymacroplay.rs index 5a73383..d4df99e 100644 --- a/src/bin/easymacroplay.rs +++ b/src/bin/easymacroplay.rs @@ -1,9 +1,10 @@ use clap::Parser; use easymacros::x11_safe_wrapper::{Keysym, XDisplay}; use std::ffi::CString; -use std::process::exit; +use std::process::{Command, exit}; use std::time::Duration; use std::{fs, thread}; +use std::io::stdin; /// Macro player module for easymacros. It's partially compatible with xmacro macros, with aim for full compatibility. #[derive(Parser, Debug)] @@ -11,67 +12,39 @@ use std::{fs, thread}; struct Args { /// The file that contains the macro to run. #[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)] - input_file: std::path::PathBuf, + input_file: Option, /// Display to run the macro on. This uses the $DISPLAY environment variable by default. #[clap(short, long)] 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 input_file_contents = - fs::read_to_string(args.input_file).expect("couldn't read macro file"); let display = get_remote(args.display); - for instruction in input_file_contents.lines() { - println!("Instruction: {}", instruction); - let command: Vec<&str> = instruction.split(' ').collect(); + if let Some(input_file_path) = args.input_file { + let input_file_contents = fs::read_to_string(input_file_path) + .expect("Couldn't read macro file"); - 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) - } + for instruction in input_file_contents.lines() { + run_instruction(instruction, &display); + } + } else { + let stdin = stdin(); + + loop { + let mut line = String::new(); + stdin.read_line(&mut line).expect("Couldn't read line from stdin"); + // Without this it crashes because apparently it doesn't properly read the next input line? + println!(); + line = line.trim().to_string(); + run_instruction(&*line, &display); } } + + display.close(); } @@ -89,3 +62,49 @@ fn get_remote(display_name: Option) -> XDisplay { display } + +fn run_instruction(instruction: &str, display: &XDisplay) { + let instruction: Vec<&str> = instruction.split(' ').collect(); + + match instruction[0] { + "Delay" => thread::sleep(Duration::from_millis(instruction[1].parse().unwrap())), + "ButtonPress" => display.send_fake_buttonpress(instruction[1].parse().unwrap()), + "ButtonRelease" => display.send_fake_buttonrelease(instruction[1].parse().unwrap()), + "MotionNotify" => display + .send_fake_motion_event(instruction[1].parse().unwrap(), instruction[2].parse().unwrap()), + "KeyCodePress" => display.send_fake_keypress_from_code(instruction[1].parse().unwrap()), + "KeyCodeRelease" => display.send_fake_keyrelease_from_code(instruction[1].parse().unwrap()), + "KeySymPress" => display.send_fake_keypress_from_keysym(instruction[1].parse().unwrap()), + "KeySymRelease" => { + display.send_fake_keyrelease_from_keysym(instruction[1].parse().unwrap()) + } + "KeySym" => { + let key: Keysym = instruction[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(instruction[1]).unwrap().as_bytes()) + } + "KeyStrRelease" => display + .send_fake_keyrelease_from_string(CString::new(instruction[1]).unwrap().as_bytes()), + "KeyStr" => { + let keystring = CString::new(instruction[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."); + } + "ExecBlock" => { + let mut command = Command::new(instruction[1]); + for arg in &instruction[2..] { + command.arg(arg); + } + command.status(); + } + c => { + panic!("Unknown command {:?}", instruction) + } + } +} \ No newline at end of file