Skip to content

Commit ce6bc8f

Browse files
authored
Bugfix/nut.js#273/modifier clicks (#125)
* (nut-tree/nut.js#273) Introduce modifier buffer to store key states and avoid input latency * (nut-tree/nut.js#273) Properly detect modifier keys and update keystate accordingly so the correct modifiers are set on the event
1 parent b033547 commit ce6bc8f

File tree

3 files changed

+179
-168
lines changed

3 files changed

+179
-168
lines changed

src/keypress.h

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ extern "C"
2323
MOD_SHIFT = kCGEventFlagMaskShift
2424
} MMKeyFlags;
2525

26+
extern MMKeyFlags flagBuffer;
27+
2628
#elif defined(USE_X11)
2729

2830
enum _MMKeyFlags {

src/macos/keypress.c

+158-158
Original file line numberDiff line numberDiff line change
@@ -8,183 +8,183 @@
88
#import <IOKit/hidsystem/IOHIDLib.h>
99
#import <IOKit/hidsystem/ev_keymap.h>
1010

11-
static io_connect_t _getAuxiliaryKeyDriver(void)
12-
{
13-
static mach_port_t sEventDrvrRef = 0;
14-
mach_port_t masterPort, service, iter;
15-
kern_return_t kr;
16-
17-
if (!sEventDrvrRef)
18-
{
19-
kr = IOMasterPort(bootstrap_port, &masterPort);
20-
assert(KERN_SUCCESS == kr);
21-
kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching(kIOHIDSystemClass), &iter);
22-
assert(KERN_SUCCESS == kr);
23-
service = IOIteratorNext(iter);
24-
assert(service);
25-
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef);
26-
assert(KERN_SUCCESS == kr);
27-
IOObjectRelease(service);
28-
IOObjectRelease(iter);
29-
}
30-
return sEventDrvrRef;
11+
MMKeyFlags flagBuffer;
12+
13+
static io_connect_t _getAuxiliaryKeyDriver(void) {
14+
static mach_port_t sEventDrvrRef = 0;
15+
mach_port_t masterPort, service, iter;
16+
kern_return_t kr;
17+
18+
if (!sEventDrvrRef) {
19+
kr = IOMasterPort(bootstrap_port, &masterPort);
20+
assert(KERN_SUCCESS == kr);
21+
kr = IOServiceGetMatchingServices(
22+
masterPort, IOServiceMatching(kIOHIDSystemClass), &iter);
23+
assert(KERN_SUCCESS == kr);
24+
service = IOIteratorNext(iter);
25+
assert(service);
26+
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType,
27+
&sEventDrvrRef);
28+
assert(KERN_SUCCESS == kr);
29+
IOObjectRelease(service);
30+
IOObjectRelease(iter);
31+
}
32+
return sEventDrvrRef;
3133
}
3234

33-
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)
34-
{
35-
/* The media keys all have 1000 added to them to help us detect them. */
36-
if (code >= 1000)
37-
{
38-
code = code - 1000; /* Get the real keycode. */
39-
NXEventData event;
40-
kern_return_t kr;
41-
IOGPoint loc = {0, 0};
42-
UInt32 evtInfo = code << 16 | (down ? NX_KEYDOWN : NX_KEYUP) << 8;
43-
bzero(&event, sizeof(NXEventData));
44-
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
45-
event.compound.misc.L[0] = evtInfo;
46-
kr = IOHIDPostEvent(_getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE);
47-
assert(KERN_SUCCESS == kr);
48-
}
49-
else
50-
{
51-
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStatePrivate);
52-
CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, (CGKeyCode)code, down);
53-
assert(keyEvent != NULL);
54-
55-
CGEventSetFlags(keyEvent, flags);
56-
CGEventPost(kCGHIDEventTap, keyEvent);
57-
CFRelease(keyEvent);
58-
CFRelease(src);
59-
}
35+
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags) {
36+
/* The media keys all have 1000 added to them to help us detect them. */
37+
if (code >= 1000) {
38+
code = code - 1000; /* Get the real keycode. */
39+
NXEventData event;
40+
kern_return_t kr;
41+
IOGPoint loc = {0, 0};
42+
UInt32 evtInfo = code << 16 | (down ? NX_KEYDOWN : NX_KEYUP) << 8;
43+
bzero(&event, sizeof(NXEventData));
44+
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
45+
event.compound.misc.L[0] = evtInfo;
46+
kr = IOHIDPostEvent(_getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event,
47+
kNXEventDataVersion, 0, FALSE);
48+
assert(KERN_SUCCESS == kr);
49+
} else {
50+
CGEventSourceRef src =
51+
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
52+
CGEventRef keyEvent =
53+
CGEventCreateKeyboardEvent(src, (CGKeyCode)code, down);
54+
assert(keyEvent != NULL);
55+
56+
// Check if keycode is one of the available modifier keys and set keyflags
57+
// accordingly
58+
if (code == K_META) {
59+
flags |= MOD_META;
60+
}
61+
if (code == K_ALT) {
62+
flags |= MOD_ALT;
63+
}
64+
if (code == K_CONTROL) {
65+
flags |= MOD_CONTROL;
66+
}
67+
if (code == K_SHIFT || code == K_RIGHTSHIFT) {
68+
flags |= MOD_SHIFT;
69+
}
70+
71+
MMKeyFlags activeKeyFlags;
72+
if (down) {
73+
activeKeyFlags = flags | flagBuffer;
74+
flagBuffer |= activeKeyFlags;
75+
} else {
76+
activeKeyFlags = flags ^ flagBuffer;
77+
flagBuffer ^= flags;
78+
}
79+
80+
CGEventSetFlags(keyEvent, activeKeyFlags);
81+
CGEventPost(kCGHIDEventTap, keyEvent);
82+
CFRelease(keyEvent);
83+
CFRelease(src);
84+
}
6085
}
6186

62-
void tapKeyCode(MMKeyCode code, MMKeyFlags flags)
63-
{
64-
toggleKeyCode(code, true, flags);
65-
toggleKeyCode(code, false, flags);
87+
void tapKeyCode(MMKeyCode code, MMKeyFlags flags) {
88+
toggleKeyCode(code, true, flags);
89+
toggleKeyCode(code, false, flags);
6690
}
6791

68-
void toggleKey(char c, const bool down, MMKeyFlags flags)
69-
{
70-
MMKeyCode keyCode = keyCodeForChar(c);
92+
void toggleKey(char c, const bool down, MMKeyFlags flags) {
93+
MMKeyCode keyCode = keyCodeForChar(c);
7194

72-
if (isupper(c) && !(flags & MOD_SHIFT))
73-
{
74-
flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */
75-
}
95+
if (isupper(c) && !(flags & MOD_SHIFT)) {
96+
flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */
97+
}
7698

77-
toggleKeyCode(keyCode, down, flags);
99+
toggleKeyCode(keyCode, down, flags);
78100
}
79101

80-
void tapKey(char c, MMKeyFlags flags)
81-
{
82-
toggleKey(c, true, flags);
83-
toggleKey(c, false, flags);
102+
void tapKey(char c, MMKeyFlags flags) {
103+
toggleKey(c, true, flags);
104+
toggleKey(c, false, flags);
84105
}
85106

86-
void toggleUnicodeKey(unsigned long ch, const bool down)
87-
{
88-
/* This function relies on the convenient
89-
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
90-
* convert characters to a keycode, but does not support adding modifier
91-
* flags. It is therefore only used in typeString() and typeStringDelayed()
92-
* -- if you need modifier keys, use the above functions instead. */
93-
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStatePrivate);
94-
CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, 0, down);
95-
if (keyEvent == NULL)
96-
{
97-
fputs("Could not create keyboard event.\n", stderr);
98-
return;
99-
}
100-
101-
if (ch > 0xFFFF)
102-
{
103-
// encode to utf-16 if necessary
104-
unsigned short surrogates[] = {
105-
0xD800 + ((ch - 0x10000) >> 10),
106-
0xDC00 + (ch & 0x3FF)};
107-
108-
CGEventKeyboardSetUnicodeString(keyEvent, 2, &surrogates);
109-
}
110-
else
111-
{
112-
CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);
113-
}
114-
115-
CGEventPost(kCGHIDEventTap, keyEvent);
116-
CFRelease(keyEvent);
117-
CFRelease(src);
107+
void toggleUnicodeKey(unsigned long ch, const bool down) {
108+
/* This function relies on the convenient
109+
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
110+
* convert characters to a keycode, but does not support adding modifier
111+
* flags. It is therefore only used in typeString() and typeStringDelayed()
112+
* -- if you need modifier keys, use the above functions instead. */
113+
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
114+
CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, 0, down);
115+
if (keyEvent == NULL) {
116+
fputs("Could not create keyboard event.\n", stderr);
117+
return;
118+
}
119+
120+
if (ch > 0xFFFF) {
121+
// encode to utf-16 if necessary
122+
unsigned short surrogates[] = {0xD800 + ((ch - 0x10000) >> 10),
123+
0xDC00 + (ch & 0x3FF)};
124+
125+
CGEventKeyboardSetUnicodeString(keyEvent, 2, &surrogates);
126+
} else {
127+
CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);
128+
}
129+
130+
CGEventPost(kCGHIDEventTap, keyEvent);
131+
CFRelease(keyEvent);
132+
CFRelease(src);
118133
}
119134

120-
void toggleUniKey(char c, const bool down)
121-
{
122-
toggleUnicodeKey(c, down);
123-
}
135+
void toggleUniKey(char c, const bool down) { toggleUnicodeKey(c, down); }
124136

125-
static void tapUniKey(char c)
126-
{
127-
toggleUniKey(c, true);
128-
toggleUniKey(c, false);
137+
static void tapUniKey(char c) {
138+
toggleUniKey(c, true);
139+
toggleUniKey(c, false);
129140
}
130141

131-
void typeString(const char *str)
132-
{
133-
unsigned short c;
134-
unsigned short c1;
135-
unsigned short c2;
136-
unsigned short c3;
137-
unsigned long n;
138-
139-
while (*str != '\0')
140-
{
141-
c = *str++;
142-
143-
// warning, the following utf8 decoder
144-
// doesn't perform validation
145-
if (c <= 0x7F)
146-
{
147-
// 0xxxxxxx one byte
148-
n = c;
149-
}
150-
else if ((c & 0xE0) == 0xC0)
151-
{
152-
// 110xxxxx two bytes
153-
c1 = (*str++) & 0x3F;
154-
n = ((c & 0x1F) << 6) | c1;
155-
}
156-
else if ((c & 0xF0) == 0xE0)
157-
{
158-
// 1110xxxx three bytes
159-
c1 = (*str++) & 0x3F;
160-
c2 = (*str++) & 0x3F;
161-
n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
162-
}
163-
else if ((c & 0xF8) == 0xF0)
164-
{
165-
// 11110xxx four bytes
166-
c1 = (*str++) & 0x3F;
167-
c2 = (*str++) & 0x3F;
168-
c3 = (*str++) & 0x3F;
169-
n = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
170-
}
171-
172-
toggleUnicodeKey(n, true);
173-
toggleUnicodeKey(n, false);
174-
}
142+
void typeString(const char *str) {
143+
unsigned short c;
144+
unsigned short c1;
145+
unsigned short c2;
146+
unsigned short c3;
147+
unsigned long n;
148+
149+
while (*str != '\0') {
150+
c = *str++;
151+
152+
// warning, the following utf8 decoder
153+
// doesn't perform validation
154+
if (c <= 0x7F) {
155+
// 0xxxxxxx one byte
156+
n = c;
157+
} else if ((c & 0xE0) == 0xC0) {
158+
// 110xxxxx two bytes
159+
c1 = (*str++) & 0x3F;
160+
n = ((c & 0x1F) << 6) | c1;
161+
} else if ((c & 0xF0) == 0xE0) {
162+
// 1110xxxx three bytes
163+
c1 = (*str++) & 0x3F;
164+
c2 = (*str++) & 0x3F;
165+
n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
166+
} else if ((c & 0xF8) == 0xF0) {
167+
// 11110xxx four bytes
168+
c1 = (*str++) & 0x3F;
169+
c2 = (*str++) & 0x3F;
170+
c3 = (*str++) & 0x3F;
171+
n = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
172+
}
173+
174+
toggleUnicodeKey(n, true);
175+
toggleUnicodeKey(n, false);
176+
}
175177
}
176178

177-
void typeStringDelayed(const char *str, const unsigned cpm)
178-
{
179-
/* Characters per second */
180-
const double cps = (double)cpm / 60.0;
179+
void typeStringDelayed(const char *str, const unsigned cpm) {
180+
/* Characters per second */
181+
const double cps = (double)cpm / 60.0;
181182

182-
/* Average milli-seconds per character */
183-
const double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;
183+
/* Average milli-seconds per character */
184+
const double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;
184185

185-
while (*str != '\0')
186-
{
187-
tapUniKey(*str++);
188-
microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5)));
189-
}
186+
while (*str != '\0') {
187+
tapUniKey(*str++);
188+
microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5)));
189+
}
190190
}

0 commit comments

Comments
 (0)