Skip to content

Commit

Permalink
Rotation value is always zero for mouse wheel events generated by a t…
Browse files Browse the repository at this point in the history
…rackpad on Windows. Fixes #139
  • Loading branch information
kwhat committed Apr 16, 2024
1 parent 27b2031 commit 8ef569e
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 90 deletions.
16 changes: 9 additions & 7 deletions demo/demo_hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
size_t length = snprintf(buffer, sizeof(buffer),
"id=%i,when=%" PRIu64 ",mask=0x%X",
event->type, event->time, event->mask);

switch (event->type) {
case EVENT_KEY_PRESSED:
// If the escape key is pressed, naturally terminate the program.
Expand Down Expand Up @@ -107,24 +107,26 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
case EVENT_MOUSE_CLICKED:
case EVENT_MOUSE_MOVED:
case EVENT_MOUSE_DRAGGED:
snprintf(buffer + length, sizeof(buffer) - length,
snprintf(buffer + length, sizeof(buffer) - length,
",x=%i,y=%i,button=%i,clicks=%i",
event->data.mouse.x, event->data.mouse.y,
event->data.mouse.button, event->data.mouse.clicks);
break;

case EVENT_MOUSE_WHEEL:
snprintf(buffer + length, sizeof(buffer) - length,
",type=%i,amount=%i,rotation=%i",
event->data.wheel.type, event->data.wheel.amount,
event->data.wheel.rotation);
snprintf(buffer + length, sizeof(buffer) - length,
",type=%u,rotation=%i,delta=%u,direction=%u",
event->data.wheel.type,
event->data.wheel.rotation,
event->data.wheel.delta,
event->data.wheel.direction);
break;

default:
break;
}

fprintf(stdout, "%s\n", buffer);
fprintf(stdout, "%s\n", buffer);
}

int main() {
Expand Down
10 changes: 6 additions & 4 deletions demo/demo_hook_async.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,12 @@ void dispatch_proc(uiohook_event * const event, void *user_data) {
break;

case EVENT_MOUSE_WHEEL:
snprintf(buffer + length, sizeof(buffer) - length,
",type=%i,amount=%i,rotation=%i",
event->data.wheel.type, event->data.wheel.amount,
event->data.wheel.rotation);
snprintf(buffer + length, sizeof(buffer) - length,
",type=%u,rotation=%i,delta=%u,direction=%u",
event->data.wheel.type,
event->data.wheel.rotation,
event->data.wheel.delta,
event->data.wheel.direction);
break;

default:
Expand Down
4 changes: 2 additions & 2 deletions demo/demo_post.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ int main() {

event->data.wheel.x = 675;
event->data.wheel.y = 675;
event->data.wheel.amount = 3;
event->data.wheel.rotation = 1;
event->data.wheel.rotation = 300;
event->data.wheel.delta = 100;
hook_post_event(event);
//*/

Expand Down
7 changes: 3 additions & 4 deletions include/uiohook.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,11 @@ typedef struct _mouse_event_data {
mouse_clicked_event_data;

typedef struct _mouse_wheel_event_data {
uint16_t clicks;
int16_t x;
int16_t y;
uint8_t type;
uint16_t amount;
int16_t rotation;
uint16_t delta;
uint8_t direction;
} mouse_wheel_event_data;

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

#define WHEEL_UNIT_SCROLL 1
#define WHEEL_BLOCK_SCROLL 2
#define WHEEL_UNIT_SCROLL 1 // Scroll by line
#define WHEEL_BLOCK_SCROLL 2 // Scroll by page

#define WHEEL_VERTICAL_DIRECTION 3
#define WHEEL_HORIZONTAL_DIRECTION 4
Expand Down
80 changes: 42 additions & 38 deletions src/darwin/dispatch_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -589,11 +589,10 @@ bool dispatch_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
bool consumed = false;

// Reset the click count and previous button.
click_count = 1;
click_count = 0;
click_button = MOUSE_NOBUTTON;

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

uio_event.data.wheel.clicks = click_count;
uio_event.data.wheel.x = event_point.x;
uio_event.data.wheel.y = event_point.y;

// TODO Figure out if kCGScrollWheelEventDeltaAxis2 causes mouse events with zero rotation.
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) == 0) {
// Scrolling data is line-based.
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;
} else {
// Scrolling data is pixel-based.
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;
}

// TODO The result of kCGScrollWheelEventIsContinuous may effect this value.
// Calculate the amount based on the Point Delta / Event Delta. Integer sign should always be homogeneous resulting in a positive result.
// NOTE kCGScrollWheelEventFixedPtDeltaAxis1 a floating point value (+0.1/-0.1) that takes acceleration into account.
// NOTE kCGScrollWheelEventPointDeltaAxis1 will not build on OS X < 10.5

if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
uio_event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis1) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1);
uio_event.data.wheel.delta = 0;
uio_event.data.wheel.rotation = 0;

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

} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
uio_event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2);
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) != 0) {
// continuous device (trackpad)
ppl *= 1;
uio_event.data.wheel.type = WHEEL_BLOCK_SCROLL;

// Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000).
uio_event.data.wheel.rotation = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) * -1;
if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
uio_event.data.wheel.rotation = (int16_t) (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis1) * ppl * 1);
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
uio_event.data.wheel.rotation = (int16_t) (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) * ppl * 1);
}
} else {
//Fail Silently if a 3rd axis gets added without changing this section of code.
uio_event.data.wheel.amount = 0;
uio_event.data.wheel.rotation = 0;
// non-continuous device (wheel mice)
ppl *= 10;
uio_event.data.wheel.type = WHEEL_UNIT_SCROLL;

if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
uio_event.data.wheel.rotation = (int16_t) (CGEventGetDoubleValueField(event_ref, kCGScrollWheelEventFixedPtDeltaAxis1) * ppl * 10);
} else if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
uio_event.data.wheel.rotation = (int16_t) (CGEventGetDoubleValueField(event_ref, kCGScrollWheelEventFixedPtDeltaAxis2) * ppl * 10);
}
}

uio_event.data.wheel.delta = (uint16_t) ppl;

if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
// Wheel Rotated Up or Down.
uio_event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
} else { // data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight
// Wheel Rotated Left or Right.
uio_event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
if (source) {
CFRelease(source);
}

logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__, uio_event.data.wheel.type,
uio_event.data.wheel.amount * uio_event.data.wheel.rotation,
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel %i / %u of type %u in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__,
uio_event.data.wheel.rotation,
uio_event.data.wheel.delta,
uio_event.data.wheel.type,
uio_event.data.wheel.direction,
uio_event.data.wheel.x, uio_event.data.wheel.y);
uio_event.data.wheel.x,
uio_event.data.wheel.y);

// Fire mouse wheel event.
dispatch_event(&uio_event);
Expand Down
2 changes: 1 addition & 1 deletion src/darwin/post_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ static int post_mouse_wheel_event(uiohook_event * const event, CGEventSourceRef
kCGScrollEventUnitLine,
// TODO Currently only support 1 wheel axis.
(CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
event->data.wheel.amount * event->data.wheel.rotation
event->data.wheel.rotation // TODO Is this value correct? Do we need PPL?
);

if (cg_event == NULL) {
Expand Down
18 changes: 18 additions & 0 deletions src/windows/dispatch_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_d
dispatch_data = user_data;
}

#ifdef USE_EPOCH_TIME
static uint64_t get_unix_timestamp() {
FILETIME system_time;

// Get the local system time in UTC.
GetSystemTimeAsFileTime(&system_time);

// Convert the local system time to a Unix epoch in MS.
// milliseconds = 100-nanoseconds / 10000
uint64_t timestamp = (((uint64_t) system_time.dwHighDateTime << 32) | system_time.dwLowDateTime) / 10000;

// Convert Windows epoch to Unix epoch. (1970 - 1601 in milliseconds)
timestamp -= 11644473600000;

return timestamp;
}
#endif

// Send out an event if a dispatcher was set.
static void dispatch_event(uiohook_event *const event) {
if (dispatch != NULL) {
Expand Down
5 changes: 1 addition & 4 deletions src/windows/input_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,7 @@ extern void unset_modifier_mask(uint16_t mask);
/* Get the current native modifier mask state. */
extern uint16_t get_modifiers();

/* Help track how much rotation should be applied to a scroll wheel event. */
extern int16_t get_scroll_wheel_rotation(DWORD data, uint8_t direction);

// Initialize the locale list and wow64 pointer size.
/* Initialize the locale list and wow64 pointer size. */
extern int load_input_helper();

/* Cleanup the initialized locales. */
Expand Down
2 changes: 1 addition & 1 deletion src/windows/post_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) {
input->mi.dwFlags = MOUSEEVENTF_WHEEL;

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

case EVENT_MOUSE_DRAGGED:
Expand Down
45 changes: 16 additions & 29 deletions src/x11/dispatch_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,24 +217,16 @@ static bool dispatch_mouse_wheel_rotated(XButtonEvent * const x_event) {
bool consumed = false;

// Reset the click count and previous button.
click.count = 1;
click.count = 0;
click.button = MOUSE_NOBUTTON;

/* Scroll wheel release events.
* Scroll type: WHEEL_UNIT_SCROLL
* Scroll amount: 3 unit increments per notch
* Units to scroll: 3 unit increments
* Vertical unit increment: 15 pixels
*/

// Populate mouse wheel event.
uio_event.time = x_event->serial;
uio_event.reserved = 0x00;

uio_event.type = EVENT_MOUSE_WHEEL;
uio_event.mask = get_modifiers();

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

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

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

/* Some scroll wheel properties are available via the new XInput2 (XI2)
* extension. Unfortunately the extension is not available on my
* development platform at this time. For the time being we will just
* use the Windows default value of 3.
*/
uio_event.data.wheel.amount = 3;

if (x_event->button == WheelUp || x_event->button == WheelLeft) {
/* Some scroll wheel properties are available via the new XInput2 (XI2) extension. Unfortunately the extension is
* not available on my development platform at this time. For the time being we will just use the Windows default
* value of 3. */
uio_event.data.wheel.delta = 100;
if (x_event->button == WheelDown || x_event->button == WheelLeft) {
// Wheel Rotated Up and Away.
uio_event.data.wheel.rotation = -1;
} else { // event.button == WheelDown || event.button == WheelRight
uio_event.data.wheel.rotation = -3 * uio_event.data.wheel.delta;
} else { // event.button == WheelUp || event.button == WheelRight
// Wheel Rotated Down and Towards.
uio_event.data.wheel.rotation = 1;
uio_event.data.wheel.rotation = 3 * uio_event.data.wheel.delta;
}

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

logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__, uio_event.data.wheel.type,
uio_event.data.wheel.amount * uio_event.data.wheel.rotation,
uio_event.data.wheel.direction,
logger(LOG_LEVEL_DEBUG, "%s [%u]: Mouse wheel %i / %u of type %u in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__,
uio_event.data.wheel.rotation, uio_event.data.wheel.delta,
uio_event.data.wheel.type, uio_event.data.wheel.direction,
uio_event.data.wheel.x, uio_event.data.wheel.y);

// Fire mouse wheel event.
Expand Down

0 comments on commit 8ef569e

Please sign in to comment.