|
31 | 31 | kCGHeadInsertEventTap,
|
32 | 32 | kCGKeyboardEventKeycode,
|
33 | 33 | kCGSessionEventTap,
|
| 34 | + kCGEventSourceStateHIDSystemState, |
34 | 35 | )
|
35 | 36 | import Foundation
|
36 | 37 | import threading
|
| 38 | +from time import time |
37 | 39 | import collections
|
38 | 40 | from plover.oslayer import mac_keycode
|
39 | 41 |
|
@@ -148,10 +150,7 @@ def down_up(seq):
|
148 | 150 | 55: kCGEventFlagMaskCommand
|
149 | 151 | }
|
150 | 152 |
|
151 |
| -# kCGEventSourceStatePrivate is -1 but when -1 is passed in here it is |
152 |
| -# unmarshalled incorrectly as 10379842816535691263. |
153 |
| -MY_EVENT_SOURCE = CGEventSourceCreate(0xFFFFFFFF) # 32 bit -1 |
154 |
| -MY_EVENT_SOURCE_ID = CGEventSourceGetSourceStateID(MY_EVENT_SOURCE) |
| 153 | +OUTPUT_SOURCE = CGEventSourceCreate(kCGEventSourceStateHIDSystemState) |
155 | 154 |
|
156 | 155 | # For the purposes of this class, we're only watching these keys.
|
157 | 156 | # We could calculate the keys, but our default layout would be misleading:
|
@@ -186,10 +185,6 @@ def callback(proxy, event_type, event, reference):
|
186 | 185 | # Don't pass on meta events meant for this event tap.
|
187 | 186 | if event_type not in self._KEYBOARD_EVENTS:
|
188 | 187 | return None
|
189 |
| - # Don't intercept events from this module. |
190 |
| - if (CGEventGetIntegerValueField(event, kCGEventSourceStateID) == |
191 |
| - MY_EVENT_SOURCE_ID): |
192 |
| - return event |
193 | 188 | # Don't intercept the event if it has modifiers, allow
|
194 | 189 | # Fn and Numeric flags so we can suppress the arrow and
|
195 | 190 | # extended (home, end, etc...) keys.
|
@@ -261,9 +256,9 @@ def __init__(self):
|
261 | 256 | def send_backspaces(number_of_backspaces):
|
262 | 257 | for _ in xrange(number_of_backspaces):
|
263 | 258 | CGEventPost(kCGSessionEventTap,
|
264 |
| - CGEventCreateKeyboardEvent(MY_EVENT_SOURCE, BACK_SPACE, True)) |
| 259 | + CGEventCreateKeyboardEvent(OUTPUT_SOURCE, BACK_SPACE, True)) |
265 | 260 | CGEventPost(kCGSessionEventTap,
|
266 |
| - CGEventCreateKeyboardEvent(MY_EVENT_SOURCE, BACK_SPACE, False)) |
| 261 | + CGEventCreateKeyboardEvent(OUTPUT_SOURCE, BACK_SPACE, False)) |
267 | 262 |
|
268 | 263 | def send_string(self, s):
|
269 | 264 | """
|
@@ -320,10 +315,10 @@ def apply_raw():
|
320 | 315 |
|
321 | 316 | @staticmethod
|
322 | 317 | def _send_string_press(c):
|
323 |
| - event = CGEventCreateKeyboardEvent(MY_EVENT_SOURCE, 0, True) |
| 318 | + event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, True) |
324 | 319 | KeyboardEmulation._set_event_string(event, c)
|
325 | 320 | CGEventPost(kCGSessionEventTap, event)
|
326 |
| - event = CGEventCreateKeyboardEvent(MY_EVENT_SOURCE, 0, False) |
| 321 | + event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, False) |
327 | 322 | KeyboardEmulation._set_event_string(event, c)
|
328 | 323 | CGEventPost(kCGSessionEventTap, event)
|
329 | 324 |
|
@@ -418,30 +413,42 @@ def _set_event_string(event, s):
|
418 | 413 | buf = Foundation.NSString.stringWithString_(s)
|
419 | 414 | CGEventKeyboardSetUnicodeString(event, len(buf), buf)
|
420 | 415 |
|
| 416 | + MODS_MASK = ( |
| 417 | + kCGEventFlagMaskAlternate | |
| 418 | + kCGEventFlagMaskControl | |
| 419 | + kCGEventFlagMaskShift | |
| 420 | + kCGEventFlagMaskCommand |
| 421 | + ) |
| 422 | + |
421 | 423 | @staticmethod
|
422 | 424 | def _send_sequence(sequence):
|
423 | 425 | # There is a bug in the event system that seems to cause inconsistent
|
424 | 426 | # modifiers on key events:
|
425 | 427 | # http://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events
|
426 | 428 | # My solution is to manage the state myself.
|
427 | 429 | # I'm not sure how to deal with caps lock.
|
428 |
| - # If event_mask is not zero at the end then bad things might happen. |
429 |
| - event_mask = 0 |
| 430 | + # If mods_flags is not zero at the end then bad things might happen. |
| 431 | + mods_flags = 0 |
| 432 | + |
430 | 433 | for keycode, key_down in sequence:
|
431 | 434 | if not key_down and keycode in MODIFIER_KEYS_TO_MASKS:
|
432 |
| - event_mask &= ~MODIFIER_KEYS_TO_MASKS[keycode] |
| 435 | + mods_flags &= ~MODIFIER_KEYS_TO_MASKS[keycode] |
| 436 | + |
| 437 | + event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, keycode, key_down) |
433 | 438 |
|
434 |
| - event = CGEventCreateKeyboardEvent(MY_EVENT_SOURCE, keycode, key_down) |
| 439 | + if key_down and keycode not in MODIFIER_KEYS_TO_MASKS: |
| 440 | + event_flags = CGEventGetFlags(event) |
| 441 | + # Add wanted flags, remove unwanted flags. |
| 442 | + goal_flags = (event_flags & ~KeyboardEmulation.MODS_MASK) | mods_flags |
| 443 | + if event_flags != goal_flags: |
| 444 | + CGEventSetFlags(event, goal_flags) |
435 | 445 |
|
436 |
| - # The event comes with flags already, check if they contain our own event_mask. |
437 |
| - if event_mask and (CGEventGetFlags(event) & event_mask) != event_mask: |
438 |
| - # If our event_mask is missing from flags, set it. |
439 |
| - CGEventSetFlags(event, event_mask) |
440 |
| - elif not event_mask and keycode not in MODIFIER_KEYS_TO_MASKS: |
441 |
| - # Always set event_mask when it is zero to force release of modifiers. |
442 |
| - CGEventSetFlags(event, event_mask) |
| 446 | + # Half millisecond pause after keydown |
| 447 | + deadline = time() + 0.0005 |
| 448 | + while time() < deadline: |
| 449 | + pass |
443 | 450 |
|
444 | 451 | CGEventPost(kCGSessionEventTap, event)
|
445 | 452 |
|
446 | 453 | if key_down and keycode in MODIFIER_KEYS_TO_MASKS:
|
447 |
| - event_mask |= MODIFIER_KEYS_TO_MASKS[keycode] |
| 454 | + mods_flags |= MODIFIER_KEYS_TO_MASKS[keycode] |
0 commit comments