/* we need a keycode + modifier to generate the proper keysym (such as @). Return 1 if successful, 0 otherwise. This function can fail if a keysym doesn't map to a keycode. */ static int keysym_to_keycode_mod (KeySym keysym, KeyCode *code, unsigned int *mod) { KeySym lower, upper; *mod = 0; *code = XKeysymToKeycode (dpy, keysym); lower = keycode_to_keysym (dpy, *code, 0, 0); upper = keycode_to_keysym (dpy, *code, 0, 1); /* If you need to press shift to get the keysym, add the shift mask. */ if (upper == keysym && lower != keysym) *mod = ShiftMask; return *code != 0; }
/* Cooks a keycode + modifier into a keysym + modifier. This should be used anytime meaningful key information is to be extracted from a KeyPress or KeyRelease event. returns the number of bytes in keysym_name. If you are not interested in the keysym name pass in NULL for keysym_name and 0 for len. */ int cook_keycode (XKeyEvent *ev, KeySym *keysym, unsigned int *mod, char *keysym_name, int len, int ignore_bad_mods) { int nbytes; int shift = 0; KeySym lower, upper; if (ignore_bad_mods) { ev->state &= ~(LockMask | rp_modifier_info.num_lock_mask | rp_modifier_info.scroll_lock_mask); } if (len > 0) len--; nbytes = XLookupString (ev, keysym_name, len, keysym, NULL); /* Null terminate the string (not all X servers do it for us). */ if (keysym_name) { keysym_name[nbytes] = '\0'; } /* Find out if XLookupString gobbled the shift modifier */ if (ev->state & ShiftMask) { lower = keycode_to_keysym (dpy, ev->keycode, 0, 0); upper = keycode_to_keysym (dpy, ev->keycode, 0, 1); /* If the keysym isn't affected by the shift key, then keep the shift modifier. */ if (lower == upper) shift = ShiftMask; } *mod = ev->state; *mod &= (rp_modifier_info.meta_mod_mask | rp_modifier_info.alt_mod_mask | rp_modifier_info.hyper_mod_mask | rp_modifier_info.super_mod_mask | ControlMask | shift); return nbytes; }
static void process_raw_key(char *buf, unsigned int len) { if ( len <= 0 ) { return; } else { unsigned int i; for (i = 0; i < len; i++) { char down = !(buf[i] & 0x80); short code = buf[i] & 0x7f; if (!code) { if (i + 2 >= len) break; code = (buf[++i] & 0x7f) << 7; code |= buf[++i] & 0x7f; if (!(buf[i] & 0x80) || !(buf[i - 1] & 0x80)) continue; } unsigned short linux_keysym = keycode_to_keysym(code, down); FcitxKeySym keysym = linux_keysym_to_fcitx_keysym(linux_keysym, code); if (keysym == Key_None) continue; FcitxIMClientFocusIn(client); if (FcitxIMClientProcessKey(client, keysym, code, state, (down ? FCITX_PRESS_KEY : FCITX_RELEASE_KEY), 0) <= 0) { char *str = keysym_to_term_string(linux_keysym, down); if (str) put_im_text(str, strlen(str)); } state = calculate_modifiers(state, keysym, down); } } }
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 {