Skip to content

Commit

Permalink
Global NSEvent listener and some mouse methods (#94)
Browse files Browse the repository at this point in the history
* Support for all NSEvent types and configurable event monitoring

* Useful mouse event methods

* rustfmt nightly fixes

* Use standard kind naming convention
  • Loading branch information
agg23 authored Jul 14, 2023
1 parent aedcfe1 commit 01507f7
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 10 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -104,4 +105,4 @@ name = "safe_area"
required-features = ["appkit"]
[[example]]
name = "popover"
required-features = ["appkit"]
required-features = ["appkit"]
109 changes: 102 additions & 7 deletions src/appkit/event/mod.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand All @@ -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
Expand All @@ -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]
Expand All @@ -47,13 +116,39 @@ 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
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), 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 local_monitor<F>(_mask: EventMask, handler: F) -> EventMonitor
pub fn global_monitor<F>(mask: EventMask, handler: F) -> EventMonitor
where
F: Fn(Event) -> Option<Event> + Send + Sync + 'static
{
Expand All @@ -68,7 +163,7 @@ impl Event {
let block = block.copy();

EventMonitor(unsafe {
msg_send![class!(NSEvent), addLocalMonitorForEventsMatchingMask:1024
msg_send![class!(NSEvent), addGlobalMonitorForEventsMatchingMask:mask.bits
handler:block]
})
}
Expand Down
42 changes: 40 additions & 2 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 2 additions & 0 deletions src/foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 01507f7

Please sign in to comment.