| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | * The evtest application is also useful to debug the protocol |
| | | */ |
| | | |
| | | |
| | | #include "SDL_evdev.h" |
| | | #define _THIS SDL_EVDEV_PrivateData *_this |
| | | static _THIS = NULL; |
| | | #include "SDL_evdev_kbd.h" |
| | | |
| | | #include <sys/stat.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <sys/ioctl.h> |
| | | #include <limits.h> /* For the definition of PATH_MAX */ |
| | | #include <linux/input.h> |
| | | #ifdef SDL_INPUT_LINUXKD |
| | | #include <linux/kd.h> |
| | | #include <linux/keyboard.h> |
| | | #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 |
| | | #endif |
| | | |
| | | #include "SDL.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_endian.h" |
| | | #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 */ |
| | | #include "../../core/linux/SDL_evdev_capabilities.h" |
| | | #include "../../core/linux/SDL_udev.h" |
| | | |
| | | /* This isn't defined in older Linux kernel headers */ |
| | | /* These are not defined in older Linux kernel headers */ |
| | | #ifndef SYN_DROPPED |
| | | #define SYN_DROPPED 3 |
| | | #endif |
| | | #ifndef ABS_MT_SLOT |
| | | #define ABS_MT_SLOT 0x2f |
| | | #define ABS_MT_POSITION_X 0x35 |
| | | #define ABS_MT_POSITION_Y 0x36 |
| | | #define ABS_MT_TRACKING_ID 0x39 |
| | | #define ABS_MT_PRESSURE 0x3a |
| | | #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 min_pressure, max_pressure, range_pressure; |
| | | |
| | | 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, pressure; |
| | | } * slots; |
| | | |
| | | } * touchscreen_data; |
| | | |
| | | struct SDL_evdevlist_item *next; |
| | | } SDL_evdevlist_item; |
| | | |
| | | typedef struct SDL_EVDEV_PrivateData |
| | | { |
| | | int ref_count; |
| | | int num_devices; |
| | | SDL_evdevlist_item *first; |
| | | SDL_evdevlist_item *last; |
| | | SDL_EVDEV_keyboard_state *kbd; |
| | | } SDL_EVDEV_PrivateData; |
| | | |
| | | #undef _THIS |
| | | #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); |
| | | static 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_X2 + 3 /* BTN_TASK 0x117 */ |
| | | }; |
| | | |
| | | static const 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) |
| | | static int |
| | | SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled) |
| | | { |
| | | 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; |
| | | /* Mice already send relative events through this interface */ |
| | | return 0; |
| | | } |
| | | |
| | | /* Prevent keystrokes from reaching the tty */ |
| | | static int SDL_EVDEV_mute_keyboard(int tty, int *kb_mode) |
| | | { |
| | | char arg; |
| | | |
| | | *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"); |
| | | } |
| | | 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"); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* Restore the keyboard mode for given tty */ |
| | | static void SDL_EVDEV_unmute_keyboard(int tty, int kb_mode) |
| | | { |
| | | if (ioctl(tty, KDSKBMUTE, 0) && ioctl(tty, KDSKBMODE, kb_mode)) { |
| | | SDL_Log("EVDEV: Failed restoring keyboard mode"); |
| | | } |
| | | } |
| | | |
| | | /* 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; |
| | | |
| | | fd = open("/sys/class/tty/tty0/active", O_RDONLY); |
| | | if (fd < 0) { |
| | | return SDL_SetError("Could not determine which tty is active"); |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | |
| | | int |
| | | SDL_EVDEV_Init(void) |
| | | { |
| | | int retval = 0; |
| | | |
| | | if (_this == NULL) { |
| | | |
| | | _this = (SDL_EVDEV_PrivateData *) SDL_calloc(1, sizeof(*_this)); |
| | | if(_this == NULL) { |
| | | _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this)); |
| | | if (_this == NULL) { |
| | | return SDL_OutOfMemory(); |
| | | } |
| | | |
| | |
| | | |
| | | /* 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; |
| | | } |
| | | |
| | | |
| | | /* Force a scan to build the initial device list */ |
| | | SDL_UDEV_Scan(); |
| | | #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(); |
| | | |
| | | /* 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; |
| | | } |
| | | } |
| | | } |
| | | |
| | | _this->kbd = SDL_EVDEV_kbd_init(); |
| | | } |
| | | |
| | | |
| | | SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode; |
| | | |
| | | _this->ref_count += 1; |
| | | |
| | | return retval; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void |
| | |
| | | if (_this == NULL) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | _this->ref_count -= 1; |
| | | |
| | | |
| | | if (_this->ref_count < 1) { |
| | | |
| | | #if SDL_USE_LIBUDEV |
| | | 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); |
| | | } |
| | | |
| | | |
| | | SDL_EVDEV_kbd_quit(_this->kbd); |
| | | |
| | | /* Remove existing devices */ |
| | | while(_this->first != NULL) { |
| | | 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->num_devices == 0); |
| | | |
| | | 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) |
| | | static 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 */ |
| | | |
| | | 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, norm_pressure; |
| | | |
| | | if (!_this) { |
| | | return; |
| | |
| | | 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)) { |
| | |
| | | break; |
| | | } |
| | | |
| | | /* BTH_TOUCH event value 1 indicates there is contact with |
| | | a touchscreen or trackpad (earlist finger's current |
| | | position is sent in EV_ABS ABS_X/ABS_Y, switching to |
| | | next finger after earlist is released) */ |
| | | if (item->is_touchscreen && events[i].code == BTN_TOUCH) { |
| | | if (item->touchscreen_data->max_slots == 1) { |
| | | if (events[i].value) |
| | | item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; |
| | | else |
| | | item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | /* Probably keyboard */ |
| | | 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 */) { |
| | | } 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 /* SDL_INPUT_LINUXKD */ |
| | | } |
| | | } |
| | | SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value); |
| | | 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_MT_PRESSURE: |
| | | if (!item->is_touchscreen) /* FIXME: temp hack */ |
| | | break; |
| | | item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = 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: |
| | | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y); |
| | | if (item->is_touchscreen) { |
| | | if (item->touchscreen_data->max_slots != 1) |
| | | break; |
| | | item->touchscreen_data->slots[0].x = events[i].value; |
| | | } else |
| | | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y); |
| | | break; |
| | | case ABS_Y: |
| | | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value); |
| | | if (item->is_touchscreen) { |
| | | if (item->touchscreen_data->max_slots != 1) |
| | | break; |
| | | item->touchscreen_data->slots[0].y = events[i].value; |
| | | } else |
| | | SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value); |
| | | break; |
| | | default: |
| | | break; |
| | |
| | | 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; |
| | | |
| | | if (item->touchscreen_data->range_pressure > 0) { |
| | | norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) / |
| | | (float)item->touchscreen_data->range_pressure; |
| | | } else { |
| | | /* This touchscreen does not support pressure */ |
| | | norm_pressure = 1.0f; |
| | | } |
| | | |
| | | /* FIXME: the touch's window shouldn't be null, but |
| | | * the coordinate space of touch positions needs to |
| | | * be window-relative in that case. */ |
| | | switch(item->touchscreen_data->slots[j].delta) { |
| | | case EVDEV_TOUCH_SLOTDELTA_DOWN: |
| | | SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure); |
| | | 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, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure); |
| | | 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, NULL, norm_x, norm_y, norm_pressure); |
| | | 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: |
| | |
| | | { |
| | | SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; |
| | | |
| | | if (keycode < SDL_arraysize(EVDEV_Keycodes)) { |
| | | scancode = EVDEV_Keycodes[keycode]; |
| | | if (keycode < SDL_arraysize(linux_scancode_table)) { |
| | | scancode = linux_scancode_table[keycode]; |
| | | |
| | | if (scancode == SDL_SCANCODE_UNKNOWN) { |
| | | /* BTN_TOUCH is handled elsewhere, but we might still end up here if |
| | | you get an unexpected BTN_TOUCH from something SDL believes is not |
| | | a touch device. In this case, we'd rather not get a misleading |
| | | SDL_Log message about an unknown key. */ |
| | | if (keycode != BTN_TOUCH) { |
| | | SDL_Log("The key you just pressed is not recognized by SDL. To help " |
| | | "get this fixed, please report this to the SDL forums/mailing list " |
| | | "<https://discourse.libsdl.org/> EVDEV KeyCode %d", 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 <sdl@libsdl.org> EVDEV KeyCode %d \n", keycode); |
| | | } |
| | | |
| | | return scancode; |
| | | } |
| | | |
| | | #ifdef SDL_USE_LIBUDEV |
| | | static int |
| | | SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item) |
| | | { |
| | | int ret, i; |
| | | unsigned long xreq, yreq; |
| | | 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_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"); |
| | | } |
| | | |
| | | if (abs_info.maximum == 0) { |
| | | item->touchscreen_data->max_slots = 1; |
| | | xreq = EVIOCGABS(ABS_X); |
| | | yreq = EVIOCGABS(ABS_Y); |
| | | } else { |
| | | item->touchscreen_data->max_slots = abs_info.maximum + 1; |
| | | xreq = EVIOCGABS(ABS_MT_POSITION_X); |
| | | yreq = EVIOCGABS(ABS_MT_POSITION_Y); |
| | | } |
| | | |
| | | ret = ioctl(item->fd, xreq, &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, yreq, &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_PRESSURE), &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_pressure = abs_info.minimum; |
| | | item->touchscreen_data->max_pressure = abs_info.maximum; |
| | | item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum; |
| | | |
| | | 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 */ |
| | | SDL_TOUCH_DEVICE_DIRECT, |
| | | 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; |
| | | } |
| | | #endif /* SDL_USE_LIBUDEV */ |
| | | |
| | | 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 */ |
| | | #ifdef EVIOCGMTSLOTS |
| | | 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 |
| | | */ |
| | | Uint32* mt_req_code; |
| | | Sint32* 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) { |
| | | return; |
| | | } |
| | | |
| | | mt_req_values = (Sint32*)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); |
| | | 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); |
| | | 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); |
| | | 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; |
| | | } |
| | | } |
| | | } |
| | | |
| | | *mt_req_code = ABS_MT_PRESSURE; |
| | | ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); |
| | | if (ret < 0) { |
| | | SDL_free(mt_req_code); |
| | | 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].pressure != mt_req_values[i]) { |
| | | item->touchscreen_data->slots[i].pressure = 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); |
| | | return; |
| | | } |
| | | item->touchscreen_data->current_slot = abs_info.value; |
| | | |
| | | SDL_free(mt_req_code); |
| | | |
| | | #endif /* EVIOCGMTSLOTS */ |
| | | } |
| | | |
| | | #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 */ |
| | | } |
| | | } |
| | | |
| | | |
| | | item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item)); |
| | | if (item == NULL) { |
| | | 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; |
| | | } else { |
| | | _this->last->next = item; |
| | | _this->last = item; |
| | | } |
| | | |
| | | |
| | | 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 { |
| | |
| | | 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; |
| | |
| | | #endif /* SDL_INPUT_LINUXEV */ |
| | | |
| | | /* vi: set ts=4 sw=4 expandtab: */ |
| | | |