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