#include "Jtk.h" #ifdef JTK_X #include #include #include #include #include "Jtk_internal.h" #include /** Do not wait longer than 100ms */ #define MAX_WAIT_TIME 100000u /** Hold a copy of the last-seen keyboard modifier state. */ static unsigned int g_x_state; static std::unordered_map g_repeating_keys; static std::unordered_map g_key_repeat_timers; static void StopKeyRepeat(unsigned int x_keycode) { auto it = g_repeating_keys.find(x_keycode); if (it != g_repeating_keys.end()) { size_t timer_id = it->second; Jtk_RemoveTimer(timer_id); g_repeating_keys.erase(x_keycode); g_key_repeat_timers.erase(timer_id); } } static uint32_t XKeyToJtkKey(unsigned int x_keycode) { XKeyEvent x_key_event; x_key_event.type = KeyPress; x_key_event.display = g_display; /* Turn off the ControlMask bit for looking up keys. We'll handle control * keys ourselves. */ x_key_event.state = g_x_state & ~ControlMask; x_key_event.keycode = x_keycode; char buffer; KeySym keysym; uint32_t key = JTK_KEY_UNKNOWN; if (XLookupString(&x_key_event, &buffer, 1, &keysym, nullptr) > 0) { if (buffer == '\r') { key = '\n'; } else { key = buffer; } } else { switch (keysym) { case XK_F1: key = JTK_KEY_F1; break; case XK_F2: key = JTK_KEY_F2; break; case XK_F3: key = JTK_KEY_F3; break; case XK_F4: key = JTK_KEY_F4; break; case XK_F5: key = JTK_KEY_F5; break; case XK_F6: key = JTK_KEY_F6; break; case XK_F7: key = JTK_KEY_F7; break; case XK_F8: key = JTK_KEY_F8; break; case XK_F9: key = JTK_KEY_F9; break; case XK_F10: key = JTK_KEY_F10; break; case XK_F11: key = JTK_KEY_F11; break; case XK_F12: key = JTK_KEY_F12; break; case XK_F13: key = JTK_KEY_F13; break; case XK_F14: key = JTK_KEY_F14; break; case XK_F15: key = JTK_KEY_F15; break; case XK_F16: key = JTK_KEY_F16; break; case XK_F17: key = JTK_KEY_F17; break; case XK_F18: key = JTK_KEY_F18; break; case XK_F19: key = JTK_KEY_F19; break; case XK_F20: key = JTK_KEY_F20; break; case XK_F21: key = JTK_KEY_F21; break; case XK_F22: key = JTK_KEY_F22; break; case XK_F23: key = JTK_KEY_F23; break; case XK_F24: key = JTK_KEY_F24; break; case XK_F25: key = JTK_KEY_F25; break; case XK_F26: key = JTK_KEY_F26; break; case XK_F27: key = JTK_KEY_F27; break; case XK_F28: key = JTK_KEY_F28; break; case XK_F29: key = JTK_KEY_F29; break; case XK_F30: key = JTK_KEY_F30; break; case XK_F31: key = JTK_KEY_F31; break; case XK_Shift_L: key = JTK_KEY_SHIFT_L; break; case XK_Shift_R: key = JTK_KEY_SHIFT_R; break; case XK_Control_L: key = JTK_KEY_CTRL_L; break; case XK_Control_R: key = JTK_KEY_CTRL_R; break; case XK_Caps_Lock: key = JTK_KEY_CAPS_LOCK; break; case XK_Shift_Lock: key = JTK_KEY_SHIFT_LOCK;break; case XK_Meta_L: key = JTK_KEY_META_L; break; case XK_Meta_R: key = JTK_KEY_META_R; break; case XK_Alt_L: key = JTK_KEY_ALT_L; break; case XK_Alt_R: key = JTK_KEY_ALT_R; break; case XK_Super_L: key = JTK_KEY_SUPER_L; break; case XK_Super_R: key = JTK_KEY_SUPER_R; break; case XK_Home: key = JTK_KEY_HOME; break; case XK_Left: key = JTK_KEY_LEFT; break; case XK_Up: key = JTK_KEY_UP; break; case XK_Right: key = JTK_KEY_RIGHT; break; case XK_Down: key = JTK_KEY_DOWN; break; case XK_Page_Up: key = JTK_KEY_PAGE_UP; break; case XK_Page_Down: key = JTK_KEY_PAGE_DOWN; break; case XK_End: key = JTK_KEY_END; break; case XK_Begin: key = JTK_KEY_BEGIN; break; case XK_Select: key = JTK_KEY_SELECT; break; case XK_Print: key = JTK_KEY_PRINT; break; case XK_Execute: key = JTK_KEY_EXECUTE; break; case XK_Insert: key = JTK_KEY_INSERT; break; case XK_Undo: key = JTK_KEY_UNDO; break; case XK_Redo: key = JTK_KEY_REDO; break; case XK_Menu: key = JTK_KEY_MENU; break; case XK_Find: key = JTK_KEY_FIND; break; case XK_Cancel: key = JTK_KEY_CANCEL; break; case XK_Help: key = JTK_KEY_HELP; break; case XK_Break: key = JTK_KEY_BREAK; break; case XK_Num_Lock: key = JTK_KEY_NUM_LOCK; break; case XK_KP_Space: key = JTK_KEY_KP_SPACE; break; case XK_KP_Tab: key = JTK_KEY_KP_TAB; break; case XK_KP_Enter: key = JTK_KEY_KP_ENTER; break; case XK_KP_F1: key = JTK_KEY_KP_F1; break; case XK_KP_F2: key = JTK_KEY_KP_F2; break; case XK_KP_F3: key = JTK_KEY_KP_F3; break; case XK_KP_F4: key = JTK_KEY_KP_F4; break; case XK_KP_Home: key = JTK_KEY_KP_HOME; break; case XK_KP_Left: key = JTK_KEY_KP_LEFT; break; case XK_KP_Up: key = JTK_KEY_KP_UP; break; case XK_KP_Right: key = JTK_KEY_KP_RIGHT; break; case XK_KP_Down: key = JTK_KEY_KP_DOWN; break; case XK_KP_Page_Up: key = JTK_KEY_KP_PAGE_UP; break; case XK_KP_Page_Down: key = JTK_KEY_KP_PAGE_DOWN; break; case XK_KP_End: key = JTK_KEY_KP_END; break; case XK_KP_Begin: key = JTK_KEY_KP_BEGIN; break; case XK_KP_Insert: key = JTK_KEY_KP_INSERT; break; case XK_KP_Delete: key = JTK_KEY_KP_DELETE; break; case XK_KP_Equal: key = JTK_KEY_KP_EQUAL; break; case XK_KP_Multiply: key = JTK_KEY_KP_MULTIPLY; break; case XK_KP_Add: key = JTK_KEY_KP_ADD; break; case XK_KP_Separator: key = JTK_KEY_KP_SEPARATOR; break; case XK_KP_Subtract: key = JTK_KEY_KP_SUBTRACT; break; case XK_KP_Decimal: key = JTK_KEY_KP_DECIMAL; break; case XK_KP_Divide: key = JTK_KEY_KP_DIVIDE; break; case XK_KP_0: key = JTK_KEY_KP_0; break; case XK_KP_1: key = JTK_KEY_KP_1; break; case XK_KP_2: key = JTK_KEY_KP_2; break; case XK_KP_3: key = JTK_KEY_KP_3; break; case XK_KP_4: key = JTK_KEY_KP_4; break; case XK_KP_5: key = JTK_KEY_KP_5; break; case XK_KP_6: key = JTK_KEY_KP_6; break; case XK_KP_7: key = JTK_KEY_KP_7; break; case XK_KP_8: key = JTK_KEY_KP_8; break; case XK_KP_9: key = JTK_KEY_KP_9; break; } } /* OR in the modifier states */ if (g_x_state & ShiftMask) { key |= JTK_KEY_MODS_SHIFT; } if (g_x_state & LockMask) { key |= JTK_KEY_MODS_LOCK; } if (g_x_state & ControlMask) { key |= JTK_KEY_MODS_CTRL; } return key; } static Bool KeyRepeatCheckIfEvent(Display * display, XEvent * chkev, XPointer arg) { XEvent * release_event = (XEvent *)arg; if (chkev->type == KeyPress && chkev->xkey.keycode == release_event->xkey.keycode && chkev->xkey.time - release_event->xkey.time < 2) return True; return False; } /** * Check to see if this is a repeated key. */ static bool IsRepeatKey(Display * display, XEvent * event) { XEvent dummyev; if (XPending(display)) { if (XCheckIfEvent(display, &dummyev, KeyRepeatCheckIfEvent, (XPointer)event) == True) { return true; } } return false; } /** * Process an X key press event. * * @param x_event * Pointer to the X event. * @param event * Pointer to the Jtk event. */ static bool ProcessXKeyPressEvent(XEvent * x_event, Jtk_Event * event) { unsigned int x_keycode = x_event->xkey.keycode; g_x_state = x_event->xkey.state; event->type = JTK_EVENT_KEY_PRESS; event->key.repeat = false; event->key.key = XKeyToJtkKey(x_keycode); event->key.x_keycode = x_keycode; return true; } /** * Process an X key release event. * * @param x_event * Pointer to the X event. * @param event * Pointer to the Jtk event. */ static bool ProcessXKeyReleaseEvent(XEvent * x_event, Jtk_Event * event) { if (IsRepeatKey(g_display, x_event)) { return false; } g_x_state = x_event->xkey.state; StopKeyRepeat(x_event->xkey.keycode); return false; } /** * Process an X event. * * @param x_event * Pointer to the X event. * @param event * Pointer to the Jtk event. * * @retval true * The event should be passed to the user and event has been filled in. * @retval false * The event should not be passed to the user. */ static bool ProcessXEvent(XEvent * x_event, Jtk_Event * event) { switch (x_event->type) { case KeyPress: return ProcessXKeyPressEvent(x_event, event); case KeyRelease: return ProcessXKeyReleaseEvent(x_event, event); case ButtonPress: break; case ButtonRelease: break; case Expose: break; case MappingNotify: XRefreshKeyboardMapping(&x_event->xmapping); return false; } return false; } void Jtk_WaitEvent(Jtk_Event * event) { for (;;) { /* First check if any timer has expired. */ size_t timer_id = Jtk_GetExpiredTimer(); if (timer_id != (size_t)-1) { auto it = g_key_repeat_timers.find(timer_id); if (it != g_key_repeat_timers.end()) { event->type = JTK_EVENT_KEY_PRESS; event->key.repeat = true; event->key.key = XKeyToJtkKey(it->second); event->key.x_keycode = it->second; } else { event->type = JTK_EVENT_TIMER; event->timer.timer_id = timer_id; event->timer.user1 = Jtk_GetTimerUserData(timer_id, 0u); event->timer.user2 = Jtk_GetTimerUserData(timer_id, 1u); } Jtk_ServiceTimer(timer_id); return; } /* Next check for an X event. */ while (XPending(g_display) > 0) { XEvent x_event; XNextEvent(g_display, &x_event); if (ProcessXEvent(&x_event, event)) return; } /* Finally, wait for something to happen. */ uint64_t time_to_wait = Jtk_TimeToNextTimerExpiration(); if (time_to_wait > MAX_WAIT_TIME) { time_to_wait = MAX_WAIT_TIME; } int x_fd = ConnectionNumber(g_display); fd_set fds; FD_ZERO(&fds); FD_SET(x_fd, &fds); struct timeval tv; tv.tv_sec = time_to_wait / 1000000u; tv.tv_usec = time_to_wait % 1000000u; select(x_fd + 1, &fds, nullptr, nullptr, &tv); } } void Jtk_BeginKeyRepeat(Jtk_KeyEvent * key_event, uint32_t delay, uint32_t interval) { StopKeyRepeat(key_event->x_keycode); size_t timer_id = Jtk_AddTimer(delay, interval, nullptr, nullptr); g_repeating_keys[key_event->x_keycode] = timer_id; g_key_repeat_timers[timer_id] = key_event->x_keycode; } #endif