diff --git a/src/bin/easymacroplay.rs b/src/bin/easymacroplay.rs index 239e06f..bf05ccb 100644 --- a/src/bin/easymacroplay.rs +++ b/src/bin/easymacroplay.rs @@ -10,6 +10,7 @@ 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; /// Macro program inspired by xmacro. #[derive(Parser, Debug)] @@ -18,57 +19,36 @@ struct Args { /// Display #[clap(short, long)] display: String, + /// xmacro compatibility + #[clap(long)] + xmacro: bool, } fn main () { let args = Args::parse(); + let xmacro_mode = args.xmacro; - let display = get_remote(args.display); + let display = get_remote(None); - let super_l_str_ptr = b"Super_L\0".as_ptr(); - let super_l_sym = unsafe { XStringToKeysym(super_l_str_ptr as *const i8) }; - let super_l_code = unsafe { XKeysymToKeycode(display, super_l_sym) }; + 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"); - let d_str_ptr = b"d\0".as_ptr(); - let d_sym = unsafe { XStringToKeysym(d_str_ptr as *const i8) }; - let d_code = unsafe { XKeysymToKeycode(display, d_sym) }; - - unsafe { - XTestFakeKeyEvent(display, super_l_code as c_uint, c_int::from(true), 10); - XFlush(display); - XTestFakeKeyEvent(display, d_code as c_uint, c_int::from(true), 10); - XFlush(display); - - XTestFakeKeyEvent(display, d_code as c_uint, c_int::from(false), 10); - XFlush(display); - XTestFakeKeyEvent(display, super_l_code as c_uint, c_int::from(false), 10); - XFlush(display); - - XCloseDisplay(display); - } - - println!("play: {}", add(5, 10)); + display.close(); } -fn get_remote(dpy_name: String) -> *mut Display { - let dpy_name = CString::new(dpy_name).unwrap(); - let display_ptr: *const u8 = dpy_name.as_bytes().as_ptr(); - let display = unsafe { XOpenDisplay(display_ptr as *const i8) }; +fn get_remote(display_name: Option) -> XDisplay { + let display = XDisplay::open(display_name); - let mut ev = c_int::default(); - let mut err = c_int::default(); - let mut version = (c_int::default(), c_int::default()); - - if unsafe { XTestQueryExtension(display, &mut ev, &mut err, &mut version.0, &mut version.1) } == 0 { + if !display.has_xtest() { eprintln!("XTest not supported!"); - unsafe { XCloseDisplay(display) }; + display.close(); exit(1) } - unsafe { - XTestGrabControl(display, c_int::from(true)); - XSync(display, c_int::from(false)); - }; + display.grab_control(); + display.sync(); display } diff --git a/src/lib.rs b/src/lib.rs index 2816d93..1761e8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +extern crate core; + +pub mod x11_safe_wrapper; + pub fn add(a: i32, b: i32) -> i32 { a + b } diff --git a/src/x11_safe_wrapper.rs b/src/x11_safe_wrapper.rs new file mode 100644 index 0000000..dc9b6c4 --- /dev/null +++ b/src/x11_safe_wrapper.rs @@ -0,0 +1,94 @@ +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}; + +pub struct XDisplay { + ptr: *mut Display, +} + +type Keysym = c_ulong; +type Keycode = c_uint; + +const FALSE_C: c_int = 0; +const TRUE_C: c_int = 1; + +impl XDisplay { + pub fn open(display_name: Option) -> Self { + let name = CString::new(if let Some(name) = display_name { name } 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 } + } + + pub fn close(self) { + unsafe { XCloseDisplay(self.ptr) }; + } + + pub fn sync(&self) { + unsafe { XSync(self.ptr, c_int::from(false)); } + } + + pub fn flush(&self) { + unsafe { XFlush(self.ptr); } + } + + pub fn keysym_to_keycode(&self, keysym: c_ulong) -> Keycode { + unsafe { XKeysymToKeycode(self.ptr, keysym) as Keycode } + } + + pub fn string_to_keycode(&self, string: &[u8]) -> Keycode { + self.keysym_to_keycode(string_to_keysym(string)) + } + + pub fn has_xtest(&self) -> bool { + let mut vals: (c_int, c_int, c_int, c_int) = (0, 0, 0, 0); + let has_extension = unsafe { + XTestQueryExtension( + self.ptr, + &mut vals.0, + &mut vals.1, + &mut vals.2, + &mut vals.3 + ) + }; + has_extension != 0 + } + + pub fn send_fake_keypress_from_string(&self, string: &[u8]) { + self.send_fake_keypress_from_keysym(string_to_keysym(string)) + } + + pub fn send_fake_keypress_from_keysym(&self, ks: Keysym) { + self.send_fake_keypress_from_code(self.keysym_to_keycode(ks)) + } + + pub fn send_fake_keypress_from_code(&self, code: Keycode) { + unsafe { XTestFakeKeyEvent(self.ptr, code, TRUE_C, 10) }; + self.flush(); + } + + pub fn send_fake_keyrelease_from_string(&self, string: &[u8]) { + self.send_fake_keyrelease_from_keysym(string_to_keysym(string)) + } + + pub fn send_fake_keyrelease_from_keysym(&self, ks: Keysym) { + self.send_fake_keyrelease_from_code(self.keysym_to_keycode(ks)) + } + + pub fn send_fake_keyrelease_from_code(&self, code: Keycode) { + unsafe { XTestFakeKeyEvent(self.ptr, code, FALSE_C, 10) }; + self.flush(); + } + + pub fn grab_control(&self) { + unsafe { XTestGrabControl(self.ptr, TRUE_C); } + } +} + +/// Wrapper for XStringToKeysym. Remember to give a null terminated string! +pub fn string_to_keysym(string: &[u8]) -> Keysym { + unsafe { XStringToKeysym(string.as_ptr() as *const c_char) } +} \ No newline at end of file