Handle key repeats in Jtk itself

This commit is contained in:
Josh Holtrop 2017-10-05 20:10:59 -04:00
parent 6ff2faf4af
commit 4e3050dbec
4 changed files with 215 additions and 186 deletions

View File

@ -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<uint32_t, KeyStatus> g_key_statuses;
/**
* Initialize OpenGL.
*
@ -139,6 +123,8 @@ bool Window::create(std::shared_ptr<Buffer> 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);
Jtk_BeginKeyRepeat(&event.key, 300u, 25u);
}
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;
}
}
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)

View File

@ -7,167 +7,179 @@
#include <sys/types.h>
#include <unistd.h>
#include "Jtk_internal.h"
#include <unordered_map>
/** 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<unsigned int, size_t> g_repeating_keys;
static std::unordered_map<size_t, unsigned int> 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;
@ -250,11 +295,22 @@ void Jtk_WaitEvent(Jtk_Event * event)
/* 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;
}
@ -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

View File

@ -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

View File

@ -115,4 +115,6 @@
#define JTK_KEY_KP_8 0x00FF0078u
#define JTK_KEY_KP_9 0x00FF0079u
#define JTK_KEY_UNKNOWN 0x00FFFFFFu
#endif