SDL_TEXTINPUT support for EVDEV
authorGabriel Jacobo <gabomdq@gmail.com>
Thu, 03 Oct 2013 10:28:10 -0300
changeset 7778a571a9947869
parent 7777 d5489cec371b
child 7779 d46f8afbcb17
SDL_TEXTINPUT support for EVDEV
README-raspberrypi.txt
configure
configure.in
include/SDL_config.h.in
src/events/SDL_keyboard.c
src/events/SDL_keyboard_c.h
src/input/evdev/SDL_evdev.c
src/input/evdev/SDL_evdev.h
     1.1 --- a/README-raspberrypi.txt	Thu Oct 03 03:31:05 2013 -0700
     1.2 +++ b/README-raspberrypi.txt	Thu Oct 03 10:28:10 2013 -0300
     1.3 @@ -20,7 +20,7 @@
     1.4   Raspbian Build Dependencies
     1.5  ================================================================================
     1.6  
     1.7 -sudo apt-get install libudev-dev libasound2-dev
     1.8 +sudo apt-get install libudev-dev libasound2-dev libdbus-1-dev
     1.9  
    1.10  You also need the VideoCore binary stuff that ships in /opt/vc for EGL and 
    1.11  OpenGL ES 2.x, it usually comes pre installed, but in any case:
    1.12 @@ -28,6 +28,14 @@
    1.13  sudo apt-get install libraspberrypi0 libraspberrypi-bin libraspberrypi-dev
    1.14  
    1.15  ================================================================================
    1.16 + No input
    1.17 +================================================================================
    1.18 +
    1.19 +Make sure you belong to the "input" group.
    1.20 +
    1.21 +    sudo usermod -aG input `whoami`
    1.22 +
    1.23 +================================================================================
    1.24   No HDMI Audio
    1.25  ================================================================================
    1.26  
    1.27 @@ -40,9 +48,40 @@
    1.28  Reference: http://www.raspberrypi.org/phpBB3/viewtopic.php?t=5062
    1.29  
    1.30  ================================================================================
    1.31 + Text Input API support
    1.32 +================================================================================
    1.33 +
    1.34 +The Text Input API is supported, with translation of scan codes done via the
    1.35 +kernel symbol tables. For this to work, SDL needs access to a valid console.
    1.36 +If you notice there's no SDL_TEXTINPUT message being emmited, double check that
    1.37 +your app has read access to one of the following:
    1.38 +    
    1.39 +* /proc/self/fd/0
    1.40 +* /dev/tty
    1.41 +* /dev/tty[0...6]
    1.42 +* /dev/vc/0
    1.43 +* /dev/console
    1.44 +
    1.45 +This is usually not a problem if you run from the physical terminal (as opposed
    1.46 +to running from a pseudo terminal, such as via SSH). If running from a PTS, a 
    1.47 +quick workaround is to run your app as root or add yourself to the tty group,
    1.48 +then re login to the system.
    1.49 +
    1.50 +   sudo usermod -aG tty `whoami`
    1.51 +    
    1.52 +The keyboard layout used by SDL is the same as the one the kernel uses.
    1.53 +To configure the layout on Raspbian:
    1.54 +    
    1.55 +    sudo dpkg-reconfigure keyboard-configuration
    1.56 +    
    1.57 +To configure the locale, which controls which keys are interpreted as letters,
    1.58 +this determining the CAPS LOCK behavior:
    1.59 +
    1.60 +    sudo dpkg-reconfigure locales
    1.61 +
    1.62 +================================================================================
    1.63   Notes
    1.64  ================================================================================
    1.65  
    1.66  * Building has only been tested natively (i.e. not cross compiled). Cross
    1.67    compilation might work though, feedback is welcome!
    1.68 -* No Text Input yet.
    1.69 \ No newline at end of file
     2.1 --- a/configure	Thu Oct 03 03:31:05 2013 -0700
     2.2 +++ b/configure	Thu Oct 03 10:28:10 2013 -0300
     2.3 @@ -20754,6 +20754,45 @@
     2.4          fi
     2.5  }
     2.6  
     2.7 +CheckInputKD()
     2.8 +{
     2.9 +
    2.10 +    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux kd.h" >&5
    2.11 +$as_echo_n "checking for Linux kd.h... " >&6; }
    2.12 +    use_input_kd=no
    2.13 +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
    2.14 +/* end confdefs.h.  */
    2.15 +
    2.16 +      #include <linux/kd.h>
    2.17 +      #include <linux/keyboard.h>
    2.18 +
    2.19 +int
    2.20 +main ()
    2.21 +{
    2.22 +
    2.23 +        struct kbentry kbe;
    2.24 +        kbe.kb_table = KG_CTRL;
    2.25 +        ioctl(0, KDGKBENT, &kbe);
    2.26 +
    2.27 +  ;
    2.28 +  return 0;
    2.29 +}
    2.30 +_ACEOF
    2.31 +if ac_fn_c_try_compile "$LINENO"; then :
    2.32 +
    2.33 +    use_input_kd=yes
    2.34 +
    2.35 +fi
    2.36 +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
    2.37 +    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_input_kd" >&5
    2.38 +$as_echo "$use_input_kd" >&6; }
    2.39 +    if test x$use_input_kd = xyes; then
    2.40 +
    2.41 +$as_echo "#define SDL_INPUT_LINUXKD 1" >>confdefs.h
    2.42 +
    2.43 +    fi
    2.44 +}
    2.45 +
    2.46  CheckLibUDev()
    2.47  {
    2.48      # Check whether --enable-libudev was given.
    2.49 @@ -22080,6 +22119,7 @@
    2.50          CheckLibUDev
    2.51          CheckDBus
    2.52          CheckInputEvents
    2.53 +        CheckInputKD
    2.54          CheckTslib
    2.55          CheckUSBHID
    2.56          CheckPTHREAD
     3.1 --- a/configure.in	Thu Oct 03 03:31:05 2013 -0700
     3.2 +++ b/configure.in	Thu Oct 03 10:28:10 2013 -0300
     3.3 @@ -1791,6 +1791,28 @@
     3.4          fi
     3.5  }
     3.6  
     3.7 +dnl See if we can use the kernel kd.h header
     3.8 +CheckInputKD()
     3.9 +{
    3.10 +
    3.11 +    AC_MSG_CHECKING(for Linux kd.h)
    3.12 +    use_input_kd=no
    3.13 +    AC_TRY_COMPILE([
    3.14 +      #include <linux/kd.h>
    3.15 +      #include <linux/keyboard.h>
    3.16 +    ],[
    3.17 +        struct kbentry kbe;
    3.18 +        kbe.kb_table = KG_CTRL;
    3.19 +        ioctl(0, KDGKBENT, &kbe);
    3.20 +    ],[
    3.21 +    use_input_kd=yes
    3.22 +    ])
    3.23 +    AC_MSG_RESULT($use_input_kd)
    3.24 +    if test x$use_input_kd = xyes; then
    3.25 +        AC_DEFINE(SDL_INPUT_LINUXKD, 1, [ ])
    3.26 +    fi
    3.27 +}
    3.28 +
    3.29  dnl See if the platform offers libudev for device enumeration and hotplugging.
    3.30  CheckLibUDev()
    3.31  {
    3.32 @@ -2395,6 +2417,7 @@
    3.33          CheckLibUDev
    3.34          CheckDBus
    3.35          CheckInputEvents
    3.36 +        CheckInputKD
    3.37          CheckTslib
    3.38          CheckUSBHID
    3.39          CheckPTHREAD
     4.1 --- a/include/SDL_config.h.in	Thu Oct 03 03:31:05 2013 -0700
     4.2 +++ b/include/SDL_config.h.in	Thu Oct 03 10:28:10 2013 -0300
     4.3 @@ -217,6 +217,7 @@
     4.4  
     4.5  /* Enable various input drivers */
     4.6  #undef SDL_INPUT_LINUXEV
     4.7 +#undef SDL_INPUT_LINUXKD
     4.8  #undef SDL_INPUT_TSLIB
     4.9  #undef SDL_JOYSTICK_BEOS
    4.10  #undef SDL_JOYSTICK_DINPUT
     5.1 --- a/src/events/SDL_keyboard.c	Thu Oct 03 03:31:05 2013 -0700
     5.2 +++ b/src/events/SDL_keyboard.c	Thu Oct 03 10:28:10 2013 -0300
     5.3 @@ -507,7 +507,7 @@
     5.4  };
     5.5  
     5.6  /* Taken from SDL_iconv() */
     5.7 -static char *
     5.8 +char *
     5.9  SDL_UCS4ToUTF8(Uint32 ch, char *dst)
    5.10  {
    5.11      Uint8 *p = (Uint8 *) dst;
     6.1 --- a/src/events/SDL_keyboard_c.h	Thu Oct 03 03:31:05 2013 -0700
     6.2 +++ b/src/events/SDL_keyboard_c.h	Thu Oct 03 10:28:10 2013 -0300
     6.3 @@ -59,6 +59,9 @@
     6.4  /* Shutdown the keyboard subsystem */
     6.5  extern void SDL_KeyboardQuit(void);
     6.6  
     6.7 +/* Convert to UTF-8 */
     6.8 +extern char *SDL_UCS4ToUTF8(Uint32 ch, char *dst);
     6.9 +
    6.10  #endif /* _SDL_keyboard_c_h */
    6.11  
    6.12  /* vi: set ts=4 sw=4 expandtab: */
     7.1 --- a/src/input/evdev/SDL_evdev.c	Thu Oct 03 03:31:05 2013 -0700
     7.2 +++ b/src/input/evdev/SDL_evdev.c	Thu Oct 03 10:28:10 2013 -0300
     7.3 @@ -39,7 +39,11 @@
     7.4  #include <fcntl.h>
     7.5  #include <sys/ioctl.h>
     7.6  #include <limits.h>             /* For the definition of PATH_MAX */
     7.7 -
     7.8 +#include <linux/input.h>
     7.9 +#ifdef SDL_INPUT_LINUXKD
    7.10 +#include <linux/kd.h>
    7.11 +#include <linux/keyboard.h>
    7.12 +#endif
    7.13  
    7.14  #include "SDL.h"
    7.15  #include "SDL_assert.h"
    7.16 @@ -325,12 +329,54 @@
    7.17      SDL_BUTTON_X2 + 3           /*  BTN_TASK        0x117 */
    7.18  };
    7.19  
    7.20 +static char* EVDEV_consoles[] = {
    7.21 +    "/proc/self/fd/0",
    7.22 +    "/dev/tty",
    7.23 +    "/dev/tty0",
    7.24 +    "/dev/tty1",
    7.25 +    "/dev/tty2",
    7.26 +    "/dev/tty3",
    7.27 +    "/dev/tty4",
    7.28 +    "/dev/tty5",
    7.29 +    "/dev/tty6",
    7.30 +    "/dev/vc/0",
    7.31 +    "/dev/console"
    7.32 +};
    7.33 +
    7.34 +#define IS_CONSOLE(fd) isatty (fd) && ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84))
    7.35 +
    7.36 +static int SDL_EVDEV_get_console_fd(void)
    7.37 +{
    7.38 +    int fd, i;
    7.39 +    char arg = 0;
    7.40 +    
    7.41 +    /* Try a few consoles to see which one we have read access to */
    7.42 +    
    7.43 +    for( i = 0; i < SDL_arraysize(EVDEV_consoles); i++) {
    7.44 +        fd = open(EVDEV_consoles[i], O_RDONLY);
    7.45 +        if (fd >= 0) {
    7.46 +            if (IS_CONSOLE(fd)) return fd;
    7.47 +            close(fd);
    7.48 +        }
    7.49 +    }
    7.50 +    
    7.51 +    /* Try stdin, stdout, stderr */
    7.52 +    
    7.53 +    for( fd = 0; fd < 3; fd++) {
    7.54 +        if (IS_CONSOLE(fd)) return fd;
    7.55 +    }
    7.56 +    
    7.57 +    /* We won't be able to send SDL_TEXTINPUT events */
    7.58 +    return -1;
    7.59 +}
    7.60 +
    7.61  int
    7.62  SDL_EVDEV_Init(void)
    7.63  {
    7.64      int retval = 0;
    7.65      
    7.66      if (_this == NULL) {
    7.67 +        
    7.68          _this = (SDL_EVDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
    7.69          if(_this == NULL) {
    7.70              return SDL_OutOfMemory();
    7.71 @@ -354,6 +400,9 @@
    7.72  #else
    7.73          /* TODO: Scan the devices manually, like a caveman */
    7.74  #endif /* SDL_USE_LIBUDEV */
    7.75 +        
    7.76 +        /* We need a physical terminal (not PTS) to be able to translate key code to symbols via the kernel tables */
    7.77 +        _this->console_fd = SDL_EVDEV_get_console_fd();
    7.78  
    7.79      }
    7.80      
    7.81 @@ -378,6 +427,9 @@
    7.82          SDL_UDEV_Quit();
    7.83  #endif /* SDL_USE_LIBUDEV */
    7.84         
    7.85 +        if (_this->console_fd >= 0) {
    7.86 +            close(_this->console_fd);
    7.87 +        }
    7.88          /* Remove existing devices */
    7.89          while(_this->first != NULL) {
    7.90              SDL_EVDEV_device_removed(_this->first->path);
    7.91 @@ -443,11 +495,18 @@
    7.92      SDL_Scancode scan_code;
    7.93      int mouse_button;
    7.94      SDL_Mouse *mouse;
    7.95 -    
    7.96 +#ifdef SDL_INPUT_LINUXKD
    7.97 +    Uint16 modstate;
    7.98 +    struct kbentry kbe;
    7.99 +    static char keysym[8];
   7.100 +    char *end;
   7.101 +    Uint32 kval;
   7.102 +#endif
   7.103 +
   7.104  #if SDL_USE_LIBUDEV
   7.105      SDL_UDEV_Poll();
   7.106  #endif
   7.107 -    
   7.108 +
   7.109      for (item = _this->first; item != NULL; item = item->next) {
   7.110          while ((len = read(item->fd, events, (sizeof events))) > 0) {
   7.111              len /= sizeof(events[0]);
   7.112 @@ -455,20 +514,57 @@
   7.113                  switch(item->devclass) {
   7.114                      case SDL_EVDEV_DEVICE_KEYBOARD:
   7.115                          switch (events[i].type) {
   7.116 -                            case EV_KEY:
   7.117 -                                scan_code = SDL_EVDEV_translate_keycode(events[i].code);
   7.118 -                                if (scan_code != SDL_SCANCODE_UNKNOWN) {
   7.119 -                                    if (events[i].value == 0) {
   7.120 -                                        SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
   7.121 +                        case EV_KEY:
   7.122 +                            scan_code = SDL_EVDEV_translate_keycode(events[i].code);
   7.123 +                            if (scan_code != SDL_SCANCODE_UNKNOWN) {
   7.124 +                                if (events[i].value == 0) {
   7.125 +                                    SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
   7.126 +                                }
   7.127 +                                else if (events[i].value == 1 || events[i].value == 2 /* Key repeated */ ) {
   7.128 +                                    SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
   7.129 +#ifdef SDL_INPUT_LINUXKD
   7.130 +                                    if (_this->console_fd >= 0) {
   7.131 +                                        kbe.kb_index = events[i].code;
   7.132 +                                        /* Convert the key to an UTF-8 char */
   7.133 +                                        /* Ref: http://www.linuxjournal.com/article/2783 */
   7.134 +                                        modstate = SDL_GetModState();
   7.135 +                                        kbe.kb_table = 0;
   7.136 +                                        
   7.137 +                                        /* Ref: http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
   7.138 +                                        kbe.kb_table |= -( (modstate & KMOD_LCTRL) != 0) & (1 << KG_CTRLL | 1 << KG_CTRL);
   7.139 +                                        kbe.kb_table |= -( (modstate & KMOD_RCTRL) != 0) & (1 << KG_CTRLR | 1 << KG_CTRL);
   7.140 +                                        kbe.kb_table |= -( (modstate & KMOD_LSHIFT) != 0) & (1 << KG_SHIFTL | 1 << KG_SHIFT);
   7.141 +                                        kbe.kb_table |= -( (modstate & KMOD_RSHIFT) != 0) & (1 << KG_SHIFTR | 1 << KG_SHIFT);
   7.142 +                                        kbe.kb_table |= -( (modstate & KMOD_LALT) != 0) & (1 << KG_ALT);
   7.143 +                                        kbe.kb_table |= -( (modstate & KMOD_RALT) != 0) & (1 << KG_ALTGR);
   7.144 +
   7.145 +                                        if(ioctl(_this->console_fd, KDGKBENT, (unsigned long)&kbe) == 0 && 
   7.146 +                                            ( (KTYP(kbe.kb_value) == KT_LATIN) || (KTYP(kbe.kb_value) == KT_ASCII) || (KTYP(kbe.kb_value) == KT_LETTER) )) 
   7.147 +                                        {
   7.148 +                                            kval = KVAL(kbe.kb_value);
   7.149 +                                            
   7.150 +                                            /* While there's a KG_CAPSSHIFT symbol, it's not useful to build the table index with it
   7.151 +                                             * because 1 << KG_CAPSSHIFT overflows the 8 bits of kb_table 
   7.152 +                                             * So, we do the CAPS LOCK logic here. Note that isalpha depends on the locale!
   7.153 +                                             */
   7.154 +                                            if ( modstate & KMOD_CAPS && isalpha(kval) ) {
   7.155 +                                                if ( isupper(kval) ) {
   7.156 +                                                    kval = tolower(kval);
   7.157 +                                                }
   7.158 +                                                else {
   7.159 +                                                    kval = toupper(kval);
   7.160 +                                                }
   7.161 +                                            }
   7.162 +                                             
   7.163 +                                            /* Convert to UTF-8 and send */
   7.164 +                                            end = SDL_UCS4ToUTF8( kval, keysym);
   7.165 +                                            *end = '\0';
   7.166 +                                            SDL_SendKeyboardText(keysym);
   7.167 +                                        }
   7.168                                      }
   7.169 -                                    else if (events[i].value == 1) {
   7.170 -                                        SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
   7.171 -                                    }
   7.172 -                                    else if (events[i].value == 2) {
   7.173 -                                        /* Key repeated */
   7.174 -                                        SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
   7.175 -                                    }
   7.176 +#endif    
   7.177                                  }
   7.178 +                            }
   7.179                                  break;
   7.180  
   7.181                              default:
   7.182 @@ -651,3 +747,4 @@
   7.183  #endif /* SDL_INPUT_LINUXEV */
   7.184  
   7.185  /* vi: set ts=4 sw=4 expandtab: */
   7.186 +
     8.1 --- a/src/input/evdev/SDL_evdev.h	Thu Oct 03 03:31:05 2013 -0700
     8.2 +++ b/src/input/evdev/SDL_evdev.h	Thu Oct 03 10:28:10 2013 -0300
     8.3 @@ -19,8 +19,6 @@
     8.4    3. This notice may not be removed or altered from any source distribution.
     8.5  */
     8.6  
     8.7 -#include <linux/input.h>
     8.8 -
     8.9  #include "SDL_config.h"
    8.10  
    8.11  #ifndef _SDL_evdev_h
    8.12 @@ -42,7 +40,7 @@
    8.13      char *path;
    8.14      int fd;
    8.15      SDL_EVDEV_deviceclass devclass;
    8.16 -    struct SDL_evdevlist_item *next;   
    8.17 +    struct SDL_evdevlist_item *next;
    8.18  } SDL_evdevlist_item;
    8.19  
    8.20  typedef struct SDL_EVDEV_PrivateData
    8.21 @@ -51,6 +49,7 @@
    8.22      SDL_evdevlist_item *last;
    8.23      int numdevices;
    8.24      int ref_count;
    8.25 +    int console_fd;
    8.26  } SDL_EVDEV_PrivateData;
    8.27  
    8.28  extern int SDL_EVDEV_Init(void);