#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef USE_SDL2 #define USE_SDL #endif #ifdef USE_SDL // we must include SDL first before emu.h, so libfsemu's #definition of main // is the current one (on Windows) when main is encountered further down #include #endif #include #include #include "paths.h" #ifndef WINDOWS #undef main #endif char *g_fs_uae_config_file_path = ""; char *g_fs_uae_config_dir_path = ""; static void flush_stdout(void) { fflush(stdout); } static char *joystick_config_name(const char* name, int with_number) { const char *in = name; char *result = malloc(strlen(name) + 1); char *out = result; int other = 0; while (*in) { if (!with_number) { if (*in == '#') { // remove #2, #3 from name break; } } char c = g_ascii_tolower(*in); if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { if (other) { *(out++) = '_'; } *(out++) = c; other = 0; } else { other = 1; } in++; } *out = '\0'; if (g_str_has_suffix(result, "_")) { result[strlen(result) - 1] = '\0'; } return result; } int ManyMouse_Init(void); void ManyMouse_Quit(void); const char *ManyMouse_DeviceName(unsigned int index); #ifdef MACOSX int multiple_mice = 0; #else int multiple_mice = 1; #endif static void list_joysticks(void) { printf("# Keyboards:\n"); flush_stdout(); GList *list = fs_ml_input_list_custom_keyboards(); GList *iterator = list; while (iterator) { printf("K: %s\n", (gchar *) iterator->data); flush_stdout(); iterator = g_list_next(iterator); } g_list_free_full(list, g_free); printf("# Mice:\n"); printf("M: Mouse\n"); flush_stdout(); if (multiple_mice) { int count = ManyMouse_Init(); if (count >= 0) { for (int i = 0; i < count; i++) { const char *name = ManyMouse_DeviceName(i); if (name[0] == 0 || g_ascii_strcasecmp(name, "mouse") == 0) { printf("M: Mouse: Unnamed Mouse\n"); flush_stdout(); } else { printf("M: Mouse: %s\n", ManyMouse_DeviceName(i)); flush_stdout(); } } ManyMouse_Quit(); } } else { printf("# Support for multiple mice not enabled\n"); } printf("# Joysticks:\n"); flush_stdout(); if (getenv("FSGS_FAKE_JOYSTICKS")) { int fake_joysticks = 0; sscanf(getenv("FSGS_FAKE_JOYSTICKS"), "%d", &fake_joysticks); for (int i = 0; i < fake_joysticks; i++) { printf("J: Fake Joystick\n"); printf(" Buttons: 0 Hats: 0 Axes: 0 Balls: 0 GUID: %s\n", "4300a03b56ae4b6dbdbf2030995ec9b0"); printf(" SDLName: \"Fake Joystick\"\n"); flush_stdout(); } } #if 0 printf("J: Fake Test Joystick %c%c\n", 0xc2, 0xae); printf(" Buttons: 0 Hats: 0 Axes: 0 Balls: 0 GUID: %s\n", "4300a03b56ae4b6dbdbf2030995ec9b0"); printf(" SDLName: \"Fake Test Joystick %c%c\"\n", 0xc2, 0xae); flush_stdout(); #endif #ifdef USE_SDL printf("# SDL_Init(SDL_INIT_JOYSTICK)\n"); flush_stdout(); if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { printf("# SDL_Init(SDL_INIT_JOYSTICK) < 0\n"); flush_stdout(); return; } printf("# SDL_NumJoysticks(): %d\n", SDL_NumJoysticks()); flush_stdout(); for(int i = 0; i < SDL_NumJoysticks(); i++) { SDL_Joystick *joystick = SDL_JoystickOpen(i); #ifdef USE_SDL2 char *name = fs_ml_input_fix_joystick_name( SDL_JoystickName(joystick), 0); #else char *name = fs_ml_input_fix_joystick_name( SDL_JoystickName(i), 0); #endif printf("J: %s\n", name); flush_stdout(); g_free(name); char guid_str[33]; SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); SDL_JoystickGetGUIDString(guid, guid_str, 33); guid_str[32] = '\0'; printf(" Buttons: %d Hats: %d Axes: %d Balls: %d GUID: %s\n", SDL_JoystickNumButtons(joystick), SDL_JoystickNumHats(joystick), SDL_JoystickNumAxes(joystick), SDL_JoystickNumBalls(joystick), guid_str); printf(" SDLName: \"%s\"\n", SDL_JoystickName(joystick)); flush_stdout(); SDL_JoystickClose(joystick); } #else printf("# USE_SDL is not defined\n"); flush_stdout(); #endif } static void print_events(void) { #ifdef USE_SDL2 printf("# Printing events\n"); printf("# Keyboards:\n"); flush_stdout(); GList *list = fs_ml_input_list_custom_keyboards(); GList *iterator = list; int k = 0; while (iterator) { printf("{\"type\": \"keyboard-device-added\", \"device\": %d, " "\"name\": \"%s\"}\n", k++, (const char *) iterator->data); flush_stdout(); iterator = g_list_next(iterator); } g_list_free_full(list, g_free); printf("# Mice:\n"); flush_stdout(); printf("{\"type\": \"mouse-device-added\", \"device\": %d, " "\"name\": \"%s\"}\n", 0, "Mouse"); flush_stdout(); if (multiple_mice) { int count = ManyMouse_Init(); if (count >= 0) { for (int i = 0; i < count; i++) { const char *name = ManyMouse_DeviceName(i); if (name[0] == 0 || g_ascii_strcasecmp(name, "mouse") == 0) { printf("{\"type\": \"mouse-device-added\", \"device\": %d, " "\"name\": \"%s\"}\n", i + 1, "Unnamed Mouse"); } else { printf("{\"type\": \"mouse-device-added\", \"device\": %d, " "\"name\": \"%s\"}\n", i + 1, ManyMouse_DeviceName(i)); } flush_stdout(); } ManyMouse_Quit(); } } else { printf("# Support for multiple mice not enabled\n"); } // With recent SDL2 versions (SDL 2.0.14+?), it seems that the video // subsystem must be initialized to get events from xinput controllers. printf("# SDL_Init(SDL_INIT_EVERYTHING)\n"); flush_stdout(); if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("# SDL_Init(SDL_INIT_EVERYTHING) < 0\n"); flush_stdout(); return; } int64_t startup = fs_get_monotonic_time(); SDL_Event event; int do_quit = 0; while (do_quit == 0) { SDL_WaitEvent(&event); switch (event.type) { case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: printf("{\"type\": \"%s\", \"device\": %d, " "\"button\": %d, \"state\": %d}\n", event.type == SDL_JOYBUTTONDOWN ? "joy-button-down" : "joy-button-up", event.jbutton.which, event.jbutton.button, event.jbutton.state); break; case SDL_JOYHATMOTION: printf("{\"type\": \"joy-hat-motion\", \"device\": %d, " "\"hat\": %d, \"state\": %d}\n", event.jbutton.which, event.jhat.hat, event.jhat.value); break; case SDL_JOYAXISMOTION: if (startup > 0) { if (fs_get_monotonic_time() - startup < 1000.0) { // at least on Linux, it has been observed that you get // a (full negative) axis motion event per axis on // startup. printf("# Ignored startup axis event\n"); break; } else { startup = 0; } } #if 0 if (event.jaxis.value > -2000 && event.jaxis.value < 2000) { break; } #endif printf("{\"type\": \"joy-axis-motion\", \"device\": %d, " "\"axis\": %d, \"state\": %d}\n", event.jbutton.which, event.jaxis.axis, event.jaxis.value); break; case SDL_JOYDEVICEADDED: printf("# Joystick device added\n"); SDL_Joystick *joystick = SDL_JoystickOpen(event.jdevice.which); char *name = fs_ml_input_fix_joystick_name( SDL_JoystickName(joystick), 0); char *name2 = strdup(name); char *c = name2; while (*c) { // simple hack, replacing a couple of chars to (easily) // make the name valid json. if (*c == '\"') { *c = '\''; } if (*c == '\\') { *c = '/'; } c++; } printf("{\"type\": \"joy-device-added\", \"device\": %d, " "\"name\": \"%s\", \"buttons\": %d, \"axes\": %d, " "\"hats\": %d, \"balls\": %d}\n", event.jdevice.which, name2, SDL_JoystickNumButtons(joystick), SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), SDL_JoystickNumBalls(joystick)); free(name2); break; case SDL_JOYDEVICEREMOVED: printf("# Joystick device removed\n"); printf("{\"type\": \"joy-device-removed\", \"device\": %d}\n", event.jdevice.which); break; case SDL_QUIT: printf("# Received quit signal\n"); do_quit = 1; break; } flush_stdout(); } #endif } static void print_state(SDL_Joystick* joystick, const char* name) { int num_buttons = SDL_JoystickNumButtons(joystick); int num_hats = SDL_JoystickNumHats(joystick); int num_axes = SDL_JoystickNumAxes(joystick); while (1) { SDL_JoystickUpdate(); printf("%s buttons", name); for (int i = 0; i < num_buttons; i++) { printf(" %d", SDL_JoystickGetButton(joystick, i)); } printf("\n"); printf("%s hats", name); for (int i = 0; i < num_hats; i++) { printf(" %d", SDL_JoystickGetHat(joystick, i)); } printf("\n"); printf("%s axes", name); for (int i = 0; i < num_axes; i++) { printf(" %d", SDL_JoystickGetAxis(joystick, i)); } printf("\n"); flush_stdout(); SDL_Delay(100); SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { printf("# Received quit signal\n"); return; } } } } #ifdef WINDOWS // FIXME fix the main macro instead int g_fs_ml_ncmdshow; HINSTANCE g_fs_ml_hinstance; #endif static void print_usage(void) { printf("\nUsage:\n"); printf(" fs-uae-device-helper --list\n"); printf(" fs-uae-device-helper \n"); printf(" fs-uae-device-helper --events\n"); printf("\n"); } int main(int argc, char* argv[]) { printf("# FS-UAE Device Helper %s\n", PACKAGE_VERSION); flush_stdout(); fs_set_argv(argc, argv); fs_set_data_dir(fs_uae_data_dir()); if (argc < 2 || strcmp(argv[1], "--help") == 0) { print_usage(); flush_stdout(); return 1; } if (strcmp(argv[1], "--list") == 0 || strcmp(argv[1], "list") == 0) { if (argc >= 3 && strcmp(argv[2], "--multiple-mice") == 0) { multiple_mice = 1; } list_joysticks(); printf("# End\n"); flush_stdout(); return 0; } if (strcmp(argv[1], "--events") == 0) { if (argc >= 3 && strcmp(argv[2], "--multiple-mice") == 0) { multiple_mice = 1; } print_events(); printf("# End\n"); flush_stdout(); return 0; } char* compare_name = joystick_config_name(argv[1], 1); // With recent SDL2 versions (SDL 2.0.14+?), it seems that the video // subsystem must be initialized to get events from xinput controllers. printf("# SDL_Init(SDL_INIT_EVERYTHING)\n"); flush_stdout(); if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("# SDL_Init(SDL_INIT_EVERYTHING) < 0\n"); flush_stdout(); return 2; } int num_joysticks = SDL_NumJoysticks(); for (int i = 0; i < num_joysticks; i++) { SDL_Joystick *joystick = SDL_JoystickOpen(i); #ifdef USE_SDL2 char *name = fs_ml_input_fix_joystick_name( SDL_JoystickName(joystick), 1); #else char *name = fs_ml_input_fix_joystick_name( SDL_JoystickName(i), 1); #endif /* fs_ml_input_unique_device_name either returns name, or frees it * and return another name, so name must be malloced and owned by * caller. */ name = fs_ml_input_unique_device_name(name); char* config_name = joystick_config_name(name, 1); printf("# %s -- %s\n", config_name, compare_name); if (strcmp(config_name, compare_name) == 0) { print_state(joystick, config_name); SDL_JoystickClose(joystick); printf("# End\n"); flush_stdout(); exit(0); } SDL_JoystickClose(joystick); } printf("# No devices matched\n"); printf("# End\n"); flush_stdout(); return 0; }