diff --git a/src/gui/Window.cc b/src/gui/Window.cc index 4463824..d301f0b 100644 --- a/src/gui/Window.cc +++ b/src/gui/Window.cc @@ -11,22 +11,6 @@ #define INITIAL_HEIGHT 800 #define FONT_SIZE 16 -#define TIMER_TYPE_KEY_REPEAT 1u - -struct KeyStatus -{ - size_t timer_id; - uint32_t keyval; - bool pressed; - bool event_pending; - - KeyStatus() - { - pressed = false; - } -}; -std::unordered_map g_key_statuses; - /** * Initialize OpenGL. * @@ -139,6 +123,8 @@ bool Window::create(std::shared_ptr buffer) resize(); + m_redraw_requested = true; + return true; } @@ -153,13 +139,13 @@ void Window::run_event_loop() while (!m_exit_requested) { - Jtk_WaitEvent(&event); - handle_event(event); if (m_redraw_requested) { m_redraw_requested = false; redraw(); } + Jtk_WaitEvent(&event); + handle_event(event); } } @@ -177,46 +163,18 @@ void Window::handle_event(Jtk_Event & event) #endif case JTK_EVENT_KEY_PRESS: + if (!event.key.repeat) { - KeyStatus & key_status = g_key_statuses[event.key.key]; - key_status.pressed = true; - key_status.keyval = event.key.key; - key_status.timer_id = Jtk_AddTimer(300u, 25u, - (void *)TIMER_TYPE_KEY_REPEAT, &key_status); - handle_keypress(event.key.key); - } - break; - - case JTK_EVENT_KEY_RELEASE: - { - KeyStatus & key_status = g_key_statuses[event.key.key]; - if (key_status.pressed) - { - Jtk_RemoveTimer(key_status.timer_id); - key_status.pressed = false; - } + Jtk_BeginKeyRepeat(&event.key, 300u, 25u); } + handle_keypress(event.key.key); + m_redraw_requested = true; break; case JTK_EVENT_WINDOW_EXPOSE: m_redraw_requested = true; break; - case JTK_EVENT_TIMER: - switch ((uintptr_t)event.timer.user1) - { - case TIMER_TYPE_KEY_REPEAT: - { - KeyStatus * key_status = (KeyStatus *)event.timer.user2; - if (key_status->pressed) - { - handle_keypress(key_status->keyval); - } - } - break; - } - break; - #if 0 case SDL_WINDOWEVENT: switch (event.window.event) diff --git a/src/gui/jtk/Jtk_event.cc b/src/gui/jtk/Jtk_event.cc index 899d0c0..cb1231e 100644 --- a/src/gui/jtk/Jtk_event.cc +++ b/src/gui/jtk/Jtk_event.cc @@ -7,167 +7,179 @@ #include #include #include "Jtk_internal.h" +#include /** Do not wait longer than 100ms */ #define MAX_WAIT_TIME 100000u -/** - * Process an X keyboard event. - * - * @param x_event - * Pointer to the X event. - * @param event - * Pointer to the Jtk event. - */ -static bool ProcessXKeyEvent(XEvent * x_event, Jtk_Event * event) +/** 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; - /* Make a copy of x_event and turn off the ControlMask bit for lookup - * up keycodes. We'll handle control keys ourselves. */ - XEvent x_event_for_key_lookup = *x_event; - x_event_for_key_lookup.xkey.state &= ~ControlMask; - if (XLookupString(&x_event_for_key_lookup.xkey, &buffer, 1, &keysym, nullptr) > 0) + uint32_t key = JTK_KEY_UNKNOWN; + + if (XLookupString(&x_key_event, &buffer, 1, &keysym, nullptr) > 0) { if (buffer == '\r') { - event->key.key = '\n'; + key = '\n'; } else { - event->key.key = buffer; + key = buffer; } } else { switch (keysym) { - case XK_F1: event->key.key = JTK_KEY_F1; break; - case XK_F2: event->key.key = JTK_KEY_F2; break; - case XK_F3: event->key.key = JTK_KEY_F3; break; - case XK_F4: event->key.key = JTK_KEY_F4; break; - case XK_F5: event->key.key = JTK_KEY_F5; break; - case XK_F6: event->key.key = JTK_KEY_F6; break; - case XK_F7: event->key.key = JTK_KEY_F7; break; - case XK_F8: event->key.key = JTK_KEY_F8; break; - case XK_F9: event->key.key = JTK_KEY_F9; break; - case XK_F10: event->key.key = JTK_KEY_F10; break; - case XK_F11: event->key.key = JTK_KEY_F11; break; - case XK_F12: event->key.key = JTK_KEY_F12; break; - case XK_F13: event->key.key = JTK_KEY_F13; break; - case XK_F14: event->key.key = JTK_KEY_F14; break; - case XK_F15: event->key.key = JTK_KEY_F15; break; - case XK_F16: event->key.key = JTK_KEY_F16; break; - case XK_F17: event->key.key = JTK_KEY_F17; break; - case XK_F18: event->key.key = JTK_KEY_F18; break; - case XK_F19: event->key.key = JTK_KEY_F19; break; - case XK_F20: event->key.key = JTK_KEY_F20; break; - case XK_F21: event->key.key = JTK_KEY_F21; break; - case XK_F22: event->key.key = JTK_KEY_F22; break; - case XK_F23: event->key.key = JTK_KEY_F23; break; - case XK_F24: event->key.key = JTK_KEY_F24; break; - case XK_F25: event->key.key = JTK_KEY_F25; break; - case XK_F26: event->key.key = JTK_KEY_F26; break; - case XK_F27: event->key.key = JTK_KEY_F27; break; - case XK_F28: event->key.key = JTK_KEY_F28; break; - case XK_F29: event->key.key = JTK_KEY_F29; break; - case XK_F30: event->key.key = JTK_KEY_F30; break; - case XK_F31: event->key.key = JTK_KEY_F31; break; + 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: event->key.key = JTK_KEY_SHIFT_L; break; - case XK_Shift_R: event->key.key = JTK_KEY_SHIFT_R; break; - case XK_Control_L: event->key.key = JTK_KEY_CTRL_L; break; - case XK_Control_R: event->key.key = JTK_KEY_CTRL_R; break; - case XK_Caps_Lock: event->key.key = JTK_KEY_CAPS_LOCK; break; - case XK_Shift_Lock: event->key.key = JTK_KEY_SHIFT_LOCK;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: event->key.key = JTK_KEY_META_L; break; - case XK_Meta_R: event->key.key = JTK_KEY_META_R; break; - case XK_Alt_L: event->key.key = JTK_KEY_ALT_L; break; - case XK_Alt_R: event->key.key = JTK_KEY_ALT_R; break; - case XK_Super_L: event->key.key = JTK_KEY_SUPER_L; break; - case XK_Super_R: event->key.key = JTK_KEY_SUPER_R; 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: event->key.key = JTK_KEY_HOME; break; - case XK_Left: event->key.key = JTK_KEY_LEFT; break; - case XK_Up: event->key.key = JTK_KEY_UP; break; - case XK_Right: event->key.key = JTK_KEY_RIGHT; break; - case XK_Down: event->key.key = JTK_KEY_DOWN; break; - case XK_Page_Up: event->key.key = JTK_KEY_PAGE_UP; break; - case XK_Page_Down: event->key.key = JTK_KEY_PAGE_DOWN; break; - case XK_End: event->key.key = JTK_KEY_END; break; - case XK_Begin: event->key.key = JTK_KEY_BEGIN; 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: event->key.key = JTK_KEY_SELECT; break; - case XK_Print: event->key.key = JTK_KEY_PRINT; break; - case XK_Execute: event->key.key = JTK_KEY_EXECUTE; break; - case XK_Insert: event->key.key = JTK_KEY_INSERT; break; - case XK_Undo: event->key.key = JTK_KEY_UNDO; break; - case XK_Redo: event->key.key = JTK_KEY_REDO; break; - case XK_Menu: event->key.key = JTK_KEY_MENU; break; - case XK_Find: event->key.key = JTK_KEY_FIND; break; - case XK_Cancel: event->key.key = JTK_KEY_CANCEL; break; - case XK_Help: event->key.key = JTK_KEY_HELP; break; - case XK_Break: event->key.key = JTK_KEY_BREAK; break; - case XK_Num_Lock: event->key.key = JTK_KEY_NUM_LOCK; 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: event->key.key = JTK_KEY_KP_SPACE; break; - case XK_KP_Tab: event->key.key = JTK_KEY_KP_TAB; break; - case XK_KP_Enter: event->key.key = JTK_KEY_KP_ENTER; break; - case XK_KP_F1: event->key.key = JTK_KEY_KP_F1; break; - case XK_KP_F2: event->key.key = JTK_KEY_KP_F2; break; - case XK_KP_F3: event->key.key = JTK_KEY_KP_F3; break; - case XK_KP_F4: event->key.key = JTK_KEY_KP_F4; break; - case XK_KP_Home: event->key.key = JTK_KEY_KP_HOME; break; - case XK_KP_Left: event->key.key = JTK_KEY_KP_LEFT; break; - case XK_KP_Up: event->key.key = JTK_KEY_KP_UP; break; - case XK_KP_Right: event->key.key = JTK_KEY_KP_RIGHT; break; - case XK_KP_Down: event->key.key = JTK_KEY_KP_DOWN; break; - case XK_KP_Page_Up: event->key.key = JTK_KEY_KP_PAGE_UP; break; - case XK_KP_Page_Down: event->key.key = JTK_KEY_KP_PAGE_DOWN; break; - case XK_KP_End: event->key.key = JTK_KEY_KP_END; break; - case XK_KP_Begin: event->key.key = JTK_KEY_KP_BEGIN; break; - case XK_KP_Insert: event->key.key = JTK_KEY_KP_INSERT; break; - case XK_KP_Delete: event->key.key = JTK_KEY_KP_DELETE; break; - case XK_KP_Equal: event->key.key = JTK_KEY_KP_EQUAL; break; - case XK_KP_Multiply: event->key.key = JTK_KEY_KP_MULTIPLY; break; - case XK_KP_Add: event->key.key = JTK_KEY_KP_ADD; break; - case XK_KP_Separator: event->key.key = JTK_KEY_KP_SEPARATOR; break; - case XK_KP_Subtract: event->key.key = JTK_KEY_KP_SUBTRACT; break; - case XK_KP_Decimal: event->key.key = JTK_KEY_KP_DECIMAL; break; - case XK_KP_Divide: event->key.key = JTK_KEY_KP_DIVIDE; 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: event->key.key = JTK_KEY_KP_0; break; - case XK_KP_1: event->key.key = JTK_KEY_KP_1; break; - case XK_KP_2: event->key.key = JTK_KEY_KP_2; break; - case XK_KP_3: event->key.key = JTK_KEY_KP_3; break; - case XK_KP_4: event->key.key = JTK_KEY_KP_4; break; - case XK_KP_5: event->key.key = JTK_KEY_KP_5; break; - case XK_KP_6: event->key.key = JTK_KEY_KP_6; break; - case XK_KP_7: event->key.key = JTK_KEY_KP_7; break; - case XK_KP_8: event->key.key = JTK_KEY_KP_8; break; - case XK_KP_9: event->key.key = JTK_KEY_KP_9; break; - - default: - return false; + 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 (x_event->xkey.state & ShiftMask) + if (g_x_state & ShiftMask) { - event->key.key |= JTK_KEY_MODS_SHIFT; + key |= JTK_KEY_MODS_SHIFT; } - if (x_event->xkey.state & LockMask) + if (g_x_state & LockMask) { - event->key.key |= JTK_KEY_MODS_LOCK; + key |= JTK_KEY_MODS_LOCK; } - if (x_event->xkey.state & ControlMask) + if (g_x_state & ControlMask) { - event->key.key |= JTK_KEY_MODS_CTRL; + key |= JTK_KEY_MODS_CTRL; } - return true; + return key; } static Bool KeyRepeatCheckIfEvent(Display * display, XEvent * chkev, @@ -198,6 +210,44 @@ static bool IsRepeatKey(Display * display, XEvent * event) 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. * @@ -216,15 +266,10 @@ static bool ProcessXEvent(XEvent * x_event, Jtk_Event * event) switch (x_event->type) { case KeyPress: - event->type = JTK_EVENT_KEY_PRESS; - return ProcessXKeyEvent(x_event, event); + return ProcessXKeyPressEvent(x_event, event); case KeyRelease: - if (IsRepeatKey(g_display, x_event)) { - return false; - } - event->type = JTK_EVENT_KEY_RELEASE; - return ProcessXKeyEvent(x_event, event); + return ProcessXKeyReleaseEvent(x_event, event); case ButtonPress: break; @@ -251,10 +296,21 @@ void Jtk_WaitEvent(Jtk_Event * event) size_t timer_id = Jtk_GetExpiredTimer(); if (timer_id != (size_t)-1) { - 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); + 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; } @@ -286,4 +342,12 @@ void Jtk_WaitEvent(Jtk_Event * event) } } +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 diff --git a/src/gui/jtk/Jtk_event.h b/src/gui/jtk/Jtk_event.h index 89d6f05..06aac1d 100644 --- a/src/gui/jtk/Jtk_event.h +++ b/src/gui/jtk/Jtk_event.h @@ -14,7 +14,11 @@ typedef struct { + bool repeat; uint32_t key; +#ifdef JTK_X + unsigned int x_keycode; +#endif } Jtk_KeyEvent; typedef struct @@ -41,5 +45,6 @@ typedef struct } Jtk_Event; void Jtk_WaitEvent(Jtk_Event * event); +void Jtk_BeginKeyRepeat(Jtk_KeyEvent * key_event, uint32_t delay, uint32_t interval); #endif diff --git a/src/gui/jtk/Jtk_keys.h b/src/gui/jtk/Jtk_keys.h index 4902ce0..4f1eaeb 100644 --- a/src/gui/jtk/Jtk_keys.h +++ b/src/gui/jtk/Jtk_keys.h @@ -115,4 +115,6 @@ #define JTK_KEY_KP_8 0x00FF0078u #define JTK_KEY_KP_9 0x00FF0079u +#define JTK_KEY_UNKNOWN 0x00FFFFFFu + #endif