formatted with cargo, cleaned up imports and dependencies.
This commit is contained in:
parent
5dc96d6da4
commit
b651009949
5 changed files with 117 additions and 104 deletions
|
@ -1,11 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "easymacros"
|
name = "easymacros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
author = "obsidianical"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
x11 = { version = "2.19.1", features = ["xlib", "xtest"] }
|
x11 = { version = "2.19.1", features = ["xlib", "xtest"] }
|
||||||
x11-keysymdef = "0.2.0"
|
|
||||||
clap = { version = "3.2.4", features = ["derive"] }
|
clap = { version = "3.2.4", features = ["derive"] }
|
|
@ -44,4 +44,8 @@ I may or may not do these, but they sound fun to implement to me!
|
||||||
- [x] Linux x11 (only tested on i3wm)
|
- [x] Linux x11 (only tested on i3wm)
|
||||||
- [ ] Linux Wayland (makes heavy use of X apis, I will only do this if I myself switch to Wayland. I'm open to suggestions how to do it though!)
|
- [ ] Linux Wayland (makes heavy use of X apis, I will only do this if I myself switch to Wayland. I'm open to suggestions how to do it though!)
|
||||||
- [ ] MacOS (Might work because of XQuartz?)
|
- [ ] MacOS (Might work because of XQuartz?)
|
||||||
- [ ] Windows (Yeah, I'm not doing that myself. Unless I have to use Windows for anything.)
|
- [ ] Windows (Yeah, I'm not doing that myself. Unless I have to use Windows for anything.)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Currently only manually possible via `cargo build --release` and then moving the result into your $PATH.
|
|
@ -1,26 +1,18 @@
|
||||||
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::{fs, thread};
|
|
||||||
use std::time::Duration;
|
|
||||||
use x11::xlib::{Display, XCloseDisplay, XDisplayString, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
|
||||||
use clap::Parser;
|
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::{Keysym, XDisplay};
|
use easymacros::x11_safe_wrapper::{Keysym, XDisplay};
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{fs, thread};
|
||||||
|
|
||||||
|
/// Macro player module for easymacros. It's partially compatible with xmacro macros, with aim for full compatibility.
|
||||||
|
|
||||||
/// 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
|
/// The file that contains the macro to run.
|
||||||
#[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)]
|
#[clap(value_parser, value_name = "input_file", value_hint = clap::ValueHint::FilePath)]
|
||||||
input_file: std::path::PathBuf,
|
input_file: std::path::PathBuf,
|
||||||
/// Display
|
/// Display to run the macro on. This uses the $DISPLAY environment variable by default.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
display: Option<String>,
|
display: Option<String>,
|
||||||
// xmacro compatibility, currently the only supported input format anyway
|
// xmacro compatibility, currently the only supported input format anyway
|
||||||
|
@ -28,11 +20,12 @@ struct Args {
|
||||||
// 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 input_file_contents = fs::read_to_string(args.input_file).expect("couldn't read macro file");
|
let input_file_contents =
|
||||||
|
fs::read_to_string(args.input_file).expect("couldn't read macro file");
|
||||||
let display = get_remote(args.display);
|
let display = get_remote(args.display);
|
||||||
|
|
||||||
for instruction in input_file_contents.lines() {
|
for instruction in input_file_contents.lines() {
|
||||||
|
@ -43,23 +36,29 @@ fn main () {
|
||||||
"Delay" => thread::sleep(Duration::from_millis(command[1].parse().unwrap())),
|
"Delay" => thread::sleep(Duration::from_millis(command[1].parse().unwrap())),
|
||||||
"ButtonPress" => display.send_fake_buttonpress(command[1].parse().unwrap()),
|
"ButtonPress" => display.send_fake_buttonpress(command[1].parse().unwrap()),
|
||||||
"ButtonRelease" => display.send_fake_buttonrelease(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()),
|
"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()),
|
"KeyCodePress" => display.send_fake_keypress_from_code(command[1].parse().unwrap()),
|
||||||
"KeyCodeRelease" => display.send_fake_keyrelease_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()),
|
"KeySymPress" => display.send_fake_keypress_from_keysym(command[1].parse().unwrap()),
|
||||||
"KeySymRelease" => display.send_fake_keyrelease_from_keysym(command[1].parse().unwrap()),
|
"KeySymRelease" => {
|
||||||
|
display.send_fake_keyrelease_from_keysym(command[1].parse().unwrap())
|
||||||
|
}
|
||||||
"KeySym" => {
|
"KeySym" => {
|
||||||
let key: Keysym = command[1].parse().unwrap();
|
let key: Keysym = command[1].parse().unwrap();
|
||||||
display.send_fake_keypress_from_keysym(key);
|
display.send_fake_keypress_from_keysym(key);
|
||||||
display.send_fake_keyrelease_from_keysym(key);
|
display.send_fake_keyrelease_from_keysym(key);
|
||||||
},
|
}
|
||||||
"KeyStrPress" => display.send_fake_keypress_from_string(CString::new(command[1]).unwrap().as_bytes()),
|
"KeyStrPress" => {
|
||||||
"KeyStrRelease" => display.send_fake_keyrelease_from_string(CString::new(command[1]).unwrap().as_bytes()),
|
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" => {
|
"KeyStr" => {
|
||||||
let keystring = CString::new(command[1]).unwrap();
|
let keystring = CString::new(command[1]).unwrap();
|
||||||
display.send_fake_keypress_from_string(keystring.as_bytes());
|
display.send_fake_keypress_from_string(keystring.as_bytes());
|
||||||
display.send_fake_keyrelease_from_string(keystring.as_bytes());
|
display.send_fake_keyrelease_from_string(keystring.as_bytes());
|
||||||
},
|
}
|
||||||
"String" => {
|
"String" => {
|
||||||
println!("Strings are currently not supported.");
|
println!("Strings are currently not supported.");
|
||||||
// for c in instruction[7..].chars() {
|
// for c in instruction[7..].chars() {
|
||||||
|
@ -67,7 +66,9 @@ fn main () {
|
||||||
// display.send_fake_keyrelease_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)}
|
c => {
|
||||||
|
panic!("Unknown command {}", c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use easymacros::add;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("rec: {}", add(2, 2));
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ulong};
|
use std::os::raw::{c_char, c_int, c_uint, c_ulong};
|
||||||
use x11::xlib::{Display, XCloseDisplay, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync};
|
use x11::xlib::{
|
||||||
use x11::xtest::{XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl, XTestQueryExtension};
|
Display, XCloseDisplay, XFlush, XKeysymToKeycode, XOpenDisplay, XStringToKeysym, XSync,
|
||||||
|
};
|
||||||
|
use x11::xtest::{
|
||||||
|
XTestFakeButtonEvent, XTestFakeKeyEvent, XTestFakeMotionEvent, XTestGrabControl,
|
||||||
|
XTestQueryExtension,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct XDisplay {
|
pub struct XDisplay {
|
||||||
ptr: *mut Display,
|
ptr: *mut Display,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Keysym = c_ulong;
|
pub type Keysym = c_ulong;
|
||||||
|
@ -15,93 +20,98 @@ const FALSE_C: c_int = 0;
|
||||||
const TRUE_C: c_int = 1;
|
const TRUE_C: c_int = 1;
|
||||||
|
|
||||||
impl XDisplay {
|
impl XDisplay {
|
||||||
pub fn open(display_name: Option<String>) -> Self {
|
pub fn open(display_name: Option<String>) -> Self {
|
||||||
let name = CString::new(if let Some(name) = display_name { name } else { env::var("DISPLAY").expect("DISPLAY is not set") }).unwrap();
|
let name = CString::new(if let Some(name) = display_name {
|
||||||
let name_ptr = name.as_bytes().as_ptr();
|
name
|
||||||
let display_ptr = unsafe { XOpenDisplay(name_ptr as *const i8) };
|
} else {
|
||||||
|
env::var("DISPLAY").expect("DISPLAY is not set")
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let name_ptr = name.as_bytes().as_ptr();
|
||||||
|
let display_ptr = unsafe { XOpenDisplay(name_ptr as *const i8) };
|
||||||
|
|
||||||
Self { ptr: display_ptr }
|
Self { ptr: display_ptr }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(self) {
|
pub fn close(self) {
|
||||||
unsafe { XCloseDisplay(self.ptr) };
|
unsafe { XCloseDisplay(self.ptr) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync(&self) {
|
pub fn sync(&self) {
|
||||||
unsafe { XSync(self.ptr, c_int::from(false)); }
|
unsafe {
|
||||||
}
|
XSync(self.ptr, c_int::from(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn flush(&self) {
|
pub fn flush(&self) {
|
||||||
unsafe { XFlush(self.ptr); }
|
unsafe {
|
||||||
}
|
XFlush(self.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn keysym_to_keycode(&self, keysym: c_ulong) -> Keycode {
|
pub fn keysym_to_keycode(&self, keysym: c_ulong) -> Keycode {
|
||||||
unsafe { XKeysymToKeycode(self.ptr, keysym) as Keycode }
|
unsafe { XKeysymToKeycode(self.ptr, keysym) as Keycode }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_to_keycode(&self, string: &[u8]) -> Keycode {
|
pub fn string_to_keycode(&self, string: &[u8]) -> Keycode {
|
||||||
self.keysym_to_keycode(string_to_keysym(string))
|
self.keysym_to_keycode(string_to_keysym(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_xtest(&self) -> bool {
|
pub fn has_xtest(&self) -> bool {
|
||||||
let mut vals: (c_int, c_int, c_int, c_int) = (0, 0, 0, 0);
|
let mut vals: (c_int, c_int, c_int, c_int) = (0, 0, 0, 0);
|
||||||
let has_extension = unsafe {
|
let has_extension = unsafe {
|
||||||
XTestQueryExtension(
|
XTestQueryExtension(self.ptr, &mut vals.0, &mut vals.1, &mut vals.2, &mut vals.3)
|
||||||
self.ptr,
|
};
|
||||||
&mut vals.0,
|
has_extension != 0
|
||||||
&mut vals.1,
|
}
|
||||||
&mut vals.2,
|
|
||||||
&mut vals.3
|
|
||||||
)
|
|
||||||
};
|
|
||||||
has_extension != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_fake_keypress_from_string(&self, string: &[u8]) {
|
pub fn send_fake_keypress_from_string(&self, string: &[u8]) {
|
||||||
self.send_fake_keypress_from_keysym(string_to_keysym(string))
|
self.send_fake_keypress_from_keysym(string_to_keysym(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_keypress_from_keysym(&self, ks: Keysym) {
|
pub fn send_fake_keypress_from_keysym(&self, ks: Keysym) {
|
||||||
self.send_fake_keypress_from_code(self.keysym_to_keycode(ks))
|
self.send_fake_keypress_from_code(self.keysym_to_keycode(ks))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_keypress_from_code(&self, code: Keycode) {
|
pub fn send_fake_keypress_from_code(&self, code: Keycode) {
|
||||||
unsafe { XTestFakeKeyEvent(self.ptr, code, TRUE_C, 10) };
|
unsafe { XTestFakeKeyEvent(self.ptr, code, TRUE_C, 10) };
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_buttonpress(&self, button: u32) {
|
pub fn send_fake_buttonpress(&self, button: u32) {
|
||||||
unsafe { XTestFakeButtonEvent(self.ptr, button, TRUE_C, 10) };
|
unsafe { XTestFakeButtonEvent(self.ptr, button, TRUE_C, 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_buttonrelease(&self, button: u32) {
|
pub fn send_fake_buttonrelease(&self, button: u32) {
|
||||||
unsafe { XTestFakeButtonEvent(self.ptr, button, FALSE_C, 10) };
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_keyrelease_from_keysym(&self, ks: Keysym) {
|
pub fn send_fake_keyrelease_from_keysym(&self, ks: Keysym) {
|
||||||
self.send_fake_keyrelease_from_code(self.keysym_to_keycode(ks))
|
self.send_fake_keyrelease_from_code(self.keysym_to_keycode(ks))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_keyrelease_from_code(&self, code: Keycode) {
|
pub fn send_fake_keyrelease_from_code(&self, code: Keycode) {
|
||||||
unsafe { XTestFakeKeyEvent(self.ptr, code, FALSE_C, 10) };
|
unsafe { XTestFakeKeyEvent(self.ptr, code, FALSE_C, 10) };
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_fake_motion_event(&self, x: c_int, y: c_int) {
|
pub fn send_fake_motion_event(&self, x: c_int, y: c_int) {
|
||||||
unsafe { XTestFakeMotionEvent(self.ptr, -1, x, y, 10)};
|
unsafe { XTestFakeMotionEvent(self.ptr, -1, x, y, 10) };
|
||||||
self.flush();
|
self.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grab_control(&self) {
|
pub fn grab_control(&self) {
|
||||||
unsafe { XTestGrabControl(self.ptr, TRUE_C); }
|
unsafe {
|
||||||
}
|
XTestGrabControl(self.ptr, TRUE_C);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper for XStringToKeysym. Remember to give a null terminated string!
|
/// Wrapper for XStringToKeysym. Remember to give a null terminated string!
|
||||||
pub fn string_to_keysym(string: &[u8]) -> Keysym {
|
pub fn string_to_keysym(string: &[u8]) -> Keysym {
|
||||||
unsafe { XStringToKeysym(string.as_ptr() as *const c_char) }
|
unsafe { XStringToKeysym(string.as_ptr() as *const c_char) }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue