@@ -10,25 +10,37 @@ use core_graphics::event::{
1010 ScrollEventUnit ,
1111} ;
1212use core_graphics:: event_source:: { CGEventSource , CGEventSourceStateID } ;
13- use input_event:: { scancode, Event , KeyboardEvent , PointerEvent } ;
13+ use input_event:: { scancode, Event , KeyboardEvent , PointerEvent , BTN_LEFT , BTN_MIDDLE , BTN_RIGHT } ;
1414use keycode:: { KeyMap , KeyMapping } ;
1515use std:: cell:: Cell ;
1616use std:: ops:: { Index , IndexMut } ;
1717use std:: rc:: Rc ;
1818use std:: sync:: Arc ;
19- use std:: time:: Duration ;
19+ use std:: time:: { Duration , Instant } ;
2020use tokio:: { sync:: Notify , task:: JoinHandle } ;
2121
2222use super :: error:: MacOSEmulationCreationError ;
2323
2424const DEFAULT_REPEAT_DELAY : Duration = Duration :: from_millis ( 500 ) ;
2525const DEFAULT_REPEAT_INTERVAL : Duration = Duration :: from_millis ( 32 ) ;
26+ const DOUBLE_CLICK_INTERVAL : Duration = Duration :: from_millis ( 500 ) ;
2627
2728pub ( crate ) struct MacOSEmulation {
29+ /// global event source for all events
2830 event_source : CGEventSource ,
31+ /// task handle for key repeats
2932 repeat_task : Option < JoinHandle < ( ) > > ,
33+ /// current state of the mouse buttons
3034 button_state : ButtonState ,
35+ /// button previously pressed
36+ previous_button : Option < CGMouseButton > ,
37+ /// timestamp of previous click (button down)
38+ previous_button_click : Option < Instant > ,
39+ /// click state, i.e. number of clicks in quick succession
40+ button_click_state : i64 ,
41+ /// current modifier state
3142 modifier_state : Rc < Cell < XMods > > ,
43+ /// notify to cancel key repeats
3244 notify_repeat_task : Arc < Notify > ,
3345}
3446
@@ -74,6 +86,9 @@ impl MacOSEmulation {
7486 Ok ( Self {
7587 event_source,
7688 button_state,
89+ previous_button : None ,
90+ previous_button_click : None ,
91+ button_click_state : 1 ,
7792 repeat_task : None ,
7893 notify_repeat_task : Arc :: new ( Notify :: new ( ) ) ,
7994 modifier_state : Rc :: new ( Cell :: new ( XMods :: empty ( ) ) ) ,
@@ -271,24 +286,12 @@ impl Emulation for MacOSEmulation {
271286 state,
272287 } => {
273288 let ( event_type, mouse_button) = match ( button, state) {
274- ( b, 1 ) if b == input_event:: BTN_LEFT => {
275- ( CGEventType :: LeftMouseDown , CGMouseButton :: Left )
276- }
277- ( b, 0 ) if b == input_event:: BTN_LEFT => {
278- ( CGEventType :: LeftMouseUp , CGMouseButton :: Left )
279- }
280- ( b, 1 ) if b == input_event:: BTN_RIGHT => {
281- ( CGEventType :: RightMouseDown , CGMouseButton :: Right )
282- }
283- ( b, 0 ) if b == input_event:: BTN_RIGHT => {
284- ( CGEventType :: RightMouseUp , CGMouseButton :: Right )
285- }
286- ( b, 1 ) if b == input_event:: BTN_MIDDLE => {
287- ( CGEventType :: OtherMouseDown , CGMouseButton :: Center )
288- }
289- ( b, 0 ) if b == input_event:: BTN_MIDDLE => {
290- ( CGEventType :: OtherMouseUp , CGMouseButton :: Center )
291- }
289+ ( BTN_LEFT , 1 ) => ( CGEventType :: LeftMouseDown , CGMouseButton :: Left ) ,
290+ ( BTN_LEFT , 0 ) => ( CGEventType :: LeftMouseUp , CGMouseButton :: Left ) ,
291+ ( BTN_RIGHT , 1 ) => ( CGEventType :: RightMouseDown , CGMouseButton :: Right ) ,
292+ ( BTN_RIGHT , 0 ) => ( CGEventType :: RightMouseUp , CGMouseButton :: Right ) ,
293+ ( BTN_MIDDLE , 1 ) => ( CGEventType :: OtherMouseDown , CGMouseButton :: Center ) ,
294+ ( BTN_MIDDLE , 0 ) => ( CGEventType :: OtherMouseUp , CGMouseButton :: Center ) ,
292295 _ => {
293296 log:: warn!( "invalid button event: {button},{state}" ) ;
294297 return Ok ( ( ) ) ;
@@ -297,6 +300,22 @@ impl Emulation for MacOSEmulation {
297300 // store button state
298301 self . button_state [ mouse_button] = state == 1 ;
299302
303+ // update previous button state
304+ if state == 1 {
305+ if self . previous_button . is_some_and ( |b| b. eq ( & mouse_button) )
306+ && self
307+ . previous_button_click
308+ . is_some_and ( |i| i. elapsed ( ) < DOUBLE_CLICK_INTERVAL )
309+ {
310+ self . button_click_state += 1 ;
311+ } else {
312+ self . button_click_state = 1 ;
313+ }
314+ self . previous_button = Some ( mouse_button) ;
315+ self . previous_button_click = Some ( Instant :: now ( ) ) ;
316+ }
317+
318+ log:: debug!( "click_state: {}" , self . button_click_state) ;
300319 let location = self . get_mouse_location ( ) . unwrap ( ) ;
301320 let event = match CGEvent :: new_mouse_event (
302321 self . event_source . clone ( ) ,
@@ -310,6 +329,10 @@ impl Emulation for MacOSEmulation {
310329 return Ok ( ( ) ) ;
311330 }
312331 } ;
332+ event. set_integer_value_field (
333+ EventField :: MOUSE_EVENT_CLICK_STATE ,
334+ self . button_click_state ,
335+ ) ;
313336 event. post ( CGEventTapLocation :: HID ) ;
314337 }
315338 PointerEvent :: Axis {
@@ -417,6 +440,21 @@ impl Emulation for MacOSEmulation {
417440 async fn terminate ( & mut self ) { }
418441}
419442
443+ trait ButtonEq {
444+ fn eq ( & self , other : & Self ) -> bool ;
445+ }
446+
447+ impl ButtonEq for CGMouseButton {
448+ fn eq ( & self , other : & Self ) -> bool {
449+ match ( self , other) {
450+ ( CGMouseButton :: Left , CGMouseButton :: Left ) => true ,
451+ ( CGMouseButton :: Right , CGMouseButton :: Right ) => true ,
452+ ( CGMouseButton :: Center , CGMouseButton :: Center ) => true ,
453+ _ => false ,
454+ }
455+ }
456+ }
457+
420458fn update_modifiers ( modifiers : & Cell < XMods > , key : u32 , state : u8 ) -> bool {
421459 if let Ok ( key) = scancode:: Linux :: try_from ( key) {
422460 let mask = match key {
0 commit comments