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,35 @@ 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) ;
134135
135- return CGDisplay :: warp_mouse_cursor_position ( new_pos)
136- . map_err ( CaptureError :: WarpCursor ) ;
137- }
136+ return CGDisplay :: warp_mouse_cursor_position ( pos) . map_err ( CaptureError :: WarpCursor ) ;
137+ }
138+
139+ fn hide_cursor ( & self ) -> Result < ( ) , CaptureError > {
140+ CGDisplay :: hide_cursor ( & CGDisplay :: main ( ) ) . map_err ( CaptureError :: CoreGraphics )
141+ }
138142
139- Err ( CaptureError :: ResetMouseWithoutClient )
143+ fn show_cursor ( & self ) -> Result < ( ) , CaptureError > {
144+ CGDisplay :: show_cursor ( & CGDisplay :: main ( ) ) . map_err ( CaptureError :: CoreGraphics )
140145 }
141146
142147 async fn handle_producer_event (
@@ -147,15 +152,13 @@ impl InputCaptureState {
147152 match producer_event {
148153 ProducerEvent :: Release => {
149154 if self . current_pos . is_some ( ) {
150- CGDisplay :: show_cursor ( & CGDisplay :: main ( ) )
151- . map_err ( CaptureError :: CoreGraphics ) ?;
155+ self . show_cursor ( ) ?;
152156 self . current_pos = None ;
153157 }
154158 }
155159 ProducerEvent :: Grab ( pos) => {
156160 if self . current_pos . is_none ( ) {
157- CGDisplay :: hide_cursor ( & CGDisplay :: main ( ) )
158- . map_err ( CaptureError :: CoreGraphics ) ?;
161+ self . hide_cursor ( ) ?;
159162 self . current_pos = Some ( pos) ;
160163 }
161164 }
@@ -165,8 +168,7 @@ impl InputCaptureState {
165168 ProducerEvent :: Destroy ( p) => {
166169 if let Some ( current) = self . current_pos {
167170 if current == p {
168- CGDisplay :: show_cursor ( & CGDisplay :: main ( ) )
169- . map_err ( CaptureError :: CoreGraphics ) ?;
171+ self . show_cursor ( ) ?;
170172 self . current_pos = None ;
171173 } ;
172174 }
@@ -364,7 +366,7 @@ fn create_event_tap<'a>(
364366 move |_proxy : CGEventTapProxy , event_type : CGEventType , cg_ev : & CGEvent | {
365367 log:: trace!( "Got event from tap: {event_type:?}" ) ;
366368 let mut state = client_state. blocking_lock ( ) ;
367- let mut pos = None ;
369+ let mut capture_position = None ;
368370 let mut res_events = vec ! [ ] ;
369371
370372 if matches ! (
@@ -381,7 +383,7 @@ fn create_event_tap<'a>(
381383
382384 // Are we in a client?
383385 if let Some ( current_pos) = state. current_pos {
384- pos = Some ( current_pos) ;
386+ capture_position = Some ( current_pos) ;
385387 get_events (
386388 & event_type,
387389 cg_ev,
@@ -393,24 +395,30 @@ fn create_event_tap<'a>(
393395 } ) ;
394396
395397 // 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- } )
398+ if matches ! (
399+ event_type,
400+ CGEventType :: MouseMoved
401+ | CGEventType :: LeftMouseDragged
402+ | CGEventType :: RightMouseDragged
403+ | CGEventType :: OtherMouseDragged
404+ ) {
405+ state. reset_cursor ( ) . unwrap_or_else ( |e| log:: warn!( "{e}" ) ) ;
400406 }
401- }
402- // Did we cross a barrier?
403- else if matches ! ( event_type, CGEventType :: MouseMoved ) {
407+ } else if matches ! ( event_type, CGEventType :: MouseMoved ) {
408+ // Did we cross a barrier?
404409 if let Some ( new_pos) = state. crossed ( cg_ev) {
405- pos = Some ( new_pos) ;
410+ capture_position = Some ( new_pos) ;
411+ state
412+ . start_capture ( cg_ev, new_pos)
413+ . unwrap_or_else ( |e| log:: warn!( "{e}" ) ) ;
406414 res_events. push ( CaptureEvent :: Begin ) ;
407415 notify_tx
408416 . blocking_send ( ProducerEvent :: Grab ( new_pos) )
409417 . expect ( "Failed to send notification" ) ;
410418 }
411419 }
412420
413- if let Some ( pos) = pos {
421+ if let Some ( pos) = capture_position {
414422 res_events. iter ( ) . for_each ( |e| {
415423 // error must be ignored, since the event channel
416424 // may already be closed when the InputCapture instance is dropped.
@@ -515,10 +523,7 @@ impl MacOSInputCapture {
515523 log:: error!( "Failed to handle producer event: {e}" ) ;
516524 } )
517525 }
518-
519- _ = & mut tap_exit_rx => {
520- break ;
521- }
526+ _ = & mut tap_exit_rx => break ,
522527 }
523528 }
524529 // show cursor
0 commit comments