11use super :: { error:: MacosCaptureCreationError , Capture , CaptureError , CaptureEvent , Position } ;
22use async_trait:: async_trait;
33use bitflags:: bitflags;
4- use core_foundation:: base:: { kCFAllocatorDefault, CFRelease } ;
5- use core_foundation:: date:: CFTimeInterval ;
6- use core_foundation:: number:: { kCFBooleanTrue, CFBooleanRef } ;
7- use core_foundation:: runloop:: { kCFRunLoopCommonModes, CFRunLoop , CFRunLoopSource } ;
8- use core_foundation:: string:: { kCFStringEncodingUTF8, CFStringCreateWithCString , CFStringRef } ;
9- use core_graphics:: base:: { kCGErrorSuccess, CGError } ;
10- use core_graphics:: display:: { CGDisplay , CGPoint } ;
11- use core_graphics:: event:: {
12- CGEvent , CGEventFlags , CGEventTap , CGEventTapLocation , CGEventTapOptions , CGEventTapPlacement ,
13- CGEventTapProxy , CGEventType , CallbackResult , EventField ,
4+ use core_foundation:: {
5+ base:: { kCFAllocatorDefault, CFRelease } ,
6+ date:: CFTimeInterval ,
7+ number:: { kCFBooleanTrue, CFBooleanRef } ,
8+ runloop:: { kCFRunLoopCommonModes, CFRunLoop , CFRunLoopSource } ,
9+ string:: { kCFStringEncodingUTF8, CFStringCreateWithCString , CFStringRef } ,
10+ } ;
11+ use core_graphics:: {
12+ base:: { kCGErrorSuccess, CGError } ,
13+ display:: { CGDisplay , CGPoint } ,
14+ event:: {
15+ CGEvent , CGEventFlags , CGEventTap , CGEventTapLocation , CGEventTapOptions ,
16+ CGEventTapPlacement , CGEventTapProxy , CGEventType , CallbackResult , EventField ,
17+ } ,
18+ event_source:: { CGEventSource , CGEventSourceStateID } ,
1419} ;
15- use core_graphics:: event_source:: { CGEventSource , CGEventSourceStateID } ;
1620use futures_core:: Stream ;
1721use input_event:: { Event , KeyboardEvent , PointerEvent , BTN_LEFT , BTN_MIDDLE , BTN_RIGHT } ;
1822use keycode:: { KeyMap , KeyMapping } ;
1923use libc:: c_void;
2024use once_cell:: unsync:: Lazy ;
21- use std:: collections:: HashSet ;
22- use std:: ffi:: { c_char, CString } ;
23- use std:: pin:: Pin ;
24- use std:: sync:: Arc ;
25- use std:: task:: { ready, Context , Poll } ;
26- use std:: thread:: { self } ;
27- use tokio:: sync:: mpsc:: { self , Receiver , Sender } ;
28- use tokio:: sync:: { oneshot, Mutex } ;
25+ use std:: {
26+ collections:: HashSet ,
27+ ffi:: { c_char, CString } ,
28+ pin:: Pin ,
29+ sync:: Arc ,
30+ task:: { ready, Context , Poll } ,
31+ thread:: { self } ,
32+ } ;
33+ use tokio:: sync:: {
34+ mpsc:: { self , Receiver , Sender } ,
35+ oneshot, Mutex ,
36+ } ;
2937
3038#[ derive( Debug , Default ) ]
3139struct Bounds {
@@ -37,9 +45,15 @@ struct Bounds {
3745
3846#[ derive( Debug ) ]
3947struct InputCaptureState {
48+ /// active capture positions
4049 active_clients : Lazy < HashSet < Position > > ,
50+ /// the currently entered capture position, if any
4151 current_pos : Option < Position > ,
52+ /// position where the cursor was captured
53+ enter_position : Option < CGPoint > ,
54+ /// bounds of the input capture area
4255 bounds : Bounds ,
56+ /// current state of modifier keys
4357 modifier_state : XMods ,
4458}
4559
@@ -57,6 +71,7 @@ impl InputCaptureState {
5771 let mut res = Self {
5872 active_clients : Lazy :: new ( HashSet :: new) ,
5973 current_pos : None ,
74+ enter_position : None ,
6075 bounds : Bounds :: default ( ) ,
6176 modifier_state : Default :: default ( ) ,
6277 } ;
@@ -98,45 +113,34 @@ impl InputCaptureState {
98113 Ok ( ( ) )
99114 }
100115
101- // We can't disable mouse movement when in a client so we need to reset the cursor position
102- // to the edge of the screen, the cursor will be hidden but we dont want it to appear in a
103- // random location when we exit the client
104- fn reset_mouse_position ( & self , event : & CGEvent ) -> Result < ( ) , CaptureError > {
105- if let Some ( pos) = self . current_pos {
106- let location = event. location ( ) ;
107- let edge_offset = 1.0 ;
108-
109- // After the cursor is warped no event is produced but the next event
110- // will carry the delta from the warp so only half the delta is needed to move the cursor
111- let delta_y = event. get_double_value_field ( EventField :: MOUSE_EVENT_DELTA_Y ) / 2.0 ;
112- let delta_x = event. get_double_value_field ( EventField :: MOUSE_EVENT_DELTA_X ) / 2.0 ;
113-
114- let mut new_x = location. x + delta_x;
115- let mut new_y = location. y + delta_y;
116-
117- match pos {
118- Position :: Left => {
119- new_x = self . bounds . xmin + edge_offset;
120- }
121- Position :: Right => {
122- new_x = self . bounds . xmax - edge_offset;
123- }
124- Position :: Top => {
125- new_y = self . bounds . ymin + edge_offset;
126- }
127- Position :: Bottom => {
128- new_y = self . bounds . ymax - edge_offset;
129- }
130- }
131- let new_pos = CGPoint :: new ( new_x, new_y) ;
116+ /// start the input capture by
117+ fn start_capture ( & mut self , event : & CGEvent , position : Position ) -> Result < ( ) , CaptureError > {
118+ let mut location = event. location ( ) ;
119+ let edge_offset = 1.0 ;
120+ // move cursor location to display bounds
121+ match position {
122+ Position :: Left => location. x = self . bounds . xmin + edge_offset,
123+ Position :: Right => location. x = self . bounds . xmax - edge_offset,
124+ Position :: Top => location. y = self . bounds . ymin + edge_offset,
125+ Position :: Bottom => location. y = self . bounds . ymax - edge_offset,
126+ } ;
127+ self . enter_position = Some ( location) ;
128+ self . reset_cursor ( )
129+ }
132130
133- log:: trace!( "Resetting cursor position to: {new_x}, {new_y}" ) ;
131+ /// resets the cursor to the position, where the capture started
132+ fn reset_cursor ( & mut self ) -> Result < ( ) , CaptureError > {
133+ let pos = self . enter_position . expect ( "capture active" ) ;
134+ log:: trace!( "Resetting cursor position to: {}, {}" , pos. x, pos. y) ;
135+ CGDisplay :: warp_mouse_cursor_position ( pos) . map_err ( CaptureError :: WarpCursor )
136+ }
134137
135- return CGDisplay :: warp_mouse_cursor_position ( new_pos )
136- . map_err ( CaptureError :: WarpCursor ) ;
137- }
138+ fn hide_cursor ( & self ) -> Result < ( ) , CaptureError > {
139+ CGDisplay :: hide_cursor ( & CGDisplay :: main ( ) ) . map_err ( CaptureError :: CoreGraphics )
140+ }
138141
139- Err ( CaptureError :: ResetMouseWithoutClient )
142+ fn show_cursor ( & self ) -> Result < ( ) , CaptureError > {
143+ CGDisplay :: show_cursor ( & CGDisplay :: main ( ) ) . map_err ( CaptureError :: CoreGraphics )
140144 }
141145
142146 async fn handle_producer_event (
@@ -147,15 +151,13 @@ impl InputCaptureState {
147151 match producer_event {
148152 ProducerEvent :: Release => {
149153 if self . current_pos . is_some ( ) {
150- CGDisplay :: show_cursor ( & CGDisplay :: main ( ) )
151- . map_err ( CaptureError :: CoreGraphics ) ?;
154+ self . show_cursor ( ) ?;
152155 self . current_pos = None ;
153156 }
154157 }
155158 ProducerEvent :: Grab ( pos) => {
156159 if self . current_pos . is_none ( ) {
157- CGDisplay :: hide_cursor ( & CGDisplay :: main ( ) )
158- . map_err ( CaptureError :: CoreGraphics ) ?;
160+ self . hide_cursor ( ) ?;
159161 self . current_pos = Some ( pos) ;
160162 }
161163 }
@@ -165,8 +167,7 @@ impl InputCaptureState {
165167 ProducerEvent :: Destroy ( p) => {
166168 if let Some ( current) = self . current_pos {
167169 if current == p {
168- CGDisplay :: show_cursor ( & CGDisplay :: main ( ) )
169- . map_err ( CaptureError :: CoreGraphics ) ?;
170+ self . show_cursor ( ) ?;
170171 self . current_pos = None ;
171172 } ;
172173 }
@@ -364,7 +365,7 @@ fn create_event_tap<'a>(
364365 move |_proxy : CGEventTapProxy , event_type : CGEventType , cg_ev : & CGEvent | {
365366 log:: trace!( "Got event from tap: {event_type:?}" ) ;
366367 let mut state = client_state. blocking_lock ( ) ;
367- let mut pos = None ;
368+ let mut capture_position = None ;
368369 let mut res_events = vec ! [ ] ;
369370
370371 if matches ! (
@@ -381,7 +382,7 @@ fn create_event_tap<'a>(
381382
382383 // Are we in a client?
383384 if let Some ( current_pos) = state. current_pos {
384- pos = Some ( current_pos) ;
385+ capture_position = Some ( current_pos) ;
385386 get_events (
386387 & event_type,
387388 cg_ev,
@@ -393,24 +394,30 @@ fn create_event_tap<'a>(
393394 } ) ;
394395
395396 // Keep (hidden) cursor at the edge of the screen
396- if matches ! ( event_type, CGEventType :: MouseMoved ) {
397- state. reset_mouse_position ( cg_ev) . unwrap_or_else ( |e| {
398- log:: error!( "Failed to reset mouse position: {e}" ) ;
399- } )
397+ if matches ! (
398+ event_type,
399+ CGEventType :: MouseMoved
400+ | CGEventType :: LeftMouseDragged
401+ | CGEventType :: RightMouseDragged
402+ | CGEventType :: OtherMouseDragged
403+ ) {
404+ state. reset_cursor ( ) . unwrap_or_else ( |e| log:: warn!( "{e}" ) ) ;
400405 }
401- }
402- // Did we cross a barrier?
403- else if matches ! ( event_type, CGEventType :: MouseMoved ) {
406+ } else if matches ! ( event_type, CGEventType :: MouseMoved ) {
407+ // Did we cross a barrier?
404408 if let Some ( new_pos) = state. crossed ( cg_ev) {
405- pos = Some ( new_pos) ;
409+ capture_position = Some ( new_pos) ;
410+ state
411+ . start_capture ( cg_ev, new_pos)
412+ . unwrap_or_else ( |e| log:: warn!( "{e}" ) ) ;
406413 res_events. push ( CaptureEvent :: Begin ) ;
407414 notify_tx
408415 . blocking_send ( ProducerEvent :: Grab ( new_pos) )
409416 . expect ( "Failed to send notification" ) ;
410417 }
411418 }
412419
413- if let Some ( pos) = pos {
420+ if let Some ( pos) = capture_position {
414421 res_events. iter ( ) . for_each ( |e| {
415422 // error must be ignored, since the event channel
416423 // may already be closed when the InputCapture instance is dropped.
@@ -515,10 +522,7 @@ impl MacOSInputCapture {
515522 log:: error!( "Failed to handle producer event: {e}" ) ;
516523 } )
517524 }
518-
519- _ = & mut tap_exit_rx => {
520- break ;
521- }
525+ _ = & mut tap_exit_rx => break ,
522526 }
523527 }
524528 // show cursor
0 commit comments