From 3fe57f976c9da8a061bc89c546c2445a4195ee83 Mon Sep 17 00:00:00 2001 From: Maximilian Mader Date: Sun, 3 May 2020 21:11:21 +0200 Subject: [PATCH] [GTK3] Implement rumble for controller one Now we store references to all initialized controllers but use only controller #0 for now. --- gtk3/main.c | 125 +++++++++++++++++++++++++----------- gtk3/resources/ui/window.ui | 16 ++--- 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/gtk3/main.c b/gtk3/main.c index 3c88f53..a5314d6 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -64,10 +64,6 @@ typedef struct GuiData { volatile bool stopping; volatile bool stopped; - // Input - SDL_GameController *controller; - uint8_t pressed_buttons; - // GTK pointers GtkApplication *main_application; GtkBuilder *builder; @@ -122,6 +118,14 @@ typedef struct GuiData { bool rewind_paused; bool turbo_down; double clock_mutliplier; + + // Input + uint8_t pressed_buttons; + struct Controller_t { + SDL_GameController *controller; + SDL_Haptic *haptic; + } *controllers; + unsigned controller_count; } GuiData; // Initialize the GuiData @@ -176,8 +180,8 @@ typedef enum { static unsigned key_map[] = { [INPUT_UP] = GDK_KEY_w, - [INPUT_DOWN] = GDK_KEY_a, - [INPUT_LEFT] = GDK_KEY_s, + [INPUT_LEFT] = GDK_KEY_a, + [INPUT_DOWN] = GDK_KEY_s, [INPUT_RIGHT] = GDK_KEY_d, [INPUT_A] = GDK_KEY_l, @@ -299,6 +303,21 @@ static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *ren g_free(color_string); } +static const char* get_sdl_joystick_power_level_name(SDL_JoystickPowerLevel level) { + switch (level) { + case SDL_JOYSTICK_POWER_EMPTY: return "Empty"; + case SDL_JOYSTICK_POWER_LOW: return "Low"; + case SDL_JOYSTICK_POWER_MEDIUM: return "Medium"; + case SDL_JOYSTICK_POWER_FULL: return "Full"; + case SDL_JOYSTICK_POWER_WIRED: return "Wired"; + case SDL_JOYSTICK_POWER_MAX: return "Max"; + + case SDL_JOYSTICK_POWER_UNKNOWN: + default: + return "Unknown"; + } +} + // This function gets called after the parsing of the commandline options has occurred. static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer null_ptr) { guint32 count; @@ -421,7 +440,6 @@ gboolean test_gl_support(void) { return result; } - static gboolean init_controllers(void) { SDL_version compiled; SDL_version linked; @@ -435,6 +453,10 @@ static gboolean init_controllers(void) { 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); @@ -455,30 +477,33 @@ static gboolean init_controllers(void) { if (error != NULL) g_clear_error(&error); - g_message("Found %d controllers", SDL_NumJoysticks()); + g_message("Number of found controllers: %d", SDL_NumJoysticks()); - SDL_GameController *controller = NULL; - for (int i = 0; i < SDL_NumJoysticks(); ++i) { - SDL_GameController *controller = SDL_GameControllerOpen(i); - - if (controller) { - g_message("Found controller (%s)", SDL_GameControllerName(controller)); - - char *mapping = SDL_GameControllerMapping(controller); - g_message("Controller is mapped as \"%s\".", mapping); - SDL_free(mapping); - - SDL_GameControllerClose(controller); - } - } + // 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)) { - gui_data.controller = SDL_GameControllerOpen(i); - if (gui_data.controller) { - g_message("Using controller (%s)", SDL_GameControllerName(gui_data.controller)); - break; + struct Controller_t *s = &gui_data.controllers[i]; + s->controller = SDL_GameControllerOpen(i); + + if (s->controller) { + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(s->controller); + SDL_JoystickPowerLevel power_level = SDL_JoystickCurrentPowerLevel(joystick); + + s->haptic = SDL_HapticOpenFromJoystick(joystick); + + if (s->haptic) { + SDL_HapticRumbleInit(s->haptic); + } + + 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)); + gui_data.controller_count++; } else { g_warning("Could not open controller %i: %s", i, SDL_GetError()); @@ -556,6 +581,22 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { GB_audio_queue_sample(sample); } +static void rumble_callback(GB_gameboy_t *gb, double amp) { + if (gui_data.controllers == NULL || gui_data.controller_count == 0) return; + + // TODO + struct Controller_t *s = &gui_data.controllers[0]; + + if (s->haptic) { + if (amp > 0.0) { + SDL_HapticRumblePlay(s->haptic, amp, 100.0); + } + else { + SDL_HapticRumbleStop(s->haptic); + } + } +} + static void clear_sidebar(void) { GtkTextView *sidebar_output = builder_get(GTK_TEXT_VIEW, "console_sidebar_output"); GtkTextBuffer *sidebar_output_text_buf = gtk_text_view_get_buffer(sidebar_output); @@ -1188,9 +1229,9 @@ static void handle_events(GB_gameboy_t *gb) { uint8_t controller_state = 0; - if (gui_data.controller) { - int16_t x_axis = SDL_GameControllerGetAxis(gui_data.controller, SDL_CONTROLLER_AXIS_LEFTX); - int16_t y_axis = SDL_GameControllerGetAxis(gui_data.controller, SDL_CONTROLLER_AXIS_LEFTY); + 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); if (x_axis >= JOYSTICK_HIGH) { controller_state |= BUTTON_MASK_RIGHT; @@ -1206,14 +1247,14 @@ static void handle_events(GB_gameboy_t *gb) { controller_state |= BUTTON_MASK_UP; } - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) controller_state |= BUTTON_MASK_RIGHT; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT)) controller_state |= BUTTON_MASK_LEFT; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_DPAD_UP)) controller_state |= BUTTON_MASK_UP; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN)) controller_state |= BUTTON_MASK_DOWN; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_A)) controller_state |= BUTTON_MASK_A; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_B)) controller_state |= BUTTON_MASK_B; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_BACK)) controller_state |= BUTTON_MASK_SELECT; - if (SDL_GameControllerGetButton(gui_data.controller, SDL_CONTROLLER_BUTTON_START)) controller_state |= BUTTON_MASK_START; + 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; } GB_set_key_state(gb, GB_KEY_RIGHT, (gui_data.pressed_buttons & BUTTON_MASK_RIGHT) | (controller_state & BUTTON_MASK_RIGHT)); @@ -1306,6 +1347,9 @@ static void init(void) { GB_set_log_callback(&gb, console_log); GB_set_boot_rom_load_callback(&gb, load_boot_rom); + GB_set_rumble_callback(&gb, rumble_callback); + GB_set_rumble_mode(&gb, GB_RUMBLE_CARTRIDGE_ONLY); // TODO + if (get_display_border_mode() <= GB_BORDER_ALWAYS) { GB_set_border_mode(&gb, get_display_border_mode()); } @@ -1465,6 +1509,13 @@ static void run(void) { static void quit(void) { stop(); + for (unsigned i = 0; i < gui_data.controller_count; i++) { + struct Controller_t *s = &gui_data.controllers[i]; + + SDL_HapticClose(s->haptic); + SDL_GameControllerClose(s->controller); + } + // Quit our application properly. // This fires the “shutdown” signal. g_application_quit(G_APPLICATION(gui_data.main_application)); diff --git a/gtk3/resources/ui/window.ui b/gtk3/resources/ui/window.ui index b439be4..cb258c5 100644 --- a/gtk3/resources/ui/window.ui +++ b/gtk3/resources/ui/window.ui @@ -1,5 +1,5 @@ -