Skip to content

Commit f9a346c

Browse files
committed
Bug 139: Rotation value is always zero for mouse wheel events generated by a trackpad on Windows
1 parent b859ac8 commit f9a346c

File tree

11 files changed

+116
-140
lines changed

11 files changed

+116
-140
lines changed

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
@@ -592,11 +592,10 @@ bool dispatch_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
592592
bool consumed = false;
593593

594594
// Reset the click count and previous button.
595-
click_count = 1;
595+
click_count = 0;
596596
click_button = MOUSE_NOBUTTON;
597597

598598
// Check to see what axis was rotated, we only care about axis 1 for vertical rotation.
599-
// TODO Implement horizontal scrolling by examining axis 2.
600599
// NOTE kCGScrollWheelEventDeltaAxis3 is currently unused.
601600
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0
602601
|| CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
@@ -609,55 +608,60 @@ bool dispatch_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
609608
uio_event.type = EVENT_MOUSE_WHEEL;
610609
uio_event.mask = get_modifiers();
611610

612-
uio_event.data.wheel.clicks = click_count;
613611
uio_event.data.wheel.x = event_point.x;
614612
uio_event.data.wheel.y = event_point.y;
615613

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

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

636-
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
637-
uio_event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2);
625+
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) != 0) {
626+
// continuous device (trackpad)
627+
ppl *= 1;
628+
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;
638629

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

651+
uio_event.data.wheel.delta = (uint16_t) ppl;
647652

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

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

662666
// Fire mouse wheel event.
663667
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: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,19 @@ UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_d
4646

4747
#ifdef USE_EPOCH_TIME
4848
static uint64_t get_unix_timestamp() {
49-
// Get the local system time in UTC.
50-
GetSystemTimeAsFileTime(&system_time);
49+
FILETIME system_time;
5150

52-
// Convert the local system time to a Unix epoch in MS.
53-
// milliseconds = 100-nanoseconds / 10000
54-
uint64_t timestamp = (((uint64_t) system_time.dwHighDateTime << 32) | system_time.dwLowDateTime) / 10000;
51+
// Get the local system time in UTC.
52+
GetSystemTimeAsFileTime(&system_time);
5553

56-
// Convert Windows epoch to Unix epoch. (1970 - 1601 in milliseconds)
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)
5759
timestamp -= 11644473600000;
5860

59-
return timestamp;
61+
return timestamp;
6062
}
6163
#endif
6264

@@ -420,7 +422,7 @@ bool dispatch_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
420422

421423
// Track the number of clicks.
422424
// Reset the click count and previous button.
423-
click_count = 1;
425+
click_count = 0;
424426
click_button = MOUSE_NOBUTTON;
425427

426428
// Populate mouse wheel event.
@@ -430,35 +432,46 @@ bool dispatch_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
430432
uio_event.type = EVENT_MOUSE_WHEEL;
431433
uio_event.mask = get_modifiers();
432434

433-
uio_event.data.wheel.clicks = click_count;
434435
uio_event.data.wheel.x = (int16_t) mshook->pt.x;
435436
uio_event.data.wheel.y = (int16_t) mshook->pt.y;
436437

437-
uio_event.data.wheel.rotation = get_scroll_wheel_rotation(mshook->mouseData, direction);
438-
439-
UINT uiAction = SPI_GETWHEELSCROLLCHARS;
440-
if (direction == WHEEL_VERTICAL_DIRECTION) {
441-
uiAction = SPI_GETWHEELSCROLLLINES;
438+
/* Delta GET_WHEEL_DELTA_WPARAM(mshook->mouseData)
439+
* A positive value indicates that the wheel was rotated
440+
* forward, away from the user; a negative value indicates that
441+
* the wheel was rotated backward, toward the user. One wheel
442+
* click is defined as WHEEL_DELTA, which is 120. */
443+
uio_event.data.wheel.rotation = (int16_t) GET_WHEEL_DELTA_WPARAM(mshook->mouseData);
444+
uio_event.data.wheel.delta = WHEEL_DELTA;
445+
446+
UINT uiAction = SPI_GETWHEELSCROLLLINES;
447+
if (direction == WHEEL_HORIZONTAL_DIRECTION) {
448+
uiAction = SPI_GETWHEELSCROLLCHARS;
442449
}
443450

444451
UINT wheel_amount = 3;
445452
if (SystemParametersInfo(uiAction, 0, &wheel_amount, 0)) {
446453
if (wheel_amount == WHEEL_PAGESCROLL) {
454+
/* If this number is WHEEL_PAGESCROLL, a wheel roll should be interpreted as clicking once in the page
455+
* down or page up regions of the scroll bar. */
456+
447457
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;
448-
uio_event.data.wheel.amount = 1;
458+
uio_event.data.wheel.rotation *= 1;
449459
} else {
460+
/* If this number is 0, no scrolling should occur.
461+
* If the number of lines to scroll is greater than the number of lines viewable, the scroll operation
462+
* should also be interpreted as a page down or page up operation. */
463+
450464
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;
451-
uio_event.data.wheel.amount = (uint16_t) wheel_amount;
465+
uio_event.data.wheel.rotation *= wheel_amount;
452466
}
453467

454468
// Set the direction based on what event was received.
455469
uio_event.data.wheel.direction = direction;
456-
457-
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
470+
471+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel %i / %u of type %u in the %u direction at %u, %u.\n",
458472
__FUNCTION__, __LINE__,
459-
uio_event.data.wheel.type,
460-
uio_event.data.wheel.amount * uio_event.data.wheel.rotation,
461-
uio_event.data.wheel.direction,
473+
uio_event.data.wheel.rotation, uio_event.data.wheel.delta,
474+
uio_event.data.wheel.type, uio_event.data.wheel.direction,
462475
uio_event.data.wheel.x, uio_event.data.wheel.y);
463476

464477
// Fire mouse wheel event.

src/windows/input_helper.c

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -364,31 +364,6 @@ uint16_t get_modifiers() {
364364
return modifier_mask;
365365
}
366366

367-
/* Track the amount of vertical and horizontal rotation between "clicks."
368-
* This is between mouse wheel delta. */
369-
static int16_t v_rotation, h_rotation;
370-
371-
int16_t get_scroll_wheel_rotation(DWORD data, uint8_t direction) {
372-
int16_t value;
373-
374-
/* Delta GET_WHEEL_DELTA_WPARAM(mshook->mouseData)
375-
* A positive value indicates that the wheel was rotated
376-
* forward, away from the user; a negative value indicates that
377-
* the wheel was rotated backward, toward the user. One wheel
378-
* click is defined as WHEEL_DELTA, which is 120. */
379-
if (direction == WHEEL_VERTICAL_DIRECTION) {
380-
v_rotation += (int16_t) GET_WHEEL_DELTA_WPARAM(data);
381-
// Vertical direction needs to be inverted on Windows to conform with other platforms.
382-
value = (int16_t) v_rotation / (-1 * WHEEL_DELTA);
383-
v_rotation %= WHEEL_DELTA;
384-
} else {
385-
h_rotation += (int16_t) GET_WHEEL_DELTA_WPARAM(data);
386-
value = (int16_t) h_rotation / WHEEL_DELTA;
387-
h_rotation %= WHEEL_DELTA;
388-
}
389-
390-
return value;
391-
}
392367

393368
/***********************************************************************
394369
* The following code is based on code provided by Marc-André Moreau
@@ -883,9 +858,6 @@ int load_input_helper() {
883858
}
884859
#endif
885860

886-
v_rotation = 0;
887-
h_rotation = 0;
888-
889861
int count = refresh_locale_list();
890862

891863
logger(LOG_LEVEL_DEBUG, "%s [%u]: refresh_locale_list() found %i locale(s).\n",

src/windows/input_helper.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,10 @@ 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

152-
// Cleanup the initialized locales.
149+
/* Cleanup the initialized locales. */
153150
extern int unload_input_helper();
154151

155152
#endif

src/windows/post_event.c

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

187187
// type, amount and rotation?
188-
input->mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
188+
input->mi.mouseData = event->data.wheel.rotation;
189189
break;
190190

191191
case EVENT_MOUSE_DRAGGED:

0 commit comments

Comments
 (0)