Skip to content

Commit 8f89c1f

Browse files
committed
(nut-tree/nut.js#273) Properly detect modifier keys and update keystate accordingly so the correct modifiers are set on the event
1 parent c55c623 commit 8f89c1f

File tree

2 files changed

+177
-168
lines changed

2 files changed

+177
-168
lines changed

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
}

src/macos/mouse.c

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "../mouse.h"
22
#include "../deadbeef_rand.h"
3+
#include "../keypress.h"
34
#include "../microsleep.h"
45
#include "mouse_utils.h"
56

@@ -115,21 +116,28 @@ MMPoint getMousePos() {
115116
* Press down a button, or release it.
116117
* @param down True for down, false for up.
117118
* @param button The button to press down or release.
118-
*
119-
* This function ships a manual implementation to handle double clicks by tracking the time interval between mouse events.
120-
* Reason for this is the fact that https://developer.apple.com/documentation/coregraphics/1408790-cgeventsourcesecondssincelasteve?language=objc
121-
* has a bit of latency and will stop working correctly if the time between two consecutive clicks is not long enough.
122-
*
123-
* This implementation captures the current timestamp for up/down events on each of left/middle/right mouse buttons.
124-
* If the interval between two clicks is lower than https://developer.apple.com/documentation/appkit/nsevent/1528384-doubleclickinterval?language=objc
125-
* and both clicks happen at the same position, we alter the mouse event to trigger a double click by setting kCGMouseEventClickState = 2 on the event
119+
*
120+
* This function ships a manual implementation to handle double clicks by
121+
* tracking the time interval between mouse events. Reason for this is the fact
122+
* that
123+
* https://developer.apple.com/documentation/coregraphics/1408790-cgeventsourcesecondssincelasteve?language=objc
124+
* has a bit of latency and will stop working correctly if the time between two
125+
* consecutive clicks is not long enough.
126+
*
127+
* This implementation captures the current timestamp for up/down events on each
128+
* of left/middle/right mouse buttons. If the interval between two clicks is
129+
* lower than
130+
* https://developer.apple.com/documentation/appkit/nsevent/1528384-doubleclickinterval?language=objc
131+
* and both clicks happen at the same position, we alter the mouse event to
132+
* trigger a double click by setting kCGMouseEventClickState = 2 on the event
126133
*/
127134
void toggleMouse(bool down, MMMouseButton button) {
128135
static ClickTimer clickTimer = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
129136

130-
MMPoint currentMMPoint= getMousePos();
137+
MMPoint currentMMPoint = getMousePos();
131138

132-
clock_t intervalSinceLastClick = timeSinceLastClick(&clickTimer, down, button, clock());
139+
clock_t intervalSinceLastClick =
140+
timeSinceLastClick(&clickTimer, down, button, clock());
133141

134142
const CGPoint currentPos = CGPointFromMMPoint(currentMMPoint);
135143
const CGEventType mouseType = MMMouseToCGEventType(down, button);
@@ -141,6 +149,7 @@ void toggleMouse(bool down, MMMouseButton button) {
141149
areSamePoint(currentMMPoint, clickTimer.clickLocation)) {
142150
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
143151
}
152+
CGEventSetFlags(event, flagBuffer);
144153
CGEventPost(kCGHIDEventTap, event);
145154
CFRelease(event);
146155
CFRelease(src);

0 commit comments

Comments
 (0)