diff --git a/dynamic.c b/dynamic.c index 9abb9a47c5..45bca30912 100644 --- a/dynamic.c +++ b/dynamic.c @@ -482,6 +482,15 @@ static bool environment_cb(unsigned cmd, void *data) break; } + + case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: + { + const struct retro_keyboard_callback *info = (const struct retro_keyboard_callback*)data; + + g_extern.system.key_event = info->callback; + + break; + } default: RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); diff --git a/general.h b/general.h index b0ac1f168b..5300fb3d99 100644 --- a/general.h +++ b/general.h @@ -324,6 +324,8 @@ struct global bool force_nonblock; const char *input_desc_btn[MAX_PLAYERS][RARCH_FIRST_CUSTOM_BIND]; + + retro_keyboard_event_t key_event; } system; struct diff --git a/gfx/context/glx_ctx.c b/gfx/context/glx_ctx.c index 320557d364..eb89ee820c 100644 --- a/gfx/context/glx_ctx.c +++ b/gfx/context/glx_ctx.c @@ -49,6 +49,108 @@ static bool g_is_double; static int (*g_pglSwapInterval)(int); +struct key_bind +{ + unsigned x; + enum retro_key sk; +}; + +static unsigned keysym_lut[RETROK_LAST]; + +static const struct key_bind lut_binds[] = { + { XK_Left, RETROK_LEFT }, + { XK_Right, RETROK_RIGHT }, + { XK_Up, RETROK_UP }, + { XK_Down, RETROK_DOWN }, + { XK_Return, RETROK_RETURN }, + { XK_Tab, RETROK_TAB }, + { XK_Insert, RETROK_INSERT }, + { XK_Home, RETROK_HOME }, + { XK_End, RETROK_END }, + { XK_Page_Up, RETROK_PAGEUP }, + { XK_Page_Down, RETROK_PAGEDOWN }, + { XK_Delete, RETROK_DELETE }, + { XK_Shift_R, RETROK_RSHIFT }, + { XK_Shift_L, RETROK_LSHIFT }, + { XK_Control_L, RETROK_LCTRL }, + { XK_Alt_L, RETROK_LALT }, + { XK_space, RETROK_SPACE }, + { XK_Escape, RETROK_ESCAPE }, + { XK_BackSpace, RETROK_BACKSPACE }, + { XK_KP_Enter, RETROK_KP_ENTER }, + { XK_KP_Add, RETROK_KP_PLUS }, + { XK_KP_Subtract, RETROK_KP_MINUS }, + { XK_KP_Multiply, RETROK_KP_MULTIPLY }, + { XK_KP_Divide, RETROK_KP_DIVIDE }, + { XK_grave, RETROK_BACKQUOTE }, + { XK_Pause, RETROK_PAUSE }, + { XK_KP_0, RETROK_KP0 }, + { XK_KP_1, RETROK_KP1 }, + { XK_KP_2, RETROK_KP2 }, + { XK_KP_3, RETROK_KP3 }, + { XK_KP_4, RETROK_KP4 }, + { XK_KP_5, RETROK_KP5 }, + { XK_KP_6, RETROK_KP6 }, + { XK_KP_7, RETROK_KP7 }, + { XK_KP_8, RETROK_KP8 }, + { XK_KP_9, RETROK_KP9 }, + { XK_0, RETROK_0 }, + { XK_1, RETROK_1 }, + { XK_2, RETROK_2 }, + { XK_3, RETROK_3 }, + { XK_4, RETROK_4 }, + { XK_5, RETROK_5 }, + { XK_6, RETROK_6 }, + { XK_7, RETROK_7 }, + { XK_8, RETROK_8 }, + { XK_9, RETROK_9 }, + { XK_F1, RETROK_F1 }, + { XK_F2, RETROK_F2 }, + { XK_F3, RETROK_F3 }, + { XK_F4, RETROK_F4 }, + { XK_F5, RETROK_F5 }, + { XK_F6, RETROK_F6 }, + { XK_F7, RETROK_F7 }, + { XK_F8, RETROK_F8 }, + { XK_F9, RETROK_F9 }, + { XK_F10, RETROK_F10 }, + { XK_F11, RETROK_F11 }, + { XK_F12, RETROK_F12 }, + { XK_a, RETROK_a }, + { XK_b, RETROK_b }, + { XK_c, RETROK_c }, + { XK_d, RETROK_d }, + { XK_e, RETROK_e }, + { XK_f, RETROK_f }, + { XK_g, RETROK_g }, + { XK_h, RETROK_h }, + { XK_i, RETROK_i }, + { XK_j, RETROK_j }, + { XK_k, RETROK_k }, + { XK_l, RETROK_l }, + { XK_m, RETROK_m }, + { XK_n, RETROK_n }, + { XK_o, RETROK_o }, + { XK_p, RETROK_p }, + { XK_q, RETROK_q }, + { XK_r, RETROK_r }, + { XK_s, RETROK_s }, + { XK_t, RETROK_t }, + { XK_u, RETROK_u }, + { XK_v, RETROK_v }, + { XK_w, RETROK_w }, + { XK_x, RETROK_x }, + { XK_y, RETROK_y }, + { XK_z, RETROK_z }, +}; + +static void init_lut(void) +{ + memset(keysym_lut, 0, sizeof(keysym_lut)); + for (unsigned i = 0; i < sizeof(lut_binds) / sizeof(lut_binds[0]); i++) + keysym_lut[lut_binds[i].sk] = lut_binds[i].x; +} + static void sighandler(int sig) { (void)sig; @@ -113,6 +215,36 @@ static void gfx_ctx_check_window(bool *quit, case UnmapNotify: g_has_focus = false; break; + + case KeyPress: + case KeyRelease: + if (g_extern.system.key_event) + { + static XComposeStatus state; + char keybuf[32]; + const unsigned x_keycode = XKeycodeToKeysym(g_dpy, event.xkey.keycode, 0); + + bool down = event.type == KeyPress; + uint32_t character = 0; + unsigned key = RETROK_UNKNOWN; + + for (int i = 0; i != RETROK_LAST; i ++) + { + if (keysym_lut[i] == x_keycode) + { + key = i; + break; + } + } + + if (down && XLookupString(&event.xkey, keybuf, sizeof(keybuf), 0, &state)) + { + character = keybuf[0]; + } + + g_extern.system.key_event(down, key, character, 0); + } + break; } } @@ -192,6 +324,8 @@ static bool gfx_ctx_init(void) g_quit = 0; + init_lut(); + g_dpy = XOpenDisplay(NULL); if (!g_dpy) goto error; @@ -249,7 +383,7 @@ static bool gfx_ctx_set_video_mode( swa.colormap = g_cmap = XCreateColormap(g_dpy, RootWindow(g_dpy, vi->screen), vi->visual, AllocNone); - swa.event_mask = StructureNotifyMask; + swa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask; swa.override_redirect = fullscreen ? True : False; if (fullscreen && !windowed_full) diff --git a/gfx/context/sdl_ctx.c b/gfx/context/sdl_ctx.c index 3dbe7038d9..11d235579f 100644 --- a/gfx/context/sdl_ctx.c +++ b/gfx/context/sdl_ctx.c @@ -1,6 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2012 - Hans-Kristian Arntzen - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -70,11 +70,11 @@ static void gfx_ctx_swap_interval(unsigned interval) GL_SYM_WRAP(glx_swap_interval, "glXSwapInterval"); if (!glx_swap_interval) GL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalMESA"); - if (!glx_swap_interval) + if (!glx_swap_interval) GL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalSGI"); if (glx_swap_interval) success = glx_swap_interval(g_interval) == 0; - else + else RARCH_WARN("Could not find GLX VSync call.\n"); #endif } @@ -118,7 +118,7 @@ static bool gfx_ctx_init(void) return ret; } -static void gfx_ctx_destroy(void) +static void gfx_ctx_destroy(void) { SDL_QuitSubSystem(SDL_INIT_VIDEO); g_inited = false; @@ -254,6 +254,34 @@ static void gfx_ctx_check_window(bool *quit, *width = event.resize.w; *height = event.resize.h; break; + + case SDL_KEYDOWN: + case SDL_KEYUP: + if (g_extern.system.key_event) + { + SDL_EnableUNICODE(true); + + uint16_t mods = 0; + + if (event.key.keysym.mod) + { + mods |= (event.key.keysym.mod & KMOD_CTRL) ? RETROKMOD_CTRL : 0; + mods |= (event.key.keysym.mod & KMOD_ALT) ? RETROKMOD_ALT : 0; + mods |= (event.key.keysym.mod & KMOD_SHIFT) ? RETROKMOD_SHIFT : 0; + mods |= (event.key.keysym.mod & KMOD_META) ? RETROKMOD_META : 0; + mods |= (event.key.keysym.mod & KMOD_NUM) ? RETROKMOD_NUMLOCK : 0; + mods |= (event.key.keysym.mod & KMOD_CAPS) ? RETROKMOD_CAPSLOCK : 0; + + // TODO: What is KMOD_MODE in SDL? + mods |= (event.key.keysym.mod & KMOD_MODE) ? RETROKMOD_SCROLLOCK : 0; + } + + // For now it seems that all RETROK_* constant values match the SDLK_* values. + // Ultimately the table in sdl_input.c should be used in case this changes. + // TODO: event.key.keysym.unicode is UTF-16 + g_extern.system.key_event(event.type == SDL_KEYDOWN, event.key.keysym.sym, event.key.keysym.unicode, mods); + } + break; } } diff --git a/libretro.h b/libretro.h index f077a17915..3881f15a77 100755 --- a/libretro.h +++ b/libretro.h @@ -310,6 +310,21 @@ enum retro_key RETROK_LAST }; +enum retro_mod +{ + RETROKMOD_NONE = 0x0000, + + RETROKMOD_SHIFT = 0x01, + RETROKMOD_CTRL = 0x02, + RETROKMOD_ALT = 0x04, + RETROKMOD_META = 0x08, + + RETROKMOD_NUMLOCK = 0x10, + RETROKMOD_CAPSLOCK = 0x20, + RETROKMOD_SCROLLOCK = 0x40 +}; + + // Environment commands. #define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * -- // Sets screen rotation of graphics. @@ -353,7 +368,7 @@ enum retro_key // // It can be used by the frontend to potentially warn // about too demanding implementations. - // + // // The levels are "floating", but roughly defined as: // 0: Low-powered embedded devices such as Raspberry Pi // 1: 6th generation consoles, such as Wii/Xbox 1, and phones, tablets, etc. @@ -388,8 +403,23 @@ enum retro_key // It is up to the frontend to present this in a usable way. // The array is terminated by retro_input_descriptor::description being set to NULL. // This function can be called at any time, but it is recommended to call it as early as possible. +#define RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 12 + // const struct retro_keyboard_callback * -- + // Sets a callback function used to notify core about keyboard events. +// Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. Called by the frontend in response to keyboard events. +// down is set if the key is being pressed, or false if it is being released. +// keycode is the RETROK value of the char. +// character is the text character of the pressed key. (UTF-32). +// key_modifiers is a set of RETROKMOD values or'ed together. +typedef void (*retro_keyboard_event_t)(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers); + +struct retro_keyboard_callback +{ + retro_keyboard_event_t callback; +}; + enum retro_pixel_format { // 0RGB1555, native endian. 0 bit must be set to 0.