@@ -7,6 +7,7 @@ use core::ffi::{c_ulong, c_void};
7
7
use core_foundation:: {
8
8
base:: { CFRelease , CFRetain , CFTypeID , TCFType } ,
9
9
mach_port:: { CFMachPort , CFMachPortInvalidate , CFMachPortRef } ,
10
+ runloop:: { kCFRunLoopCommonModes, CFRunLoop } ,
10
11
} ;
11
12
use foreign_types:: { foreign_type, ForeignType } ;
12
13
use std:: { mem:: ManuallyDrop , ptr} ;
@@ -547,7 +548,7 @@ unsafe extern "C" fn cg_event_tap_callback_internal(
547
548
/// use core_graphics::event::{CGEventTap, CGEventTapLocation, CGEventTapPlacement, CGEventTapOptions, CGEventType, CallbackResult};
548
549
/// let current = CFRunLoop::get_current();
549
550
///
550
- /// CGEventTap::with (
551
+ /// CGEventTap::with_enabled (
551
552
/// CGEventTapLocation::HID,
552
553
/// CGEventTapPlacement::HeadInsertEventTap,
553
554
/// CGEventTapOptions::Default,
@@ -556,15 +557,7 @@ unsafe extern "C" fn cg_event_tap_callback_internal(
556
557
/// println!("{:?}", event.location());
557
558
/// CallbackResult::Keep
558
559
/// },
559
- /// |tap| {
560
- /// let loop_source = tap
561
- /// .mach_port()
562
- /// .create_runloop_source(0)
563
- /// .expect("Runloop source creation failed");
564
- /// current.add_source(&loop_source, unsafe { kCFRunLoopCommonModes });
565
- /// tap.enable();
566
- /// CFRunLoop::run_current();
567
- /// },
560
+ /// || CFRunLoop::run_current(),
568
561
/// ).expect("Failed to install event tap");
569
562
/// ```
570
563
#[ must_use = "CGEventTap is disabled when dropped" ]
@@ -574,38 +567,53 @@ pub struct CGEventTap<'tap_life> {
574
567
}
575
568
576
569
impl CGEventTap < ' static > {
577
- pub fn new < F : Fn ( CGEventTapProxy , CGEventType , & CGEvent ) -> CallbackResult + ' static > (
570
+ pub fn new < F : Fn ( CGEventTapProxy , CGEventType , & CGEvent ) -> CallbackResult + Send + ' static > (
578
571
tap : CGEventTapLocation ,
579
572
place : CGEventTapPlacement ,
580
573
options : CGEventTapOptions ,
581
574
events_of_interest : std:: vec:: Vec < CGEventType > ,
582
575
callback : F ,
583
576
) -> Result < Self , ( ) > {
584
577
// SAFETY: callback is 'static so even if this object is forgotten it
585
- // will be valid to call.
578
+ // will be valid to call. F is safe to send across threads.
586
579
unsafe { Self :: new_unchecked ( tap, place, options, events_of_interest, callback) }
587
580
}
588
581
}
589
582
590
583
impl < ' tap_life > CGEventTap < ' tap_life > {
591
- pub fn with < R > (
584
+ /// Configures an event tap with the supplied options and callback, then
585
+ /// calls `with_fn`.
586
+ ///
587
+ /// Note that the current thread run loop must run within `with_fn` for the
588
+ /// tap to process events. The tap is destroyed when `with_fn` returns.
589
+ pub fn with_enabled < R > (
592
590
tap : CGEventTapLocation ,
593
591
place : CGEventTapPlacement ,
594
592
options : CGEventTapOptions ,
595
593
events_of_interest : std:: vec:: Vec < CGEventType > ,
596
594
callback : impl Fn ( CGEventTapProxy , CGEventType , & CGEvent ) -> CallbackResult + ' tap_life ,
597
- with_fn : impl FnOnce ( & Self ) -> R ,
595
+ with_fn : impl FnOnce ( ) -> R ,
598
596
) -> Result < R , ( ) > {
599
597
// SAFETY: We are okay to bypass the 'static restriction because the
600
598
// event tap is dropped before returning. The callback therefore cannot
601
- // be called after its lifetime expires.
599
+ // be called after its lifetime expires. Since we only enable the tap
600
+ // on the current thread run loop and don't hand it to user code, we
601
+ // know that the callback will only be called from the current thread.
602
602
let event_tap: Self =
603
603
unsafe { Self :: new_unchecked ( tap, place, options, events_of_interest, callback) ? } ;
604
- Ok ( with_fn ( & event_tap) )
604
+ let loop_source = event_tap
605
+ . mach_port ( )
606
+ . create_runloop_source ( 0 )
607
+ . expect ( "Runloop source creation failed" ) ;
608
+ CFRunLoop :: get_current ( ) . add_source ( & loop_source, unsafe { kCFRunLoopCommonModes } ) ;
609
+ event_tap. enable ( ) ;
610
+ Ok ( with_fn ( ) )
605
611
}
606
612
607
613
/// Caller is responsible for ensuring that this object is dropped before
608
- /// `'tap_life` expires.
614
+ /// `'tap_life` expires. Either state captured by `callback` must be safe to
615
+ /// send across threads, or the tap must only be installed on the current
616
+ /// thread's run loop.
609
617
pub unsafe fn new_unchecked (
610
618
tap : CGEventTapLocation ,
611
619
place : CGEventTapPlacement ,
0 commit comments