int main() { // Disable the logger. hook_set_logger_proc(&logger_proc); // Retrieves an array of screen data for each available monitor. uint8_t count = 0; screen_data *screens = hook_create_screen_info(&count); if (screens != NULL) { fprintf(stdout, "Found %d Monitors:\n", count); for (int i = 0; i < count; i++) { fprintf(stdout, "\tNumber:\t\t%d\n", screens[i].number); fprintf(stdout, "\tOffset X:\t%d\n", screens[i].x); fprintf(stdout, "\tOffset Y:\t%d\n", screens[i].y); fprintf(stdout, "\tWidth:\t\t%d\n", screens[i].width); fprintf(stdout, "\tHeight:\t\t%d\n", screens[i].height); fprintf(stdout, "\n"); } // You are responsible for freeing the memory returned by hook_create_screen_info. free(screens); } else { fprintf(stderr, "Failed to aquire screen information!\n"); } // Retrieves the keyboard auto repeat rate. long int repeat_rate = hook_get_auto_repeat_rate(); if (repeat_rate >= 0) { fprintf(stdout, "Auto Repeat Rate:\t%ld\n", repeat_rate); } else { fprintf(stderr, "Failed to aquire keyboard auto repeat rate!\n"); } // Retrieves the keyboard auto repeat delay. long int repeat_delay = hook_get_auto_repeat_delay(); if (repeat_delay >= 0) { fprintf(stdout, "Auto Repeat Delay:\t%ld\n", repeat_delay); } else { fprintf(stderr, "Failed to aquire keyboard auto repeat delay!\n"); } // Retrieves the mouse acceleration multiplier. long int acceleration_multiplier = hook_get_pointer_acceleration_multiplier(); if (acceleration_multiplier >= 0) { fprintf(stdout, "Mouse Acceleration Multiplier:\t%ld\n", acceleration_multiplier); } else { fprintf(stderr, "Failed to aquire mouse acceleration multiplier!\n"); } // Retrieves the mouse acceleration threshold. long int acceleration_threshold = hook_get_pointer_acceleration_threshold(); if (acceleration_threshold >= 0) { fprintf(stdout, "Mouse Acceleration Threshold:\t%ld\n", acceleration_threshold); } else { fprintf(stderr, "Failed to aquire mouse acceleration threshold!\n"); } // Retrieves the mouse sensitivity. long int sensitivity = hook_get_pointer_sensitivity(); if (sensitivity >= 0) { fprintf(stdout, "Mouse Sensitivity:\t%ld\n", sensitivity); } else { fprintf(stderr, "Failed to aquire keyboard auto repeat rate!\n"); } // Retrieves the double/triple click interval. long int click_time = hook_get_multi_click_time(); if (click_time >= 0) { fprintf(stdout, "Multi-Click Time:\t%ld\n", click_time); } else { fprintf(stderr, "Failed to aquire keyboard auto repeat rate!\n"); } return EXIT_SUCCESS; }
UIOHOOK_API void hook_post_event(uiohook_event * const event) { #ifdef USE_XTEST // XTest does not have modifier support, so we fake it by depressing the // appropriate modifier keys. for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) { if (event->mask & 1 << i) { XTestFakeKeyEvent(disp, XKeysymToKeycode(disp, keymask_lookup[i]), True, 0); } } for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) { if (event->mask & btnmask_lookup[i]) { XTestFakeButtonEvent(disp, i + 1, True, 0); } } switch (event->type) { case EVENT_KEY_PRESSED: XTestFakeKeyEvent( disp, scancode_to_keycode(event->data.keyboard.keycode), True, 0); break; case EVENT_KEY_RELEASED: XTestFakeKeyEvent( disp, scancode_to_keycode(event->data.keyboard.keycode), False, 0); break; case EVENT_MOUSE_PRESSED: XTestFakeButtonEvent(disp, event->data.mouse.button, True, 0); break; case EVENT_MOUSE_RELEASED: XTestFakeButtonEvent(disp, event->data.mouse.button, False, 0); break; case EVENT_MOUSE_WHEEL: // Wheel events should be the same as click events on X11. // type, amount and rotation if (event->data.wheel.rotation < 0) { XTestFakeButtonEvent(disp, WheelUp, True, 0); XTestFakeButtonEvent(disp, WheelUp, False, 0); } else { XTestFakeButtonEvent(disp, WheelDown, True, 0); XTestFakeButtonEvent(disp, WheelDown, False, 0); } break; case EVENT_MOUSE_DRAGGED: // The button masks are all applied with the modifier masks. case EVENT_MOUSE_MOVED: XTestFakeMotionEvent(disp, -1, event->data.mouse.x, event->data.mouse.y, 0); break; case EVENT_MOUSE_CLICKED: case EVENT_KEY_TYPED: // Ignore clicked and typed events. case EVENT_HOOK_ENABLED: case EVENT_HOOK_DISABLED: // Ignore hook enabled / disabled events. default: // Ignore any other garbage. logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", __FUNCTION__, __LINE__, event->type); break; } // Release the previously held modifier keys used to fake the event mask. for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) { if (event->mask & 1 << i) { XTestFakeKeyEvent(disp, XKeysymToKeycode(disp, keymask_lookup[i]), False, 0); } } for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) { if (event->mask & btnmask_lookup[i]) { XTestFakeButtonEvent(disp, i + 1, False, 0); } } #else XEvent *x_event; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t screen_count; screen_data *screens; #endif switch (event->type) { case EVENT_KEY_PRESSED: case EVENT_KEY_RELEASED: // Allocate memory for XKeyEvent and pre-populate. x_event = (XEvent *) create_key_event(); ((XKeyEvent *) x_event)->state = convert_to_native_mask(event->mask); ((XKeyEvent *) x_event)->keycode = XKeysymToKeycode(disp, scancode_to_keycode(event->data.keyboard.keycode)); if (event->type == EVENT_KEY_PRESSED) { ((XKeyEvent *) x_event)->type = KeyPress; XSendEvent(disp, InputFocus, False, KeyPressMask, x_event); } else { ((XKeyEvent *) x_event)->type = KeyRelease; XSendEvent(disp, InputFocus, False, KeyReleaseMask, x_event); } free(x_event); break; case EVENT_MOUSE_PRESSED: case EVENT_MOUSE_RELEASED: case EVENT_MOUSE_WHEEL: // Allocate memory for XButtonEvent and pre-populate. x_event = (XEvent *) create_button_event(); ((XButtonEvent *) x_event)->state = convert_to_native_mask(event->mask); ((XButtonEvent *) x_event)->x = event->data.mouse.x; ((XButtonEvent *) x_event)->y = event->data.mouse.y; #if defined(USE_XINERAMA) || defined(USE_XRANDR) screens = hook_create_screen_info(&screen_count); if (screen_count > 1) { ((XButtonEvent *) x_event)->x += screens[0].x; ((XButtonEvent *) x_event)->y += screens[0].y; } if (screens != NULL) { free(screens); } #endif // These are the same because Window == Root Window. ((XButtonEvent *) x_event)->x_root = ((XButtonEvent *) x_event)->x; ((XButtonEvent *) x_event)->y_root = ((XButtonEvent *) x_event)->y; if (event->type == EVENT_MOUSE_WHEEL) { ((XButtonEvent *) x_event)->type = ButtonPress; // type, amount and rotation if (event->data.wheel.rotation < 0) { ((XButtonEvent *) x_event)->button = WheelUp; } else { ((XButtonEvent *) x_event)->button = WheelDown; } XSendEvent(disp, InputFocus, False, ButtonPressMask, x_event); } if (event->type == EVENT_KEY_PRESSED) { ((XButtonEvent *) x_event)->type = ButtonPress; XSendEvent(disp, InputFocus, False, ButtonPressMask, x_event); } else { ((XButtonEvent *) x_event)->type = ButtonRelease; XSendEvent(disp, InputFocus, False, ButtonReleaseMask, x_event); } free(x_event); break; case EVENT_MOUSE_MOVED: case EVENT_MOUSE_DRAGGED: x_event = (XEvent *) create_motion_event(); ((XMotionEvent *) x_event)->state = convert_to_native_mask(event->mask); ((XButtonEvent *) x_event)->x = event->data.mouse.x; ((XButtonEvent *) x_event)->y = event->data.mouse.y; #if defined(USE_XINERAMA) || defined(USE_XRANDR) screens = hook_create_screen_info(&screen_count); if (screen_count > 1) { ((XButtonEvent *) x_event)->x += screens[0].x; ((XButtonEvent *) x_event)->y += screens[0].y; } if (screens != NULL) { free(screens); } #endif // These are the same because Window == Root Window. ((XButtonEvent *) x_event)->x_root = ((XButtonEvent *) x_event)->x; ((XButtonEvent *) x_event)->y_root = ((XButtonEvent *) x_event)->y; long int x_mask = NoEventMask; if (event->type == EVENT_MOUSE_DRAGGED) { #if Button1Mask == Button1MotionMask && \ Button2Mask == Button2MotionMask && \ Button3Mask == Button3MotionMask && \ Button4Mask == Button4MotionMask && \ Button5Mask == Button5MotionMask // This little trick only works if Button#MotionMasks align with // the Button#Masks. x_mask = ((XMotionEvent *) x_event)->state & (Button1MotionMask | Button2MotionMask | Button2MotionMask | Button3MotionMask | Button5MotionMask); #else // Fallback to some slightly larger... if (((XMotionEvent *) x_event)->state & Button1Mask) { x_mask |= Button1MotionMask; } if (((XMotionEvent *) x_event)->state & Button2Mask) { x_mask |= Button2MotionMask; } if (((XMotionEvent *) x_event)->state & Button3Mask) { x_mask |= Button3MotionMask; } if (((XMotionEvent *) x_event)->state & Button4Mask) { x_mask |= Button4MotionMask; } if (((XMotionEvent *) x_event)->state & Button5Mask) { x_mask |= Button5MotionMask; } #endif } // NOTE x_mask = NoEventMask. XSendEvent(disp, InputFocus, False, x_mask, x_event); free(x_event); break; case EVENT_MOUSE_CLICKED: case EVENT_KEY_TYPED: // Ignore clicked and typed events. case EVENT_HOOK_ENABLED: case EVENT_HOOK_DISABLED: // Ignore hook enabled / disabled events. default: // Ignore any other garbage. logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", __FUNCTION__, __LINE__, event->type); break; } #endif // Don't forget to flush! XFlush(disp); }
static inline void post_mouse_motion_event(uiohook_event * const event) { #ifdef USE_XTEST XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0); #else XMotionEvent mov_event; mov_event.serial = MotionNotify; mov_event.send_event = False; mov_event.display = properties_disp; mov_event.time = CurrentTime; mov_event.same_screen = True; mov_event.is_hint = NotifyNormal, mov_event.root = DefaultRootWindow(properties_disp); mov_event.window = mov_event.root; mov_event.subwindow = None; mov_event.type = 0x00; mov_event.state = 0x00; mov_event.x_root = 0; mov_event.y_root = 0; mov_event.x = 0; mov_event.y = 0; mov_event.state = convert_to_native_mask(event->mask); mov_event.x = event->data.mouse.x; mov_event.y = event->data.mouse.y; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t screen_count; screen_data *screens = hook_create_screen_info(&screen_count); if (screen_count > 1) { mov_event.x += screens[0].x; mov_event.y += screens[0].y; } if (screens != NULL) { free(screens); } #endif // These are the same because Window == Root Window. mov_event.x_root = mov_event.x; mov_event.y_root = mov_event.y; long int event_mask = NoEventMask; if (event->type == EVENT_MOUSE_DRAGGED) { #if Button1Mask == Button1MotionMask && \ Button2Mask == Button2MotionMask && \ Button3Mask == Button3MotionMask && \ Button4Mask == Button4MotionMask && \ Button5Mask == Button5MotionMask // This little trick only works if Button#MotionMasks align with // the Button#Masks. event_mask = mov_event.state & (Button1MotionMask | Button2MotionMask | Button2MotionMask | Button3MotionMask | Button5MotionMask); #else // Fallback to some slightly larger... if (event->state & Button1Mask) { event_mask |= Button1MotionMask; } if (event->state & Button2Mask) { event_mask |= Button2MotionMask; } if (event->state & Button3Mask) { event_mask |= Button3MotionMask; } if (event->state & Button4Mask) { event_mask |= Button4MotionMask; } if (event->state & Button5Mask) { event_mask |= Button5MotionMask; } #endif } // NOTE x_mask = NoEventMask. XSendEvent(properties_disp, InputFocus, False, event_mask, (XEvent *) &mov_event); #endif }
void hook_event_proc(XPointer closeure, XRecordInterceptData *recorded_data) { uint64_t timestamp = (uint64_t) recorded_data->server_time; if (recorded_data->category == XRecordStartOfData) { // Populate the hook start event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_HOOK_ENABLED; event.mask = 0x00; // Fire the hook start event. dispatch_event(&event); } else if (recorded_data->category == XRecordEndOfData) { // Populate the hook stop event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_HOOK_DISABLED; event.mask = 0x00; // Fire the hook stop event. dispatch_event(&event); } else if (recorded_data->category == XRecordFromServer || recorded_data->category == XRecordFromClient) { // Get XRecord data. XRecordDatum *data = (XRecordDatum *) recorded_data->data; if (data->type == KeyPress) { // The X11 KeyCode associated with this event. KeyCode keycode = (KeyCode) data->event.u.u.detail; KeySym keysym = 0x00; #if defined(USE_XKBCOMMON) if (state != NULL) { keysym = xkb_state_key_get_one_sym(state, keycode); } #else keysym = keycode_to_keysym(keycode, data->event.u.keyButtonPointer.state); #endif unsigned short int scancode = keycode_to_scancode(keycode); // TODO If you have a better suggestion for this ugly, let me know. if (scancode == VC_SHIFT_L) { set_modifier_mask(MASK_SHIFT_L); } else if (scancode == VC_SHIFT_R) { set_modifier_mask(MASK_SHIFT_R); } else if (scancode == VC_CONTROL_L) { set_modifier_mask(MASK_CTRL_L); } else if (scancode == VC_CONTROL_R) { set_modifier_mask(MASK_CTRL_R); } else if (scancode == VC_ALT_L) { set_modifier_mask(MASK_ALT_L); } else if (scancode == VC_ALT_R) { set_modifier_mask(MASK_ALT_R); } else if (scancode == VC_META_L) { set_modifier_mask(MASK_META_L); } else if (scancode == VC_META_R) { set_modifier_mask(MASK_META_R); } xkb_state_update_key(state, keycode, XKB_KEY_DOWN); initialize_locks(); if ((get_modifiers() & MASK_NUM_LOCK) == 0) { switch (scancode) { case VC_KP_SEPARATOR: case VC_KP_1: case VC_KP_2: case VC_KP_3: case VC_KP_4: case VC_KP_5: case VC_KP_6: case VC_KP_7: case VC_KP_8: case VC_KP_0: case VC_KP_9: scancode |= 0xEE00; break; } } // Populate key pressed event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_PRESSED; event.mask = get_modifiers(); event.data.keyboard.keycode = scancode; event.data.keyboard.rawcode = keysym; event.data.keyboard.keychar = CHAR_UNDEFINED; logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n", __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode); // Fire key pressed event. dispatch_event(&event); // If the pressed event was not consumed... if (event.reserved ^ 0x01) { uint16_t buffer[2]; size_t count = 0; // Check to make sure the key is printable. #ifdef USE_XKBCOMMON if (state != NULL) { count = keycode_to_unicode(state, keycode, buffer, sizeof(buffer) / sizeof(uint16_t)); } #else count = keysym_to_unicode(keysym, buffer, sizeof(buffer) / sizeof(uint16_t)); #endif for (unsigned int i = 0; i < count; i++) { // Populate key typed event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_TYPED; event.mask = get_modifiers(); event.data.keyboard.keycode = VC_UNDEFINED; event.data.keyboard.rawcode = keysym; event.data.keyboard.keychar = buffer[i]; logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n", __FUNCTION__, __LINE__, event.data.keyboard.keycode, (uint16_t) event.data.keyboard.keychar); // Fire key typed event. dispatch_event(&event); } } } else if (data->type == KeyRelease) { // The X11 KeyCode associated with this event. KeyCode keycode = (KeyCode) data->event.u.u.detail; KeySym keysym = 0x00; #ifdef USE_XKBCOMMON if (state != NULL) { keysym = xkb_state_key_get_one_sym(state, keycode); } #else keysym = keycode_to_keysym(keycode, data->event.u.keyButtonPointer.state); #endif unsigned short int scancode = keycode_to_scancode(keycode); // TODO If you have a better suggestion for this ugly, let me know. if (scancode == VC_SHIFT_L) { unset_modifier_mask(MASK_SHIFT_L); } else if (scancode == VC_SHIFT_R) { unset_modifier_mask(MASK_SHIFT_R); } else if (scancode == VC_CONTROL_L) { unset_modifier_mask(MASK_CTRL_L); } else if (scancode == VC_CONTROL_R) { unset_modifier_mask(MASK_CTRL_R); } else if (scancode == VC_ALT_L) { unset_modifier_mask(MASK_ALT_L); } else if (scancode == VC_ALT_R) { unset_modifier_mask(MASK_ALT_R); } else if (scancode == VC_META_L) { unset_modifier_mask(MASK_META_L); } else if (scancode == VC_META_R) { unset_modifier_mask(MASK_META_R); } xkb_state_update_key(state, keycode, XKB_KEY_UP); initialize_locks(); if ((get_modifiers() & MASK_NUM_LOCK) == 0) { switch (scancode) { case VC_KP_SEPARATOR: case VC_KP_1: case VC_KP_2: case VC_KP_3: case VC_KP_4: case VC_KP_5: case VC_KP_6: case VC_KP_7: case VC_KP_8: case VC_KP_0: case VC_KP_9: scancode |= 0xEE00; break; } } // Populate key released event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_RELEASED; event.mask = get_modifiers(); event.data.keyboard.keycode = scancode; event.data.keyboard.rawcode = keysym; event.data.keyboard.keychar = CHAR_UNDEFINED; logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n", __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode); // Fire key released event. dispatch_event(&event); } else if (data->type == ButtonPress) { // X11 handles wheel events as button events. if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelDown || data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight) { // Reset the click count and previous button. hook->input.mouse.click.count = 1; hook->input.mouse.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. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_WHEEL; event.mask = get_modifiers(); event.data.wheel.clicks = hook->input.mouse.click.count; event.data.wheel.x = data->event.u.keyButtonPointer.rootX; event.data.wheel.y = data->event.u.keyButtonPointer.rootY; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t count; screen_data *screens = hook_create_screen_info(&count); if (count > 1) { event.data.wheel.x -= screens[0].x; event.data.wheel.y -= screens[0].y; } if (screens != NULL) { free(screens); } #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. */ 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. */ event.data.wheel.amount = 3; if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelLeft) { // Wheel Rotated Up and Away. event.data.wheel.rotation = -1; } else { // data->event.u.u.detail == WheelDown // Wheel Rotated Down and Towards. event.data.wheel.rotation = 1; } if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelDown) { // Wheel Rotated Up or Down. 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. event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION; } logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n", __FUNCTION__, __LINE__, event.data.wheel.type, event.data.wheel.amount * event.data.wheel.rotation, event.data.wheel.direction, event.data.wheel.x, event.data.wheel.y); // Fire mouse wheel event. dispatch_event(&event); } else { /* This information is all static for X11, its up to the WM to * decide how to interpret the wheel events. */ uint16_t button = MOUSE_NOBUTTON; switch (data->event.u.u.detail) { // FIXME This should use a lookup table to handle button remapping. case Button1: button = MOUSE_BUTTON1; set_modifier_mask(MASK_BUTTON1); break; case Button2: button = MOUSE_BUTTON2; set_modifier_mask(MASK_BUTTON2); break; case Button3: button = MOUSE_BUTTON3; set_modifier_mask(MASK_BUTTON3); break; case XButton1: button = MOUSE_BUTTON4; set_modifier_mask(MASK_BUTTON5); break; case XButton2: button = MOUSE_BUTTON5; set_modifier_mask(MASK_BUTTON5); break; default: // Do not set modifier masks past button MASK_BUTTON5. break; } // Track the number of clicks, the button must match the previous button. if (button == hook->input.mouse.click.button && (long int) (timestamp - hook->input.mouse.click.time) <= hook_get_multi_click_time()) { if (hook->input.mouse.click.count < USHRT_MAX) { hook->input.mouse.click.count++; } else { logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n", __FUNCTION__, __LINE__); } } else { // Reset the click count. hook->input.mouse.click.count = 1; // Set the previous button. hook->input.mouse.click.button = button; } // Save this events time to calculate the hook->input.mouse.click.count. hook->input.mouse.click.time = timestamp; // Populate mouse pressed event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_PRESSED; event.mask = get_modifiers(); event.data.mouse.button = button; event.data.mouse.clicks = hook->input.mouse.click.count; event.data.mouse.x = data->event.u.keyButtonPointer.rootX; event.data.mouse.y = data->event.u.keyButtonPointer.rootY; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t count; screen_data *screens = hook_create_screen_info(&count); if (count > 1) { event.data.mouse.x -= screens[0].x; event.data.mouse.y -= screens[0].y; } if (screens != NULL) { free(screens); } #endif logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n", __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks, event.data.mouse.x, event.data.mouse.y); // Fire mouse pressed event. dispatch_event(&event); } } else if (data->type == ButtonRelease) { // X11 handles wheel events as button events. if (data->event.u.u.detail != WheelUp && data->event.u.u.detail != WheelDown) { /* This information is all static for X11, its up to the WM to * decide how to interpret the wheel events. */ uint16_t button = MOUSE_NOBUTTON; switch (data->event.u.u.detail) { // FIXME This should use a lookup table to handle button remapping. case Button1: button = MOUSE_BUTTON1; unset_modifier_mask(MASK_BUTTON1); break; case Button2: button = MOUSE_BUTTON2; unset_modifier_mask(MASK_BUTTON2); break; case Button3: button = MOUSE_BUTTON3; unset_modifier_mask(MASK_BUTTON3); break; case XButton1: button = MOUSE_BUTTON4; unset_modifier_mask(MASK_BUTTON5); break; case XButton2: button = MOUSE_BUTTON5; unset_modifier_mask(MASK_BUTTON5); break; default: // Do not set modifier masks past button MASK_BUTTON5. break; } // Populate mouse released event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_RELEASED; event.mask = get_modifiers(); event.data.mouse.button = button; event.data.mouse.clicks = hook->input.mouse.click.count; event.data.mouse.x = data->event.u.keyButtonPointer.rootX; event.data.mouse.y = data->event.u.keyButtonPointer.rootY; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t count; screen_data *screens = hook_create_screen_info(&count); if (count > 1) { event.data.mouse.x -= screens[0].x; event.data.mouse.y -= screens[0].y; } if (screens != NULL) { free(screens); } #endif logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n", __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks, event.data.mouse.x, event.data.mouse.y); // Fire mouse released event. dispatch_event(&event); // If the pressed event was not consumed... if (event.reserved ^ 0x01 && hook->input.mouse.is_dragged != true) { // Populate mouse clicked event. event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_CLICKED; event.mask = get_modifiers(); event.data.mouse.button = button; event.data.mouse.clicks = hook->input.mouse.click.count; event.data.mouse.x = data->event.u.keyButtonPointer.rootX; event.data.mouse.y = data->event.u.keyButtonPointer.rootY; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t count; screen_data *screens = hook_create_screen_info(&count); if (count > 1) { event.data.mouse.x -= screens[0].x; event.data.mouse.y -= screens[0].y; } if (screens != NULL) { free(screens); } #endif logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n", __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks, event.data.mouse.x, event.data.mouse.y); // Fire mouse clicked event. dispatch_event(&event); } // Reset the number of clicks. if (button == hook->input.mouse.click.button && (long int) (event.time - hook->input.mouse.click.time) > hook_get_multi_click_time()) { // Reset the click count. hook->input.mouse.click.count = 0; } } } else if (data->type == MotionNotify) { // Reset the click count. if (hook->input.mouse.click.count != 0 && (long int) (timestamp - hook->input.mouse.click.time) > hook_get_multi_click_time()) { hook->input.mouse.click.count = 0; } // Populate mouse move event. event.time = timestamp; event.reserved = 0x00; event.mask = get_modifiers(); // Check the upper half of virtual modifiers for non-zero // values and set the mouse dragged flag. hook->input.mouse.is_dragged = (event.mask >> 8 > 0); if (hook->input.mouse.is_dragged) { // Create Mouse Dragged event. event.type = EVENT_MOUSE_DRAGGED; } else { // Create a Mouse Moved event. event.type = EVENT_MOUSE_MOVED; } event.data.mouse.button = MOUSE_NOBUTTON; event.data.mouse.clicks = hook->input.mouse.click.count; event.data.mouse.x = data->event.u.keyButtonPointer.rootX; event.data.mouse.y = data->event.u.keyButtonPointer.rootY; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t count; screen_data *screens = hook_create_screen_info(&count); if (count > 1) { event.data.mouse.x -= screens[0].x; event.data.mouse.y -= screens[0].y; } if (screens != NULL) { free(screens); } #endif logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %i, %i. (%#X)\n", __FUNCTION__, __LINE__, hook->input.mouse.is_dragged ? "dragged" : "moved", event.data.mouse.x, event.data.mouse.y, event.mask); // Fire mouse move event. dispatch_event(&event); } else {
static inline void post_mouse_button_event(uiohook_event * const event) { #ifdef USE_XTEST Window ret_root; Window ret_child; int root_x; int root_y; int win_x; int win_y; unsigned int mask; Window win_root = XDefaultRootWindow(properties_disp); Bool query_status = XQueryPointer(properties_disp, win_root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask); if (query_status) { if (event->data.mouse.x != root_x || event->data.mouse.y != root_y) { // Move the pointer to the specified position. XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0); } else { query_status = False; } } if (event->type == EVENT_MOUSE_WHEEL) { // Wheel events should be the same as click events on X11. // type, amount and rotation if (event->data.wheel.rotation < 0) { XTestFakeButtonEvent(properties_disp, WheelUp, True, 0); XTestFakeButtonEvent(properties_disp, WheelUp, False, 0); } else { XTestFakeButtonEvent(properties_disp, WheelDown, True, 0); XTestFakeButtonEvent(properties_disp, WheelDown, False, 0); } } else if (event->type == EVENT_MOUSE_PRESSED) { XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0); } else if (event->type == EVENT_MOUSE_RELEASED) { XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0); } else if (event->type == EVENT_MOUSE_CLICKED) { XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0); XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0); } if (query_status) { // Move the pointer back to the original position. XTestFakeMotionEvent(properties_disp, -1, root_x, root_y, 0); } #else XButtonEvent btn_event; btn_event.serial = 0x00; btn_event.send_event = False; btn_event.display = properties_disp; btn_event.time = CurrentTime; btn_event.same_screen = True; btn_event.root = DefaultRootWindow(properties_disp); btn_event.window = btn_event.root; btn_event.subwindow = None; btn_event.type = 0x00; btn_event.state = 0x00; btn_event.x_root = 0; btn_event.y_root = 0; btn_event.x = 0; btn_event.y = 0; btn_event.button = 0x00; btn_event.state = convert_to_native_mask(event->mask); btn_event.x = event->data.mouse.x; btn_event.y = event->data.mouse.y; #if defined(USE_XINERAMA) || defined(USE_XRANDR) uint8_t screen_count; screen_data *screens = hook_create_screen_info(&screen_count); if (screen_count > 1) { btn_event.x += screens[0].x; btn_event.y += screens[0].y; } if (screens != NULL) { free(screens); } #endif // These are the same because Window == Root Window. btn_event.x_root = btn_event.x; btn_event.y_root = btn_event.y; if (event->type == EVENT_MOUSE_WHEEL) { // type, amount and rotation if (event->data.wheel.rotation < 0) { btn_event.button = WheelUp; } else { btn_event.button = WheelDown; } } if (event->type != EVENT_MOUSE_RELEASED) { // FIXME Where do we set event->button? btn_event.type = ButtonPress; XSendEvent(properties_disp, InputFocus, False, ButtonPressMask, (XEvent *) &btn_event); } if (event->type != EVENT_MOUSE_PRESSED) { btn_event.type = ButtonRelease; XSendEvent(properties_disp, InputFocus, False, ButtonReleaseMask, (XEvent *) &btn_event); } #endif }