cargo fmt

This commit is contained in:
Schrottkatze 2022-10-10 20:03:00 +02:00
parent 16c0826d3a
commit 801f8b6265
10 changed files with 887 additions and 818 deletions

View file

@ -1,146 +1,173 @@
use clap::Parser;
use easymacros::x11_safe_wrapper::{Keysym, string_to_keysym, XDisplay};
use easymacros::chartbl::CHARTBL;
use easymacros::x11_safe_wrapper::{string_to_keysym, Keysym, XDisplay};
use std::ffi::CString;
use std::process::{Command, exit};
use std::io::stdin;
use std::process::{exit, Command};
use std::time::Duration;
use std::{fs, thread};
use std::io::stdin;
use x11::keysym::XK_Shift_L;
use easymacros::chartbl::CHARTBL;
/// Macro player module for easymacros. It's compatible with xmacro macros.
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// The file that contains the macro to run.
#[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)]
input_file: Option<std::path::PathBuf>,
/// Display to run the macro on. This uses the $DISPLAY environment variable by default.
#[clap(short = 'D', long)]
display: Option<String>,
/// Delay for events to be sent.
#[clap(short, long)]
delay: Option<u64>,
/// The file that contains the macro to run.
#[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)]
input_file: Option<std::path::PathBuf>,
/// Display to run the macro on. This uses the $DISPLAY environment variable by default.
#[clap(short = 'D', long)]
display: Option<String>,
/// Delay for events to be sent.
#[clap(short, long)]
delay: Option<u64>,
}
fn main() {
let args = Args::parse();
let args = Args::parse();
let display = get_remote(args.display);
let delay = args.delay.unwrap_or(10);
let display = get_remote(args.display);
let delay = args.delay.unwrap_or(10);
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");
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");
for instruction in input_file_contents.lines() {
run_instruction(instruction, &display, delay);
}
} else {
println!("No input file specified, reading from stdin.");
let stdin = stdin();
for instruction in input_file_contents.lines() {
run_instruction(instruction, &display, delay);
}
} else {
println!("No input file specified, reading from stdin.");
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, delay);
}
}
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, delay);
}
}
display.close();
display.close();
}
fn get_remote(display_name: Option<String>) -> XDisplay {
let display = XDisplay::open(display_name);
let display = XDisplay::open(display_name);
if !display.has_xtest() {
eprintln!("XTest not supported!");
display.close();
exit(1)
}
if !display.has_xtest() {
eprintln!("XTest not supported!");
display.close();
exit(1)
}
display.grab_control();
display.sync();
display.grab_control();
display.sync();
display
display
}
fn run_instruction(instruction: &str, dpy: &XDisplay, delay: u64) {
let instruction_split: Vec<&str> = instruction.split(' ').collect();
let instruction_split: Vec<&str> = instruction.split(' ').collect();
match instruction_split[0] {
"Delay" => thread::sleep(Duration::from_millis(instruction_split[1].parse().unwrap())),
"ButtonPress" => dpy.send_fake_buttonpress(instruction_split[1].parse().unwrap(), delay),
"ButtonRelease" => dpy.send_fake_buttonrelease(instruction_split[1].parse().unwrap(), delay),
"MotionNotify" => dpy.send_fake_motion_event(instruction_split[1].parse().unwrap(), instruction_split[2].parse().unwrap(), delay),
"KeyCodePress" => dpy.send_fake_keypress_from_code(instruction_split[1].parse().unwrap(), delay),
"KeyCodeRelease" => dpy.send_fake_keyrelease_from_code(instruction_split[1].parse().unwrap(), delay),
"KeySymPress" => dpy.send_fake_keypress_from_keysym(instruction_split[1].parse().unwrap(), delay),
"KeySymRelease" => dpy.send_fake_keyrelease_from_keysym(instruction_split[1].parse().unwrap(), delay),
match instruction_split[0] {
"Delay" => thread::sleep(Duration::from_millis(instruction_split[1].parse().unwrap())),
"ButtonPress" => dpy.send_fake_buttonpress(instruction_split[1].parse().unwrap(), delay),
"ButtonRelease" => {
dpy.send_fake_buttonrelease(instruction_split[1].parse().unwrap(), delay)
}
"MotionNotify" => dpy.send_fake_motion_event(
instruction_split[1].parse().unwrap(),
instruction_split[2].parse().unwrap(),
delay,
),
"KeyCodePress" => {
dpy.send_fake_keypress_from_code(instruction_split[1].parse().unwrap(), delay)
}
"KeyCodeRelease" => {
dpy.send_fake_keyrelease_from_code(instruction_split[1].parse().unwrap(), delay)
}
"KeySymPress" => {
dpy.send_fake_keypress_from_keysym(instruction_split[1].parse().unwrap(), delay)
}
"KeySymRelease" => {
dpy.send_fake_keyrelease_from_keysym(instruction_split[1].parse().unwrap(), delay)
}
"KeySym" => {
let key: Keysym = instruction_split[1].parse().unwrap();
dpy.send_fake_keypress_from_keysym(key, delay);
dpy.send_fake_keyrelease_from_keysym(key, delay);
}
"KeyStrPress" => dpy.send_fake_keypress_from_string(CString::new(instruction_split[1]).unwrap().as_bytes(), delay),
"KeyStrRelease" => dpy.send_fake_keyrelease_from_string(CString::new(instruction_split[1]).unwrap().as_bytes(), delay),
"KeyStr" => {
let keystring = CString::new(instruction_split[1]).unwrap();
dpy.send_fake_keypress_from_string(keystring.as_bytes(), delay);
dpy.send_fake_keyrelease_from_string(keystring.as_bytes(), delay);
}
"String" => {
for c in instruction["String".len() + 1..].chars() {
send_char(dpy, c, delay);
}
}
"ExecBlock" | "ExecNoBlock" => {
let mut command = Command::new(instruction_split[1]);
for arg in &instruction_split[2..] {
command.arg(arg);
}
if instruction_split[0] == "ExecBlock" {
command.status().unwrap();
} else {
command.spawn().unwrap();
}
}
c => panic!("Unknown command {:?}", instruction_split),
}
"KeySym" => {
let key: Keysym = instruction_split[1].parse().unwrap();
dpy.send_fake_keypress_from_keysym(key, delay);
dpy.send_fake_keyrelease_from_keysym(key, delay);
}
"KeyStrPress" => dpy.send_fake_keypress_from_string(
CString::new(instruction_split[1]).unwrap().as_bytes(),
delay,
),
"KeyStrRelease" => dpy.send_fake_keyrelease_from_string(
CString::new(instruction_split[1]).unwrap().as_bytes(),
delay,
),
"KeyStr" => {
let keystring = CString::new(instruction_split[1]).unwrap();
dpy.send_fake_keypress_from_string(keystring.as_bytes(), delay);
dpy.send_fake_keyrelease_from_string(keystring.as_bytes(), delay);
}
"String" => {
for c in instruction["String".len() + 1..].chars() {
send_char(dpy, c, delay);
}
}
"ExecBlock" | "ExecNoBlock" => {
let mut command = Command::new(instruction_split[1]);
for arg in &instruction_split[2..] {
command.arg(arg);
}
if instruction_split[0] == "ExecBlock" {
command.status().unwrap();
} else {
command.spawn().unwrap();
}
}
c => panic!("Unknown command {:?}", instruction_split),
}
}
fn send_char(dpy: &XDisplay, c: char, delay: u64) {
// get keystring from character and turn it into a keysym
let keysym = string_to_keysym(CHARTBL[c as usize].as_ref());
let keycode = dpy.keysym_to_keycode(keysym);
// get keystring from character and turn it into a keysym
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;
}
if keycode == 0 {
eprintln!("No keycode found for character '{}'", c);
return;
}
let map_ks = dpy.get_keyboard_mapping(keycode, 1);
if map_ks[0] == 0 {
eprintln!("XGetKeyboardMapping failed (keycode: {})", keycode);
return;
}
let map_ks = dpy.get_keyboard_mapping(keycode, 1);
if map_ks[0] == 0 {
eprintln!("XGetKeyboardMapping failed (keycode: {})", keycode);
return;
}
let (ks_lower, ks_upper) = dpy.convert_case(keysym);
let (ks_lower, ks_upper) = dpy.convert_case(keysym);
// check if shift has to be pressed as well
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;
}
// check if shift has to be pressed as well
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, delay); }
dpy.send_fake_keypress_from_code(keycode, delay);
dpy.send_fake_keyrelease_from_code(keycode, delay);
if shift_needed { dpy.send_fake_keyrelease_from_keysym(XK_Shift_L as Keysym, delay); }
}
if shift_needed {
dpy.send_fake_keypress_from_keysym(XK_Shift_L as Keysym, delay);
}
dpy.send_fake_keypress_from_code(keycode, delay);
dpy.send_fake_keyrelease_from_code(keycode, delay);
if shift_needed {
dpy.send_fake_keyrelease_from_keysym(XK_Shift_L as Keysym, delay);
}
}

View file

@ -1,189 +1,198 @@
extern crate core;
use std::ffi::c_void;
use std::os::raw::{c_char};
use std::process::{exit};
use std::os::raw::c_char;
use std::process::exit;
use std::ptr::addr_of;
use clap::Parser;
use x11::xlib::{CurrentTime, GrabModeAsync, GrabModeSync, GrabSuccess, KeyPressMask, SyncPointer, Time, XFree, XKeyEvent};
use x11::xrecord::{XRecordAllocRange, XRecordEndOfData, XRecordFreeData, XRecordInterceptData, XRecordStartOfData};
use x11::xlib::{
CurrentTime, GrabModeAsync, GrabModeSync, GrabSuccess, KeyPressMask, SyncPointer, Time, XFree,
XKeyEvent,
};
use x11::xrecord::{
XRecordAllocRange, XRecordEndOfData, XRecordFreeData, XRecordInterceptData, XRecordStartOfData,
};
use easymacros::{BUTTONPRESS_U8, BUTTONRELEASE_U8, Instructions, KEYPRESS_U8, KEYRELEASE_U8, MOTIONNOTIFY_U8, Position};
use easymacros::ev_callback_data::EvCallbackData;
use easymacros::macro_writer::MacroWriter;
use easymacros::x11_safe_wrapper::{Keycode, XDisplay};
use easymacros::{
Instructions, Position, BUTTONPRESS_U8, BUTTONRELEASE_U8, KEYPRESS_U8, KEYRELEASE_U8,
MOTIONNOTIFY_U8,
};
/// Macro recording module for easymacros. Outputs are partially compatible with xmacro.
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// The file to record the macro to. Defaults to writing to stdout.
#[clap(value_parser, value_name = "output_file", value_hint = clap::ValueHint::FilePath)]
output_file: Option<std::path::PathBuf>,
/// Display to run the macro on. This uses the $DISPLAY environment variable by default.
#[clap(short = 'D', long)]
display: Option<String>,
/// Max Delay in milliseconds for macro delays
#[clap(short, long)]
max_delay: Option<u64>,
/// Allow delay capturing in recording output. If this flag is set, the program will ignore the max_delay.
#[clap(short, long)]
ignore_delay_capturing: bool,
/// The file to record the macro to. Defaults to writing to stdout.
#[clap(value_parser, value_name = "output_file", value_hint = clap::ValueHint::FilePath)]
output_file: Option<std::path::PathBuf>,
/// Display to run the macro on. This uses the $DISPLAY environment variable by default.
#[clap(short = 'D', long)]
display: Option<String>,
/// Max Delay in milliseconds for macro delays
#[clap(short, long)]
max_delay: Option<u64>,
/// Allow delay capturing in recording output. If this flag is set, the program will ignore the max_delay.
#[clap(short, long)]
ignore_delay_capturing: bool,
}
fn main() {
let args = Args::parse();
let args = Args::parse();
let display = XDisplay::open(args.display.clone());
let recorded_display = XDisplay::open(args.display.clone());
let stop_key = get_stop_key(display);
let writer = MacroWriter::new(args.output_file, args.ignore_delay_capturing);
let display = XDisplay::open(args.display.clone());
let recorded_display = XDisplay::open(args.display.clone());
let stop_key = get_stop_key(display);
let writer = MacroWriter::new(args.output_file, args.ignore_delay_capturing);
event_loop(
display,
recorded_display,
stop_key,
writer,
args.max_delay,
);
event_loop(display, recorded_display, stop_key, writer, args.max_delay);
display.close();
display.close();
}
fn get_stop_key(display: XDisplay) -> Keycode {
let screen = display.get_default_screen();
let screen = display.get_default_screen();
let root = display.get_root_window(screen);
let potential_err = display.grab_keyboard(root, false, GrabModeSync, GrabModeAsync, CurrentTime);
let root = display.get_root_window(screen);
let potential_err =
display.grab_keyboard(root, false, GrabModeSync, GrabModeAsync, CurrentTime);
if potential_err != GrabSuccess {
eprintln!("Couldn't grab keyboard!");
exit(1);
}
if potential_err != GrabSuccess {
eprintln!("Couldn't grab keyboard!");
exit(1);
}
println!("Press the key you want to use to stop recording the macro.");
println!("Press the key you want to use to stop recording the macro.");
let stop_key;
loop {
display.allow_events(SyncPointer, CurrentTime);
let ev = XKeyEvent::from(display.window_event(root, KeyPressMask));
stop_key = ev.keycode;
break;
}
let stop_key;
loop {
display.allow_events(SyncPointer, CurrentTime);
let ev = XKeyEvent::from(display.window_event(root, KeyPressMask));
stop_key = ev.keycode;
break;
}
display.ungrab_keyboard(CurrentTime);
display.ungrab_pointer(CurrentTime);
display.ungrab_keyboard(CurrentTime);
display.ungrab_pointer(CurrentTime);
stop_key
stop_key
}
fn event_loop(
xdpy: XDisplay,
recdpy: XDisplay,
stop_key: Keycode,
mut writer: MacroWriter,
max_delay: Option<Time>,
xdpy: XDisplay,
recdpy: XDisplay,
stop_key: Keycode,
mut writer: MacroWriter,
max_delay: Option<Time>,
) {
let protocol_ranges = unsafe { XRecordAllocRange() };
let protocol_ranges = unsafe { XRecordAllocRange() };
let pointer_pos: Position<i16> = Position::from(xdpy.query_pointer_pos());
let pointer_pos: Position<i16> = Position::from(xdpy.query_pointer_pos());
if pointer_pos != Position(-1, -1) {
writer.write(Instructions::MotionNotify(pointer_pos))
}
if pointer_pos != Position(-1, -1) {
writer.write(Instructions::MotionNotify(pointer_pos))
}
let ctx = recdpy.create_record_context(protocol_ranges);
let ev_cb_data = EvCallbackData::new(writer, xdpy, recdpy, ctx, stop_key, pointer_pos, max_delay);
let ctx = recdpy.create_record_context(protocol_ranges);
let ev_cb_data =
EvCallbackData::new(writer, xdpy, recdpy, ctx, stop_key, pointer_pos, max_delay);
if !recdpy.enable_context_async(ctx, Some(ev_callback), addr_of!(ev_cb_data) as *mut c_char) {
panic!("Failed to enable record context")
}
while ev_cb_data.working {
recdpy.process_replies();
}
if !recdpy.enable_context_async(ctx, Some(ev_callback), addr_of!(ev_cb_data) as *mut c_char) {
panic!("Failed to enable record context")
}
while ev_cb_data.working {
recdpy.process_replies();
}
xdpy.disable_context(ctx);
xdpy.free_context(ctx);
unsafe { XFree(protocol_ranges as *mut c_void) };
xdpy.disable_context(ctx);
xdpy.free_context(ctx);
unsafe { XFree(protocol_ranges as *mut c_void) };
}
unsafe extern "C" fn ev_callback(closure: *mut c_char, intercept_data: *mut XRecordInterceptData) {
let data = &mut *(closure as *mut EvCallbackData);
let intercept_data = &mut *intercept_data;
let data = &mut *(closure as *mut EvCallbackData);
let intercept_data = &mut *intercept_data;
if intercept_data.category == XRecordStartOfData {
println!("Got start of data!");
data.last_event = intercept_data.server_time;
XRecordFreeData(intercept_data);
return;
} else if intercept_data.category == XRecordEndOfData {
println!("Got end of data!");
XRecordFreeData(intercept_data);
return;
}
if intercept_data.category == XRecordStartOfData {
println!("Got start of data!");
data.last_event = intercept_data.server_time;
XRecordFreeData(intercept_data);
return;
} else if intercept_data.category == XRecordEndOfData {
println!("Got end of data!");
XRecordFreeData(intercept_data);
return;
}
let ev_type = *(intercept_data.data as *const u8);
let ev_type = *(intercept_data.data as *const u8);
if data.pos.0 == 0 || data.pos.1 == -1 {
if ev_type == MOTIONNOTIFY_U8 {
data.update_pos(intercept_data);
data.write_pos();
} else {
println!("Move your cursor so the macro can start with a fixed cursor position!
Skipping event...");
}
} else if data.no_keypress_yet && ev_type == KEYRELEASE_U8 {
println!("Skipping KeyRelease without recorded KeyPress...");
} else {
match ev_type {
MOTIONNOTIFY_U8 => {
data.update_pos(intercept_data);
if data.pos.0 == 0 || data.pos.1 == -1 {
if ev_type == MOTIONNOTIFY_U8 {
data.update_pos(intercept_data);
data.write_pos();
} else {
println!(
"Move your cursor so the macro can start with a fixed cursor position!
Skipping event..."
);
}
} else if data.no_keypress_yet && ev_type == KEYRELEASE_U8 {
println!("Skipping KeyRelease without recorded KeyPress...");
} else {
match ev_type {
MOTIONNOTIFY_U8 => {
data.update_pos(intercept_data);
if !data.moving {
data.moving = true;
}
}
KEYPRESS_U8 | KEYRELEASE_U8 => {
let kc: u8 = *((intercept_data.data as usize + 1) as *const u8);
let keyname = data.xdpy.keycode_to_string(kc as u32);
if !data.moving {
data.moving = true;
}
}
KEYPRESS_U8 | KEYRELEASE_U8 => {
let kc: u8 = *((intercept_data.data as usize + 1) as *const u8);
let keyname = data.xdpy.keycode_to_string(kc as u32);
if ev_type == KEYPRESS_U8 && kc == data.stop_key as u8 {
data.working = false;
} else {
if ev_type == KEYPRESS_U8 {
data.no_keypress_yet = false;
}
if ev_type == KEYPRESS_U8 && kc == data.stop_key as u8 {
data.working = false;
} else {
if ev_type == KEYPRESS_U8 {
data.no_keypress_yet = false;
}
data.maybe_write_delay(intercept_data.server_time);
data.maybe_write_delay(intercept_data.server_time);
if data.ptr_is_moving() { data.write_pos(); }
if data.ptr_is_moving() {
data.write_pos();
}
data.writer.write(if ev_type == KEYPRESS_U8 {
Instructions::KeyStrPress(keyname)
} else {
Instructions::KeyStrRelease(keyname)
});
}
}
BUTTONPRESS_U8 | BUTTONRELEASE_U8 => {
let bc: u8 = *((intercept_data.data as usize + 1) as *const u8);
data.writer.write(if ev_type == KEYPRESS_U8 {
Instructions::KeyStrPress(keyname)
} else {
Instructions::KeyStrRelease(keyname)
});
}
}
BUTTONPRESS_U8 | BUTTONRELEASE_U8 => {
let bc: u8 = *((intercept_data.data as usize + 1) as *const u8);
data.maybe_write_delay(intercept_data.server_time);
data.maybe_write_delay(intercept_data.server_time);
if data.ptr_is_moving() { data.write_pos(); }
if data.ptr_is_moving() {
data.write_pos();
}
data.writer.write(if ev_type == BUTTONPRESS_U8 {
Instructions::ButtonPress(bc)
} else {
Instructions::ButtonRelease(bc)
});
}
_ => eprintln!("Unknown event type: {:?}", ev_type)
}
}
data.writer.write(if ev_type == BUTTONPRESS_U8 {
Instructions::ButtonPress(bc)
} else {
Instructions::ButtonRelease(bc)
});
}
_ => eprintln!("Unknown event type: {:?}", ev_type),
}
}
data.ev_nr += 2;
XRecordFreeData(intercept_data)
data.ev_nr += 2;
XRecordFreeData(intercept_data)
}