From b39a4daf04da3cff44ff5f019eb3964bf22240df Mon Sep 17 00:00:00 2001 From: Gabriel Jacobo Date: Thu, 3 Oct 2013 10:28:10 -0300 Subject: [PATCH] SDL_TEXTINPUT support for EVDEV --- README-raspberrypi.txt | 43 +++++++++++- configure | 40 +++++++++++ configure.in | 23 +++++++ include/SDL_config.h.in | 1 + src/events/SDL_keyboard.c | 2 +- src/events/SDL_keyboard_c.h | 3 + src/input/evdev/SDL_evdev.c | 129 +++++++++++++++++++++++++++++++----- src/input/evdev/SDL_evdev.h | 5 +- 8 files changed, 224 insertions(+), 22 deletions(-) diff --git a/README-raspberrypi.txt b/README-raspberrypi.txt index a95932dae..b0f0b25b2 100644 --- a/README-raspberrypi.txt +++ b/README-raspberrypi.txt @@ -20,13 +20,21 @@ Raspbian (other Linux distros may work as well). Raspbian Build Dependencies ================================================================================ -sudo apt-get install libudev-dev libasound2-dev +sudo apt-get install libudev-dev libasound2-dev libdbus-1-dev You also need the VideoCore binary stuff that ships in /opt/vc for EGL and OpenGL ES 2.x, it usually comes pre installed, but in any case: sudo apt-get install libraspberrypi0 libraspberrypi-bin libraspberrypi-dev +================================================================================ + No input +================================================================================ + +Make sure you belong to the "input" group. + + sudo usermod -aG input `whoami` + ================================================================================ No HDMI Audio ================================================================================ @@ -39,10 +47,41 @@ to your config.txt file and reboot. Reference: http://www.raspberrypi.org/phpBB3/viewtopic.php?t=5062 +================================================================================ + Text Input API support +================================================================================ + +The Text Input API is supported, with translation of scan codes done via the +kernel symbol tables. For this to work, SDL needs access to a valid console. +If you notice there's no SDL_TEXTINPUT message being emmited, double check that +your app has read access to one of the following: + +* /proc/self/fd/0 +* /dev/tty +* /dev/tty[0...6] +* /dev/vc/0 +* /dev/console + +This is usually not a problem if you run from the physical terminal (as opposed +to running from a pseudo terminal, such as via SSH). If running from a PTS, a +quick workaround is to run your app as root or add yourself to the tty group, +then re login to the system. + + sudo usermod -aG tty `whoami` + +The keyboard layout used by SDL is the same as the one the kernel uses. +To configure the layout on Raspbian: + + sudo dpkg-reconfigure keyboard-configuration + +To configure the locale, which controls which keys are interpreted as letters, +this determining the CAPS LOCK behavior: + + sudo dpkg-reconfigure locales + ================================================================================ Notes ================================================================================ * Building has only been tested natively (i.e. not cross compiled). Cross compilation might work though, feedback is welcome! -* No Text Input yet. \ No newline at end of file diff --git a/configure b/configure index c38982cc7..68b62c537 100755 --- a/configure +++ b/configure @@ -20754,6 +20754,45 @@ $as_echo "#define SDL_INPUT_LINUXEV 1" >>confdefs.h fi } +CheckInputKD() +{ + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux kd.h" >&5 +$as_echo_n "checking for Linux kd.h... " >&6; } + use_input_kd=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #include + +int +main () +{ + + struct kbentry kbe; + kbe.kb_table = KG_CTRL; + ioctl(0, KDGKBENT, &kbe); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + use_input_kd=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_input_kd" >&5 +$as_echo "$use_input_kd" >&6; } + if test x$use_input_kd = xyes; then + +$as_echo "#define SDL_INPUT_LINUXKD 1" >>confdefs.h + + fi +} + CheckLibUDev() { # Check whether --enable-libudev was given. @@ -22080,6 +22119,7 @@ case "$host" in CheckLibUDev CheckDBus CheckInputEvents + CheckInputKD CheckTslib CheckUSBHID CheckPTHREAD diff --git a/configure.in b/configure.in index f1d64497b..f6810cbba 100644 --- a/configure.in +++ b/configure.in @@ -1791,6 +1791,28 @@ CheckInputEvents() fi } +dnl See if we can use the kernel kd.h header +CheckInputKD() +{ + + AC_MSG_CHECKING(for Linux kd.h) + use_input_kd=no + AC_TRY_COMPILE([ + #include + #include + ],[ + struct kbentry kbe; + kbe.kb_table = KG_CTRL; + ioctl(0, KDGKBENT, &kbe); + ],[ + use_input_kd=yes + ]) + AC_MSG_RESULT($use_input_kd) + if test x$use_input_kd = xyes; then + AC_DEFINE(SDL_INPUT_LINUXKD, 1, [ ]) + fi +} + dnl See if the platform offers libudev for device enumeration and hotplugging. CheckLibUDev() { @@ -2395,6 +2417,7 @@ case "$host" in CheckLibUDev CheckDBus CheckInputEvents + CheckInputKD CheckTslib CheckUSBHID CheckPTHREAD diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 08299411b..b2daf6fa3 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -217,6 +217,7 @@ /* Enable various input drivers */ #undef SDL_INPUT_LINUXEV +#undef SDL_INPUT_LINUXKD #undef SDL_INPUT_TSLIB #undef SDL_JOYSTICK_BEOS #undef SDL_JOYSTICK_DINPUT diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index 607046e0e..34d94d0ef 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -507,7 +507,7 @@ static const char *SDL_scancode_names[SDL_NUM_SCANCODES] = { }; /* Taken from SDL_iconv() */ -static char * +char * SDL_UCS4ToUTF8(Uint32 ch, char *dst) { Uint8 *p = (Uint8 *) dst; diff --git a/src/events/SDL_keyboard_c.h b/src/events/SDL_keyboard_c.h index 17203998b..cd9f1bb6b 100644 --- a/src/events/SDL_keyboard_c.h +++ b/src/events/SDL_keyboard_c.h @@ -59,6 +59,9 @@ extern int SDL_SendEditingText(const char *text, int start, int end); /* Shutdown the keyboard subsystem */ extern void SDL_KeyboardQuit(void); +/* Convert to UTF-8 */ +extern char *SDL_UCS4ToUTF8(Uint32 ch, char *dst); + #endif /* _SDL_keyboard_c_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/input/evdev/SDL_evdev.c b/src/input/evdev/SDL_evdev.c index 4581ac0ce..b9b647306 100644 --- a/src/input/evdev/SDL_evdev.c +++ b/src/input/evdev/SDL_evdev.c @@ -39,7 +39,11 @@ static _THIS = NULL; #include #include #include /* For the definition of PATH_MAX */ - +#include +#ifdef SDL_INPUT_LINUXKD +#include +#include +#endif #include "SDL.h" #include "SDL_assert.h" @@ -325,12 +329,54 @@ static Uint8 EVDEV_MouseButtons[] = { SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */ }; +static char* EVDEV_consoles[] = { + "/proc/self/fd/0", + "/dev/tty", + "/dev/tty0", + "/dev/tty1", + "/dev/tty2", + "/dev/tty3", + "/dev/tty4", + "/dev/tty5", + "/dev/tty6", + "/dev/vc/0", + "/dev/console" +}; + +#define IS_CONSOLE(fd) isatty (fd) && ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84)) + +static int SDL_EVDEV_get_console_fd(void) +{ + int fd, i; + char arg = 0; + + /* Try a few consoles to see which one we have read access to */ + + for( i = 0; i < SDL_arraysize(EVDEV_consoles); i++) { + fd = open(EVDEV_consoles[i], O_RDONLY); + if (fd >= 0) { + if (IS_CONSOLE(fd)) return fd; + close(fd); + } + } + + /* Try stdin, stdout, stderr */ + + for( fd = 0; fd < 3; fd++) { + if (IS_CONSOLE(fd)) return fd; + } + + /* We won't be able to send SDL_TEXTINPUT events */ + return -1; +} + int SDL_EVDEV_Init(void) { int retval = 0; if (_this == NULL) { + _this = (SDL_EVDEV_PrivateData *) SDL_calloc(1, sizeof(*_this)); if(_this == NULL) { return SDL_OutOfMemory(); @@ -354,6 +400,9 @@ SDL_EVDEV_Init(void) #else /* TODO: Scan the devices manually, like a caveman */ #endif /* SDL_USE_LIBUDEV */ + + /* We need a physical terminal (not PTS) to be able to translate key code to symbols via the kernel tables */ + _this->console_fd = SDL_EVDEV_get_console_fd(); } @@ -378,6 +427,9 @@ SDL_EVDEV_Quit(void) SDL_UDEV_Quit(); #endif /* SDL_USE_LIBUDEV */ + if (_this->console_fd >= 0) { + close(_this->console_fd); + } /* Remove existing devices */ while(_this->first != NULL) { SDL_EVDEV_device_removed(_this->first->path); @@ -443,11 +495,18 @@ SDL_EVDEV_Poll(void) SDL_Scancode scan_code; int mouse_button; SDL_Mouse *mouse; - +#ifdef SDL_INPUT_LINUXKD + Uint16 modstate; + struct kbentry kbe; + static char keysym[8]; + char *end; + Uint32 kval; +#endif + #if SDL_USE_LIBUDEV SDL_UDEV_Poll(); #endif - + for (item = _this->first; item != NULL; item = item->next) { while ((len = read(item->fd, events, (sizeof events))) > 0) { len /= sizeof(events[0]); @@ -455,20 +514,57 @@ SDL_EVDEV_Poll(void) switch(item->devclass) { case SDL_EVDEV_DEVICE_KEYBOARD: switch (events[i].type) { - case EV_KEY: - scan_code = SDL_EVDEV_translate_keycode(events[i].code); - if (scan_code != SDL_SCANCODE_UNKNOWN) { - if (events[i].value == 0) { - SDL_SendKeyboardKey(SDL_RELEASED, scan_code); - } - else if (events[i].value == 1) { - SDL_SendKeyboardKey(SDL_PRESSED, scan_code); - } - else if (events[i].value == 2) { - /* Key repeated */ - SDL_SendKeyboardKey(SDL_PRESSED, scan_code); - } + case EV_KEY: + scan_code = SDL_EVDEV_translate_keycode(events[i].code); + if (scan_code != SDL_SCANCODE_UNKNOWN) { + if (events[i].value == 0) { + SDL_SendKeyboardKey(SDL_RELEASED, scan_code); } + else if (events[i].value == 1 || events[i].value == 2 /* Key repeated */ ) { + SDL_SendKeyboardKey(SDL_PRESSED, scan_code); +#ifdef SDL_INPUT_LINUXKD + if (_this->console_fd >= 0) { + kbe.kb_index = events[i].code; + /* Convert the key to an UTF-8 char */ + /* Ref: http://www.linuxjournal.com/article/2783 */ + modstate = SDL_GetModState(); + kbe.kb_table = 0; + + /* Ref: http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */ + kbe.kb_table |= -( (modstate & KMOD_LCTRL) != 0) & (1 << KG_CTRLL | 1 << KG_CTRL); + kbe.kb_table |= -( (modstate & KMOD_RCTRL) != 0) & (1 << KG_CTRLR | 1 << KG_CTRL); + kbe.kb_table |= -( (modstate & KMOD_LSHIFT) != 0) & (1 << KG_SHIFTL | 1 << KG_SHIFT); + kbe.kb_table |= -( (modstate & KMOD_RSHIFT) != 0) & (1 << KG_SHIFTR | 1 << KG_SHIFT); + kbe.kb_table |= -( (modstate & KMOD_LALT) != 0) & (1 << KG_ALT); + kbe.kb_table |= -( (modstate & KMOD_RALT) != 0) & (1 << KG_ALTGR); + + if(ioctl(_this->console_fd, KDGKBENT, (unsigned long)&kbe) == 0 && + ( (KTYP(kbe.kb_value) == KT_LATIN) || (KTYP(kbe.kb_value) == KT_ASCII) || (KTYP(kbe.kb_value) == KT_LETTER) )) + { + kval = KVAL(kbe.kb_value); + + /* While there's a KG_CAPSSHIFT symbol, it's not useful to build the table index with it + * because 1 << KG_CAPSSHIFT overflows the 8 bits of kb_table + * So, we do the CAPS LOCK logic here. Note that isalpha depends on the locale! + */ + if ( modstate & KMOD_CAPS && isalpha(kval) ) { + if ( isupper(kval) ) { + kval = tolower(kval); + } + else { + kval = toupper(kval); + } + } + + /* Convert to UTF-8 and send */ + end = SDL_UCS4ToUTF8( kval, keysym); + *end = '\0'; + SDL_SendKeyboardText(keysym); + } + } +#endif + } + } break; default: @@ -651,3 +747,4 @@ SDL_EVDEV_device_removed(const char *devpath) #endif /* SDL_INPUT_LINUXEV */ /* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/input/evdev/SDL_evdev.h b/src/input/evdev/SDL_evdev.h index 5a6038f61..d63b87f7a 100644 --- a/src/input/evdev/SDL_evdev.h +++ b/src/input/evdev/SDL_evdev.h @@ -19,8 +19,6 @@ 3. This notice may not be removed or altered from any source distribution. */ -#include - #include "SDL_config.h" #ifndef _SDL_evdev_h @@ -42,7 +40,7 @@ typedef struct SDL_evdevlist_item char *path; int fd; SDL_EVDEV_deviceclass devclass; - struct SDL_evdevlist_item *next; + struct SDL_evdevlist_item *next; } SDL_evdevlist_item; typedef struct SDL_EVDEV_PrivateData @@ -51,6 +49,7 @@ typedef struct SDL_EVDEV_PrivateData SDL_evdevlist_item *last; int numdevices; int ref_count; + int console_fd; } SDL_EVDEV_PrivateData; extern int SDL_EVDEV_Init(void);