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