diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c index a8f2b138e..3cd9dfb3e 100644 --- a/src/core/linux/SDL_evdev.c +++ b/src/core/linux/SDL_evdev.c @@ -29,10 +29,7 @@ * The evtest application is also useful to debug the protocol */ - #include "SDL_evdev.h" -#define _THIS SDL_EVDEV_PrivateData *_this -static _THIS = NULL; #include #include @@ -43,18 +40,8 @@ static _THIS = NULL; #ifdef SDL_INPUT_LINUXKD #include #include -#endif - - -/* We need this to prevent keystrokes from appear in the console */ -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif -#ifndef KDSKBMODE -#define KDSKBMODE 0x4B45 -#endif -#ifndef K_OFF -#define K_OFF 0x04 +#include +#include /* for TIOCL_GETSHIFTSTATE */ #endif #include "SDL.h" @@ -63,273 +50,71 @@ static _THIS = NULL; #include "../../core/linux/SDL_udev.h" #include "SDL_scancode.h" #include "../../events/SDL_events_c.h" +#include "../../events/scancodes_linux.h" /* adds linux_scancode_table */ /* This isn't defined in older Linux kernel headers */ #ifndef SYN_DROPPED #define SYN_DROPPED 3 #endif +typedef struct SDL_evdevlist_item +{ + char *path; + int fd; + + /* TODO: use this for every device, not just touchscreen */ + int out_of_sync; + + /* TODO: expand on this to have data for every possible class (mouse, + keyboard, touchpad, etc.). Also there's probably some things in here we + can pull out to the SDL_evdevlist_item i.e. name */ + int is_touchscreen; + struct { + char* name; + + int min_x, max_x, range_x; + int min_y, max_y, range_y; + + int max_slots; + int current_slot; + struct { + enum { + EVDEV_TOUCH_SLOTDELTA_NONE = 0, + EVDEV_TOUCH_SLOTDELTA_DOWN, + EVDEV_TOUCH_SLOTDELTA_UP, + EVDEV_TOUCH_SLOTDELTA_MOVE + } delta; + int tracking_id; + int x, y; + } * slots; + } * touchscreen_data; + + struct SDL_evdevlist_item *next; +} SDL_evdevlist_item; + +typedef struct SDL_EVDEV_PrivateData +{ + SDL_evdevlist_item *first; + SDL_evdevlist_item *last; + int num_devices; + int ref_count; + int console_fd; + int kb_mode; +} SDL_EVDEV_PrivateData; + +#define _THIS SDL_EVDEV_PrivateData *_this +static _THIS = NULL; + static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode); static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item); -static int SDL_EVDEV_device_removed(const char *devpath); +static int SDL_EVDEV_device_removed(const char *dev_path); #if SDL_USE_LIBUDEV -static int SDL_EVDEV_device_added(const char *devpath); -void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); +static int SDL_EVDEV_device_added( const char *dev_path, int udev_class ); +void SDL_EVDEV_udev_callback( SDL_UDEV_deviceevent udev_type, int udev_class, + const char *dev_path ); #endif /* SDL_USE_LIBUDEV */ -static SDL_Scancode EVDEV_Keycodes[] = { - SDL_SCANCODE_UNKNOWN, /* KEY_RESERVED 0 */ - SDL_SCANCODE_ESCAPE, /* KEY_ESC 1 */ - SDL_SCANCODE_1, /* KEY_1 2 */ - SDL_SCANCODE_2, /* KEY_2 3 */ - SDL_SCANCODE_3, /* KEY_3 4 */ - SDL_SCANCODE_4, /* KEY_4 5 */ - SDL_SCANCODE_5, /* KEY_5 6 */ - SDL_SCANCODE_6, /* KEY_6 7 */ - SDL_SCANCODE_7, /* KEY_7 8 */ - SDL_SCANCODE_8, /* KEY_8 9 */ - SDL_SCANCODE_9, /* KEY_9 10 */ - SDL_SCANCODE_0, /* KEY_0 11 */ - SDL_SCANCODE_MINUS, /* KEY_MINUS 12 */ - SDL_SCANCODE_EQUALS, /* KEY_EQUAL 13 */ - SDL_SCANCODE_BACKSPACE, /* KEY_BACKSPACE 14 */ - SDL_SCANCODE_TAB, /* KEY_TAB 15 */ - SDL_SCANCODE_Q, /* KEY_Q 16 */ - SDL_SCANCODE_W, /* KEY_W 17 */ - SDL_SCANCODE_E, /* KEY_E 18 */ - SDL_SCANCODE_R, /* KEY_R 19 */ - SDL_SCANCODE_T, /* KEY_T 20 */ - SDL_SCANCODE_Y, /* KEY_Y 21 */ - SDL_SCANCODE_U, /* KEY_U 22 */ - SDL_SCANCODE_I, /* KEY_I 23 */ - SDL_SCANCODE_O, /* KEY_O 24 */ - SDL_SCANCODE_P, /* KEY_P 25 */ - SDL_SCANCODE_LEFTBRACKET, /* KEY_LEFTBRACE 26 */ - SDL_SCANCODE_RIGHTBRACKET, /* KEY_RIGHTBRACE 27 */ - SDL_SCANCODE_RETURN, /* KEY_ENTER 28 */ - SDL_SCANCODE_LCTRL, /* KEY_LEFTCTRL 29 */ - SDL_SCANCODE_A, /* KEY_A 30 */ - SDL_SCANCODE_S, /* KEY_S 31 */ - SDL_SCANCODE_D, /* KEY_D 32 */ - SDL_SCANCODE_F, /* KEY_F 33 */ - SDL_SCANCODE_G, /* KEY_G 34 */ - SDL_SCANCODE_H, /* KEY_H 35 */ - SDL_SCANCODE_J, /* KEY_J 36 */ - SDL_SCANCODE_K, /* KEY_K 37 */ - SDL_SCANCODE_L, /* KEY_L 38 */ - SDL_SCANCODE_SEMICOLON, /* KEY_SEMICOLON 39 */ - SDL_SCANCODE_APOSTROPHE, /* KEY_APOSTROPHE 40 */ - SDL_SCANCODE_GRAVE, /* KEY_GRAVE 41 */ - SDL_SCANCODE_LSHIFT, /* KEY_LEFTSHIFT 42 */ - SDL_SCANCODE_BACKSLASH, /* KEY_BACKSLASH 43 */ - SDL_SCANCODE_Z, /* KEY_Z 44 */ - SDL_SCANCODE_X, /* KEY_X 45 */ - SDL_SCANCODE_C, /* KEY_C 46 */ - SDL_SCANCODE_V, /* KEY_V 47 */ - SDL_SCANCODE_B, /* KEY_B 48 */ - SDL_SCANCODE_N, /* KEY_N 49 */ - SDL_SCANCODE_M, /* KEY_M 50 */ - SDL_SCANCODE_COMMA, /* KEY_COMMA 51 */ - SDL_SCANCODE_PERIOD, /* KEY_DOT 52 */ - SDL_SCANCODE_SLASH, /* KEY_SLASH 53 */ - SDL_SCANCODE_RSHIFT, /* KEY_RIGHTSHIFT 54 */ - SDL_SCANCODE_KP_MULTIPLY, /* KEY_KPASTERISK 55 */ - SDL_SCANCODE_LALT, /* KEY_LEFTALT 56 */ - SDL_SCANCODE_SPACE, /* KEY_SPACE 57 */ - SDL_SCANCODE_CAPSLOCK, /* KEY_CAPSLOCK 58 */ - SDL_SCANCODE_F1, /* KEY_F1 59 */ - SDL_SCANCODE_F2, /* KEY_F2 60 */ - SDL_SCANCODE_F3, /* KEY_F3 61 */ - SDL_SCANCODE_F4, /* KEY_F4 62 */ - SDL_SCANCODE_F5, /* KEY_F5 63 */ - SDL_SCANCODE_F6, /* KEY_F6 64 */ - SDL_SCANCODE_F7, /* KEY_F7 65 */ - SDL_SCANCODE_F8, /* KEY_F8 66 */ - SDL_SCANCODE_F9, /* KEY_F9 67 */ - SDL_SCANCODE_F10, /* KEY_F10 68 */ - SDL_SCANCODE_NUMLOCKCLEAR, /* KEY_NUMLOCK 69 */ - SDL_SCANCODE_SCROLLLOCK, /* KEY_SCROLLLOCK 70 */ - SDL_SCANCODE_KP_7, /* KEY_KP7 71 */ - SDL_SCANCODE_KP_8, /* KEY_KP8 72 */ - SDL_SCANCODE_KP_9, /* KEY_KP9 73 */ - SDL_SCANCODE_KP_MINUS, /* KEY_KPMINUS 74 */ - SDL_SCANCODE_KP_4, /* KEY_KP4 75 */ - SDL_SCANCODE_KP_5, /* KEY_KP5 76 */ - SDL_SCANCODE_KP_6, /* KEY_KP6 77 */ - SDL_SCANCODE_KP_PLUS, /* KEY_KPPLUS 78 */ - SDL_SCANCODE_KP_1, /* KEY_KP1 79 */ - SDL_SCANCODE_KP_2, /* KEY_KP2 80 */ - SDL_SCANCODE_KP_3, /* KEY_KP3 81 */ - SDL_SCANCODE_KP_0, /* KEY_KP0 82 */ - SDL_SCANCODE_KP_PERIOD, /* KEY_KPDOT 83 */ - SDL_SCANCODE_UNKNOWN, /* 84 */ - SDL_SCANCODE_LANG5, /* KEY_ZENKAKUHANKAKU 85 */ - SDL_SCANCODE_UNKNOWN, /* KEY_102ND 86 */ - SDL_SCANCODE_F11, /* KEY_F11 87 */ - SDL_SCANCODE_F12, /* KEY_F12 88 */ - SDL_SCANCODE_UNKNOWN, /* KEY_RO 89 */ - SDL_SCANCODE_LANG3, /* KEY_KATAKANA 90 */ - SDL_SCANCODE_LANG4, /* KEY_HIRAGANA 91 */ - SDL_SCANCODE_UNKNOWN, /* KEY_HENKAN 92 */ - SDL_SCANCODE_LANG3, /* KEY_KATAKANAHIRAGANA 93 */ - SDL_SCANCODE_UNKNOWN, /* KEY_MUHENKAN 94 */ - SDL_SCANCODE_KP_COMMA, /* KEY_KPJPCOMMA 95 */ - SDL_SCANCODE_KP_ENTER, /* KEY_KPENTER 96 */ - SDL_SCANCODE_RCTRL, /* KEY_RIGHTCTRL 97 */ - SDL_SCANCODE_KP_DIVIDE, /* KEY_KPSLASH 98 */ - SDL_SCANCODE_SYSREQ, /* KEY_SYSRQ 99 */ - SDL_SCANCODE_RALT, /* KEY_RIGHTALT 100 */ - SDL_SCANCODE_UNKNOWN, /* KEY_LINEFEED 101 */ - SDL_SCANCODE_HOME, /* KEY_HOME 102 */ - SDL_SCANCODE_UP, /* KEY_UP 103 */ - SDL_SCANCODE_PAGEUP, /* KEY_PAGEUP 104 */ - SDL_SCANCODE_LEFT, /* KEY_LEFT 105 */ - SDL_SCANCODE_RIGHT, /* KEY_RIGHT 106 */ - SDL_SCANCODE_END, /* KEY_END 107 */ - SDL_SCANCODE_DOWN, /* KEY_DOWN 108 */ - SDL_SCANCODE_PAGEDOWN, /* KEY_PAGEDOWN 109 */ - SDL_SCANCODE_INSERT, /* KEY_INSERT 110 */ - SDL_SCANCODE_DELETE, /* KEY_DELETE 111 */ - SDL_SCANCODE_UNKNOWN, /* KEY_MACRO 112 */ - SDL_SCANCODE_MUTE, /* KEY_MUTE 113 */ - SDL_SCANCODE_VOLUMEDOWN, /* KEY_VOLUMEDOWN 114 */ - SDL_SCANCODE_VOLUMEUP, /* KEY_VOLUMEUP 115 */ - SDL_SCANCODE_POWER, /* KEY_POWER 116 SC System Power Down */ - SDL_SCANCODE_KP_EQUALS, /* KEY_KPEQUAL 117 */ - SDL_SCANCODE_KP_MINUS, /* KEY_KPPLUSMINUS 118 */ - SDL_SCANCODE_PAUSE, /* KEY_PAUSE 119 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SCALE 120 AL Compiz Scale (Expose) */ - SDL_SCANCODE_KP_COMMA, /* KEY_KPCOMMA 121 */ - SDL_SCANCODE_LANG1, /* KEY_HANGEUL,KEY_HANGUEL 122 */ - SDL_SCANCODE_LANG2, /* KEY_HANJA 123 */ - SDL_SCANCODE_INTERNATIONAL3,/* KEY_YEN 124 */ - SDL_SCANCODE_LGUI, /* KEY_LEFTMETA 125 */ - SDL_SCANCODE_RGUI, /* KEY_RIGHTMETA 126 */ - SDL_SCANCODE_APPLICATION, /* KEY_COMPOSE 127 */ - SDL_SCANCODE_STOP, /* KEY_STOP 128 AC Stop */ - SDL_SCANCODE_AGAIN, /* KEY_AGAIN 129 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PROPS 130 AC Properties */ - SDL_SCANCODE_UNDO, /* KEY_UNDO 131 AC Undo */ - SDL_SCANCODE_UNKNOWN, /* KEY_FRONT 132 */ - SDL_SCANCODE_COPY, /* KEY_COPY 133 AC Copy */ - SDL_SCANCODE_UNKNOWN, /* KEY_OPEN 134 AC Open */ - SDL_SCANCODE_PASTE, /* KEY_PASTE 135 AC Paste */ - SDL_SCANCODE_FIND, /* KEY_FIND 136 AC Search */ - SDL_SCANCODE_CUT, /* KEY_CUT 137 AC Cut */ - SDL_SCANCODE_HELP, /* KEY_HELP 138 AL Integrated Help Center */ - SDL_SCANCODE_MENU, /* KEY_MENU 139 Menu (show menu) */ - SDL_SCANCODE_CALCULATOR, /* KEY_CALC 140 AL Calculator */ - SDL_SCANCODE_UNKNOWN, /* KEY_SETUP 141 */ - SDL_SCANCODE_SLEEP, /* KEY_SLEEP 142 SC System Sleep */ - SDL_SCANCODE_UNKNOWN, /* KEY_WAKEUP 143 System Wake Up */ - SDL_SCANCODE_UNKNOWN, /* KEY_FILE 144 AL Local Machine Browser */ - SDL_SCANCODE_UNKNOWN, /* KEY_SENDFILE 145 */ - SDL_SCANCODE_UNKNOWN, /* KEY_DELETEFILE 146 */ - SDL_SCANCODE_UNKNOWN, /* KEY_XFER 147 */ - SDL_SCANCODE_APP1, /* KEY_PROG1 148 */ - SDL_SCANCODE_APP1, /* KEY_PROG2 149 */ - SDL_SCANCODE_WWW, /* KEY_WWW 150 AL Internet Browser */ - SDL_SCANCODE_UNKNOWN, /* KEY_MSDOS 151 */ - SDL_SCANCODE_UNKNOWN, /* KEY_COFFEE,KEY_SCREENLOCK 152 AL Terminal Lock/Screensaver */ - SDL_SCANCODE_UNKNOWN, /* KEY_DIRECTION 153 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CYCLEWINDOWS 154 */ - SDL_SCANCODE_MAIL, /* KEY_MAIL 155 */ - SDL_SCANCODE_AC_BOOKMARKS, /* KEY_BOOKMARKS 156 AC Bookmarks */ - SDL_SCANCODE_COMPUTER, /* KEY_COMPUTER 157 */ - SDL_SCANCODE_AC_BACK, /* KEY_BACK 158 AC Back */ - SDL_SCANCODE_AC_FORWARD, /* KEY_FORWARD 159 AC Forward */ - SDL_SCANCODE_UNKNOWN, /* KEY_CLOSECD 160 */ - SDL_SCANCODE_EJECT, /* KEY_EJECTCD 161 */ - SDL_SCANCODE_UNKNOWN, /* KEY_EJECTCLOSECD 162 */ - SDL_SCANCODE_AUDIONEXT, /* KEY_NEXTSONG 163 */ - SDL_SCANCODE_AUDIOPLAY, /* KEY_PLAYPAUSE 164 */ - SDL_SCANCODE_AUDIOPREV, /* KEY_PREVIOUSSONG 165 */ - SDL_SCANCODE_AUDIOSTOP, /* KEY_STOPCD 166 */ - SDL_SCANCODE_UNKNOWN, /* KEY_RECORD 167 */ - SDL_SCANCODE_UNKNOWN, /* KEY_REWIND 168 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PHONE 169 Media Select Telephone */ - SDL_SCANCODE_UNKNOWN, /* KEY_ISO 170 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CONFIG 171 AL Consumer Control Configuration */ - SDL_SCANCODE_AC_HOME, /* KEY_HOMEPAGE 172 AC Home */ - SDL_SCANCODE_AC_REFRESH, /* KEY_REFRESH 173 AC Refresh */ - SDL_SCANCODE_UNKNOWN, /* KEY_EXIT 174 AC Exit */ - SDL_SCANCODE_UNKNOWN, /* KEY_MOVE 175 */ - SDL_SCANCODE_UNKNOWN, /* KEY_EDIT 176 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SCROLLUP 177 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SCROLLDOWN 178 */ - SDL_SCANCODE_KP_LEFTPAREN, /* KEY_KPLEFTPAREN 179 */ - SDL_SCANCODE_KP_RIGHTPAREN, /* KEY_KPRIGHTPAREN 180 */ - SDL_SCANCODE_UNKNOWN, /* KEY_NEW 181 AC New */ - SDL_SCANCODE_AGAIN, /* KEY_REDO 182 AC Redo/Repeat */ - SDL_SCANCODE_F13, /* KEY_F13 183 */ - SDL_SCANCODE_F14, /* KEY_F14 184 */ - SDL_SCANCODE_F15, /* KEY_F15 185 */ - SDL_SCANCODE_F16, /* KEY_F16 186 */ - SDL_SCANCODE_F17, /* KEY_F17 187 */ - SDL_SCANCODE_F18, /* KEY_F18 188 */ - SDL_SCANCODE_F19, /* KEY_F19 189 */ - SDL_SCANCODE_F20, /* KEY_F20 190 */ - SDL_SCANCODE_F21, /* KEY_F21 191 */ - SDL_SCANCODE_F22, /* KEY_F22 192 */ - SDL_SCANCODE_F23, /* KEY_F23 193 */ - SDL_SCANCODE_F24, /* KEY_F24 194 */ - SDL_SCANCODE_UNKNOWN, /* 195 */ - SDL_SCANCODE_UNKNOWN, /* 196 */ - SDL_SCANCODE_UNKNOWN, /* 197 */ - SDL_SCANCODE_UNKNOWN, /* 198 */ - SDL_SCANCODE_UNKNOWN, /* 199 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PLAYCD 200 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PAUSECD 201 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PROG3 202 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PROG4 203 */ - SDL_SCANCODE_UNKNOWN, /* KEY_DASHBOARD 204 AL Dashboard */ - SDL_SCANCODE_UNKNOWN, /* KEY_SUSPEND 205 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CLOSE 206 AC Close */ - SDL_SCANCODE_UNKNOWN, /* KEY_PLAY 207 */ - SDL_SCANCODE_UNKNOWN, /* KEY_FASTFORWARD 208 */ - SDL_SCANCODE_UNKNOWN, /* KEY_BASSBOOST 209 */ - SDL_SCANCODE_UNKNOWN, /* KEY_PRINT 210 AC Print */ - SDL_SCANCODE_UNKNOWN, /* KEY_HP 211 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CAMERA 212 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SOUND 213 */ - SDL_SCANCODE_UNKNOWN, /* KEY_QUESTION 214 */ - SDL_SCANCODE_UNKNOWN, /* KEY_EMAIL 215 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CHAT 216 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SEARCH 217 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CONNECT 218 */ - SDL_SCANCODE_UNKNOWN, /* KEY_FINANCE 219 AL Checkbook/Finance */ - SDL_SCANCODE_UNKNOWN, /* KEY_SPORT 220 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SHOP 221 */ - SDL_SCANCODE_UNKNOWN, /* KEY_ALTERASE 222 */ - SDL_SCANCODE_UNKNOWN, /* KEY_CANCEL 223 AC Cancel */ - SDL_SCANCODE_UNKNOWN, /* KEY_BRIGHTNESSDOWN 224 */ - SDL_SCANCODE_UNKNOWN, /* KEY_BRIGHTNESSUP 225 */ - SDL_SCANCODE_UNKNOWN, /* KEY_MEDIA 226 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SWITCHVIDEOMODE 227 Cycle between available video outputs (Monitor/LCD/TV-out/etc) */ - SDL_SCANCODE_UNKNOWN, /* KEY_KBDILLUMTOGGLE 228 */ - SDL_SCANCODE_UNKNOWN, /* KEY_KBDILLUMDOWN 229 */ - SDL_SCANCODE_UNKNOWN, /* KEY_KBDILLUMUP 230 */ - SDL_SCANCODE_UNKNOWN, /* KEY_SEND 231 AC Send */ - SDL_SCANCODE_UNKNOWN, /* KEY_REPLY 232 AC Reply */ - SDL_SCANCODE_UNKNOWN, /* KEY_FORWARDMAIL 233 AC Forward Msg */ - SDL_SCANCODE_UNKNOWN, /* KEY_SAVE 234 AC Save */ - SDL_SCANCODE_UNKNOWN, /* KEY_DOCUMENTS 235 */ - SDL_SCANCODE_UNKNOWN, /* KEY_BATTERY 236 */ - SDL_SCANCODE_UNKNOWN, /* KEY_BLUETOOTH 237 */ - SDL_SCANCODE_UNKNOWN, /* KEY_WLAN 238 */ - SDL_SCANCODE_UNKNOWN, /* KEY_UWB 239 */ - SDL_SCANCODE_UNKNOWN, /* KEY_UNKNOWN 240 */ - SDL_SCANCODE_UNKNOWN, /* KEY_VIDEO_NEXT 241 drive next video source */ - SDL_SCANCODE_UNKNOWN, /* KEY_VIDEO_PREV 242 drive previous video source */ - SDL_SCANCODE_UNKNOWN, /* KEY_BRIGHTNESS_CYCLE 243 brightness up, after max is min */ - SDL_SCANCODE_UNKNOWN, /* KEY_BRIGHTNESS_ZERO 244 brightness off, use ambient */ - SDL_SCANCODE_UNKNOWN, /* KEY_DISPLAY_OFF 245 display device to off state */ - SDL_SCANCODE_UNKNOWN, /* KEY_WIMAX 246 */ - SDL_SCANCODE_UNKNOWN, /* KEY_RFKILL 247 Key that controls all radios */ - SDL_SCANCODE_UNKNOWN, /* KEY_MICMUTE 248 Mute / unmute the microphone */ -}; - static Uint8 EVDEV_MouseButtons[] = { SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */ SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */ @@ -342,134 +127,113 @@ static Uint8 EVDEV_MouseButtons[] = { }; static const char* EVDEV_consoles[] = { - "/proc/self/fd/0", + /* "/proc/self/fd/0", "/dev/tty", - "/dev/tty0", + "/dev/tty0", */ /* the tty ioctl's prohibit these */ "/dev/tty1", "/dev/tty2", "/dev/tty3", "/dev/tty4", "/dev/tty5", "/dev/tty6", + "/dev/tty7", /* usually X is spawned in tty7 */ "/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; +static int SDL_EVDEV_is_console( int fd ) { + int type; - /* 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; + return isatty( fd ) && ioctl( fd, KDGKBTYPE, &type ) == 0 && + ( type == KB_101 || type == KB_84 ); } /* Prevent keystrokes from reaching the tty */ -static int SDL_EVDEV_mute_keyboard(int tty, int *kb_mode) +static int SDL_EVDEV_mute_keyboard( int tty_fd, int* old_kb_mode ) { - char arg; + if( !SDL_EVDEV_is_console( tty_fd ) ) { + return SDL_SetError( "Tried to mute an invalid tty" ); + } - *kb_mode = 0; /* FIXME: Is this a sane default in case KDGKBMODE fails? */ - if (!IS_CONSOLE(tty)) { - return SDL_SetError("Tried to mute an invalid tty"); + if( ioctl( tty_fd, KDGKBMODE, old_kb_mode ) < 0 ) { + return SDL_SetError( "Failed to get keyboard mode during muting" ); } - ioctl(tty, KDGKBMODE, kb_mode); /* It's not fatal if this fails */ - if (ioctl(tty, KDSKBMUTE, 1) && ioctl(tty, KDSKBMODE, K_OFF)) { - return SDL_SetError("EVDEV: Failed muting keyboard"); + + /* FIXME: atm this absolutely ruins the vt, and KDSKBMUTE isn't implemented + in the kernel */ + /* + if( ioctl( tty_fd, KDSKBMODE, K_OFF ) < 0 ) { + return SDL_SetError( "Failed to set keyboard mode during muting" ); } + */ return 0; } /* Restore the keyboard mode for given tty */ -static void SDL_EVDEV_unmute_keyboard(int tty, int kb_mode) +static void SDL_EVDEV_unmute_keyboard( int tty_fd, int kb_mode ) { - if (ioctl(tty, KDSKBMUTE, 0) && ioctl(tty, KDSKBMODE, kb_mode)) { - SDL_Log("EVDEV: Failed restoring keyboard mode"); + /* read above */ + /* + if( ioctl( tty_fd, KDSKBMODE, kb_mode ) < 0 ) { + SDL_Log( "Failed to unmute keyboard" ); } + */ } -/* Read /sys/class/tty/tty0/active and open the tty */ static int SDL_EVDEV_get_active_tty() -{ - int fd, len; - char ttyname[NAME_MAX + 1]; - char ttypath[PATH_MAX+1] = "/dev/"; - char arg; +{ + int i, fd, ret, tty = 0; + char tiocl; + struct vt_stat vt_state; + char path[PATH_MAX + 1]; - fd = open("/sys/class/tty/tty0/active", O_RDONLY); - if (fd < 0) { - return SDL_SetError("Could not determine which tty is active"); + for( i = 0; i < SDL_arraysize( EVDEV_consoles ); i++ ) { + fd = open( EVDEV_consoles[i], O_RDONLY ); + + if( fd < 0 && !SDL_EVDEV_is_console( fd ) ) + break; + + tiocl = TIOCL_GETFGCONSOLE; + if( ( ret = ioctl( fd, TIOCLINUX, &tiocl ) ) >= 0 ) + tty = ret + 1; + else if( ioctl( fd, VT_GETSTATE, &vt_state ) == 0 ) + tty = vt_state.v_active; + + close( fd ); + + if( tty ) { + sprintf( path, "/dev/tty%u", tty ); + fd = open( path, O_RDONLY ); + if( fd >= 0 && SDL_EVDEV_is_console( fd ) ) + return fd; + } } - len = read(fd, ttyname, NAME_MAX); - close(fd); - - if (len <= 0) { - return SDL_SetError("Could not read which tty is active"); - } - - if (ttyname[len-1] == '\n') { - ttyname[len-1] = '\0'; - } - else { - ttyname[len] = '\0'; - } - - SDL_strlcat(ttypath, ttyname, PATH_MAX); - fd = open(ttypath, O_RDWR | O_NOCTTY); - if (fd < 0) { - return SDL_SetError("Could not open tty: %s", ttypath); - } - - if (!IS_CONSOLE(fd)) { - close(fd); - return SDL_SetError("Invalid tty obtained: %s", ttypath); - } - - return fd; + return SDL_SetError( "Failed to determine active tty" ); } int SDL_EVDEV_Init(void) { - int retval = 0; - - if (_this == NULL) { - - _this = (SDL_EVDEV_PrivateData *) SDL_calloc(1, sizeof(*_this)); - if(_this == NULL) { + if( _this == NULL ) { + _this = (SDL_EVDEV_PrivateData*)SDL_calloc( 1, sizeof( *_this ) ); + if( _this == NULL ) { return SDL_OutOfMemory(); } #if SDL_USE_LIBUDEV - if (SDL_UDEV_Init() < 0) { - SDL_free(_this); + if( SDL_UDEV_Init() < 0 ) { + SDL_free( _this ); _this = NULL; return -1; } /* Set up the udev callback */ if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) { - SDL_EVDEV_Quit(); + SDL_UDEV_Quit(); + SDL_free( _this ); + _this = NULL; return -1; } @@ -479,109 +243,165 @@ SDL_EVDEV_Init(void) /* 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(); + /* 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_active_tty(); - /* Mute the keyboard so keystrokes only generate evdev events and do not leak through to the console */ - _this->tty = STDIN_FILENO; - if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) { - /* stdin is not a tty, probably we were launched remotely, so we try to disable the active tty */ - _this->tty = SDL_EVDEV_get_active_tty(); - if (_this->tty >= 0) { - if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) { - close(_this->tty); - _this->tty = -1; - } - } - } + /* Mute the keyboard so keystrokes only generate evdev events and do not + leak through to the console */ + SDL_EVDEV_mute_keyboard( _this->console_fd, &_this->kb_mode ); } _this->ref_count += 1; - return retval; + return 0; } void SDL_EVDEV_Quit(void) { - if (_this == NULL) { + if( _this == NULL ) { return; } _this->ref_count -= 1; - if (_this->ref_count < 1) { - + if( _this->ref_count < 1 ) { #if SDL_USE_LIBUDEV - SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback); + SDL_UDEV_DelCallback( SDL_EVDEV_udev_callback ); SDL_UDEV_Quit(); #endif /* SDL_USE_LIBUDEV */ - if (_this->console_fd >= 0) { - close(_this->console_fd); - } - - if (_this->tty >= 0) { - SDL_EVDEV_unmute_keyboard(_this->tty, _this->kb_mode); - close(_this->tty); + if( _this->console_fd >= 0 ) { + SDL_EVDEV_unmute_keyboard( _this->console_fd, _this->kb_mode ); + close( _this->console_fd ); } /* Remove existing devices */ while(_this->first != NULL) { - SDL_EVDEV_device_removed(_this->first->path); + SDL_EVDEV_device_removed( _this->first->path ); } - SDL_assert(_this->first == NULL); - SDL_assert(_this->last == NULL); - SDL_assert(_this->numdevices == 0); + SDL_assert( _this->first == NULL ); + SDL_assert( _this->last == NULL ); + SDL_assert( _this->num_devices == 0 ); - SDL_free(_this); + SDL_free( _this ); _this = NULL; } } #if SDL_USE_LIBUDEV -void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +void SDL_EVDEV_udev_callback( SDL_UDEV_deviceevent udev_event, int udev_class, + const char* dev_path ) { - if (devpath == NULL) { + if( dev_path == NULL ) { return; } - switch(udev_type) { + switch( udev_event ) { case SDL_UDEV_DEVICEADDED: - if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE|SDL_UDEV_DEVICE_KEYBOARD))) { + if( !( udev_class & ( SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD | + SDL_UDEV_DEVICE_TOUCHSCREEN ) ) ) return; - } - SDL_EVDEV_device_added(devpath); - break; - + + SDL_EVDEV_device_added( dev_path, udev_class ); + break; case SDL_UDEV_DEVICEREMOVED: - SDL_EVDEV_device_removed(devpath); + SDL_EVDEV_device_removed( dev_path ); break; - default: break; } } - #endif /* SDL_USE_LIBUDEV */ +#ifdef SDL_INPUT_LINUXKD +/* this logic is pulled from kbd_keycode() in drivers/tty/vt/keyboard.c in the + Linux kernel source */ +static void SDL_EVDEV_do_text_input( unsigned short keycode ) { + char shift_state; + int locks_state; + struct kbentry kbe; + unsigned char type; + char text[2] = { 0 }; + + if( _this->console_fd < 0 ) + return; + + shift_state = TIOCL_GETSHIFTSTATE; + if( ioctl( _this->console_fd, TIOCLINUX, &shift_state ) < 0 ) { + /* TODO: error */ + return; + } + + kbe.kb_table = shift_state; + kbe.kb_index = keycode; + + if( ioctl( _this->console_fd, KDGKBENT, &kbe ) < 0 ) { + /* TODO: error */ + return; + } + + type = KTYP( kbe.kb_value ); + + if( type < 0xf0 ) { + /* + * FIXME: keysyms with a type below 0xf0 represent a unicode character + * which requires special handling due to dead characters, diacritics, + * etc. For perfect input a proper way to deal with such characters + * should be implemented. + * + * For reference, the only place I was able to find out about this + * special 0xf0 value was in an unused? couple of patches listed below. + * + * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-keyboard.diff + * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-console.diff + */ + + return; + } + + type -= 0xf0; + + /* if type is KT_LETTER then it can be affected by Caps Lock */ + if( type == KT_LETTER ) { + type = KT_LATIN; + + if( ioctl( _this->console_fd, KDGKBLED, &locks_state ) < 0 ) { + /* TODO: error */ + return; + } + + if( locks_state & K_CAPSLOCK ) { + kbe.kb_table = shift_state ^ ( 1 << KG_SHIFT ); + + if( ioctl( _this->console_fd, KDGKBENT, &kbe ) < 0 ) { + /* TODO: error */ + return; + } + } + } + + /* TODO: convert values >= 0x80 from ISO-8859-1? to UTF-8 */ + if( type != KT_LATIN || KVAL( kbe.kb_value ) >= 0x80 ) + return; + + *text = KVAL( kbe.kb_value ); + SDL_SendKeyboardText( text ); +} +#endif /* SDL_INPUT_LINUXKD */ + void SDL_EVDEV_Poll(void) { struct input_event events[32]; - int i, len; + int i, j, len; SDL_evdevlist_item *item; 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 + float norm_x, norm_y; if (!_this) { return; @@ -597,6 +417,13 @@ SDL_EVDEV_Poll(void) while ((len = read(item->fd, events, (sizeof events))) > 0) { len /= sizeof(events[0]); for (i = 0; i < len; ++i) { + /* special handling for touchscreen, that should eventually be + used for all devices */ + if( item->out_of_sync && item->is_touchscreen && + events[i].type == EV_SYN && events[i].code != SYN_REPORT ) { + break; + } + switch (events[i].type) { case EV_KEY: if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) { @@ -614,57 +441,55 @@ SDL_EVDEV_Poll(void) 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 */) { + } 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); - } - } + SDL_EVDEV_do_text_input( events[i].code ); #endif /* SDL_INPUT_LINUXKD */ } } break; case EV_ABS: switch(events[i].code) { + case ABS_MT_SLOT: + if( !item->is_touchscreen ) /* FIXME: temp hack */ + break; + item->touchscreen_data->current_slot = events[i].value; + break; + case ABS_MT_TRACKING_ID: + if( !item->is_touchscreen ) /* FIXME: temp hack */ + break; + if( events[i].value >= 0 ) { + item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value; + item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; + } else { + item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP; + } + break; + case ABS_MT_POSITION_X: + if( !item->is_touchscreen ) /* FIXME: temp hack */ + break; + item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value; + if( item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE ) { + item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; + } + break; + case ABS_MT_POSITION_Y: + if( !item->is_touchscreen ) /* FIXME: temp hack */ + break; + item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value; + if( item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE ) { + item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE; + } + break; case ABS_X: + if( item->is_touchscreen ) /* FIXME: temp hack */ + break; SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y); break; case ABS_Y: + if( item->is_touchscreen ) /* FIXME: temp hack */ + break; SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value); break; default: @@ -691,7 +516,41 @@ SDL_EVDEV_Poll(void) break; case EV_SYN: switch (events[i].code) { + case SYN_REPORT: + if( !item->is_touchscreen ) /* FIXME: temp hack */ + break; + + for( j = 0; j < item->touchscreen_data->max_slots; j++ ) { + norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) / + (float)item->touchscreen_data->range_x; + norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) / + (float)item->touchscreen_data->range_y; + + switch( item->touchscreen_data->slots[j].delta ) { + case EVDEV_TOUCH_SLOTDELTA_DOWN: + SDL_SendTouch( item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f ); + item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; + break; + case EVDEV_TOUCH_SLOTDELTA_UP: + SDL_SendTouch( item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f ); + item->touchscreen_data->slots[j].tracking_id = -1; + item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; + break; + case EVDEV_TOUCH_SLOTDELTA_MOVE: + SDL_SendTouchMotion( item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f ); + item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE; + break; + default: + break; + } + } + + if( item->out_of_sync ) + item->out_of_sync = 0; + break; case SYN_DROPPED: + if( item->is_touchscreen ) + item->out_of_sync = 1; SDL_EVDEV_sync_device(item); break; default: @@ -705,34 +564,230 @@ SDL_EVDEV_Poll(void) } static SDL_Scancode -SDL_EVDEV_translate_keycode(int keycode) +SDL_EVDEV_translate_keycode( int keycode ) { SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; - if (keycode < SDL_arraysize(EVDEV_Keycodes)) { - scancode = EVDEV_Keycodes[keycode]; - } - if (scancode == SDL_SCANCODE_UNKNOWN) { - SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL mailing list EVDEV KeyCode %d \n", keycode); + if( keycode < SDL_arraysize( linux_scancode_table ) ) + scancode = linux_scancode_table[keycode]; + + if( scancode == SDL_SCANCODE_UNKNOWN ) { + SDL_Log( "The key you just pressed is not recognized by SDL. To help " + "get this fixed, please report this to the SDL mailing list " + " EVDEV KeyCode %d\n", keycode ); } + return scancode; } +static int +SDL_EVDEV_init_touchscreen( SDL_evdevlist_item* item ) { + int ret, i; + char name[64]; + struct input_absinfo abs_info; + + if( !item->is_touchscreen ) + return 0; + + item->touchscreen_data = SDL_calloc( 1, sizeof( *item->touchscreen_data ) ); + if( item->touchscreen_data == NULL ) + return SDL_OutOfMemory(); + + ret = ioctl( item->fd, EVIOCGNAME( sizeof( name ) ), name ); + if( ret < 0 ) { + SDL_free( item->touchscreen_data ); + return SDL_SetError( "Failed to get evdev touchscreen name" ); + } + + item->touchscreen_data->name = SDL_strdup( name ); + if( item->touchscreen_data->name == NULL ) { + SDL_free( item->touchscreen_data ); + return SDL_OutOfMemory(); + } + + ret = ioctl( item->fd, EVIOCGABS( ABS_MT_POSITION_X ), &abs_info ); + if( ret < 0 ) { + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); + return SDL_SetError( "Failed to get evdev touchscreen limits" ); + } + item->touchscreen_data->min_x = abs_info.minimum; + item->touchscreen_data->max_x = abs_info.maximum; + item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum; + + ret = ioctl( item->fd, EVIOCGABS( ABS_MT_POSITION_Y ), &abs_info ); + if( ret < 0 ) { + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); + return SDL_SetError( "Failed to get evdev touchscreen limits" ); + } + item->touchscreen_data->min_y = abs_info.minimum; + item->touchscreen_data->max_y = abs_info.maximum; + item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum; + + ret = ioctl( item->fd, EVIOCGABS( ABS_MT_SLOT ), &abs_info ); + if( ret < 0 ) { + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); + return SDL_SetError( "Failed to get evdev touchscreen limits" ); + } + item->touchscreen_data->max_slots = abs_info.maximum + 1; + + item->touchscreen_data->slots = SDL_calloc( + item->touchscreen_data->max_slots, + sizeof( *item->touchscreen_data->slots ) ); + if( item->touchscreen_data->slots == NULL ) { + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); + return SDL_OutOfMemory(); + } + + for( i = 0; i < item->touchscreen_data->max_slots; i++ ) { + item->touchscreen_data->slots[i].tracking_id = -1; + } + + ret = SDL_AddTouch( item->fd, /* I guess our fd is unique enough */ + item->touchscreen_data->name ); + if( ret < 0 ) { + SDL_free( item->touchscreen_data->slots ); + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); + return ret; + } + + return 0; +} + +static void +SDL_EVDEV_destroy_touchscreen( SDL_evdevlist_item* item ) { + if( !item->is_touchscreen ) + return; + + SDL_DelTouch( item->fd ); + SDL_free( item->touchscreen_data->slots ); + SDL_free( item->touchscreen_data->name ); + SDL_free( item->touchscreen_data ); +} + static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item) { - /* TODO: get full state of device and report whatever is required */ + int i, ret; + struct input_absinfo abs_info; + /* + * struct input_mt_request_layout { + * __u32 code; + * __s32 values[num_slots]; + * }; + * + * this is the structure we're trying to emulate + */ + __u32* mt_req_code; + __s32* mt_req_values; + size_t mt_req_size; + + /* TODO: sync devices other than touchscreen */ + if( !item->is_touchscreen ) + return; + + mt_req_size = sizeof( *mt_req_code ) + + sizeof( *mt_req_values ) * item->touchscreen_data->max_slots; + + mt_req_code = SDL_calloc( 1, mt_req_size ); + if( mt_req_code == NULL ) { + SDL_Log( "Failed to sync device" ); + return; + } + + mt_req_values = (__s32*)mt_req_code + 1; + + *mt_req_code = ABS_MT_TRACKING_ID; + ret = ioctl( item->fd, EVIOCGMTSLOTS( mt_req_size ), mt_req_code ); + if( ret < 0 ) { + SDL_free( mt_req_code ); + SDL_Log( "Failed to sync device" ); + return; + } + for( i = 0; i < item->touchscreen_data->max_slots; i++ ) { + /* + * This doesn't account for the very edge case of the user removing their + * finger and replacing it on the screen during the time we're out of sync, + * which'll mean that we're not going from down -> up or up -> down, we're + * going from down -> down but with a different tracking id, meaning we'd + * have to tell SDL of the two events, but since we wait till SYN_REPORT in + * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't + * allow it. Lets just pray to God it doesn't happen. + */ + if( item->touchscreen_data->slots[i].tracking_id < 0 && + mt_req_values[i] >= 0 ) { + item->touchscreen_data->slots[i].tracking_id = mt_req_values[i]; + item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; + } else if( item->touchscreen_data->slots[i].tracking_id >= 0 && + mt_req_values[i] < 0 ) { + item->touchscreen_data->slots[i].tracking_id = -1; + item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP; + } + } + + *mt_req_code = ABS_MT_POSITION_X; + ret = ioctl( item->fd, EVIOCGMTSLOTS( mt_req_size ), mt_req_code ); + if( ret < 0 ) { + SDL_free( mt_req_code ); + SDL_Log( "Failed to sync device" ); + return; + } + for( i = 0; i < item->touchscreen_data->max_slots; i++ ) { + if( item->touchscreen_data->slots[i].tracking_id >= 0 && + item->touchscreen_data->slots[i].x != mt_req_values[i] ) { + item->touchscreen_data->slots[i].x = mt_req_values[i]; + if( item->touchscreen_data->slots[i].delta == + EVDEV_TOUCH_SLOTDELTA_NONE ) { + item->touchscreen_data->slots[i].delta = + EVDEV_TOUCH_SLOTDELTA_MOVE; + } + } + } + + *mt_req_code = ABS_MT_POSITION_Y; + ret = ioctl( item->fd, EVIOCGMTSLOTS( mt_req_size ), mt_req_code ); + if( ret < 0 ) { + SDL_free( mt_req_code ); + SDL_Log( "Failed to sync device" ); + return; + } + for( i = 0; i < item->touchscreen_data->max_slots; i++ ) { + if( item->touchscreen_data->slots[i].tracking_id >= 0 && + item->touchscreen_data->slots[i].y != mt_req_values[i] ) { + item->touchscreen_data->slots[i].y = mt_req_values[i]; + if( item->touchscreen_data->slots[i].delta == + EVDEV_TOUCH_SLOTDELTA_NONE ) { + item->touchscreen_data->slots[i].delta = + EVDEV_TOUCH_SLOTDELTA_MOVE; + } + } + } + + ret = ioctl( item->fd, EVIOCGABS( ABS_MT_SLOT ), &abs_info ); + if( ret < 0 ) { + SDL_free( mt_req_code ); + SDL_Log( "Failed to sync device" ); + return; + } + item->touchscreen_data->current_slot = abs_info.value; + + SDL_free( mt_req_code ); } #if SDL_USE_LIBUDEV static int -SDL_EVDEV_device_added(const char *devpath) +SDL_EVDEV_device_added(const char *dev_path, int udev_class) { + int ret; SDL_evdevlist_item *item; /* Check to make sure it's not already in list. */ for (item = _this->first; item != NULL; item = item->next) { - if (SDL_strcmp(devpath, item->path) == 0) { + if (SDL_strcmp(dev_path, item->path) == 0) { return -1; /* already have this one */ } } @@ -742,21 +797,28 @@ SDL_EVDEV_device_added(const char *devpath) return SDL_OutOfMemory(); } - item->fd = open(devpath, O_RDONLY, 0); + item->fd = open( dev_path, O_RDONLY | O_NONBLOCK ); if (item->fd < 0) { SDL_free(item); - return SDL_SetError("Unable to open %s", devpath); + return SDL_SetError("Unable to open %s", dev_path); } - item->path = SDL_strdup(devpath); + item->path = SDL_strdup(dev_path); if (item->path == NULL) { close(item->fd); SDL_free(item); return SDL_OutOfMemory(); } - /* Non blocking read mode */ - fcntl(item->fd, F_SETFL, O_NONBLOCK); + if( udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN ) { + item->is_touchscreen = 1; + + if( ( ret = SDL_EVDEV_init_touchscreen( item ) ) < 0 ) { + close(item->fd); + SDL_free(item); + return ret; + } + } if (_this->last == NULL) { _this->first = _this->last = item; @@ -767,19 +829,19 @@ SDL_EVDEV_device_added(const char *devpath) SDL_EVDEV_sync_device(item); - return _this->numdevices++; + return _this->num_devices++; } #endif /* SDL_USE_LIBUDEV */ static int -SDL_EVDEV_device_removed(const char *devpath) +SDL_EVDEV_device_removed(const char *dev_path) { SDL_evdevlist_item *item; SDL_evdevlist_item *prev = NULL; for (item = _this->first; item != NULL; item = item->next) { /* found it, remove it. */ - if (SDL_strcmp(devpath, item->path) == 0) { + if (SDL_strcmp(dev_path, item->path) == 0) { if (prev != NULL) { prev->next = item->next; } else { @@ -789,10 +851,13 @@ SDL_EVDEV_device_removed(const char *devpath) if (item == _this->last) { _this->last = prev; } + if( item->is_touchscreen ) { + SDL_EVDEV_destroy_touchscreen( item ); + } close(item->fd); SDL_free(item->path); SDL_free(item); - _this->numdevices--; + _this->num_devices--; return 0; } prev = item; @@ -805,4 +870,3 @@ SDL_EVDEV_device_removed(const char *devpath) #endif /* SDL_INPUT_LINUXEV */ /* vi: set ts=4 sw=4 expandtab: */ - diff --git a/src/core/linux/SDL_evdev.h b/src/core/linux/SDL_evdev.h index 989ced858..85b193864 100644 --- a/src/core/linux/SDL_evdev.h +++ b/src/core/linux/SDL_evdev.h @@ -27,31 +27,11 @@ #ifdef SDL_INPUT_LINUXEV #include "SDL_events.h" -#include - -typedef struct SDL_evdevlist_item -{ - char *path; - int fd; - struct SDL_evdevlist_item *next; -} SDL_evdevlist_item; - -typedef struct SDL_EVDEV_PrivateData -{ - SDL_evdevlist_item *first; - SDL_evdevlist_item *last; - int numdevices; - int ref_count; - int console_fd; - int kb_mode; - int tty; -} SDL_EVDEV_PrivateData; extern int SDL_EVDEV_Init(void); extern void SDL_EVDEV_Quit(void); extern void SDL_EVDEV_Poll(void); - #endif /* SDL_INPUT_LINUXEV */ #endif /* _SDL_evdev_h */ diff --git a/src/core/linux/SDL_udev.c b/src/core/linux/SDL_udev.c index 099cc435e..ae78ddd68 100644 --- a/src/core/linux/SDL_udev.c +++ b/src/core/linux/SDL_udev.c @@ -349,7 +349,9 @@ guess_device_class(struct udev_device *dev) } else if (test_bit(BTN_MOUSE, bitmask_key)) { devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */ } else if (test_bit(BTN_TOUCH, bitmask_key)) { - ; /* ID_INPUT_TOUCHSCREEN */ + /* TODO: better determining between touchscreen and multitouch touchpad, + see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */ + devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */ } if (test_bit(BTN_TRIGGER, bitmask_key) || @@ -411,6 +413,11 @@ device_event(SDL_UDEV_deviceevent type, struct udev_device *dev) if (val != NULL && SDL_strcmp(val, "1") == 0 ) { devclass |= SDL_UDEV_DEVICE_MOUSE; } + + val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"); + if (val != NULL && SDL_strcmp(val, "1") == 0 ) { + devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; + } /* The undocumented rule is: - All devices with keys get ID_INPUT_KEY diff --git a/src/core/linux/SDL_udev.h b/src/core/linux/SDL_udev.h index 2e4434e62..9ffbb3252 100644 --- a/src/core/linux/SDL_udev.h +++ b/src/core/linux/SDL_udev.h @@ -42,17 +42,19 @@ typedef enum { - SDL_UDEV_DEVICEADDED = 0x0001, + SDL_UDEV_DEVICEADDED = 1, SDL_UDEV_DEVICEREMOVED } SDL_UDEV_deviceevent; /* A device can be any combination of these classes */ typedef enum { + SDL_UDEV_DEVICE_UNKNOWN = 0x0000, SDL_UDEV_DEVICE_MOUSE = 0x0001, SDL_UDEV_DEVICE_KEYBOARD = 0x0002, SDL_UDEV_DEVICE_JOYSTICK = 0x0004, - SDL_UDEV_DEVICE_SOUND = 0x0008 + SDL_UDEV_DEVICE_SOUND = 0x0008, + SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010 } SDL_UDEV_deviceclass; typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);