diff --git a/src/ev_callback_data.rs b/src/ev_callback_data.rs index 50a9cad..5c269f9 100644 --- a/src/ev_callback_data.rs +++ b/src/ev_callback_data.rs @@ -81,7 +81,11 @@ impl EvCallbackData { } } -fn calculate_delay(server_time: xlib::Time, last_event: xlib::Time, max_delay: Option) -> xlib::Time { +fn calculate_delay( + server_time: xlib::Time, + last_event: xlib::Time, + max_delay: Option, +) -> xlib::Time { if let Some(max) = max_delay { let max = max as u64; let delay = server_time - last_event; diff --git a/src/macro_writer.rs b/src/macro_writer.rs index c569697..cecc8a3 100644 --- a/src/macro_writer.rs +++ b/src/macro_writer.rs @@ -1,5 +1,5 @@ use crate::Instructions; -use std::{io, fs}; +use std::{fs, io}; pub struct MacroWriter { outfile: Box, @@ -9,19 +9,18 @@ pub struct MacroWriter { impl MacroWriter { pub fn new(outfile: Option, ignore_delay_capturing: bool) -> Self { Self { - outfile: - if let Some(outfile) = outfile { - Box::new(fs::File::create(outfile).expect("Failed to create output file")) - } else { - Box::new(io::stdout()) - }, + outfile: if let Some(outfile) = outfile { + Box::new(fs::File::create(outfile).expect("Failed to create output file")) + } else { + Box::new(io::stdout()) + }, ignore_delay_capturing, } } pub fn write(&mut self, instruction: Instructions) { if self.ignore_delay_capturing { - if let Instructions::Delay(_) = instruction { } + if let Instructions::Delay(_) = instruction {} } writeln!(&mut self.outfile, "{}", instruction) diff --git a/src/xwrap/display.rs b/src/xwrap/display.rs index 2b4580b..1bfce90 100644 --- a/src/xwrap/display.rs +++ b/src/xwrap/display.rs @@ -1,4 +1,4 @@ -use std::{env, ffi, ptr, ops}; +use std::{env, ffi, ops, ptr}; use x11::xlib::{self, BadGC}; @@ -26,7 +26,7 @@ pub enum Grabbables { Pointer, } -impl std::fmt::Display for Grabbables{ +impl std::fmt::Display for Grabbables { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Grabbables::Keyboard => write!(f, "keyboard"), @@ -50,6 +50,33 @@ impl From for i32 { } } +/// The event mode for XAllowEvents +pub enum EventMode { + AsyncPointer, + SyncPointer, + AsyncKeyboard, + SyncKeyboard, + ReplayPointer, + ReplayKeyboard, + AsyncBoth, + SyncBoth +} + +impl From for i32 { + fn from(event_mode: EventMode) -> Self { + match event_mode { + EventMode::AsyncPointer => xlib::AsyncPointer, + EventMode::SyncPointer => xlib::SyncPointer, + EventMode::AsyncKeyboard => xlib::AsyncKeyboard, + EventMode::SyncKeyboard => xlib::SyncKeyboard, + EventMode::ReplayPointer => xlib::ReplayPointer, + EventMode::ReplayKeyboard => xlib::ReplayKeyboard, + EventMode::AsyncBoth => xlib::AsyncBoth, + EventMode::SyncBoth => xlib::SyncBoth, + } + } +} + impl Display { /// Call XOpenDisplay to open a connection to the X Server. /// If `display_name` is `None`, the value of the `DISPLAY` environment variable will be used. @@ -87,7 +114,7 @@ impl Display { // TODO: Figure out how to properly handle errors /// Calls XSync to flush the output buffer and then wait until all events have been received and processed - /// by the server. + /// by the server. /// The `discard` parameter specifies, whether to discard all events in the queue. pub fn sync(&self, discard: bool) { unsafe { @@ -95,30 +122,24 @@ impl Display { } } - /// Calls xlib::XDefaultScreen to get the default screen number referenced by Display::open. + /// Calls xlib::XDefaultScreen to get the default screen number referenced by Display::open. /// This should be used to retrieve the screen number in applications that'll only use a single /// screen. pub fn default_screen_nr(&self) -> i32 { - unsafe { - xlib::XDefaultScreen(self.ptr) - } + unsafe { xlib::XDefaultScreen(self.ptr) } } /// Gets the default screen pub fn default_screen(&self) -> screen::Screen { screen::Screen { - ptr: unsafe { - xlib::XDefaultScreenOfDisplay(self.ptr) - } + ptr: unsafe { xlib::XDefaultScreenOfDisplay(self.ptr) }, } } /// Gets the root window of the default screen pub fn default_root_window(&self) -> window::Window { window::Window { - wid: unsafe { - xlib::XDefaultRootWindow(self.ptr) - } + wid: unsafe { xlib::XDefaultRootWindow(self.ptr) }, } } @@ -133,35 +154,69 @@ impl Display { } } - pub fn grab_keyboard( &mut self, grab_window: window::Window, owner_events: bool, pointer_mode: GrabMode, keyboard_mode: GrabMode, time:xlib::Time) -> Result<()> { - match unsafe { - xlib::XGrabKeyboard( - self.ptr, - grab_window.wid, - owner_events.into(), - pointer_mode.into(), - keyboard_mode.into(), - time - ) - } { - xlib::GrabSuccess => { - self.keyboard_grab = Some(GrabbablesModes { - keyboard_mode, - pointer_mode - }); + /// Performs an active Grab on the keyboard. Further key events are only reported to the + /// grabbing client. + pub fn grab_keyboard( + &mut self, + grab_window: window::Window, + owner_events: bool, + pointer_mode: GrabMode, + keyboard_mode: GrabMode, + time: xlib::Time, + ) -> Result<()> { + if let None = self.keyboard_grab { + match unsafe { + xlib::XGrabKeyboard( + self.ptr, + grab_window.wid, + owner_events.into(), + pointer_mode.into(), + keyboard_mode.into(), + time, + ) + } { + xlib::GrabSuccess => { + self.keyboard_grab = Some(GrabbablesModes { + keyboard_mode, + pointer_mode, + }); + Ok(()) + } + xlib::AlreadyGrabbed => + Err(error::XError::XAlreadyGrabbed(Grabbables::Keyboard).into()), + xlib::GrabInvalidTime => + Err(error::XError::XGrabInvalidTime.into()), + xlib::GrabNotViewable => + Err(error::XError::XGrabNotViewable.into()), + xlib::GrabFrozen => + Err(error::XError::XGrabFrozen(Grabbables::Keyboard).into()), + code => Err(error::XError::UnknownError(code).into()), + } + } else { + Err(error::XError::AlreadyGrabbed(Grabbables::Keyboard).into()) + } + } + + /// Ends the active keyboard grab. + pub fn ungrab_keyboard(&self, time: xlib::Time) -> Result<()>{ + if let Some(_) = self.keyboard_grab { + unsafe { + xlib::XUngrabKeyboard(self.ptr, time); Ok(()) - }, - xlib::AlreadyGrabbed => todo!(), - xlib::GrabInvalidTime => todo!(), - xlib::GrabNotViewable => todo!(), - xlib::GrabFrozen => todo!(), - _ => todo!() + } + } else { + Err(error::XError::NotGrabbed(Grabbables::Keyboard).into()) + } + } + + pub fn allow_events(&self, event_mode: EventMode, time: xlib::Time) { + unsafe { + xlib::XAllowEvents(self.ptr, event_mode.into(), time); } } } - impl Drop for Display { fn drop(&mut self) { if unsafe { xlib::XCloseDisplay(self.ptr) } == BadGC.into() { diff --git a/src/xwrap/error.rs b/src/xwrap/error.rs index f16f459..cbda7ad 100644 --- a/src/xwrap/error.rs +++ b/src/xwrap/error.rs @@ -11,10 +11,13 @@ pub enum XError { DisplayKeycodesError, InvalidKeycodeError(xlib::KeyCode, ops::Range), AlreadyGrabbed(display::Grabbables), + NotGrabbed(display::Grabbables), XAlreadyGrabbed(display::Grabbables), XGrabFrozen(display::Grabbables), XGrabInvalidTime, XGrabNotViewable, + XEventConversionError { event_type: i32 }, + UnknownError(i32), } impl std::fmt::Display for XError { @@ -25,20 +28,32 @@ impl std::fmt::Display for XError { match self { XError::OpenDisplayError(display_name) => format!("error when opening display '{}'", display_name), - XError::DisplayKeycodesError => - String::from("error when running XDisplayKeycodes"), - XError::InvalidKeycodeError(code, range) => - format!("keycode {} outside of range {:?}", code, range), - XError::AlreadyGrabbed(thing_attempted_to_grab) => - format!("this display already grabbed the {}", thing_attempted_to_grab), - XError::XAlreadyGrabbed(thing_attempted_to_grab) => - format!("{} is already actively grabbed by another client", thing_attempted_to_grab), - XError::XGrabFrozen(thing_attempted_to_grab) => - format!("{} is frozen by an active grab of another client", thing_attempted_to_grab), - XError::XGrabInvalidTime => - String::from("invalid grab time"), - XError::XGrabNotViewable => - String::from("grab_window is not viewable"), + XError::DisplayKeycodesError => String::from("error when running XDisplayKeycodes"), + XError::InvalidKeycodeError(code, range) => + format!("keycode {} outside of range {:?}", code, range), + XError::AlreadyGrabbed(thing_attempted_to_grab) => format!( + "this display already grabbed the {}", + thing_attempted_to_grab + ), + XError::NotGrabbed(thing_attempted_to_ungrab) => format!( + "couldn't ungrab the {} because it wasn't grabbed", + thing_attempted_to_ungrab + ), + XError::XAlreadyGrabbed(thing_attempted_to_grab) => format!( + "{} is already actively grabbed by another client", + thing_attempted_to_grab + ), + XError::XGrabFrozen(thing_attempted_to_grab) => format!( + "{} is frozen by an active grab of another client", + thing_attempted_to_grab + ), + XError::XGrabInvalidTime => String::from("invalid grab time"), + XError::XGrabNotViewable => String::from("grab_window is not viewable"), + XError::XEventConversionError { event_type } => format!( + "invalid event type: {}", + event_type + ), + XError::UnknownError(code) => format!("unknown error code was returned: {}", code), } ) } diff --git a/src/xwrap/event.rs b/src/xwrap/event.rs new file mode 100644 index 0000000..1bc4448 --- /dev/null +++ b/src/xwrap/event.rs @@ -0,0 +1,166 @@ +use x11::xlib::{self, XKeyEvent}; + +use anyhow::Result; + +use super::{display, window, key, error}; + +pub type Vec2 = (T, T); + +pub enum Event { + // only for now relevant event types + KeyPressEvent(KeyEvent), + KeyReleaseEvent(KeyEvent), + ButtonPressEvent(ButtonEvent), + ButtonReleaseEvent(ButtonEvent), + MotionEvent(MotionEvent), + + ErrorEvent(ErrorEvent), + CrossingEvent(CrossingEvent), + FocusChangeEvent(FocusChangeEvent), + ExposeEvent(ExposeEvent), + GraphicsExposeEvent(GraphicsExposeEvent), + NoExposeEvent(NoExposeEvent), + VisibilityEvent(VisibilityEvent), + CreateWindowEvent(CreateWindowEvent), + DestroyWindowEvent(DestroyWindowEvent), + UnmapEvent(UnmapEvent), + MapEvent(MapEvent), + MapRequestEvent(MapRequestEvent), + ReparentEvent(ReparentEvent), + ConfigureEvent(ConfigureEvent), + GravityEvent(GravityEvent), + ResizeRequestEvent(ResizeRequestEvent), + ConfigureRequestEvent(ConfigureRequestEvent), + CirculateEvent(CirculateEvent), + CirculateRequestEvent(CirculateRequestEvent), + PropertyEvent(PropertyEvent), + SelectionClearEvent(SelectionClearEvent), + SelectionRequestEvent(SelectionRequestEvent), + SelectionEvent(SelectionEvent), + ColormapEvent(ColormapEvent), + ClientMessageEvent(ClientMessageEvent), + MappingEvent(MappingEvent), + KeymapEvent(KeymapEvent), +} + +impl TryFrom for Event { + type Error = error::XError; + fn try_from(ev_union: xlib::XEvent) -> Result { + match ev_union.get_type() { + xlib::KeyPress => , + xlib::KeyRelease => , + xlib::ButtonPress => , + xlib::ButtonRelease => , + xlib::MotionNotify => , + } + } +} + +struct GenericEventData { + serial_nr: u64, + sent_by_different_client: bool, + source_display_ptr: *mut xlib::_XDisplay, + window: window::Window, +} + +pub enum SwitchState { + Pressed, + Released +} + +pub struct KeyEvent { + generic: GenericEventData, + root: window::Window, + subwindow: window::Window, + time: xlib::Time, + pointer_pos: Vec2, + pointer_pos_root: Vec2, + state: SwitchState, + key: key::Key, + same_screen: bool +} + +impl TryFrom for KeyEvent { + type Error = error::XError; + fn try_from(raw_ev: xlib::XEvent) -> Result { + let state = match raw_ev.get_type() { + xlib::KeyPress => SwitchState::Pressed, + xlib::KeyRelease => SwitchState::Released, + ev_type => return Err(error::XError::XEventConversionError { event_type: ev_type }) + }; + + let raw_ev = XKeyEvent::from(raw_ev); + + Ok(Self { + generic: GenericEventData { + serial_nr: raw_ev.serial, + sent_by_different_client: raw_ev.send_event != 0, + // make conversion method for display from ptr that maybe should try to check??? + // how would i even do this safely + source_display_ptr: raw_ev.display, + window: window::Window { wid: raw_ev.window }, + }, + root: window::Window { wid: raw_ev.root }, + subwindow: window::Window { wid: raw_ev.subwindow } , + time: raw_ev.time, + pointer_pos: (raw_ev.x, raw_ev.y), + pointer_pos_root: (raw_ev.x_root, raw_ev.y_root), + state: raw_ev.state, + key: key::Key { code: raw_ev.keycode }, + same_screen: raw_ev.same_screen != 0 + }) + } +} + +pub struct ButtonEvent { + generic: GenericEventData, + root: window::Window, + time: xlib::Time, + pointer_pos: Vec2, + pointer_pos_root: Vec2, + button: u32, + state: SwitchState, + same_screen: bool +} + +// change fields and data for this and other events according to xlib doc p. 188-189 +pub struct MotionEvent { + generic: GenericEventData, + root: window::Window, + subwindow: window::Window, + time: xlib::Time, + pointer_pos: Vec2, + pointer_pos_root: Vec2, + state: u32, + is_hint: u32, + same_screen: bool, +} + +// TODO: make these into cool event stuff too +pub struct ErrorEvent {} +pub struct CrossingEvent {} +pub struct FocusChangeEvent {} +pub struct ExposeEvent {} +pub struct GraphicsExposeEvent {} +pub struct NoExposeEvent {} +pub struct VisibilityEvent {} +pub struct CreateWindowEvent {} +pub struct DestroyWindowEvent {} +pub struct UnmapEvent {} +pub struct MapEvent {} +pub struct MapRequestEvent {} +pub struct ReparentEvent {} +pub struct ConfigureEvent {} +pub struct GravityEvent {} +pub struct ResizeRequestEvent {} +pub struct ConfigureRequestEvent {} +pub struct CirculateEvent {} +pub struct CirculateRequestEvent {} +pub struct PropertyEvent {} +pub struct SelectionClearEvent {} +pub struct SelectionRequestEvent {} +pub struct SelectionEvent {} +pub struct ColormapEvent {} +pub struct ClientMessageEvent {} +pub struct MappingEvent {} +pub struct KeymapEvent {} diff --git a/src/xwrap/mod.rs b/src/xwrap/mod.rs index 3f520cb..0292d7f 100644 --- a/src/xwrap/mod.rs +++ b/src/xwrap/mod.rs @@ -1,5 +1,6 @@ pub mod display; -pub mod screen; -pub mod window; pub mod error; pub mod key; +pub mod screen; +pub mod window; +pub mod event;