diff --git a/Makefile.engine.linux.mak b/Makefile.engine.linux.mak index e61cace7..569fc2a6 100644 --- a/Makefile.engine.linux.mak +++ b/Makefile.engine.linux.mak @@ -6,7 +6,7 @@ ASSEMBLY := engine EXTENSION := .so COMPILER_FLAGS := -g -MD -Wall -Werror -Wvla -Wgnu-folding-constant -Wno-missing-braces -fdeclspec -fPIC INCLUDE_FLAGS := -Iengine/src -I$(VULKAN_SDK)/include -LINKER_FLAGS := -g -shared -lvulkan -lxcb -lX11 -lX11-xcb -lxkbcommon -L$(VULKAN_SDK)/lib -L/usr/X11R6/lib +LINKER_FLAGS := -g -shared -lvulkan -lxcb -lxcb-keysyms -lxcb-xkb -L$(VULKAN_SDK)/lib -L/usr/X11R6/lib DEFINES := -D_DEBUG -DKEXPORT # Make does not offer a recursive wildcard function, so here's one: diff --git a/engine/src/platform/platform_linux.c b/engine/src/platform/platform_linux.c index 5a03bc39..165ec0ab 100644 --- a/engine/src/platform/platform_linux.c +++ b/engine/src/platform/platform_linux.c @@ -10,10 +10,8 @@ #include "containers/darray.h" #include -#include -#include // sudo apt-get install libx11-dev -#include -#include // sudo apt-get install libxkbcommon-x11-dev libx11-xcb-dev +#include +#include #include #if _POSIX_C_SOURCE >= 199309L @@ -31,18 +29,147 @@ #include #include "renderer/vulkan/vulkan_types.inl" +#define MAX_KEY_LOOKUP 232 + +static const u32 key_lookup_table[MAX_KEY_LOOKUP] = { + 0xff08, KEY_BACKSPACE, + 0xff0d, KEY_ENTER, + 0xff09, KEY_TAB, + 0xff13, KEY_PAUSE, + 0xffe5, KEY_CAPITAL, + 0xff1b, KEY_ESCAPE, + 0xff7e, KEY_MODECHANGE, + 0x0020, KEY_SPACE, + 0xff55, KEY_PRIOR, + 0xff56, KEY_NEXT, + 0xff57, KEY_END, + 0xff50, KEY_HOME, + 0xff51, KEY_LEFT, + 0xff52, KEY_UP, + 0xff53, KEY_RIGHT, + 0xff54, KEY_DOWN, + 0xff60, KEY_SELECT, + 0xff61, KEY_PRINT, + 0xff62, KEY_EXECUTE, + 0xff63, KEY_INSERT, + 0xffff, KEY_DELETE, + 0xff6a, KEY_HELP, + + 0xffeb, KEY_LWIN, // TODO: not sure this is right + 0xffec, KEY_RWIN, + 0xff9e, KEY_NUMPAD0, + 0xff9c, KEY_NUMPAD1, + 0xff99, KEY_NUMPAD2, + 0xff9b, KEY_NUMPAD3, + 0xff96, KEY_NUMPAD4, + 0xff9d, KEY_NUMPAD5, + 0xff98, KEY_NUMPAD6, + 0xff95, KEY_NUMPAD7, + 0xff97, KEY_NUMPAD8, + 0xff9a, KEY_NUMPAD9, + 0xffaa, KEY_MULTIPLY, + 0xffab, KEY_ADD, + 0xffac, KEY_SEPARATOR, + 0xffad, KEY_SUBTRACT, + 0xff9f, KEY_DECIMAL, + 0xffaf, KEY_DIVIDE, + 0xffbe, KEY_F1, + 0xffbf, KEY_F2, + 0xffc0, KEY_F3, + 0xffc1, KEY_F4, + 0xffc2, KEY_F5, + 0xffc3, KEY_F6, + 0xffc4, KEY_F7, + 0xffc5, KEY_F8, + 0xffc6, KEY_F9, + 0xffc7, KEY_F10, + 0xffc8, KEY_F11, + 0xffc9, KEY_F12, + 0xffca, KEY_F13, + 0xffcb, KEY_F14, + 0xffcc, KEY_F15, + 0xffcd, KEY_F16, + 0xffce, KEY_F17, + 0xffcf, KEY_F18, + 0xffd0, KEY_F19, + 0xffd1, KEY_F20, + 0xffd2, KEY_F21, + 0xffd3, KEY_F22, + 0xffd4, KEY_F23, + 0xffd5, KEY_F24, + + 0xff7f, KEY_NUMLOCK, + 0xff14, KEY_SCROLL, + + 0xffbd, KEY_NUMPAD_EQUAL, + + 0xffe1, KEY_LSHIFT, + 0xffe2, KEY_RSHIFT, + 0xffe3, KEY_LCONTROL, + 0xffe4, KEY_RCONTROL, + 0xffe9, KEY_LALT, + 0xfe03, KEY_RALT, + + 0x003b, KEY_SEMICOLON, + 0x002b, KEY_PLUS, + 0x002c, KEY_COMMA, + 0x002d, KEY_MINUS, + 0x002e, KEY_PERIOD, + 0x002f, KEY_SLASH, + 0x0060, KEY_GRAVE, + + 0x0030, KEY_0, + 0x0031, KEY_1, + 0x0032, KEY_2, + 0x0033, KEY_3, + 0x0034, KEY_4, + 0x0035, KEY_5, + 0x0036, KEY_6, + 0x0037, KEY_7, + 0x0038, KEY_8, + 0x0039, KEY_9, + + 0x0041, KEY_A, + 0x0042, KEY_B, + 0x0043, KEY_C, + 0x0044, KEY_D, + 0x0045, KEY_E, + 0x0046, KEY_F, + 0x0047, KEY_G, + 0x0048, KEY_H, + 0x0049, KEY_I, + 0x004a, KEY_J, + 0x004b, KEY_K, + 0x004c, KEY_L, + 0x004d, KEY_M, + 0x004e, KEY_N, + 0x004f, KEY_O, + 0x0050, KEY_P, + 0x0051, KEY_Q, + 0x0052, KEY_R, + 0x0053, KEY_S, + 0x0054, KEY_T, + 0x0055, KEY_U, + 0x0056, KEY_V, + 0x0057, KEY_W, + 0x0058, KEY_X, + 0x0059, KEY_Y, + 0x005a, KEY_Z +}; + typedef struct platform_state { - Display* display; xcb_connection_t* connection; xcb_window_t window; xcb_screen_t* screen; xcb_atom_t wm_protocols; xcb_atom_t wm_delete_win; + xcb_key_symbols_t *syms; VkSurfaceKHR surface; } platform_state; static platform_state* state_ptr; +b8 internal_poll_for_event(xcb_generic_event_t **event); // Key translation keys translate_keycode(u32 x_keycode); @@ -61,32 +188,62 @@ b8 platform_system_startup( state_ptr = state; - // Connect to X - state_ptr->display = XOpenDisplay(NULL); - - // Turn off key repeats. - XAutoRepeatOff(state_ptr->display); - - // Retrieve the connection from the display. - state_ptr->connection = XGetXCBConnection(state_ptr->display); - + // We get the connection through xcb + int screenp = 0; + state_ptr->connection = xcb_connect(0, &screenp); if (xcb_connection_has_error(state_ptr->connection)) { KFATAL("Failed to connect to X server via XCB."); return false; } - // Get data from the X server - const struct xcb_setup_t* setup = xcb_get_setup(state_ptr->connection); + // Unlike most reply_t this one must not be freed. + const xcb_query_extension_reply_t *ext_reply = xcb_get_extension_data(state_ptr->connection, &xcb_xkb_id); + if (!ext_reply) { + KFATAL("XKB extension not available on host X11 server."); + return false; + } - // Loop through screens using iterator - xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup); - int screen_p = 0; - for (i32 s = screen_p; s > 0; s--) { - xcb_screen_next(&it); + // We can now load xcb's extensions (xkb) + xcb_generic_error_t *error; + xcb_xkb_use_extension_cookie_t use_ext_cookie; + xcb_xkb_use_extension_reply_t *use_ext_reply; + use_ext_cookie = xcb_xkb_use_extension(state_ptr->connection, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + use_ext_reply = xcb_xkb_use_extension_reply(state_ptr->connection, use_ext_cookie, &error); + if (!use_ext_reply) { + KFATAL("Couldn't load the xcb-xkb extension."); + free(use_ext_reply); + return false; + } + if (!use_ext_reply->supported) { + KFATAL("The XKB extension is not supported on this X server."); + free(use_ext_reply); + return false; + } + free(use_ext_reply); + + // We can now deactivate repeat for this app only without affecting the system + xcb_xkb_per_client_flags_cookie_t pcf_cookie; + xcb_xkb_per_client_flags_reply_t *pcf_reply; + pcf_cookie = xcb_xkb_per_client_flags( + state_ptr->connection, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + 0, 0, 0); + pcf_reply = xcb_xkb_per_client_flags_reply(state_ptr->connection, pcf_cookie, &error); + free(pcf_reply); + if (error) { + KERROR("Failed to set XKB per-client flags, not using detectable repeat. error code: %u", error->major_code); + return false; } - // After screens have been looped through, assign it. - state_ptr->screen = it.data; + // Now let's grab the keysyms we will use later + state_ptr->syms = xcb_key_symbols_alloc(state_ptr->connection); + + // Get data from the X server + const struct xcb_setup_t* setup = xcb_get_setup(state_ptr->connection); + + state_ptr->screen = xcb_setup_roots_iterator(setup).data; // Allocate a XID for the window to be created. state_ptr->window = xcb_generate_id(state_ptr->connection); @@ -111,8 +268,8 @@ b8 platform_system_startup( XCB_COPY_FROM_PARENT, // depth state_ptr->window, state_ptr->screen->root, // parent - x, //x - y, //y + (i16)x, //x + (i16)y, //y width, //width height, //height 0, // No border @@ -134,36 +291,38 @@ b8 platform_system_startup( // Tell the server to notify when the window manager // attempts to destroy the window. - xcb_intern_atom_cookie_t wm_delete_cookie = xcb_intern_atom( + xcb_intern_atom_cookie_t wm_cookie = xcb_intern_atom( state_ptr->connection, 0, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW"); - xcb_intern_atom_cookie_t wm_protocols_cookie = xcb_intern_atom( + xcb_intern_atom_reply_t* wm_reply = xcb_intern_atom_reply( + state_ptr->connection, + wm_cookie, + NULL); + state_ptr->wm_delete_win = wm_reply->atom; + free(wm_reply); + wm_cookie = xcb_intern_atom( state_ptr->connection, 0, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS"); - xcb_intern_atom_reply_t* wm_delete_reply = xcb_intern_atom_reply( + wm_reply = xcb_intern_atom_reply( state_ptr->connection, - wm_delete_cookie, + wm_cookie, NULL); - xcb_intern_atom_reply_t* wm_protocols_reply = xcb_intern_atom_reply( - state_ptr->connection, - wm_protocols_cookie, - NULL); - state_ptr->wm_delete_win = wm_delete_reply->atom; - state_ptr->wm_protocols = wm_protocols_reply->atom; + state_ptr->wm_protocols = wm_reply->atom; + free(wm_reply); xcb_change_property( state_ptr->connection, XCB_PROP_MODE_REPLACE, state_ptr->window, - wm_protocols_reply->atom, + state_ptr->wm_protocols, 4, 32, 1, - &wm_delete_reply->atom); + &state_ptr->wm_delete_win); // Map the window to the screen xcb_map_window(state_ptr->connection, state_ptr->window); @@ -180,10 +339,9 @@ b8 platform_system_startup( void platform_system_shutdown(void* plat_state) { if (state_ptr) { - // Turn key repeats back on since this is global for the OS... just... wow. - XAutoRepeatOn(state_ptr->display); - xcb_destroy_window(state_ptr->connection, state_ptr->window); + xcb_key_symbols_free(state_ptr->syms); + xcb_disconnect(state_ptr->connection); } } @@ -194,48 +352,34 @@ b8 platform_pump_messages() { b8 quit_flagged = false; - // Poll for events until null is returned. - while ((event = xcb_poll_for_event(state_ptr->connection))) { + // Poll for events until false is returned. + while (internal_poll_for_event(&event)) { // Input events switch (event->response_type & ~0x80) { - case XCB_KEY_PRESS: + case XCB_KEY_PRESS: { + xcb_key_press_event_t *key_event = (xcb_key_press_event_t *)event; + xcb_keysym_t key_sym = xcb_key_symbols_get_keysym(state_ptr->syms, key_event->detail, 0); + input_process_key(translate_keycode(key_sym), true); + } break; case XCB_KEY_RELEASE: { - // Key press event - xcb_key_press_event_t and xcb_key_release_event_t are the same - xcb_key_press_event_t* kb_event = (xcb_key_press_event_t*)event; - b8 pressed = event->response_type == XCB_KEY_PRESS; - xcb_keycode_t code = kb_event->detail; - KeySym key_sym = XkbKeycodeToKeysym( - state_ptr->display, - (KeyCode)code, //event.xkey.keycode, - 0, - 0 /*code & ShiftMask ? 1 : 0*/); - - keys key = translate_keycode(key_sym); - - // Pass to the input subsystem for processing. - input_process_key(key, pressed); + xcb_key_release_event_t *key_event = (xcb_key_release_event_t *)event; + xcb_keysym_t key_sym = xcb_key_symbols_get_keysym(state_ptr->syms, key_event->detail, 0); + input_process_key(translate_keycode(key_sym), false); } break; - case XCB_BUTTON_PRESS: - case XCB_BUTTON_RELEASE: { - xcb_button_press_event_t* mouse_event = (xcb_button_press_event_t*)event; - b8 pressed = event->response_type == XCB_BUTTON_PRESS; - buttons mouse_button = BUTTON_MAX_BUTTONS; - switch (mouse_event->detail) { - case XCB_BUTTON_INDEX_1: - mouse_button = BUTTON_LEFT; - break; - case XCB_BUTTON_INDEX_2: - mouse_button = BUTTON_MIDDLE; - break; - case XCB_BUTTON_INDEX_3: - mouse_button = BUTTON_RIGHT; - break; - } - - // Pass over to the input subsystem. - if (mouse_button != BUTTON_MAX_BUTTONS) { - input_process_button(mouse_button, pressed); + // we need to separate the PRESS and RELEASE events to handle the WHEEL event + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t *button_event = (xcb_button_press_event_t *)event; + // the wheel event is mapped to button 4 and 5 + // 4 is down, while 5 is up + if (button_event->detail > 3) { + input_process_mouse_wheel(button_event->detail == 4 ? -1 : 1); + } else { + input_process_button(button_event->detail, true); } + } + case XCB_BUTTON_RELEASE: { + xcb_button_press_event_t *button_event = (xcb_button_press_event_t *)event; + input_process_button(button_event->detail, false); } break; case XCB_MOTION_NOTIFY: { // Mouse move @@ -354,294 +498,29 @@ b8 platform_create_vulkan_surface(vulkan_context* context) { return true; } +b8 internal_poll_for_event(xcb_generic_event_t **event) { + if (state_ptr) { + *event = xcb_poll_for_event(state_ptr->connection); + } + + return (*event != NULL); +} + // Key translation keys translate_keycode(u32 x_keycode) { - switch (x_keycode) { - case XK_BackSpace: - return KEY_BACKSPACE; - case XK_Return: - return KEY_ENTER; - case XK_Tab: - return KEY_TAB; - //case XK_Shift: return KEY_SHIFT; - //case XK_Control: return KEY_CONTROL; - - case XK_Pause: - return KEY_PAUSE; - case XK_Caps_Lock: - return KEY_CAPITAL; - - case XK_Escape: - return KEY_ESCAPE; - - // Not supported - // case : return KEY_CONVERT; - // case : return KEY_NONCONVERT; - // case : return KEY_ACCEPT; - - case XK_Mode_switch: - return KEY_MODECHANGE; - - case XK_space: - return KEY_SPACE; - case XK_Prior: - return KEY_PRIOR; - case XK_Next: - return KEY_NEXT; - case XK_End: - return KEY_END; - case XK_Home: - return KEY_HOME; - case XK_Left: - return KEY_LEFT; - case XK_Up: - return KEY_UP; - case XK_Right: - return KEY_RIGHT; - case XK_Down: - return KEY_DOWN; - case XK_Select: - return KEY_SELECT; - case XK_Print: - return KEY_PRINT; - case XK_Execute: - return KEY_EXECUTE; - // case XK_snapshot: return KEY_SNAPSHOT; // not supported - case XK_Insert: - return KEY_INSERT; - case XK_Delete: - return KEY_DELETE; - case XK_Help: - return KEY_HELP; - - case XK_Meta_L: - return KEY_LWIN; // TODO: not sure this is right - case XK_Meta_R: - return KEY_RWIN; - // case XK_apps: return KEY_APPS; // not supported - - // case XK_sleep: return KEY_SLEEP; //not supported - - case XK_KP_0: - return KEY_NUMPAD0; - case XK_KP_1: - return KEY_NUMPAD1; - case XK_KP_2: - return KEY_NUMPAD2; - case XK_KP_3: - return KEY_NUMPAD3; - case XK_KP_4: - return KEY_NUMPAD4; - case XK_KP_5: - return KEY_NUMPAD5; - case XK_KP_6: - return KEY_NUMPAD6; - case XK_KP_7: - return KEY_NUMPAD7; - case XK_KP_8: - return KEY_NUMPAD8; - case XK_KP_9: - return KEY_NUMPAD9; - case XK_multiply: - return KEY_MULTIPLY; - case XK_KP_Add: - return KEY_ADD; - case XK_KP_Separator: - return KEY_SEPARATOR; - case XK_KP_Subtract: - return KEY_SUBTRACT; - case XK_KP_Decimal: - return KEY_DECIMAL; - case XK_KP_Divide: - return KEY_DIVIDE; - case XK_F1: - return KEY_F1; - case XK_F2: - return KEY_F2; - case XK_F3: - return KEY_F3; - case XK_F4: - return KEY_F4; - case XK_F5: - return KEY_F5; - case XK_F6: - return KEY_F6; - case XK_F7: - return KEY_F7; - case XK_F8: - return KEY_F8; - case XK_F9: - return KEY_F9; - case XK_F10: - return KEY_F10; - case XK_F11: - return KEY_F11; - case XK_F12: - return KEY_F12; - case XK_F13: - return KEY_F13; - case XK_F14: - return KEY_F14; - case XK_F15: - return KEY_F15; - case XK_F16: - return KEY_F16; - case XK_F17: - return KEY_F17; - case XK_F18: - return KEY_F18; - case XK_F19: - return KEY_F19; - case XK_F20: - return KEY_F20; - case XK_F21: - return KEY_F21; - case XK_F22: - return KEY_F22; - case XK_F23: - return KEY_F23; - case XK_F24: - return KEY_F24; - - case XK_Num_Lock: - return KEY_NUMLOCK; - case XK_Scroll_Lock: - return KEY_SCROLL; - - case XK_KP_Equal: - return KEY_NUMPAD_EQUAL; - - case XK_Shift_L: - return KEY_LSHIFT; - case XK_Shift_R: - return KEY_RSHIFT; - case XK_Control_L: - return KEY_LCONTROL; - case XK_Control_R: - return KEY_RCONTROL; - case XK_Alt_L: - return KEY_LALT; - case XK_Alt_R: - return KEY_RALT; - - case XK_semicolon: - return KEY_SEMICOLON; - case XK_plus: - return KEY_PLUS; - case XK_comma: - return KEY_COMMA; - case XK_minus: - return KEY_MINUS; - case XK_period: - return KEY_PERIOD; - case XK_slash: - return KEY_SLASH; - case XK_grave: - return KEY_GRAVE; - - case XK_0: - return KEY_0; - case XK_1: - return KEY_1; - case XK_2: - return KEY_2; - case XK_3: - return KEY_3; - case XK_4: - return KEY_4; - case XK_5: - return KEY_5; - case XK_6: - return KEY_6; - case XK_7: - return KEY_7; - case XK_8: - return KEY_8; - case XK_9: - return KEY_9; - - case XK_a: - case XK_A: - return KEY_A; - case XK_b: - case XK_B: - return KEY_B; - case XK_c: - case XK_C: - return KEY_C; - case XK_d: - case XK_D: - return KEY_D; - case XK_e: - case XK_E: - return KEY_E; - case XK_f: - case XK_F: - return KEY_F; - case XK_g: - case XK_G: - return KEY_G; - case XK_h: - case XK_H: - return KEY_H; - case XK_i: - case XK_I: - return KEY_I; - case XK_j: - case XK_J: - return KEY_J; - case XK_k: - case XK_K: - return KEY_K; - case XK_l: - case XK_L: - return KEY_L; - case XK_m: - case XK_M: - return KEY_M; - case XK_n: - case XK_N: - return KEY_N; - case XK_o: - case XK_O: - return KEY_O; - case XK_p: - case XK_P: - return KEY_P; - case XK_q: - case XK_Q: - return KEY_Q; - case XK_r: - case XK_R: - return KEY_R; - case XK_s: - case XK_S: - return KEY_S; - case XK_t: - case XK_T: - return KEY_T; - case XK_u: - case XK_U: - return KEY_U; - case XK_v: - case XK_V: - return KEY_V; - case XK_w: - case XK_W: - return KEY_W; - case XK_x: - case XK_X: - return KEY_X; - case XK_y: - case XK_Y: - return KEY_Y; - case XK_z: - case XK_Z: - return KEY_Z; - - default: - return 0; + xcb_keysym_t upper = x_keycode; + if ((x_keycode >> 8) == 0) { + if (x_keycode >= 0x0061 && x_keycode <= 0x007a) { + upper -= (0x0061 - 0x0041); + } + } + for (u32 i = 0; i < MAX_KEY_LOOKUP; ++i) { + if (key_lookup_table[i] == upper) { + return key_lookup_table[i + 1]; + } } + + return KEYS_MAX_KEYS; } #endif \ No newline at end of file