From 8c3154a061ac1b68e749ea5ea0b577c7c3fe41d3 Mon Sep 17 00:00:00 2001 From: Maximilian Mader Date: Mon, 4 May 2020 00:02:15 +0200 Subject: [PATCH] [GTK3] Accept input from all connected controllers Rumble will be used on the most recently used controller. --- Makefile | 11 +++- gtk3/main.c | 153 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 117 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index eecb5c0..e571d72 100644 --- a/Makefile +++ b/Makefile @@ -120,10 +120,17 @@ ifneq ($(findstring gtk3,$(MAKECMDGOALS)),) $(error The gtk3 target requires pkg-config) endif else -GTK_OPTIONS := -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DG_LOG_DOMAIN=\"SameBoy\" -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\" +GTK_OPTIONS := -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DG_LOG_DOMAIN=\"SameBoy\" -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\" ifeq ($(SDL_AUDIO_DRIVER),sdl) -GTK_OPTIONS += -DUSE_SDL_AUDIO +GTK_OPTIONS += -DUSE_SDL_AUDIO +endif + +# Use SDL’s haptic feedback subsystem by default, although +# some controllers only work with the simpler SDL_GameControllerRumble / SDL_JoystickRumble, +# but SDL’s WUP-028 (Wii U GameCube Controller Adapter) driver rumble handling seems to make the adapter lock up after a while. +ifneq ($(USE_SDL_HAPTIC),0) + GTK_OPTIONS += -DUSE_SDL_HAPTIC endif GTK3_CFLAGS := $(shell $(PKG_CONFIG) --cflags gio-2.0 gtk+-3.0 epoxy) $(GTK_OPTIONS) diff --git a/gtk3/main.c b/gtk3/main.c index a5314d6..145e408 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -126,6 +126,7 @@ typedef struct GuiData { SDL_Haptic *haptic; } *controllers; unsigned controller_count; + struct Controller_t *last_used_controller; // Used for rumble } GuiData; // Initialize the GuiData @@ -448,18 +449,20 @@ static gboolean init_controllers(void) { g_debug("Compiled against SDL version %d.%d.%d", compiled.major, compiled.minor, compiled.patch); g_debug("Linked against SDL version %d.%d.%d", linked.major, linked.minor, linked.patch); + g_debug("Initializing game controllers"); if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) { g_warning("Failed to initialize game controller support: %s", SDL_GetError()); return false; } - if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) { - g_warning("Failed to initialize haptic feedback support: %s", SDL_GetError()); - } - - SDL_QuitSubSystem(SDL_INIT_EVENTS); - SDL_GameControllerEventState(SDL_ENABLE); + #ifdef USE_SDL_HAPTIC + g_debug("Initializing haptic feedback"); + if (SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) { + g_warning("Failed to initialize haptic feedback support: %s", SDL_GetError()); + } + #endif + g_debug("Loading custom game controller database"); GError *error = NULL; GBytes *db_f = g_resources_lookup_data(RESOURCE_PREFIX "gamecontrollerdb.txt", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); @@ -482,7 +485,6 @@ static gboolean init_controllers(void) { // In the “worst” case all joysticks are valid game controllers gui_data.controllers = g_malloc0(sizeof(struct Controller_t) * SDL_NumJoysticks()); - // Open the first available controller for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { struct Controller_t *s = &gui_data.controllers[i]; @@ -492,17 +494,29 @@ static gboolean init_controllers(void) { SDL_Joystick *joystick = SDL_GameControllerGetJoystick(s->controller); SDL_JoystickPowerLevel power_level = SDL_JoystickCurrentPowerLevel(joystick); - s->haptic = SDL_HapticOpenFromJoystick(joystick); + #ifdef USE_SDL_HAPTIC + if (SDL_JoystickIsHaptic(joystick)) { + s->haptic = SDL_HapticOpenFromJoystick(joystick); - if (s->haptic) { - SDL_HapticRumbleInit(s->haptic); - } + if (s->haptic && SDL_HapticRumbleSupported(s->haptic)) { + SDL_HapticRumbleInit(s->haptic); + } + else { + if (s->haptic == NULL) { + g_warning("%s", SDL_GetError()); + } + + SDL_HapticClose(s->haptic); + s->haptic = NULL; + } + } + #endif char guid_str[33]; SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); - g_message("Controller #%u (%s): %s; Power level: %s", i, guid_str, SDL_GameControllerName(s->controller), get_sdl_joystick_power_level_name(power_level)); + g_message("Controller #%u (%s): %s; Haptic Feedback: %d; Power level: %s; Player index: %u; Instance ID: %u", i, guid_str, SDL_GameControllerName(s->controller), s->haptic != NULL, get_sdl_joystick_power_level_name(power_level), SDL_JoystickGetPlayerIndex(joystick), SDL_JoystickInstanceID(joystick)); gui_data.controller_count++; } else { @@ -522,12 +536,12 @@ static gboolean init_audio(void) { gui_data.audio_initialized = false; } -#ifdef USE_SDL_AUDIO - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { - g_warning("Failed to initialize audio: %s", SDL_GetError()); - return false; - } -#endif + #ifdef USE_SDL_AUDIO + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + g_warning("Failed to initialize audio: %s", SDL_GetError()); + return false; + } + #endif GB_audio_init(gui_data.sample_rate); GB_set_sample_rate(&gb, GB_audio_get_sample_rate()); @@ -582,19 +596,28 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { } static void rumble_callback(GB_gameboy_t *gb, double amp) { - if (gui_data.controllers == NULL || gui_data.controller_count == 0) return; + if (!gui_data.controllers || gui_data.controller_count == 0 || !gui_data.last_used_controller) return; - // TODO - struct Controller_t *s = &gui_data.controllers[0]; + struct Controller_t *s = gui_data.last_used_controller; - if (s->haptic) { - if (amp > 0.0) { - SDL_HapticRumblePlay(s->haptic, amp, 100.0); + #ifdef USE_SDL_HAPTIC + if (s->haptic) { + if (amp > 0.0) { + SDL_HapticRumblePlay(s->haptic, amp, 100.0); + } + else { + SDL_HapticRumbleStop(s->haptic); + } + } + #else + if (amp == 0.0) { + SDL_GameControllerRumble(s->controller, 0, 0, 0); } else { - SDL_HapticRumbleStop(s->haptic); + Uint16 intensity = (float) 0xFFFF * amp; + SDL_GameControllerRumble(s->controller, intensity, intensity, 100); } - } + #endif } static void clear_sidebar(void) { @@ -1229,32 +1252,69 @@ static void handle_events(GB_gameboy_t *gb) { uint8_t controller_state = 0; - if (gui_data.controllers && gui_data.controllers[0].controller) { - int16_t x_axis = SDL_GameControllerGetAxis(gui_data.controllers[0].controller, SDL_CONTROLLER_AXIS_LEFTX); - int16_t y_axis = SDL_GameControllerGetAxis(gui_data.controllers[0].controller, SDL_CONTROLLER_AXIS_LEFTY); + for (unsigned i = 0; i < gui_data.controller_count; i++) { + struct Controller_t *s = &gui_data.controllers[i]; + + int16_t x_axis = SDL_GameControllerGetAxis(s->controller, SDL_CONTROLLER_AXIS_LEFTX); + int16_t y_axis = SDL_GameControllerGetAxis(s->controller, SDL_CONTROLLER_AXIS_LEFTY); if (x_axis >= JOYSTICK_HIGH) { + gui_data.last_used_controller = s; controller_state |= BUTTON_MASK_RIGHT; } else if (x_axis <= -JOYSTICK_HIGH) { + gui_data.last_used_controller = s; controller_state |= BUTTON_MASK_LEFT; } if (y_axis >= JOYSTICK_HIGH) { + gui_data.last_used_controller = s; controller_state |= BUTTON_MASK_DOWN; } else if (y_axis <= -JOYSTICK_HIGH) { + gui_data.last_used_controller = s; controller_state |= BUTTON_MASK_UP; } - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) controller_state |= BUTTON_MASK_RIGHT; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT)) controller_state |= BUTTON_MASK_LEFT; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_DPAD_UP)) controller_state |= BUTTON_MASK_UP; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN)) controller_state |= BUTTON_MASK_DOWN; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_A)) controller_state |= BUTTON_MASK_A; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_B)) controller_state |= BUTTON_MASK_B; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_BACK)) controller_state |= BUTTON_MASK_SELECT; - if (SDL_GameControllerGetButton(gui_data.controllers[0].controller, SDL_CONTROLLER_BUTTON_START)) controller_state |= BUTTON_MASK_START; + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_RIGHT; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_LEFT; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_DPAD_UP)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_UP; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_DOWN; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_A)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_A; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_B)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_B; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_BACK)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_SELECT; + } + + if (SDL_GameControllerGetButton(s->controller, SDL_CONTROLLER_BUTTON_START)) { + gui_data.last_used_controller = s; + controller_state |= BUTTON_MASK_START; + } } GB_set_key_state(gb, GB_KEY_RIGHT, (gui_data.pressed_buttons & BUTTON_MASK_RIGHT) | (controller_state & BUTTON_MASK_RIGHT)); @@ -1512,7 +1572,10 @@ static void quit(void) { for (unsigned i = 0; i < gui_data.controller_count; i++) { struct Controller_t *s = &gui_data.controllers[i]; - SDL_HapticClose(s->haptic); + #ifdef USE_SDL_HAPTIC + SDL_HapticClose(s->haptic); + #endif + SDL_GameControllerClose(s->controller); } @@ -1864,14 +1927,14 @@ static void startup(GApplication *app, gpointer null_ptr) { g_action_change_state(action, g_variant_new_string(gui_data.cli_options.prefix)); } -#if NDEBUG - // Disable when not compiled in debug mode - action_set_enabled(app, "open_gtk_debugger", false); + #if NDEBUG + // Disable when not compiled in debug mode + action_set_enabled(app, "open_gtk_debugger", false); - // Remove the menubar override - gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector_label")); - gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector")); -#endif + // Remove the menubar override + gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector_label")); + gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector")); + #endif gui_data.preferences = GTK_WINDOW(get_object("preferences"));