[GTK3] Implement rumble for controller one
Now we store references to all initialized controllers but use only controller #0 for now.
This commit is contained in:
parent
4042b7f38c
commit
3fe57f976c
125
gtk3/main.c
125
gtk3/main.c
@ -64,10 +64,6 @@ typedef struct GuiData {
|
|||||||
volatile bool stopping;
|
volatile bool stopping;
|
||||||
volatile bool stopped;
|
volatile bool stopped;
|
||||||
|
|
||||||
// Input
|
|
||||||
SDL_GameController *controller;
|
|
||||||
uint8_t pressed_buttons;
|
|
||||||
|
|
||||||
// GTK pointers
|
// GTK pointers
|
||||||
GtkApplication *main_application;
|
GtkApplication *main_application;
|
||||||
GtkBuilder *builder;
|
GtkBuilder *builder;
|
||||||
@ -122,6 +118,14 @@ typedef struct GuiData {
|
|||||||
bool rewind_paused;
|
bool rewind_paused;
|
||||||
bool turbo_down;
|
bool turbo_down;
|
||||||
double clock_mutliplier;
|
double clock_mutliplier;
|
||||||
|
|
||||||
|
// Input
|
||||||
|
uint8_t pressed_buttons;
|
||||||
|
struct Controller_t {
|
||||||
|
SDL_GameController *controller;
|
||||||
|
SDL_Haptic *haptic;
|
||||||
|
} *controllers;
|
||||||
|
unsigned controller_count;
|
||||||
} GuiData;
|
} GuiData;
|
||||||
|
|
||||||
// Initialize the GuiData
|
// Initialize the GuiData
|
||||||
@ -176,8 +180,8 @@ typedef enum {
|
|||||||
|
|
||||||
static unsigned key_map[] = {
|
static unsigned key_map[] = {
|
||||||
[INPUT_UP] = GDK_KEY_w,
|
[INPUT_UP] = GDK_KEY_w,
|
||||||
[INPUT_DOWN] = GDK_KEY_a,
|
[INPUT_LEFT] = GDK_KEY_a,
|
||||||
[INPUT_LEFT] = GDK_KEY_s,
|
[INPUT_DOWN] = GDK_KEY_s,
|
||||||
[INPUT_RIGHT] = GDK_KEY_d,
|
[INPUT_RIGHT] = GDK_KEY_d,
|
||||||
|
|
||||||
[INPUT_A] = GDK_KEY_l,
|
[INPUT_A] = GDK_KEY_l,
|
||||||
@ -299,6 +303,21 @@ static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *ren
|
|||||||
g_free(color_string);
|
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.
|
// 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) {
|
static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer null_ptr) {
|
||||||
guint32 count;
|
guint32 count;
|
||||||
@ -421,7 +440,6 @@ gboolean test_gl_support(void) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static gboolean init_controllers(void) {
|
static gboolean init_controllers(void) {
|
||||||
SDL_version compiled;
|
SDL_version compiled;
|
||||||
SDL_version linked;
|
SDL_version linked;
|
||||||
@ -435,6 +453,10 @@ static gboolean init_controllers(void) {
|
|||||||
return false;
|
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_QuitSubSystem(SDL_INIT_EVENTS);
|
||||||
SDL_GameControllerEventState(SDL_ENABLE);
|
SDL_GameControllerEventState(SDL_ENABLE);
|
||||||
|
|
||||||
@ -455,30 +477,33 @@ static gboolean init_controllers(void) {
|
|||||||
|
|
||||||
if (error != NULL) g_clear_error(&error);
|
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;
|
// In the “worst” case all joysticks are valid game controllers
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
gui_data.controllers = g_malloc0(sizeof(struct Controller_t) * SDL_NumJoysticks());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the first available controller
|
// Open the first available controller
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||||
if (SDL_IsGameController(i)) {
|
if (SDL_IsGameController(i)) {
|
||||||
gui_data.controller = SDL_GameControllerOpen(i);
|
struct Controller_t *s = &gui_data.controllers[i];
|
||||||
if (gui_data.controller) {
|
s->controller = SDL_GameControllerOpen(i);
|
||||||
g_message("Using controller (%s)", SDL_GameControllerName(gui_data.controller));
|
|
||||||
break;
|
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 {
|
else {
|
||||||
g_warning("Could not open controller %i: %s", i, SDL_GetError());
|
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);
|
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) {
|
static void clear_sidebar(void) {
|
||||||
GtkTextView *sidebar_output = builder_get(GTK_TEXT_VIEW, "console_sidebar_output");
|
GtkTextView *sidebar_output = builder_get(GTK_TEXT_VIEW, "console_sidebar_output");
|
||||||
GtkTextBuffer *sidebar_output_text_buf = gtk_text_view_get_buffer(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;
|
uint8_t controller_state = 0;
|
||||||
|
|
||||||
if (gui_data.controller) {
|
if (gui_data.controllers && gui_data.controllers[0].controller) {
|
||||||
int16_t x_axis = SDL_GameControllerGetAxis(gui_data.controller, SDL_CONTROLLER_AXIS_LEFTX);
|
int16_t x_axis = SDL_GameControllerGetAxis(gui_data.controllers[0].controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||||
int16_t y_axis = SDL_GameControllerGetAxis(gui_data.controller, SDL_CONTROLLER_AXIS_LEFTY);
|
int16_t y_axis = SDL_GameControllerGetAxis(gui_data.controllers[0].controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||||
|
|
||||||
if (x_axis >= JOYSTICK_HIGH) {
|
if (x_axis >= JOYSTICK_HIGH) {
|
||||||
controller_state |= BUTTON_MASK_RIGHT;
|
controller_state |= BUTTON_MASK_RIGHT;
|
||||||
@ -1206,14 +1247,14 @@ static void handle_events(GB_gameboy_t *gb) {
|
|||||||
controller_state |= BUTTON_MASK_UP;
|
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.controllers[0].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.controllers[0].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.controllers[0].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.controllers[0].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.controllers[0].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.controllers[0].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.controllers[0].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_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));
|
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_log_callback(&gb, console_log);
|
||||||
GB_set_boot_rom_load_callback(&gb, load_boot_rom);
|
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) {
|
if (get_display_border_mode() <= GB_BORDER_ALWAYS) {
|
||||||
GB_set_border_mode(&gb, get_display_border_mode());
|
GB_set_border_mode(&gb, get_display_border_mode());
|
||||||
}
|
}
|
||||||
@ -1465,6 +1509,13 @@ static void run(void) {
|
|||||||
static void quit(void) {
|
static void quit(void) {
|
||||||
stop();
|
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.
|
// Quit our application properly.
|
||||||
// This fires the “shutdown” signal.
|
// This fires the “shutdown” signal.
|
||||||
g_application_quit(G_APPLICATION(gui_data.main_application));
|
g_application_quit(G_APPLICATION(gui_data.main_application));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.22.1
|
<!-- Generated with glade 3.22.2
|
||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Preferences</property>
|
<property name="title" translatable="yes">Preferences</property>
|
||||||
<property name="resizable">False</property>
|
<property name="resizable">False</property>
|
||||||
<child>
|
<child type="titlebar">
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@ -555,7 +555,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="margin_top">16</property>
|
<property name="margin_top">16</property>
|
||||||
<property name="margin_bottom">16</property>
|
<property name="margin_bottom">16</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -598,7 +597,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -632,7 +630,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">3</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -664,7 +661,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">5</property>
|
<property name="position">5</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -697,7 +693,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">7</property>
|
<property name="position">7</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -729,7 +724,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">9</property>
|
<property name="position">9</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="integer_scaling_toggle">
|
<object class="GtkCheckButton" id="integer_scaling_toggle">
|
||||||
<property name="label" translatable="yes">Use Integer Scaling</property>
|
<property name="label" translatable="yes">Use Integer Scaling</property>
|
||||||
@ -747,7 +741,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">10</property>
|
<property name="position">10</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="aspect_ratio_toggle">
|
<object class="GtkCheckButton" id="aspect_ratio_toggle">
|
||||||
<property name="label" translatable="yes">Keep Aspect Ratio</property>
|
<property name="label" translatable="yes">Keep Aspect Ratio</property>
|
||||||
@ -765,7 +758,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">11</property>
|
<property name="position">11</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="menubar_override_selector_label">
|
<object class="GtkLabel" id="menubar_override_selector_label">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
@ -799,7 +791,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child type="tab">
|
<child type="tab">
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -983,6 +974,7 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="margin_left">5</property>
|
<property name="margin_left">5</property>
|
||||||
<property name="margin_right">5</property>
|
<property name="margin_right">5</property>
|
||||||
<property name="active">0</property>
|
<property name="active">0</property>
|
||||||
|
<property name="active_id">0</property>
|
||||||
<items>
|
<items>
|
||||||
<item id="0" translatable="yes">Player 1</item>
|
<item id="0" translatable="yes">Player 1</item>
|
||||||
<item id="1" translatable="yes">Player 2</item>
|
<item id="1" translatable="yes">Player 2</item>
|
||||||
@ -1293,7 +1285,7 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">VRAM Viewer</property>
|
<property name="title" translatable="yes">VRAM Viewer</property>
|
||||||
<property name="resizable">False</property>
|
<property name="resizable">False</property>
|
||||||
<child>
|
<child type="titlebar">
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
Loading…
Reference in New Issue
Block a user