@@ -6,17 +6,15 @@ extern crate napi_derive;
6
6
use arboard:: { Clipboard , ImageData } ;
7
7
use enigo:: { Direction :: { Click , Press , Release } , Enigo , Key , Keyboard , Settings } ;
8
8
use std:: { thread, time:: Duration } ;
9
+ #[ cfg( target_os = "macos" ) ]
9
10
use core_graphics:: {
10
- event:: {
11
- CGEvent ,
12
- CGEventTapLocation ,
13
- KeyCode ,
14
- CGEventFlags ,
15
- } ,
11
+ event:: { CGEvent , CGEventTapLocation , KeyCode , CGEventFlags } ,
16
12
event_source:: { CGEventSource , CGEventSourceStateID } ,
17
13
} ;
18
14
15
+ #[ cfg( target_os = "macos" ) ]
19
16
static DEFAULT_MAC_CG_EVENT_WAIT_TIME_MS : u64 = 20 ;
17
+
20
18
static DEFAULT_PASTE_WAIT_TIME_MS : u32 = 30 ;
21
19
22
20
/// Insert the given text at the current cursor position.
@@ -25,17 +23,17 @@ static DEFAULT_PASTE_WAIT_TIME_MS: u32 = 30;
25
23
///
26
24
/// ##### Arguments
27
25
/// * `text` - Text to be inserted
28
- /// * `insertWithPaste` - An optional boolean that sets whether to insert text with the paste method.
26
+ /// * `insertWithPaste` - An optional boolean that sets whether to insert text with the paste method.
29
27
/// Default to false. (Setting true to use the paste method is useful to bypass
30
- /// some limitations in the default insert method. For example, the default
28
+ /// some limitations in the default insert method. For example, the default
31
29
/// insert method may not work for some apps, and in Mac, it doesn't work
32
30
/// when certain key, such as Cmd, is pressed during insert.)
33
31
/// * `arrowKeyToClickBeforeInsert` - An optional string that sets which arrow key to click before
34
32
/// inserting text. Can be either "left" or "right". Default to None.
35
33
/// * `pasteWaitTimeMs` - An optional number that sets how long to wait after performing the paste
36
34
/// operation before restoring the previous clipboard state. Default to 30ms.
37
35
/// `pasteWaitTimeMs` is only used when using the paste method, i.e. when
38
- /// `insertWithPaste` is set to true. (Beware of setting this value too low,
36
+ /// `insertWithPaste` is set to true. (Beware of setting this value too low,
39
37
/// as it may end up pasting the previous clipboard text/image)
40
38
#[ napi]
41
39
pub fn insert_text (
@@ -54,7 +52,7 @@ pub fn insert_text(
54
52
let insert_with_paste = insert_with_paste. unwrap_or ( false ) ;
55
53
if !insert_with_paste {
56
54
// Insert text using default method
57
- // Note: This may not work for some apps, and in Mac, it doesn't work when
55
+ // Note: This may not work for some apps, and in Mac, it doesn't work when
58
56
// certain key, such as Cmd, is pressed during insert (https://github.com/enigo-rs/enigo/issues/297)
59
57
enigo. text ( & text) . unwrap ( ) ;
60
58
} else {
@@ -64,12 +62,12 @@ pub fn insert_text(
64
62
// 1. Save clipboard existing text or image
65
63
let clipboard_text = clipboard. get_text ( ) . unwrap_or ( String :: new ( ) ) ;
66
64
let clipboard_image = clipboard
67
- . get_image ( )
68
- . unwrap_or ( ImageData { width : 0 , height : 0 , bytes : [ ] . as_ref ( ) . into ( ) } ) ;
65
+ . get_image ( )
66
+ . unwrap_or ( ImageData { width : 0 , height : 0 , bytes : [ ] . as_ref ( ) . into ( ) } ) ;
69
67
70
68
// 2. Clear clipboard
71
69
clipboard. clear ( ) . unwrap ( ) ;
72
-
70
+
73
71
// 3. Set text to be inserted to clipboard
74
72
clipboard. set_text ( & text) . unwrap ( ) ;
75
73
@@ -103,9 +101,7 @@ pub fn insert_text(
103
101
/// * `arrowKeyToClickBeforePaste` - An optional string that sets which arrow key to click before
104
102
/// pasting. Can be either "left" or "right". Default to None.
105
103
#[ napi]
106
- pub fn paste (
107
- arrow_key_to_click_before_paste : Option < String > ,
108
- ) {
104
+ pub fn paste ( arrow_key_to_click_before_paste : Option < String > ) {
109
105
let mut enigo = Enigo :: new ( & Settings :: default ( ) ) . unwrap ( ) ;
110
106
111
107
let arrow_key = arrow_key_to_click_before_paste. unwrap_or ( String :: new ( ) ) ;
@@ -116,87 +112,96 @@ pub fn paste(
116
112
_paste ( & mut enigo) ;
117
113
}
118
114
119
- /// Simulate arrow key click (left or right)
115
+ /// Simulate arrow key click (left or right) - Mac
116
+ #[ cfg( target_os = "macos" ) ]
120
117
fn _click_arrow_key ( enigo : & mut Enigo , arrow_key : String ) {
121
- if cfg ! ( target_os = "macos" ) {
122
- let arrow_key_code = if arrow_key == "left" {
123
- KeyCode :: LEFT_ARROW
124
- } else {
125
- KeyCode :: RIGHT_ARROW
126
- } ;
127
-
128
- let event_source_state_id = CGEventSourceStateID :: CombinedSessionState ;
129
- let event_source = CGEventSource :: new ( event_source_state_id) . unwrap ( ) ;
130
- let event_tap_location = CGEventTapLocation :: HID ;
131
-
132
- let press_arrow_key_event = CGEvent :: new_keyboard_event (
133
- event_source. clone ( ) ,
134
- arrow_key_code,
135
- true
136
- ) . unwrap ( ) ;
137
- press_arrow_key_event. post ( event_tap_location) ;
138
-
139
- let release_arrow_key_event = CGEvent :: new_keyboard_event (
140
- event_source. clone ( ) ,
141
- arrow_key_code,
142
- false
143
- ) . unwrap ( ) ;
144
- release_arrow_key_event. post ( event_tap_location) ;
145
- thread:: sleep ( Duration :: from_millis ( DEFAULT_MAC_CG_EVENT_WAIT_TIME_MS ) ) ;
118
+ let arrow_key_code = if arrow_key == "left" {
119
+ KeyCode :: LEFT_ARROW
146
120
} else {
147
- let key = if arrow_key == "left" { Key :: LeftArrow } else { Key :: RightArrow } ;
148
- enigo. key ( key, Click ) . unwrap ( ) ;
149
- }
121
+ KeyCode :: RIGHT_ARROW
122
+ } ;
123
+
124
+ let event_source_state_id = CGEventSourceStateID :: CombinedSessionState ;
125
+ let event_source = CGEventSource :: new ( event_source_state_id) . unwrap ( ) ;
126
+ let event_tap_location = CGEventTapLocation :: HID ;
127
+
128
+ let press_arrow_key_event = CGEvent :: new_keyboard_event (
129
+ event_source. clone ( ) ,
130
+ arrow_key_code,
131
+ true
132
+ ) . unwrap ( ) ;
133
+ press_arrow_key_event. post ( event_tap_location) ;
134
+
135
+ let release_arrow_key_event = CGEvent :: new_keyboard_event (
136
+ event_source. clone ( ) ,
137
+ arrow_key_code,
138
+ false
139
+ ) . unwrap ( ) ;
140
+ release_arrow_key_event. post ( event_tap_location) ;
141
+ thread:: sleep ( Duration :: from_millis ( DEFAULT_MAC_CG_EVENT_WAIT_TIME_MS ) ) ;
142
+ }
143
+
144
+ /// Simulate arrow key click (left or right) - Windows
145
+ #[ cfg( not( target_os = "macos" ) ) ]
146
+ fn _click_arrow_key ( enigo : & mut Enigo , arrow_key : String ) {
147
+ let key = if arrow_key == "left" { Key :: LeftArrow } else { Key :: RightArrow } ;
148
+ enigo. key ( key, Click ) . unwrap ( ) ;
150
149
}
151
150
152
151
// Define CG key code for "v" key
153
152
// Reference: https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX10.13.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h#L206
153
+ #[ cfg( target_os = "macos" ) ]
154
154
static V_KEY_CODE : u16 = 0x09 ;
155
155
156
- /// Simulate Ctrl+ V (Cmd + V in Mac) keyboard input to perform paste
157
- ///
158
- /// Windows calls into Enigo to simulate keyboard input. But for MacOS , it calls into
159
- /// Mac's Core Graphics CGEvent libary directly to work around 2 issues with Enigo's current
160
- /// implementation, which casues additional delay (https://github.com/enigo-rs/enigo/issues/105)
156
+ /// Simulate Ctrl+ V (Cmd + V in Mac) keyboard input to perform paste - Mac
157
+ ///
158
+ /// Windows calls into Enigo to simulate keyboard input. But for Mac , it calls into
159
+ /// Mac's Core Graphics CGEvent library directly to work around 2 issues with Enigo's current
160
+ /// implementation, which causes additional delay (https://github.com/enigo-rs/enigo/issues/105)
161
161
/// and subjects to mouse movement/keyboard interruption (https://github.com/enigo-rs/enigo/issues/201).
162
162
/// Calling into CGEvent and setting event flag solves both issues.
163
+ #[ cfg( target_os = "macos" ) ]
163
164
fn _paste ( enigo : & mut Enigo ) {
164
- if cfg ! ( target_os = "macos" ) {
165
- // Implementation reference: https://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events
166
-
167
- // Event source state id reference: https://developer.apple.com/documentation/coregraphics/cgeventsourcestateid
168
- let event_source_state_id = CGEventSourceStateID :: CombinedSessionState ;
169
- let event_source = CGEventSource :: new ( event_source_state_id) . unwrap ( ) ;
170
- // Event tap location reference: https://developer.apple.com/documentation/coregraphics/cgeventtaplocation
171
- let event_tap_location = CGEventTapLocation :: HID ;
172
-
173
- let press_cmd_v_event = CGEvent :: new_keyboard_event (
174
- event_source. clone ( ) ,
175
- V_KEY_CODE ,
176
- true
177
- ) . unwrap ( ) ;
178
- press_cmd_v_event. set_flags ( CGEventFlags :: CGEventFlagCommand ) ; // Set flags to Cmd
179
- press_cmd_v_event. post ( event_tap_location) ;
180
-
181
- let release_v_event = CGEvent :: new_keyboard_event (
182
- event_source. clone ( ) ,
183
- V_KEY_CODE ,
184
- false
185
- ) . unwrap ( ) ;
186
- release_v_event. set_flags ( CGEventFlags :: CGEventFlagNull ) ; // Reset flags to null
187
- release_v_event. post ( event_tap_location) ;
188
-
189
- // Release Cmd Key for completeness. May or may not be necessary
190
- // given Apple's documentation is not clear on this.
191
- let release_cmd_event = CGEvent :: new_keyboard_event (
192
- event_source. clone ( ) ,
193
- KeyCode :: COMMAND ,
194
- false
195
- ) . unwrap ( ) ;
196
- release_cmd_event. post ( event_tap_location) ;
197
- } else {
198
- enigo. key ( Key :: Control , Press ) . unwrap ( ) ;
199
- enigo. key ( Key :: Unicode ( 'v' ) , Click ) . unwrap ( ) ;
200
- enigo. key ( Key :: Control , Release ) . unwrap ( ) ;
201
- }
165
+ // Implementation reference: https://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events
166
+
167
+ // Event source state id reference: https://developer.apple.com/documentation/coregraphics/cgeventsourcestateid
168
+ let event_source_state_id = CGEventSourceStateID :: CombinedSessionState ;
169
+ let event_source = CGEventSource :: new ( event_source_state_id) . unwrap ( ) ;
170
+ // Event tap location reference: https://developer.apple.com/documentation/coregraphics/cgeventtaplocation
171
+ let event_tap_location = CGEventTapLocation :: HID ;
172
+
173
+ let press_cmd_v_event = CGEvent :: new_keyboard_event (
174
+ event_source. clone ( ) ,
175
+ V_KEY_CODE ,
176
+ true
177
+ ) . unwrap ( ) ;
178
+ press_cmd_v_event. set_flags ( CGEventFlags :: CGEventFlagCommand ) ; // Set flags to Cmd
179
+ press_cmd_v_event. post ( event_tap_location) ;
180
+
181
+ let release_v_event = CGEvent :: new_keyboard_event (
182
+ event_source. clone ( ) ,
183
+ V_KEY_CODE ,
184
+ false
185
+ ) . unwrap ( ) ;
186
+ release_v_event. set_flags ( CGEventFlags :: CGEventFlagNull ) ; // Reset flags to null
187
+ release_v_event. post ( event_tap_location) ;
188
+
189
+ // Release Cmd Key for completeness. May or may not be necessary
190
+ // given Apple's documentation is not clear on this.
191
+ let release_cmd_event = CGEvent :: new_keyboard_event (
192
+ event_source. clone ( ) ,
193
+ KeyCode :: COMMAND ,
194
+ false
195
+ ) . unwrap ( ) ;
196
+ release_cmd_event. post ( event_tap_location) ;
197
+ }
198
+
199
+ /// Simulate Ctrl+ V (Cmd + V in Mac) keyboard input to perform paste - Windows
200
+ ///
201
+ /// Windows calls into Enigo to simulate keyboard input
202
+ #[ cfg( not( target_os = "macos" ) ) ]
203
+ fn _paste ( enigo : & mut Enigo ) {
204
+ enigo. key ( Key :: Control , Press ) . unwrap ( ) ;
205
+ enigo. key ( Key :: Unicode ( 'v' ) , Click ) . unwrap ( ) ;
206
+ enigo. key ( Key :: Control , Release ) . unwrap ( ) ;
202
207
}
0 commit comments