add jtk event functionality C backend

This commit is contained in:
Josh Holtrop 2020-06-30 17:16:34 -04:00
parent 7a381b87c5
commit d3d6fd0fbf
2 changed files with 506 additions and 7 deletions

View File

@ -7,7 +7,6 @@ struct Event
WINDOW_CLOSE, WINDOW_CLOSE,
WINDOW_EXPOSE, WINDOW_EXPOSE,
KEY_PRESS, KEY_PRESS,
KEY_RELEASE,
BUTTON_PRESS, BUTTON_PRESS,
BUTTON_RELEASE, BUTTON_RELEASE,
TIMER, TIMER,
@ -16,9 +15,7 @@ struct Event
struct KeyEvent struct KeyEvent
{ {
bool repeat;
uint key; uint key;
uint x_keycode;
}; };
struct ButtonEvent struct ButtonEvent
@ -43,9 +40,9 @@ struct Event
Type type; Type type;
union union
{ {
KeyEvent key; KeyEvent key_event;
ButtonEvent button; ButtonEvent button_event;
TimerEvent timer; TimerEvent timer_event;
WindowResizeEvent resize; WindowResizeEvent resize_event;
}; };
} }

View File

@ -164,3 +164,505 @@ void jtk_window_set_icon(void * window, const uint8_t * data,
XFlush(g_display); XFlush(g_display);
free(property_data); free(property_data);
} }
/**************************************************************************
* Event
*************************************************************************/
/** Do not wait longer than 100ms */
#define MAX_WAIT_TIME 100000u
#define JTK_KEY_MODS_MASK 0xFF000000u
#define JTK_KEY_KEYCODE_MASK 0x00FFFFFFu
#define JTK_KEY_MODS_SHIFT 0x01000000u
#define JTK_KEY_MODS_LOCK 0x02000000u
#define JTK_KEY_MODS_CTRL 0x04000000u
#define JTK_KEY_F1 0x00000080u
#define JTK_KEY_F2 0x00000081u
#define JTK_KEY_F3 0x00000082u
#define JTK_KEY_F4 0x00000083u
#define JTK_KEY_F5 0x00000084u
#define JTK_KEY_F6 0x00000085u
#define JTK_KEY_F7 0x00000086u
#define JTK_KEY_F8 0x00000087u
#define JTK_KEY_F9 0x00000088u
#define JTK_KEY_F10 0x00000089u
#define JTK_KEY_F11 0x0000008Au
#define JTK_KEY_F12 0x0000008Bu
#define JTK_KEY_F13 0x0000008Cu
#define JTK_KEY_F14 0x0000008Du
#define JTK_KEY_F15 0x0000008Eu
#define JTK_KEY_F16 0x0000008Fu
#define JTK_KEY_F17 0x00000090u
#define JTK_KEY_F18 0x00000091u
#define JTK_KEY_F19 0x00000092u
#define JTK_KEY_F20 0x00000093u
#define JTK_KEY_F21 0x00000094u
#define JTK_KEY_F22 0x00000095u
#define JTK_KEY_F23 0x00000096u
#define JTK_KEY_F24 0x00000097u
#define JTK_KEY_F25 0x00000098u
#define JTK_KEY_F26 0x00000099u
#define JTK_KEY_F27 0x0000009Au
#define JTK_KEY_F28 0x0000009Bu
#define JTK_KEY_F29 0x0000009Cu
#define JTK_KEY_F30 0x0000009Du
#define JTK_KEY_F31 0x0000009Eu
#define JTK_KEY_SHIFT_L 0x0000009Fu
#define JTK_KEY_SHIFT_R 0x000000A0u
#define JTK_KEY_CTRL_L 0x000000A1u
#define JTK_KEY_CTRL_R 0x000000A2u
#define JTK_KEY_CAPS_LOCK 0x000000A3u
#define JTK_KEY_SHIFT_LOCK 0x000000A4u
#define JTK_KEY_META_L 0x000000A5u
#define JTK_KEY_META_R 0x000000A6u
#define JTK_KEY_ALT_L 0x000000A7u
#define JTK_KEY_ALT_R 0x000000A8u
#define JTK_KEY_SUPER_L 0x000000A9u
#define JTK_KEY_SUPER_R 0x000000AAu
#define JTK_KEY_HOME 0x000000ABu
#define JTK_KEY_LEFT 0x000000ACu
#define JTK_KEY_UP 0x000000ADu
#define JTK_KEY_RIGHT 0x000000AEu
#define JTK_KEY_DOWN 0x000000AFu
#define JTK_KEY_PAGE_UP 0x000000B1u
#define JTK_KEY_PAGE_DOWN 0x000000B3u
#define JTK_KEY_END 0x000000B4u
#define JTK_KEY_BEGIN 0x000000B5u
#define JTK_KEY_SELECT 0x000000B6u
#define JTK_KEY_PRINT 0x000000B7u
#define JTK_KEY_EXECUTE 0x000000B8u
#define JTK_KEY_INSERT 0x000000B9u
#define JTK_KEY_UNDO 0x000000BAu
#define JTK_KEY_REDO 0x000000BBu
#define JTK_KEY_MENU 0x000000BCu
#define JTK_KEY_FIND 0x000000BDu
#define JTK_KEY_CANCEL 0x000000BEu
#define JTK_KEY_HELP 0x000000BFu
#define JTK_KEY_BREAK 0x000000C0u
#define JTK_KEY_NUM_LOCK 0x000000C3u
#define JTK_KEY_KP_SPACE 0x000000C4u
#define JTK_KEY_KP_TAB 0x000000C5u
#define JTK_KEY_KP_ENTER 0x000000C6u
#define JTK_KEY_KP_F1 0x000000C7u
#define JTK_KEY_KP_F2 0x000000C8u
#define JTK_KEY_KP_F3 0x000000C9u
#define JTK_KEY_KP_F4 0x000000CAu
#define JTK_KEY_KP_HOME 0x000000CBu
#define JTK_KEY_KP_LEFT 0x000000CCu
#define JTK_KEY_KP_UP 0x000000CDu
#define JTK_KEY_KP_RIGHT 0x000000CEu
#define JTK_KEY_KP_DOWN 0x000000CFu
#define JTK_KEY_KP_PAGE_UP 0x000000D1u
#define JTK_KEY_KP_PAGE_DOWN 0x000000D3u
#define JTK_KEY_KP_END 0x000000D4u
#define JTK_KEY_KP_BEGIN 0x000000D5u
#define JTK_KEY_KP_INSERT 0x000000D6u
#define JTK_KEY_KP_DELETE 0x000000D7u
#define JTK_KEY_KP_EQUAL 0x000000D8u
#define JTK_KEY_KP_MULTIPLY 0x000000D9u
#define JTK_KEY_KP_ADD 0x000000DAu
#define JTK_KEY_KP_SEPARATOR 0x000000DBu
#define JTK_KEY_KP_SUBTRACT 0x000000DCu
#define JTK_KEY_KP_DECIMAL 0x000000DDu
#define JTK_KEY_KP_DIVIDE 0x000000DEu
#define JTK_KEY_KP_0 0x000000DFu
#define JTK_KEY_KP_1 0x000000E0u
#define JTK_KEY_KP_2 0x000000E1u
#define JTK_KEY_KP_3 0x000000E2u
#define JTK_KEY_KP_4 0x000000E3u
#define JTK_KEY_KP_5 0x000000E4u
#define JTK_KEY_KP_6 0x000000E5u
#define JTK_KEY_KP_7 0x000000E6u
#define JTK_KEY_KP_8 0x000000E7u
#define JTK_KEY_KP_9 0x000000E8u
#define JTK_KEY_UNKNOWN 0x00FFFFFFu
#define JTK_EVENT_WINDOW_CLOSE 0u
#define JTK_EVENT_WINDOW_EXPOSE 1u
#define JTK_EVENT_KEY_PRESS 2u
#define JTK_EVENT_BUTTON_PRESS 3u
#define JTK_EVENT_BUTTON_RELEASE 4u
#define JTK_EVENT_TIMER 5u
#define JTK_EVENT_WINDOW_RESIZE 6u
typedef struct {
uint32_t key;
} jtk_key_event_t;
typedef struct {
uint32_t mods;
uint8_t button;
} jtk_button_event_t;
typedef struct {
uint32_t width;
uint32_t height;
} jtk_window_resize_event_t;
typedef struct {
uint8_t type;
union {
jtk_key_event_t key_event;
jtk_button_event_t button_event;
jtk_window_resize_event_t window_resize;
};
} jtk_event_t;
static uint32_t x_state_to_jtk_key_mods(unsigned int x_state)
{
uint32_t mods = 0u;
/* OR in the modifier states */
if (x_state & ShiftMask)
{
mods |= JTK_KEY_MODS_SHIFT;
}
if (x_state & LockMask)
{
mods |= JTK_KEY_MODS_LOCK;
}
if (x_state & ControlMask)
{
mods |= JTK_KEY_MODS_CTRL;
}
return mods;
}
static uint32_t x_key_to_jtk_key(unsigned int x_keycode, unsigned int x_state)
{
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 = 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, NULL) > 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 */
key |= x_state_to_jtk_key_mods(x_state);
return key;
}
static Bool match_key_press(Display * display, XEvent * event, XPointer arg)
{
XEvent * match_event = (XEvent *)arg;
return (event->type == match_event->type) &&
(event->xkey.window == match_event->xkey.window) &&
(event->xkey.state == match_event->xkey.state) &&
(event->xkey.keycode == match_event->xkey.keycode);
}
/**
* Process an X key press event.
*
* @param x_event
* Pointer to the X event.
* @param event
* Pointer to the Jtk event.
*/
static bool process_x_key_press_event(XEvent * x_event, jtk_event_t * event)
{
unsigned int x_keycode = x_event->xkey.keycode;
event->type = JTK_EVENT_KEY_PRESS;
event->key_event.key = x_key_to_jtk_key(x_keycode, x_event->xkey.state);
/* Remove any following keypress events for the same keycode from the X
* queue. */
XEvent remove_event;
while (XCheckIfEvent(g_display, &remove_event, match_key_press, (XPointer)x_event) == True)
{
}
return true;
}
static Bool match_button_press(Display * display, XEvent * event, XPointer arg)
{
XEvent * match_event = (XEvent *)arg;
return (event->type == match_event->type) &&
(event->xbutton.window == match_event->xbutton.window) &&
(event->xbutton.state == match_event->xbutton.state) &&
(event->xbutton.button == match_event->xbutton.button);
}
/**
* Process an X button press event.
*/
static bool process_x_button_press_event(XEvent * x_event, jtk_event_t * event)
{
event->type = JTK_EVENT_BUTTON_PRESS;
event->button_event.mods = x_state_to_jtk_key_mods(x_event->xbutton.state);
event->button_event.button = x_event->xbutton.button;
/* If this is a mouse wheel scroll event, remove any following scroll
* events. */
if ((event->button_event.button == 4) || (event->button_event.button == 5))
{
XEvent remove_event;
while (XCheckIfEvent(g_display, &remove_event, match_button_press, (XPointer)x_event) == True)
{
}
}
return true;
}
/**
* Process an X configure event.
*
* @param x_event
* Pointer to the X event.
* @param event
* Pointer to the Jtk event.
*/
static bool process_x_configure_event(XEvent * x_event, jtk_event_t * event)
{
event->type = JTK_EVENT_WINDOW_RESIZE;
event->window_resize.width = x_event->xconfigure.width;
event->window_resize.height = x_event->xconfigure.height;
return true;
}
/**
* Process an X ClientMessage event.
*
* @param x_event
* Pointer to the X event.
* @param event
* Pointer to the Jtk event.
*/
static bool process_x_client_message_event(XEvent * x_event, jtk_event_t * event)
{
Atom wm_delete_window_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", False);
if (x_event->xclient.data.l[0] == (long)wm_delete_window_atom)
{
event->type = JTK_EVENT_WINDOW_CLOSE;
return true;
}
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 process_x_event(XEvent * x_event, jtk_event_t * event)
{
switch (x_event->type)
{
case KeyPress:
return process_x_key_press_event(x_event, event);
case KeyRelease:
return false;
case ButtonPress:
return process_x_button_press_event(x_event, event);
case ButtonRelease:
break;
case Expose:
event->type = JTK_EVENT_WINDOW_EXPOSE;
return true;
case GraphicsExpose:
event->type = JTK_EVENT_WINDOW_EXPOSE;
return true;
case MapNotify:
event->type = JTK_EVENT_WINDOW_EXPOSE;
return true;
case ConfigureNotify:
return process_x_configure_event(x_event, event);
case ClientMessage:
return process_x_client_message_event(x_event, event);
case MappingNotify:
XRefreshKeyboardMapping(&x_event->xmapping);
return false;
}
return false;
}
bool jtk_check_event(jtk_event_t * event)
{
/* First check for an X event. */
while (XPending(g_display) > 0)
{
XEvent x_event;
XNextEvent(g_display, &x_event);
if (process_x_event(&x_event, event))
return true;
}
return false;
}
void jtk_wait_event(jtk_event_t * event, uint64_t time_to_wait)
{
for (;;)
{
if (jtk_check_event(event))
return;
/* Wait for something to happen. */
if ((time_to_wait > MAX_WAIT_TIME) || (time_to_wait == 0u))
{
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, NULL, NULL, &tv);
}
}