Fix build on GCC 4.9
[lsnes.git] / src / platform / evdev / joystick.cpp
blob17e2bc2313c0111af3b49dd43174b2e9525a7bdf
1 #include "core/command.hpp"
2 #include "core/joystickapi.hpp"
3 #include "core/keymapper.hpp"
4 #include "core/messages.hpp"
5 #include "library/minmax.hpp"
6 #include "library/string.hpp"
8 #include <unistd.h>
9 #include <map>
10 #include <dirent.h>
11 #include <set>
12 #include <string>
13 #include <cstring>
14 #include <cctype>
15 #include <sstream>
16 #include <cerrno>
17 #include <fcntl.h>
18 #include <cstdint>
19 extern "C"
21 #include <linux/input.h>
24 namespace
26 const char* axisnames[64] = {
27 "X", "Y", "Z", "RX", "RY", "RZ", "THROTTLE", "RUDDER", "WHEEL", "GAS", "BRAKE", "Unknown axis #11",
28 "Unknown axis #12", "Unknown axis #13", "Unknown axis #14", "Unknown axis #15", "HAT0X", "HAT0Y",
29 "HAT1X", "HAT1Y", "HAT2X", "HAT2Y", "HAT3X", "HAT3Y", "PRESSURE", "DISTANCE", "TILT_X", "TILT_Y",
30 "TOOL_WIDTH", "Unknown axis #29", "Unknown axis #30", "Unknown axis #31", "VOLUME", "Unknown axis #33",
31 "Unknown axis #34", "Unknown axis #35", "Unknown axis #36", "Unknown axis #37", "Unknown axis #38",
32 "Unknown axis #39", "MISC", "Unknown axis #41", "Unknown axis #42", "Unknown axis #43",
33 "Unknown axis #44", "Unknown axis #45", "Unknown axis #46", "MT_SLOT", "MT_TOUCH_MAJOR",
34 "MT_TOUCH_MINOR", "MT_WIDTH_MAJOR", "MT_WIDTH_MINOR", "MT_ORIENTATION", "MT_POSITION_X",
35 "MT_POSITION_Y", "MT_TOOL_TYPE", "MT_BLOB_ID", "MT_TRACKING_ID", "MT_PRESSURE", "MT_DISTANCE",
36 "Unknown axis #60", "Unknown axis #61", "Unknown axis #62", "Unknown axis #63"
38 const char* buttonnames[768] = {
39 "RESERVED", "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "MINUS", "EQUAL", "BACKSPACE",
40 "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "LEFTBRACE", "RIGHTBRACE", "ENTER",
41 "LEFTCTRL", "A", "S", "D", "F", "G", "H", "J", "K", "L", "SEMICOLON", "APOSTROPHE", "GRAVE",
42 "LEFTSHIFT", "BACKSLASH", "Z", "X", "C", "V", "B", "N", "M", "COMMA", "DOT", "SLASH", "RIGHTSHIFT",
43 "KPASTERISK", "LEFTALT", "SPACE", "CAPSLOCK", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
44 "F10", "NUMLOCK", "SCROLLLOCK", "KP7", "KP8", "KP9", "KPMINUS", "KP4", "KP5", "KP6", "KPPLUS", "KP1",
45 "KP2", "KP3", "KP0", "KPDOT", "Unknown button #84", "ZENKAKUHANKAKU", "102ND", "F11", "F12", "RO",
46 "KATAKANA", "HIRAGANA", "HENKAN", "KATAKANAHIRAGANA", "MUHENKAN", "KPJPCOMMA", "KPENTER", "RIGHTCTRL",
47 "KPSLASH", "SYSRQ", "RIGHTALT", "LINEFEED", "HOME", "UP", "PAGEUP", "LEFT", "RIGHT", "END", "DOWN",
48 "PAGEDOWN", "INSERT", "DELETE", "MACRO", "MUTE", "VOLUMEDOWN", "VOLUMEUP", "POWER", "KPEQUAL",
49 "KPPLUSMINUS", "PAUSE", "SCALE", "KPCOMMA", "HANGUEL", "HANJA", "YEN", "LEFTMETA", "RIGHTMETA",
50 "COMPOSE", "STOP", "AGAIN", "PROPS", "UNDO", "FRONT", "COPY", "OPEN", "PASTE", "FIND", "CUT", "HELP",
51 "MENU", "CALC", "SETUP", "SLEEP", "WAKEUP", "FILE", "SENDFILE", "DELETEFILE", "XFER", "PROG1", "PROG2",
52 "WWW", "MSDOS", "SCREENLOCK", "DIRECTION", "CYCLEWINDOWS", "MAIL", "BOOKMARKS", "COMPUTER", "BACK",
53 "FORWARD", "CLOSECD", "EJECTCD", "EJECTCLOSECD", "NEXTSONG", "PLAYPAUSE", "PREVIOUSSONG", "STOPCD",
54 "RECORD", "REWIND", "PHONE", "ISO", "CONFIG", "HOMEPAGE", "REFRESH", "EXIT", "MOVE", "EDIT",
55 "SCROLLUP", "SCROLLDOWN", "KPLEFTPAREN", "KPRIGHTPAREN", "NEW", "REDO", "F13", "F14", "F15", "F16",
56 "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "Unknown button #195", "Unknown button #196",
57 "Unknown button #197", "Unknown button #198", "Unknown button #199", "PLAYCD", "PAUSECD", "PROG3",
58 "PROG4", "DASHBOARD", "SUSPEND", "CLOSE", "PLAY", "FASTFORWARD", "BASSBOOST", "PRINT", "HP", "CAMERA",
59 "SOUND", "QUESTION", "EMAIL", "CHAT", "SEARCH", "CONNECT", "FINANCE", "SPORT", "SHOP", "ALTERASE",
60 "CANCEL", "BRIGHTNESSDOWN", "BRIGHTNESSUP", "MEDIA", "SWITCHVIDEOMODE", "KBDILLUMTOGGLE",
61 "KBDILLUMDOWN", "KBDILLUMUP", "SEND", "REPLY", "FORWARDMAIL", "SAVE", "DOCUMENTS", "BATTERY",
62 "BLUETOOTH", "WLAN", "UWB", "UNKNOWN", "VIDEO_NEXT", "VIDEO_PREV", "BRIGHTNESS_CYCLE",
63 "BRIGHTNESS_ZERO", "DISPLAY_OFF", "WIMAX", "RFKILL", "MICMUTE", "Unknown button #249",
64 "Unknown button #250", "Unknown button #251", "Unknown button #252", "Unknown button #253",
65 "Unknown button #254", "Unknown button #255", "Button 0", "Button 1", "Button 2", "Button 3",
66 "Button 4", "Button 5", "Button 6", "Button 7", "Button 8", "Button 9", "Unknown button #266",
67 "Unknown button #267", "Unknown button #268", "Unknown button #269", "Unknown button #270",
68 "Unknown button #271", "Button LEFT", "Button RIGHT", "Button MIDDLE", "Button SIDE", "Button EXTRA",
69 "Button FORWARD", "Button BACK", "Button TASK", "Unknown button #280", "Unknown button #281",
70 "Unknown button #282", "Unknown button #283", "Unknown button #284", "Unknown button #285",
71 "Unknown button #286", "Unknown button #287", "Button TRIGGER", "Button THUMB", "Button THUMB2",
72 "Button TOP", "Button TOP2", "Button PINKIE", "Button BASE", "Button BASE2", "Button BASE3",
73 "Button BASE4", "Button BASE5", "Button BASE6", "Unknown button #300", "Unknown button #301",
74 "Unknown button #302", "Button DEAD", "Button A", "Button B", "Button C", "Button X", "Button Y",
75 "Button Z", "Button TL", "Button TR", "Button TL2", "Button TR2", "Button SELECT", "Button START",
76 "Button MODE", "Button THUMBL", "Button THUMBR", "Unknown button #319", "Button TOOL_PEN",
77 "Button TOOL_RUBBER", "Button TOOL_BRUSH", "Button TOOL_PENCIL", "Button TOOL_AIRBRUSH",
78 "Button TOOL_FINGER", "Button TOOL_MOUSE", "Button TOOL_LENS", "Button TOOL_QUINTTAP",
79 "Unknown button #329", "Button TOUCH", "Button STYLUS", "Button STYLUS2", "Button TOOL_DOUBLETAP",
80 "Button TOOL_TRIPLETAP", "Button TOOL_QUADTAP", "Button GEAR_DOWN", "Button GEAR_UP",
81 "Unknown button #338", "Unknown button #339", "Unknown button #340", "Unknown button #341",
82 "Unknown button #342", "Unknown button #343", "Unknown button #344", "Unknown button #345",
83 "Unknown button #346", "Unknown button #347", "Unknown button #348", "Unknown button #349",
84 "Unknown button #350", "Unknown button #351", "OK", "SELECT", "GOTO", "CLEAR", "POWER2", "OPTION",
85 "INFO", "TIME", "VENDOR", "ARCHIVE", "PROGRAM", "CHANNEL", "FAVORITES", "EPG", "PVR", "MHP",
86 "LANGUAGE", "TITLE", "SUBTITLE", "ANGLE", "ZOOM", "MODE", "KEYBOARD", "SCREEN", "PC", "TV", "TV2",
87 "VCR", "VCR2", "SAT", "SAT2", "CD", "TAPE", "RADIO", "TUNER", "PLAYER", "TEXT", "DVD", "AUX", "MP3",
88 "AUDIO", "VIDEO", "DIRECTORY", "LIST", "MEMO", "CALENDAR", "RED", "GREEN", "YELLOW", "BLUE",
89 "CHANNELUP", "CHANNELDOWN", "FIRST", "LAST", "AB", "NEXT", "RESTART", "SLOW", "SHUFFLE", "BREAK",
90 "PREVIOUS", "DIGITS", "TEEN", "TWEN", "VIDEOPHONE", "GAMES", "ZOOMIN", "ZOOMOUT", "ZOOMRESET",
91 "WORDPROCESSOR", "EDITOR", "SPREADSHEET", "GRAPHICSEDITOR", "PRESENTATION", "DATABASE", "NEWS",
92 "VOICEMAIL", "ADDRESSBOOK", "MESSENGER", "DISPLAYTOGGLE", "SPELLCHECK", "LOGOFF", "DOLLAR", "EURO",
93 "FRAMEBACK", "FRAMEFORWARD", "CONTEXT_MENU", "MEDIA_REPEAT", "10CHANNELSUP", "10CHANNELSDOWN",
94 "IMAGES", "Unknown button #443", "Unknown button #444", "Unknown button #445", "Unknown button #446",
95 "Unknown button #447", "DEL_EOL", "DEL_EOS", "INS_LINE", "DEL_LINE", "Unknown button #452",
96 "Unknown button #453", "Unknown button #454", "Unknown button #455", "Unknown button #456",
97 "Unknown button #457", "Unknown button #458", "Unknown button #459", "Unknown button #460",
98 "Unknown button #461", "Unknown button #462", "Unknown button #463", "FN", "FN_ESC", "FN_F1", "FN_F2",
99 "FN_F3", "FN_F4", "FN_F5", "FN_F6", "FN_F7", "FN_F8", "FN_F9", "FN_F10", "FN_F11", "FN_F12", "FN_1",
100 "FN_2", "FN_D", "FN_E", "FN_F", "FN_S", "FN_B", "Unknown button #485", "Unknown button #486",
101 "Unknown button #487", "Unknown button #488", "Unknown button #489", "Unknown button #490",
102 "Unknown button #491", "Unknown button #492", "Unknown button #493", "Unknown button #494",
103 "Unknown button #495", "Unknown button #496", "BRL_DOT1", "BRL_DOT2", "BRL_DOT3", "BRL_DOT4",
104 "BRL_DOT5", "BRL_DOT6", "BRL_DOT7", "BRL_DOT8", "BRL_DOT9", "BRL_DOT10", "Unknown button #507",
105 "Unknown button #508", "Unknown button #509", "Unknown button #510", "Unknown button #511",
106 "NUMERIC_0", "NUMERIC_1", "NUMERIC_2", "NUMERIC_3", "NUMERIC_4", "NUMERIC_5", "NUMERIC_6", "NUMERIC_7",
107 "NUMERIC_8", "NUMERIC_9", "NUMERIC_STAR", "NUMERIC_POUND", "Unknown button #524",
108 "Unknown button #525", "Unknown button #526", "Unknown button #527", "CAMERA_FOCUS", "WPS_BUTTON",
109 "TOUCHPAD_TOGGLE", "TOUCHPAD_ON", "TOUCHPAD_OFF", "CAMERA_ZOOMIN", "CAMERA_ZOOMOUT", "CAMERA_UP",
110 "CAMERA_DOWN", "CAMERA_LEFT", "CAMERA_RIGHT", "Unknown button #539", "Unknown button #540",
111 "Unknown button #541", "Unknown button #542", "Unknown button #543", "Unknown button #544",
112 "Unknown button #545", "Unknown button #546", "Unknown button #547", "Unknown button #548",
113 "Unknown button #549", "Unknown button #550", "Unknown button #551", "Unknown button #552",
114 "Unknown button #553", "Unknown button #554", "Unknown button #555", "Unknown button #556",
115 "Unknown button #557", "Unknown button #558", "Unknown button #559", "Unknown button #560",
116 "Unknown button #561", "Unknown button #562", "Unknown button #563", "Unknown button #564",
117 "Unknown button #565", "Unknown button #566", "Unknown button #567", "Unknown button #568",
118 "Unknown button #569", "Unknown button #570", "Unknown button #571", "Unknown button #572",
119 "Unknown button #573", "Unknown button #574", "Unknown button #575", "Unknown button #576",
120 "Unknown button #577", "Unknown button #578", "Unknown button #579", "Unknown button #580",
121 "Unknown button #581", "Unknown button #582", "Unknown button #583", "Unknown button #584",
122 "Unknown button #585", "Unknown button #586", "Unknown button #587", "Unknown button #588",
123 "Unknown button #589", "Unknown button #590", "Unknown button #591", "Unknown button #592",
124 "Unknown button #593", "Unknown button #594", "Unknown button #595", "Unknown button #596",
125 "Unknown button #597", "Unknown button #598", "Unknown button #599", "Unknown button #600",
126 "Unknown button #601", "Unknown button #602", "Unknown button #603", "Unknown button #604",
127 "Unknown button #605", "Unknown button #606", "Unknown button #607", "Unknown button #608",
128 "Unknown button #609", "Unknown button #610", "Unknown button #611", "Unknown button #612",
129 "Unknown button #613", "Unknown button #614", "Unknown button #615", "Unknown button #616",
130 "Unknown button #617", "Unknown button #618", "Unknown button #619", "Unknown button #620",
131 "Unknown button #621", "Unknown button #622", "Unknown button #623", "Unknown button #624",
132 "Unknown button #625", "Unknown button #626", "Unknown button #627", "Unknown button #628",
133 "Unknown button #629", "Unknown button #630", "Unknown button #631", "Unknown button #632",
134 "Unknown button #633", "Unknown button #634", "Unknown button #635", "Unknown button #636",
135 "Unknown button #637", "Unknown button #638", "Unknown button #639", "Unknown button #640",
136 "Unknown button #641", "Unknown button #642", "Unknown button #643", "Unknown button #644",
137 "Unknown button #645", "Unknown button #646", "Unknown button #647", "Unknown button #648",
138 "Unknown button #649", "Unknown button #650", "Unknown button #651", "Unknown button #652",
139 "Unknown button #653", "Unknown button #654", "Unknown button #655", "Unknown button #656",
140 "Unknown button #657", "Unknown button #658", "Unknown button #659", "Unknown button #660",
141 "Unknown button #661", "Unknown button #662", "Unknown button #663", "Unknown button #664",
142 "Unknown button #665", "Unknown button #666", "Unknown button #667", "Unknown button #668",
143 "Unknown button #669", "Unknown button #670", "Unknown button #671", "Unknown button #672",
144 "Unknown button #673", "Unknown button #674", "Unknown button #675", "Unknown button #676",
145 "Unknown button #677", "Unknown button #678", "Unknown button #679", "Unknown button #680",
146 "Unknown button #681", "Unknown button #682", "Unknown button #683", "Unknown button #684",
147 "Unknown button #685", "Unknown button #686", "Unknown button #687", "Unknown button #688",
148 "Unknown button #689", "Unknown button #690", "Unknown button #691", "Unknown button #692",
149 "Unknown button #693", "Unknown button #694", "Unknown button #695", "Unknown button #696",
150 "Unknown button #697", "Unknown button #698", "Unknown button #699", "Unknown button #700",
151 "Unknown button #701", "Unknown button #702", "Unknown button #703", "Button TRIGGER_HAPPY1",
152 "Button TRIGGER_HAPPY2", "Button TRIGGER_HAPPY3", "Button TRIGGER_HAPPY4", "Button TRIGGER_HAPPY5",
153 "Button TRIGGER_HAPPY6", "Button TRIGGER_HAPPY7", "Button TRIGGER_HAPPY8", "Button TRIGGER_HAPPY9",
154 "Button TRIGGER_HAPPY10", "Button TRIGGER_HAPPY11", "Button TRIGGER_HAPPY12", "Button TRIGGER_HAPPY13",
155 "Button TRIGGER_HAPPY14", "Button TRIGGER_HAPPY15", "Button TRIGGER_HAPPY16", "Button TRIGGER_HAPPY17",
156 "Button TRIGGER_HAPPY18", "Button TRIGGER_HAPPY19", "Button TRIGGER_HAPPY20", "Button TRIGGER_HAPPY21",
157 "Button TRIGGER_HAPPY22", "Button TRIGGER_HAPPY23", "Button TRIGGER_HAPPY24", "Button TRIGGER_HAPPY25",
158 "Button TRIGGER_HAPPY26", "Button TRIGGER_HAPPY27", "Button TRIGGER_HAPPY28", "Button TRIGGER_HAPPY29",
159 "Button TRIGGER_HAPPY30", "Button TRIGGER_HAPPY31", "Button TRIGGER_HAPPY32", "Button TRIGGER_HAPPY33",
160 "Button TRIGGER_HAPPY34", "Button TRIGGER_HAPPY35", "Button TRIGGER_HAPPY36", "Button TRIGGER_HAPPY37",
161 "Button TRIGGER_HAPPY38", "Button TRIGGER_HAPPY39", "Button TRIGGER_HAPPY40", "Unknown button #744",
162 "Unknown button #745", "Unknown button #746", "Unknown button #747", "Unknown button #748",
163 "Unknown button #749", "Unknown button #750", "Unknown button #751", "Unknown button #752",
164 "Unknown button #753", "Unknown button #754", "Unknown button #755", "Unknown button #756",
165 "Unknown button #757", "Unknown button #758", "Unknown button #759", "Unknown button #760",
166 "Unknown button #761", "Unknown button #762", "Unknown button #763", "Unknown button #764",
167 "Unknown button #765", "Unknown button #766", "Unknown button #767"
170 std::map<int, unsigned> gamepad_map;
172 bool read_one_input_event(int fd)
174 struct input_event ev;
175 int r = read(fd, &ev, sizeof(ev));
176 if(r < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
177 return false;
178 if(r < 0) {
179 messages << "Error reading from joystick (fd=" << fd << "): " << strerror(errno)
180 << std::endl;
181 return false;
183 if(ev.type == EV_KEY)
184 lsnes_gamepads[gamepad_map[fd]].report_button(ev.code, ev.value != 0);
185 if(ev.type == EV_ABS)
186 lsnes_gamepads[gamepad_map[fd]].report_axis(ev.code, ev.value);
187 return true;
190 bool probe_joystick(int fd, const std::string& filename)
192 const size_t div = 8 * sizeof(unsigned long);
193 unsigned long keys[(KEY_MAX + div) / div] = {0};
194 unsigned long axes[(ABS_MAX + div) / div] = {0};
195 unsigned long evtypes[(EV_MAX + div) / div] = {0};
196 char namebuffer[256];
197 if(ioctl(fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) {
198 int merrno = errno;
199 messages << "Error probing joystick (evmap; " << filename << "): " << strerror(merrno)
200 << std::endl;
201 return false;
203 if(!(evtypes[EV_KEY / div] & (1 << EV_KEY % div)) || !(evtypes[EV_ABS / div] & (1 << EV_ABS % div))) {
204 messages << "Input (" << filename << ") doesn't look like joystick" << std::endl;
205 return false;
207 if(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys) < 0) {
208 int merrno = errno;
209 messages << "Error probing joystick (keymap; " <<filename << "): " << strerror(merrno)
210 << std::endl;
211 return false;
213 if(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes) < 0) {
214 int merrno = errno;
215 messages << "Error probing joystick (axismap; " << filename << "): " << strerror(merrno)
216 << std::endl;
217 return false;
219 if(ioctl(fd, EVIOCGNAME(sizeof(namebuffer)), namebuffer) <= 0) {
220 int merrno = errno;
221 messages << "Error probing joystick (name; " << filename << "): " << strerror(merrno)
222 << std::endl;
223 return false;
225 unsigned jid = gamepad_map[fd] = lsnes_gamepads.add(namebuffer);
226 gamepad::pad& ngp = lsnes_gamepads[jid];
227 for(unsigned i = 0; i <= KEY_MAX; i++)
228 if(keys[i / div] & (1ULL << (i % div)))
229 ngp.add_button(i, buttonnames[i]);
230 for(unsigned i = 0; i <= ABS_MAX; i++)
231 if(axes[i / div] & (1ULL << (i % div))) {
232 if(i < ABS_HAT0X || i > ABS_HAT3Y) {
233 int32_t V[5];
234 if(ioctl(fd, EVIOCGABS(i), V) < 0) {
235 int merrno = errno;
236 messages << "Error getting parameters for axis " << i << " (fd="
237 << fd << "): " << strerror(merrno) << std::endl;
238 continue;
240 ngp.add_axis(i, V[1], V[2], V[1] == 0, axisnames[i]);
241 } else if(i % 2 == 0)
242 ngp.add_hat(i, i + 1, 1, axisnames[i], axisnames[i + 1]);
244 messages << "Joystick #" << jid << " online: " << namebuffer << std::endl;
245 return true;
248 void probe_all_joysticks()
250 DIR* d = opendir("/dev/input");
251 struct dirent* dentry;
252 if(!d) {
253 int merrno = errno;
254 messages << "Can't list /dev/input: " << strerror(merrno) << std::endl;
255 return;
257 while((dentry = readdir(d)) != NULL) {
258 if(strlen(dentry->d_name) < 6)
259 continue;
260 if(strncmp(dentry->d_name, "event", 5))
261 continue;
262 for(size_t i = 5; dentry->d_name[i]; i++)
263 if(!isdigit(static_cast<uint8_t>(dentry->d_name[i])))
264 continue;
265 std::string filename = std::string("/dev/input/") + dentry->d_name;
266 int r = open(filename.c_str(), O_RDONLY | O_NONBLOCK);
267 if(r < 0)
268 continue;
269 if(!probe_joystick(r, filename))
270 close(r);
272 closedir(d);
274 volatile bool quit_signaled = false;
275 volatile bool quit_ack = false;
277 #define POLL_WAIT 50000
279 void do_fd_zero(fd_set& s)
281 FD_ZERO(&s);
284 struct _joystick_driver drv = {
285 .init = []() -> void {
286 probe_all_joysticks();
287 quit_ack = quit_signaled = false;
289 .quit = []() -> void {
290 quit_signaled = true;
291 while(!quit_ack);
293 .thread_fn = []() -> void {
294 while(!quit_signaled) {
295 fd_set rfds;
296 do_fd_zero(rfds);
297 int limit = 0;
298 for(auto fd : gamepad_map) {
299 limit = max(limit, fd.first + 1);
300 FD_SET(fd.first, &rfds);
302 if(!limit) {
303 usleep(POLL_WAIT);
304 continue;
306 struct timeval tv;
307 tv.tv_sec = 0;
308 tv.tv_usec = POLL_WAIT;
309 int r = select(limit, &rfds, NULL, NULL, &tv);
310 if(r <= 0)
311 continue;
312 for(auto fd : gamepad_map)
313 if(FD_ISSET(fd.first, &rfds))
314 while(read_one_input_event(fd.first));
316 quit_ack = true;
318 .signal = []() -> void {
319 quit_signaled = true;
320 while(!quit_ack);
322 .name = []() -> const char* { return "Evdev joystick plugin"; }
324 struct joystick_driver _drv(drv);