Global NSEvent listener and some mouse methods (#94)

* Support for all NSEvent types and configurable event monitoring

* Useful mouse event methods

* rustfmt nightly fixes

* Use standard kind naming convention
This commit is contained in:
Adam Gastineau 2023-07-13 17:22:54 -07:00 committed by GitHub
parent aedcfe1c65
commit 01507f7642
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 146 additions and 10 deletions

View file

@ -19,6 +19,7 @@ default-target = "x86_64-apple-darwin"
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
bitmask-enum = "2.2.1"
block = "0.1.6"
core-foundation = "0.9"
core-graphics = "0.23"

View file

@ -1,15 +1,54 @@
use bitmask_enum::bitmask;
use block::ConcreteBlock;
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use crate::foundation::{id, nil, NSString};
use crate::events::EventType;
use crate::foundation::{id, nil, NSInteger, NSPoint, NSString};
/// An EventMask describes the type of event.
#[derive(Debug)]
#[bitmask(u64)]
pub enum EventMask {
KeyDown
LeftMouseDown = 1 << 1,
LeftMouseUp = 1 << 2,
RightMouseDown = 1 << 3,
RightMouseUp = 1 << 4,
MouseMoved = 1 << 5,
LeftMouseDragged = 1 << 6,
RightMouseDragged = 1 << 7,
MouseEntered = 1 << 8,
MouseExited = 1 << 9,
KeyDown = 1 << 10,
KeyUp = 1 << 11,
FlagsChanged = 1 << 12,
AppKitDefined = 1 << 13,
SystemDefined = 1 << 14,
ApplicationDefined = 1 << 15,
Periodic = 1 << 16,
CursorUpdate = 1 << 17,
ScrollWheel = 1 << 22,
TabletPoint = 1 << 23,
TabletProximity = 1 << 24,
OtherMouseDown = 1 << 25,
OtherMouseUp = 1 << 26,
OtherMouseDragged = 1 << 27,
Gesture = 1 << 29,
Magnify = 1 << 30,
Swipe = 1 << 31,
Rotate = 1 << 18,
BeginGesture = 1 << 19,
EndGesture = 1 << 20,
SmartMagnify = 1 << 32,
QuickLook = 1 << 33,
Pressure = 1 << 34,
DirectTouch = 1 << 37,
ChangeMode = 1 << 38
}
/// A wrapper over an `NSEvent`.
@ -25,6 +64,16 @@ impl Event {
Event(unsafe { Id::from_ptr(objc) })
}
/// The event's type.
///
/// Corresponds to the `type` getter.
pub fn kind(&self) -> EventType {
let kind: NSUInteger = unsafe { msg_send![&*self.0, type] };
unsafe { ::std::mem::transmute(kind) }
}
/// The characters associated with a key-up or key-down event.
pub fn characters(&self) -> String {
// @TODO: Check here if key event, invalid otherwise.
// @TODO: Figure out if we can just return &str here, since the Objective-C side
@ -34,6 +83,26 @@ impl Event {
characters.to_string()
}
/// The indices of the currently pressed mouse buttons.
pub fn pressed_mouse_buttons() -> NSUInteger {
unsafe { msg_send![class!(NSEvent), pressedMouseButtons] }
}
/// Reports the current mouse position in screen coordinates.
pub fn mouse_location() -> NSPoint {
unsafe { msg_send![class!(NSEvent), mouseLocation] }
}
/// The button number for a mouse event.
pub fn button_number(&self) -> NSInteger {
unsafe { msg_send![&*self.0, buttonNumber] }
}
/// The number of mouse clicks associated with a mouse-down or mouse-up event.
pub fn click_count(&self) -> NSInteger {
unsafe { msg_send![&*self.0, clickCount] }
}
/*pub fn contains_modifier_flags(&self, flags: &[EventModifierFlag]) -> bool {
let modifier_flags: NSUInteger = unsafe {
msg_send![&*self.0, modifierFlags]
@ -47,13 +116,13 @@ impl Event {
false
}*/
/// Register an event handler with the system event stream. This method
/// Register an event handler with the local system event stream. This method
/// watches for events that occur _within the application_. Events outside
/// of the application require installing a `monitor_global_events` handler.
/// of the application require installing a `global_monitor` handler.
///
/// Note that in order to monitor all possible events, both local and global
/// monitors are required - the streams don't mix.
pub fn local_monitor<F>(_mask: EventMask, handler: F) -> EventMonitor
pub fn local_monitor<F>(mask: EventMask, handler: F) -> EventMonitor
where
F: Fn(Event) -> Option<Event> + Send + Sync + 'static
{
@ -68,7 +137,33 @@ impl Event {
let block = block.copy();
EventMonitor(unsafe {
msg_send![class!(NSEvent), addLocalMonitorForEventsMatchingMask:1024
msg_send![class!(NSEvent), addLocalMonitorForEventsMatchingMask:mask.bits
handler:block]
})
}
/// Register an event handler with the global system event stream. This method
/// watches for events that occur _outside the application_. Events within
/// the application require installing a `local_monitor` handler.
///
/// Note that in order to monitor all possible events, both local and global
/// monitors are required - the streams don't mix.
pub fn global_monitor<F>(mask: EventMask, handler: F) -> EventMonitor
where
F: Fn(Event) -> Option<Event> + Send + Sync + 'static
{
let block = ConcreteBlock::new(move |event: id| {
let evt = Event::new(event);
match handler(evt) {
Some(mut evt) => &mut *evt.0,
None => nil
}
});
let block = block.copy();
EventMonitor(unsafe {
msg_send![class!(NSEvent), addGlobalMonitorForEventsMatchingMask:mask.bits
handler:block]
})
}

View file

@ -48,7 +48,45 @@ impl From<&EventModifierFlag> for NSUInteger {
/// Represents an event type that you can request to be notified about.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(target_pointer_width = "32", repr(u32))]
#[cfg_attr(target_pointer_width = "64", repr(u64))]
pub enum EventType {
/// A keydown event.
KeyDown
LeftMouseDown = 1,
LeftMouseUp = 2,
RightMouseDown = 3,
RightMouseUp = 4,
MouseMoved = 5,
LeftMouseDragged = 6,
RightMouseDragged = 7,
MouseEntered = 8,
MouseExited = 9,
KeyDown = 10,
KeyUp = 11,
FlagsChanged = 12,
AppKitDefined = 13,
SystemDefined = 14,
ApplicationDefined = 15,
Periodic = 16,
CursorUpdate = 17,
ScrollWheel = 22,
TabletPoint = 23,
TabletProximity = 24,
OtherMouseDown = 25,
OtherMouseUp = 26,
OtherMouseDragged = 27,
Gesture = 29,
Magnify = 30,
Swipe = 31,
Rotate = 18,
BeginGesture = 19,
EndGesture = 20,
SmartMagnify = 32,
QuickLook = 33,
Pressure = 34,
DirectTouch = 37,
ChangeMode = 38
}

View file

@ -86,3 +86,5 @@ pub type NSInteger = libc::c_long;
/// Platform-specific.
#[cfg(target_pointer_width = "64")]
pub type NSUInteger = libc::c_ulong;
pub type NSPoint = core_graphics::geometry::CGPoint;