354 lines
12 KiB
C++
354 lines
12 KiB
C++
#include "Jtk.h"
|
|
|
|
#ifdef JTK_X
|
|
|
|
#include <GL/glx.h>
|
|
#include <sys/select.h>
|
|
#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
|
|
|
|
/** 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;
|
|
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
|