Skip to content

Commit 0d3ba3b

Browse files
committed
Rotation value is always zero for mouse wheel events generated by a trackpad on Windows. Fixes #139
1 parent 3e6ff08 commit 0d3ba3b

10 files changed

Lines changed: 99 additions & 90 deletions

File tree

demo/demo_hook.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
6161
size_t length = snprintf(buffer, sizeof(buffer),
6262
"id=%i,when=%" PRIu64 ",mask=0x%X",
6363
event->type, event->time, event->mask);
64-
64+
6565
switch (event->type) {
6666
case EVENT_KEY_PRESSED:
6767
// If the escape key is pressed, naturally terminate the program.
@@ -107,24 +107,26 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
107107
case EVENT_MOUSE_CLICKED:
108108
case EVENT_MOUSE_MOVED:
109109
case EVENT_MOUSE_DRAGGED:
110-
snprintf(buffer + length, sizeof(buffer) - length,
110+
snprintf(buffer + length, sizeof(buffer) - length,
111111
",x=%i,y=%i,button=%i,clicks=%i",
112112
event->data.mouse.x, event->data.mouse.y,
113113
event->data.mouse.button, event->data.mouse.clicks);
114114
break;
115115

116116
case EVENT_MOUSE_WHEEL:
117-
snprintf(buffer + length, sizeof(buffer) - length,
118-
",type=%i,amount=%i,rotation=%i",
119-
event->data.wheel.type, event->data.wheel.amount,
120-
event->data.wheel.rotation);
117+
snprintf(buffer + length, sizeof(buffer) - length,
118+
",type=%u,rotation=%i,delta=%u,direction=%u",
119+
event->data.wheel.type,
120+
event->data.wheel.rotation,
121+
event->data.wheel.delta,
122+
event->data.wheel.direction);
121123
break;
122124

123125
default:
124126
break;
125127
}
126128

127-
fprintf(stdout, "%s\n", buffer);
129+
fprintf(stdout, "%s\n", buffer);
128130
}
129131

130132
int main() {

demo/demo_hook_async.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,12 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
182182
break;
183183

184184
case EVENT_MOUSE_WHEEL:
185-
snprintf(buffer + length, sizeof(buffer) - length,
186-
",type=%i,amount=%i,rotation=%i",
187-
event->data.wheel.type, event->data.wheel.amount,
188-
event->data.wheel.rotation);
185+
snprintf(buffer + length, sizeof(buffer) - length,
186+
",type=%u,rotation=%i,delta=%u,direction=%u",
187+
event->data.wheel.type,
188+
event->data.wheel.rotation,
189+
event->data.wheel.delta,
190+
event->data.wheel.direction);
189191
break;
190192

191193
default:

demo/demo_post.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ int main() {
133133

134134
event->data.wheel.x = 675;
135135
event->data.wheel.y = 675;
136-
event->data.wheel.amount = 3;
137-
event->data.wheel.rotation = 1;
136+
event->data.wheel.rotation = 300;
137+
event->data.wheel.delta = 100;
138138
hook_post_event(event);
139139
//*/
140140

include/uiohook.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,11 @@ typedef struct _mouse_event_data {
106106
mouse_clicked_event_data;
107107

108108
typedef struct _mouse_wheel_event_data {
109-
uint16_t clicks;
110109
int16_t x;
111110
int16_t y;
112111
uint8_t type;
113-
uint16_t amount;
114112
int16_t rotation;
113+
uint16_t delta;
115114
uint8_t direction;
116115
} mouse_wheel_event_data;
117116

@@ -465,8 +464,8 @@ typedef void (*dispatcher_t)(uiohook_event * const, void *);
465464
#define MOUSE_BUTTON4 4 // Extra Mouse Button
466465
#define MOUSE_BUTTON5 5 // Extra Mouse Button
467466

468-
#define WHEEL_UNIT_SCROLL 1
469-
#define WHEEL_BLOCK_SCROLL 2
467+
#define WHEEL_UNIT_SCROLL 1 // Scroll by line
468+
#define WHEEL_BLOCK_SCROLL 2 // Scroll by page
470469

471470
#define WHEEL_VERTICAL_DIRECTION 3
472471
#define WHEEL_HORIZONTAL_DIRECTION 4

src/darwin/dispatch_event.c

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -589,11 +589,10 @@ bool dispatch_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
589589
bool consumed = false;
590590

591591
// Reset the click count and previous button.
592-
click_count = 1;
592+
click_count = 0;
593593
click_button = MOUSE_NOBUTTON;
594594

595595
// Check to see what axis was rotated, we only care about axis 1 for vertical rotation.
596-
// TODO Implement horizontal scrolling by examining axis 2.
597596
// NOTE kCGScrollWheelEventDeltaAxis3 is currently unused.
598597
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0
599598
|| CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
@@ -606,55 +605,60 @@ bool dispatch_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
606605
uio_event.type = EVENT_MOUSE_WHEEL;
607606
uio_event.mask = get_modifiers();
608607

609-
uio_event.data.wheel.clicks = click_count;
610608
uio_event.data.wheel.x = event_point.x;
611609
uio_event.data.wheel.y = event_point.y;
612610

613-
// TODO Figure out if kCGScrollWheelEventDeltaAxis2 causes mouse events with zero rotation.
614-
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) == 0) {
615-
// Scrolling data is line-based.
616-
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;
617-
} else {
618-
// Scrolling data is pixel-based.
619-
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;
620-
}
621-
622-
// TODO The result of kCGScrollWheelEventIsContinuous may effect this value.
623-
// Calculate the amount based on the Point Delta / Event Delta. Integer sign should always be homogeneous resulting in a positive result.
624-
// NOTE kCGScrollWheelEventFixedPtDeltaAxis1 a floating point value (+0.1/-0.1) that takes acceleration into account.
625-
// NOTE kCGScrollWheelEventPointDeltaAxis1 will not build on OS X < 10.5
626-
627-
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
628-
uio_event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis1) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1);
611+
uio_event.data.wheel.delta = 0;
612+
uio_event.data.wheel.rotation = 0;
629613

630-
// Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000).
631-
uio_event.data.wheel.rotation = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) * -1;
614+
/* This function returns the scale of pixels per line in the specified event source. For example, if the
615+
* scale in the event source is 10.5 pixels per line, this function would return 10.5. Every scrolling event
616+
* can be interpreted to be scrolling by pixel or by line. By default, the scale is about ten pixels per
617+
* line. You can alter the scale with the function CGEventSourceSetPixelsPerLine.
618+
* See: https://gist.github.com/svoisen/5215826 */
619+
CGEventSourceRef source = CGEventCreateSourceFromEvent(event_ref);
620+
double ppl = CGEventSourceGetPixelsPerLine(source);
632621

633-
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
634-
uio_event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2);
622+
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) != 0) {
623+
// continuous device (trackpad)
624+
ppl *= 1;
625+
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;
635626

636-
// Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000).
637-
uio_event.data.wheel.rotation = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) * -1;
627+
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
628+
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
629+
uio_event.data.wheel.rotation = (int16_t) (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis1) * ppl * 1);
630+
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
631+
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
632+
uio_event.data.wheel.rotation = (int16_t) (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) * ppl * 1);
633+
}
638634
} else {
639-
//Fail Silently if a 3rd axis gets added without changing this section of code.
640-
uio_event.data.wheel.amount = 0;
641-
uio_event.data.wheel.rotation = 0;
635+
// non-continuous device (wheel mice)
636+
ppl *= 10;
637+
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;
638+
639+
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
640+
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
641+
uio_event.data.wheel.rotation = (int16_t) (CGEventGetDoubleValueField(event_ref, kCGScrollWheelEventFixedPtDeltaAxis1) * ppl * 10);
642+
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
643+
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
644+
uio_event.data.wheel.rotation = (int16_t) (CGEventGetDoubleValueField(event_ref, kCGScrollWheelEventFixedPtDeltaAxis2) * ppl * 10);
645+
}
642646
}
643647

648+
uio_event.data.wheel.delta = (uint16_t) ppl;
644649

645-
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
646-
// Wheel Rotated Up or Down.
647-
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
648-
} else { // data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight
649-
// Wheel Rotated Left or Right.
650-
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
650+
if (source) {
651+
CFRelease(source);
651652
}
652653

653-
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
654-
__FUNCTION__, __LINE__, uio_event.data.wheel.type,
655-
uio_event.data.wheel.amount * uio_event.data.wheel.rotation,
654+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel %i / %u of type %u in the %u direction at %u, %u.\n",
655+
__FUNCTION__, __LINE__,
656+
uio_event.data.wheel.rotation,
657+
uio_event.data.wheel.delta,
658+
uio_event.data.wheel.type,
656659
uio_event.data.wheel.direction,
657-
uio_event.data.wheel.x, uio_event.data.wheel.y);
660+
uio_event.data.wheel.x,
661+
uio_event.data.wheel.y);
658662

659663
// Fire mouse wheel event.
660664
dispatch_event(&uio_event);

src/darwin/post_event.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ static int post_mouse_wheel_event(uiohook_event * const event, CGEventSourceRef
241241
kCGScrollEventUnitLine,
242242
// TODO Currently only support 1 wheel axis.
243243
(CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
244-
event->data.wheel.amount * event->data.wheel.rotation
244+
event->data.wheel.rotation // TODO Is this value correct? Do we need PPL?
245245
);
246246

247247
if (cg_event == NULL) {

src/windows/dispatch_event.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_d
4444
dispatch_data = user_data;
4545
}
4646

47+
#ifdef USE_EPOCH_TIME
48+
static uint64_t get_unix_timestamp() {
49+
FILETIME system_time;
50+
51+
// Get the local system time in UTC.
52+
GetSystemTimeAsFileTime(&system_time);
53+
54+
// Convert the local system time to a Unix epoch in MS.
55+
// milliseconds = 100-nanoseconds / 10000
56+
uint64_t timestamp = (((uint64_t) system_time.dwHighDateTime << 32) | system_time.dwLowDateTime) / 10000;
57+
58+
// Convert Windows epoch to Unix epoch. (1970 - 1601 in milliseconds)
59+
timestamp -= 11644473600000;
60+
61+
return timestamp;
62+
}
63+
#endif
64+
4765
// Send out an event if a dispatcher was set.
4866
static void dispatch_event(uiohook_event *const event) {
4967
if (dispatch != NULL) {

src/windows/input_helper.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,7 @@ extern void unset_modifier_mask(uint16_t mask);
143143
/* Get the current native modifier mask state. */
144144
extern uint16_t get_modifiers();
145145

146-
/* Help track how much rotation should be applied to a scroll wheel event. */
147-
extern int16_t get_scroll_wheel_rotation(DWORD data, uint8_t direction);
148-
149-
// Initialize the locale list and wow64 pointer size.
146+
/* Initialize the locale list and wow64 pointer size. */
150147
extern int load_input_helper();
151148

152149
/* Cleanup the initialized locales. */

src/windows/post_event.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) {
167167
input->mi.dwFlags = MOUSEEVENTF_WHEEL;
168168

169169
// type, amount and rotation?
170-
input->mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
170+
input->mi.mouseData = event->data.wheel.rotation;
171171
break;
172172

173173
case EVENT_MOUSE_DRAGGED:

src/x11/dispatch_event.c

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -217,24 +217,16 @@ static bool dispatch_mouse_wheel_rotated(XButtonEvent * const x_event) {
217217
bool consumed = false;
218218

219219
// Reset the click count and previous button.
220-
click.count = 1;
220+
click.count = 0;
221221
click.button = MOUSE_NOBUTTON;
222222

223-
/* Scroll wheel release events.
224-
* Scroll type: WHEEL_UNIT_SCROLL
225-
* Scroll amount: 3 unit increments per notch
226-
* Units to scroll: 3 unit increments
227-
* Vertical unit increment: 15 pixels
228-
*/
229-
230223
// Populate mouse wheel event.
231224
uio_event.time = x_event->serial;
232225
uio_event.reserved = 0x00;
233226

234227
uio_event.type = EVENT_MOUSE_WHEEL;
235228
uio_event.mask = get_modifiers();
236229

237-
uio_event.data.wheel.clicks = click.count;
238230
uio_event.data.wheel.x = x_event->x_root;
239231
uio_event.data.wheel.y = x_event->y_root;
240232

@@ -251,26 +243,21 @@ static bool dispatch_mouse_wheel_rotated(XButtonEvent * const x_event) {
251243
}
252244
#endif
253245

254-
/* X11 does not have an API call for acquiring the mouse scroll type. This
255-
* maybe part of the XInput2 (XI2) extention but I will wont know until it
256-
* is available on my platform. For the time being we will just use the
257-
* unit scroll value.
258-
*/
246+
/* X11 does not have an API call for acquiring the mouse scroll type. This maybe part of the XInput2 (XI2)
247+
* extension but I will wont know until it is available on my platform. For the time being we will just use the
248+
* unit scroll value. */
259249
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;
260250

261-
/* Some scroll wheel properties are available via the new XInput2 (XI2)
262-
* extension. Unfortunately the extension is not available on my
263-
* development platform at this time. For the time being we will just
264-
* use the Windows default value of 3.
265-
*/
266-
uio_event.data.wheel.amount = 3;
267-
268-
if (x_event->button == WheelUp || x_event->button == WheelLeft) {
251+
/* Some scroll wheel properties are available via the new XInput2 (XI2) extension. Unfortunately the extension is
252+
* not available on my development platform at this time. For the time being we will just use the Windows default
253+
* value of 3. */
254+
uio_event.data.wheel.delta = 100;
255+
if (x_event->button == WheelDown || x_event->button == WheelLeft) {
269256
// Wheel Rotated Up and Away.
270-
uio_event.data.wheel.rotation = -1;
271-
} else { // event.button == WheelDown || event.button == WheelRight
257+
uio_event.data.wheel.rotation = -3 * uio_event.data.wheel.delta;
258+
} else { // event.button == WheelUp || event.button == WheelRight
272259
// Wheel Rotated Down and Towards.
273-
uio_event.data.wheel.rotation = 1;
260+
uio_event.data.wheel.rotation = 3 * uio_event.data.wheel.delta;
274261
}
275262

276263
if (x_event->button == WheelUp || x_event->button == WheelDown) {
@@ -281,10 +268,10 @@ static bool dispatch_mouse_wheel_rotated(XButtonEvent * const x_event) {
281268
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
282269
}
283270

284-
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
285-
__FUNCTION__, __LINE__, uio_event.data.wheel.type,
286-
uio_event.data.wheel.amount * uio_event.data.wheel.rotation,
287-
uio_event.data.wheel.direction,
271+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel %i / %u of type %u in the %u direction at %u, %u.\n",
272+
__FUNCTION__, __LINE__,
273+
uio_event.data.wheel.rotation, uio_event.data.wheel.delta,
274+
uio_event.data.wheel.type, uio_event.data.wheel.direction,
288275
uio_event.data.wheel.x, uio_event.data.wheel.y);
289276

290277
// Fire mouse wheel event.

0 commit comments

Comments
 (0)