1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdio.h>
6 #include <string.h>
7
8 #ifdef USE_SDL2
9 #define USE_SDL
10 #endif
11
12 #ifdef USE_SDL
13 // we must include SDL first before emu.h, so libfsemu's #definition of main
14 // is the current one (on Windows) when main is encountered further down
15 #include <SDL.h>
16 #endif
17
18 #include <fs/emu.h>
19 #include <fs/glib.h>
20 #include "paths.h"
21
22 #ifndef WINDOWS
23 #undef main
24 #endif
25
26 char *g_fs_uae_config_file_path = "";
27 char *g_fs_uae_config_dir_path = "";
28
flush_stdout(void)29 static void flush_stdout(void) {
30 fflush(stdout);
31 }
32
joystick_config_name(const char * name,int with_number)33 static char *joystick_config_name(const char* name, int with_number)
34 {
35 const char *in = name;
36 char *result = malloc(strlen(name) + 1);
37 char *out = result;
38 int other = 0;
39 while (*in) {
40 if (!with_number) {
41 if (*in == '#') {
42 // remove #2, #3 from name
43 break;
44 }
45 }
46 char c = g_ascii_tolower(*in);
47 if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
48 if (other) {
49 *(out++) = '_';
50 }
51 *(out++) = c;
52 other = 0;
53 } else {
54 other = 1;
55 }
56 in++;
57 }
58 *out = '\0';
59 if (g_str_has_suffix(result, "_")) {
60 result[strlen(result) - 1] = '\0';
61 }
62 return result;
63 }
64
65 int ManyMouse_Init(void);
66 void ManyMouse_Quit(void);
67 const char *ManyMouse_DeviceName(unsigned int index);
68 #ifdef MACOSX
69 int multiple_mice = 0;
70 #else
71 int multiple_mice = 1;
72 #endif
73
list_joysticks(void)74 static void list_joysticks(void)
75 {
76 printf("# Keyboards:\n");
77 flush_stdout();
78 GList *list = fs_ml_input_list_custom_keyboards();
79 GList *iterator = list;
80 while (iterator) {
81 printf("K: %s\n", (gchar *) iterator->data);
82 flush_stdout();
83 iterator = g_list_next(iterator);
84 }
85 g_list_free_full(list, g_free);
86 printf("# Mice:\n");
87 printf("M: Mouse\n");
88 flush_stdout();
89 if (multiple_mice) {
90 int count = ManyMouse_Init();
91 if (count >= 0) {
92 for (int i = 0; i < count; i++) {
93 const char *name = ManyMouse_DeviceName(i);
94 if (name[0] == 0 || g_ascii_strcasecmp(name, "mouse") == 0) {
95 printf("M: Mouse: Unnamed Mouse\n");
96 flush_stdout();
97 } else {
98 printf("M: Mouse: %s\n", ManyMouse_DeviceName(i));
99 flush_stdout();
100 }
101 }
102 ManyMouse_Quit();
103 }
104 } else {
105 printf("# Support for multiple mice not enabled\n");
106 }
107 printf("# Joysticks:\n");
108 flush_stdout();
109 if (getenv("FSGS_FAKE_JOYSTICKS")) {
110 int fake_joysticks = 0;
111 sscanf(getenv("FSGS_FAKE_JOYSTICKS"), "%d", &fake_joysticks);
112 for (int i = 0; i < fake_joysticks; i++) {
113 printf("J: Fake Joystick\n");
114 printf(" Buttons: 0 Hats: 0 Axes: 0 Balls: 0 GUID: %s\n",
115 "4300a03b56ae4b6dbdbf2030995ec9b0");
116 printf(" SDLName: \"Fake Joystick\"\n");
117 flush_stdout();
118 }
119 }
120 #if 0
121 printf("J: Fake Test Joystick %c%c\n", 0xc2, 0xae);
122 printf(" Buttons: 0 Hats: 0 Axes: 0 Balls: 0 GUID: %s\n",
123 "4300a03b56ae4b6dbdbf2030995ec9b0");
124 printf(" SDLName: \"Fake Test Joystick %c%c\"\n", 0xc2, 0xae);
125 flush_stdout();
126 #endif
127 #ifdef USE_SDL
128 printf("# SDL_Init(SDL_INIT_JOYSTICK)\n");
129 flush_stdout();
130 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
131 printf("# SDL_Init(SDL_INIT_JOYSTICK) < 0\n");
132 flush_stdout();
133 return;
134 }
135 printf("# SDL_NumJoysticks(): %d\n", SDL_NumJoysticks());
136 flush_stdout();
137 for(int i = 0; i < SDL_NumJoysticks(); i++) {
138 SDL_Joystick *joystick = SDL_JoystickOpen(i);
139
140 #ifdef USE_SDL2
141 char *name = fs_ml_input_fix_joystick_name(
142 SDL_JoystickName(joystick), 0);
143 #else
144 char *name = fs_ml_input_fix_joystick_name(
145 SDL_JoystickName(i), 0);
146 #endif
147 printf("J: %s\n", name);
148 flush_stdout();
149 g_free(name);
150
151 char guid_str[33];
152 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
153 SDL_JoystickGetGUIDString(guid, guid_str, 33);
154 guid_str[32] = '\0';
155
156 printf(" Buttons: %d Hats: %d Axes: %d Balls: %d GUID: %s\n",
157 SDL_JoystickNumButtons(joystick),
158 SDL_JoystickNumHats(joystick),
159 SDL_JoystickNumAxes(joystick),
160 SDL_JoystickNumBalls(joystick),
161 guid_str);
162 printf(" SDLName: \"%s\"\n", SDL_JoystickName(joystick));
163 flush_stdout();
164 SDL_JoystickClose(joystick);
165 }
166 #else
167 printf("# USE_SDL is not defined\n");
168 flush_stdout();
169 #endif
170 }
171
print_events(void)172 static void print_events(void)
173 {
174 #ifdef USE_SDL2
175 printf("# Printing events\n");
176
177 printf("# Keyboards:\n");
178 flush_stdout();
179 GList *list = fs_ml_input_list_custom_keyboards();
180 GList *iterator = list;
181 int k = 0;
182 while (iterator) {
183 printf("{\"type\": \"keyboard-device-added\", \"device\": %d, "
184 "\"name\": \"%s\"}\n",
185 k++, (const char *) iterator->data);
186 flush_stdout();
187 iterator = g_list_next(iterator);
188 }
189 g_list_free_full(list, g_free);
190
191 printf("# Mice:\n");
192 flush_stdout();
193 printf("{\"type\": \"mouse-device-added\", \"device\": %d, "
194 "\"name\": \"%s\"}\n",
195 0, "Mouse");
196 flush_stdout();
197
198 if (multiple_mice) {
199 int count = ManyMouse_Init();
200 if (count >= 0) {
201 for (int i = 0; i < count; i++) {
202 const char *name = ManyMouse_DeviceName(i);
203 if (name[0] == 0 || g_ascii_strcasecmp(name, "mouse") == 0) {
204 printf("{\"type\": \"mouse-device-added\", \"device\": %d, "
205 "\"name\": \"%s\"}\n",
206 i + 1, "Unnamed Mouse");
207 } else {
208 printf("{\"type\": \"mouse-device-added\", \"device\": %d, "
209 "\"name\": \"%s\"}\n",
210 i + 1, ManyMouse_DeviceName(i));
211 }
212 flush_stdout();
213 }
214 ManyMouse_Quit();
215 }
216 } else {
217 printf("# Support for multiple mice not enabled\n");
218 }
219
220 // With recent SDL2 versions (SDL 2.0.14+?), it seems that the video
221 // subsystem must be initialized to get events from xinput controllers.
222 printf("# SDL_Init(SDL_INIT_EVERYTHING)\n");
223 flush_stdout();
224 if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
225 printf("# SDL_Init(SDL_INIT_EVERYTHING) < 0\n");
226 flush_stdout();
227 return;
228 }
229
230 int64_t startup = fs_get_monotonic_time();
231
232 SDL_Event event;
233 int do_quit = 0;
234 while (do_quit == 0) {
235 SDL_WaitEvent(&event);
236 switch (event.type) {
237 case SDL_JOYBUTTONDOWN:
238 case SDL_JOYBUTTONUP:
239 printf("{\"type\": \"%s\", \"device\": %d, "
240 "\"button\": %d, \"state\": %d}\n",
241 event.type == SDL_JOYBUTTONDOWN ? "joy-button-down" :
242 "joy-button-up", event.jbutton.which, event.jbutton.button,
243 event.jbutton.state);
244 break;
245 case SDL_JOYHATMOTION:
246 printf("{\"type\": \"joy-hat-motion\", \"device\": %d, "
247 "\"hat\": %d, \"state\": %d}\n",
248 event.jbutton.which, event.jhat.hat, event.jhat.value);
249 break;
250 case SDL_JOYAXISMOTION:
251 if (startup > 0) {
252 if (fs_get_monotonic_time() - startup < 1000.0) {
253 // at least on Linux, it has been observed that you get
254 // a (full negative) axis motion event per axis on
255 // startup.
256 printf("# Ignored startup axis event\n");
257 break;
258 } else {
259 startup = 0;
260 }
261 }
262 #if 0
263 if (event.jaxis.value > -2000 && event.jaxis.value < 2000) {
264 break;
265 }
266 #endif
267 printf("{\"type\": \"joy-axis-motion\", \"device\": %d, "
268 "\"axis\": %d, \"state\": %d}\n",
269 event.jbutton.which, event.jaxis.axis, event.jaxis.value);
270 break;
271 case SDL_JOYDEVICEADDED:
272 printf("# Joystick device added\n");
273 SDL_Joystick *joystick = SDL_JoystickOpen(event.jdevice.which);
274
275 char *name = fs_ml_input_fix_joystick_name(
276 SDL_JoystickName(joystick), 0);
277
278 char *name2 = strdup(name);
279 char *c = name2;
280 while (*c) {
281 // simple hack, replacing a couple of chars to (easily)
282 // make the name valid json.
283 if (*c == '\"') {
284 *c = '\'';
285 }
286 if (*c == '\\') {
287 *c = '/';
288 }
289 c++;
290 }
291 printf("{\"type\": \"joy-device-added\", \"device\": %d, "
292 "\"name\": \"%s\", \"buttons\": %d, \"axes\": %d, "
293 "\"hats\": %d, \"balls\": %d}\n",
294 event.jdevice.which, name2,
295 SDL_JoystickNumButtons(joystick),
296 SDL_JoystickNumAxes(joystick),
297 SDL_JoystickNumHats(joystick),
298 SDL_JoystickNumBalls(joystick));
299 free(name2);
300 break;
301 case SDL_JOYDEVICEREMOVED:
302 printf("# Joystick device removed\n");
303 printf("{\"type\": \"joy-device-removed\", \"device\": %d}\n",
304 event.jdevice.which);
305 break;
306 case SDL_QUIT:
307 printf("# Received quit signal\n");
308 do_quit = 1;
309 break;
310 }
311 flush_stdout();
312 }
313
314 #endif
315 }
316
print_state(SDL_Joystick * joystick,const char * name)317 static void print_state(SDL_Joystick* joystick, const char* name)
318 {
319 int num_buttons = SDL_JoystickNumButtons(joystick);
320 int num_hats = SDL_JoystickNumHats(joystick);
321 int num_axes = SDL_JoystickNumAxes(joystick);
322 while (1) {
323 SDL_JoystickUpdate();
324 printf("%s buttons", name);
325 for (int i = 0; i < num_buttons; i++) {
326 printf(" %d", SDL_JoystickGetButton(joystick, i));
327 }
328 printf("\n");
329 printf("%s hats", name);
330 for (int i = 0; i < num_hats; i++) {
331 printf(" %d", SDL_JoystickGetHat(joystick, i));
332 }
333 printf("\n");
334 printf("%s axes", name);
335 for (int i = 0; i < num_axes; i++) {
336 printf(" %d", SDL_JoystickGetAxis(joystick, i));
337 }
338 printf("\n");
339 flush_stdout();
340 SDL_Delay(100);
341
342 SDL_Event event;
343 while (SDL_PollEvent(&event)) {
344 if (event.type == SDL_QUIT) {
345 printf("# Received quit signal\n");
346 return;
347 }
348 }
349 }
350 }
351
352 #ifdef WINDOWS
353 // FIXME fix the main macro instead
354 int g_fs_ml_ncmdshow;
355 HINSTANCE g_fs_ml_hinstance;
356 #endif
357
print_usage(void)358 static void print_usage(void) {
359 printf("\nUsage:\n");
360 printf(" fs-uae-device-helper --list\n");
361 printf(" fs-uae-device-helper <device-name>\n");
362 printf(" fs-uae-device-helper --events\n");
363 printf("\n");
364 }
365
main(int argc,char * argv[])366 int main(int argc, char* argv[])
367 {
368 printf("# FS-UAE Device Helper %s\n", PACKAGE_VERSION);
369 flush_stdout();
370
371 fs_set_argv(argc, argv);
372 fs_set_data_dir(fs_uae_data_dir());
373
374 if (argc < 2 || strcmp(argv[1], "--help") == 0) {
375 print_usage();
376 flush_stdout();
377 return 1;
378 }
379
380 if (strcmp(argv[1], "--list") == 0 || strcmp(argv[1], "list") == 0) {
381 if (argc >= 3 && strcmp(argv[2], "--multiple-mice") == 0) {
382 multiple_mice = 1;
383 }
384 list_joysticks();
385 printf("# End\n");
386 flush_stdout();
387 return 0;
388 }
389 if (strcmp(argv[1], "--events") == 0) {
390 if (argc >= 3 && strcmp(argv[2], "--multiple-mice") == 0) {
391 multiple_mice = 1;
392 }
393 print_events();
394 printf("# End\n");
395 flush_stdout();
396 return 0;
397 }
398
399 char* compare_name = joystick_config_name(argv[1], 1);
400
401 // With recent SDL2 versions (SDL 2.0.14+?), it seems that the video
402 // subsystem must be initialized to get events from xinput controllers.
403 printf("# SDL_Init(SDL_INIT_EVERYTHING)\n");
404 flush_stdout();
405 if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
406 printf("# SDL_Init(SDL_INIT_EVERYTHING) < 0\n");
407 flush_stdout();
408 return 2;
409 }
410 int num_joysticks = SDL_NumJoysticks();
411 for (int i = 0; i < num_joysticks; i++) {
412 SDL_Joystick *joystick = SDL_JoystickOpen(i);
413 #ifdef USE_SDL2
414 char *name = fs_ml_input_fix_joystick_name(
415 SDL_JoystickName(joystick), 1);
416 #else
417 char *name = fs_ml_input_fix_joystick_name(
418 SDL_JoystickName(i), 1);
419 #endif
420 /* fs_ml_input_unique_device_name either returns name, or frees it
421 * and return another name, so name must be malloced and owned by
422 * caller. */
423 name = fs_ml_input_unique_device_name(name);
424 char* config_name = joystick_config_name(name, 1);
425 printf("# %s -- %s\n", config_name, compare_name);
426 if (strcmp(config_name, compare_name) == 0) {
427 print_state(joystick, config_name);
428 SDL_JoystickClose(joystick);
429 printf("# End\n");
430 flush_stdout();
431 exit(0);
432 }
433 SDL_JoystickClose(joystick);
434 }
435 printf("# No devices matched\n");
436 printf("# End\n");
437 flush_stdout();
438 return 0;
439 }
440