Reinitialize gamepads command and fix EVDEV going bonkers on gamepad suddenly disconn...
[lsnes.git] / src / platform / evdev / joystick.cpp
blob755a8a900a6c035605e167d4c99e06f044932839
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 int 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 0;
178 if(r < 0) {
179 if(errno == ENODEV)
180 return -1; //Disconnected.
181 messages << "Error reading from joystick (fd=" << fd << "): " << strerror(errno)
182 << std::endl;
183 return 0;
185 if(ev.type == EV_KEY)
186 lsnes_gamepads[gamepad_map[fd]].report_button(ev.code, ev.value != 0);
187 if(ev.type == EV_ABS)
188 lsnes_gamepads[gamepad_map[fd]].report_axis(ev.code, ev.value);
189 return 1;
192 bool probe_joystick(int fd, const std::string& filename)
194 const size_t div = 8 * sizeof(unsigned long);
195 unsigned long keys[(KEY_MAX + div) / div] = {0};
196 unsigned long axes[(ABS_MAX + div) / div] = {0};
197 unsigned long evtypes[(EV_MAX + div) / div] = {0};
198 char namebuffer[256];
199 if(ioctl(fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) {
200 int merrno = errno;
201 messages << "Error probing joystick (evmap; " << filename << "): " << strerror(merrno)
202 << std::endl;
203 return false;
205 if(!(evtypes[EV_KEY / div] & (1 << EV_KEY % div)) || !(evtypes[EV_ABS / div] & (1 << EV_ABS % div))) {
206 messages << "Input (" << filename << ") doesn't look like joystick" << std::endl;
207 return false;
209 if(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys) < 0) {
210 int merrno = errno;
211 messages << "Error probing joystick (keymap; " <<filename << "): " << strerror(merrno)
212 << std::endl;
213 return false;
215 if(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes) < 0) {
216 int merrno = errno;
217 messages << "Error probing joystick (axismap; " << filename << "): " << strerror(merrno)
218 << std::endl;
219 return false;
221 if(ioctl(fd, EVIOCGNAME(sizeof(namebuffer)), namebuffer) <= 0) {
222 int merrno = errno;
223 messages << "Error probing joystick (name; " << filename << "): " << strerror(merrno)
224 << std::endl;
225 return false;
227 unsigned jid = gamepad_map[fd] = lsnes_gamepads.add(namebuffer);
228 gamepad::pad& ngp = lsnes_gamepads[jid];
229 for(unsigned i = 0; i <= KEY_MAX; i++)
230 if(keys[i / div] & (1ULL << (i % div)))
231 ngp.add_button(i, buttonnames[i]);
232 for(unsigned i = 0; i <= ABS_MAX; i++)
233 if(axes[i / div] & (1ULL << (i % div))) {
234 if(i < ABS_HAT0X || i > ABS_HAT3Y) {
235 int32_t V[5];
236 if(ioctl(fd, EVIOCGABS(i), V) < 0) {
237 int merrno = errno;
238 messages << "Error getting parameters for axis " << i << " (fd="
239 << fd << "): " << strerror(merrno) << std::endl;
240 continue;
242 ngp.add_axis(i, V[1], V[2], V[1] == 0, axisnames[i]);
243 } else if(i % 2 == 0)
244 ngp.add_hat(i, i + 1, 1, axisnames[i], axisnames[i + 1]);
246 messages << "Joystick #" << jid << " online: " << namebuffer << std::endl;
247 return true;
250 void probe_all_joysticks()
252 DIR* d = opendir("/dev/input");
253 struct dirent* dentry;
254 if(!d) {
255 int merrno = errno;
256 messages << "Can't list /dev/input: " << strerror(merrno) << std::endl;
257 return;
259 while((dentry = readdir(d)) != NULL) {
260 if(strlen(dentry->d_name) < 6)
261 continue;
262 if(strncmp(dentry->d_name, "event", 5))
263 continue;
264 for(size_t i = 5; dentry->d_name[i]; i++)
265 if(!isdigit(static_cast<uint8_t>(dentry->d_name[i])))
266 continue;
267 std::string filename = std::string("/dev/input/") + dentry->d_name;
268 int r = open(filename.c_str(), O_RDONLY | O_NONBLOCK);
269 if(r < 0)
270 continue;
271 if(!probe_joystick(r, filename))
272 close(r);
274 closedir(d);
276 volatile bool quit_signaled = false;
277 volatile bool quit_ack = false;
279 #define POLL_WAIT 50000
281 void do_fd_zero(fd_set& s)
283 FD_ZERO(&s);
286 struct _joystick_driver drv = {
287 .init = []() -> void {
288 quit_signaled = false;
289 quit_ack = false;
290 probe_all_joysticks();
291 quit_ack = quit_signaled = false;
293 .quit = []() -> void {
294 quit_signaled = true;
295 while(!quit_ack);
297 .thread_fn = []() -> void {
298 while(!quit_signaled) {
299 fd_set rfds;
300 do_fd_zero(rfds);
301 int limit = 0;
302 for(auto fd : gamepad_map) {
303 limit = max(limit, fd.first + 1);
304 FD_SET(fd.first, &rfds);
306 if(!limit) {
307 usleep(POLL_WAIT);
308 continue;
310 struct timeval tv;
311 tv.tv_sec = 0;
312 tv.tv_usec = POLL_WAIT;
313 int r = select(limit, &rfds, NULL, NULL, &tv);
314 if(r <= 0)
315 continue;
316 std::set<int> cleanup;
317 for(auto fd : gamepad_map)
318 if(FD_ISSET(fd.first, &rfds)) {
319 while(true) {
320 int r = read_one_input_event(fd.first);
321 if(!r)
322 break;
323 if(r < 0) {
324 cleanup.insert(fd.first);
325 break;
329 for(auto i : cleanup) {
330 unsigned jid = gamepad_map[i];
331 close(i);
332 gamepad_map.erase(i);
333 lsnes_gamepads[jid].set_online(false);
334 messages << "Gamepad #" << jid << "[" << lsnes_gamepads[jid].name()
335 << "] disconnected." << std::endl;
338 //Get rid of joystick handles.
339 for(auto fd : gamepad_map) {
340 close(fd.first);
341 lsnes_gamepads[fd.second].set_online(false);
343 gamepad_map.clear();
345 quit_ack = true;
347 .signal = []() -> void {
348 quit_signaled = true;
349 while(!quit_ack);
351 .name = []() -> const char* { return "Evdev joystick plugin"; }
353 struct joystick_driver _drv(drv);