[GTK3] Move some functions into util.c

This commit is contained in:
Maximilian Mader 2020-05-16 17:48:29 +02:00
parent 7dbd0e18f9
commit accaedbdac
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
4 changed files with 290 additions and 254 deletions

View File

@ -1,7 +1,6 @@
#define G_LOG_USE_STRUCTURED
#include <gtk/gtk.h>
#include <epoxy/gl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
@ -9,7 +8,9 @@
#include <Core/gb.h>
#include "types.h"
#include "config.h"
#include "util.h"
#include "shader.h"
#include "check_menu_radio_group.h"
@ -29,113 +30,12 @@
#define BUTTON_MASK_LEFT 0x40
#define BUTTON_MASK_RIGHT 0x80
#define tileset_buffer_length 256 * 192 * 4
#define tilemap_buffer_length 256 * 256 * 4
#define str(x) #x
#define xstr(x) str(x)
#define get_object(id) gtk_builder_get_object(gui_data.builder, id)
#define builder_get(type, id) type(get_object(id))
#define action_set_enabled(map, name, value) g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(map), name)), value);
typedef struct{
int16_t x, y;
uint16_t w, h;
} Rect;
typedef struct GuiData {
struct CliOptionData {
gchar *config_path;
gchar *boot_rom_path;
gboolean fullscreen;
GB_model_t model;
gboolean force_software_renderer;
} cli_options;
GFile *file;
gint sample_rate;
GDateTime *config_modification_date;
char *battery_save_path;
char *cheats_save_path;
GB_model_t prev_model;
const GThread *main_thread;
volatile bool running;
volatile bool stopping;
volatile bool stopped;
// GTK pointers
GtkApplication *main_application;
GtkBuilder *builder;
GtkApplicationWindow *main_window;
GtkBox *main_window_container;
GtkGLArea *gl_area;
GtkDrawingArea *fallback_canvas;
GtkWindow *preferences;
GtkWindow *vram_viewer;
GtkWindow *memory_viewer;
GtkWindow *console;
GtkWindow *printer;
// Debugger state
GtkTextBuffer *pending_console_output;
gboolean in_sync_input;
gchar *last_console_input;
gboolean log_to_sidebar;
gboolean should_clear_sidebar;
GMutex debugger_input_mutex;
GCond debugger_input_cond;
GRecMutex console_output_lock;
GPtrArray *debugger_input_queue;
bool vram_viewer_visible;
bool vram_viewer_updating;
gchar *vram_viewer_active_tab;
gboolean vram_viewer_is_cgb;
uint8_t vram_viewer_palette_data[16][0x40];
GB_oam_info_t oam_info[40];
uint16_t oam_count;
uint8_t oam_height;
uint32_t tileset_buffer[tileset_buffer_length];
uint32_t tilemap_buffer[tilemap_buffer_length];
GMutex tileset_buffer_mutex;
GMutex tilemap_buffer_mutex;
Rect scroll_rect;
// Audio and video
bool audio_initialized;
uint32_t *image_buffers[3];
unsigned char current_buffer;
Rect viewport;
bool border_mode_changed;
bool is_fullscreen;
bool supports_gl;
shader_t shader;
unsigned last_screen_width;
unsigned last_screen_height;
// Fast forward / slow motion
bool underclock_down;
bool rewind_down;
bool do_rewind;
bool rewind_paused;
bool turbo_down;
double clock_mutliplier;
double analog_clock_multiplier;
bool analog_clock_multiplier_valid;
// Input
uint8_t pressed_buttons;
struct Controller_t {
SDL_GameController *controller;
SDL_Haptic *haptic;
bool ignore_rumble;
} *controllers;
unsigned controller_count;
struct Controller_t *last_used_controller; // Used for rumble
} GuiData;
// Initialize the GuiData
static GuiData gui_data = {
.cli_options = {
@ -168,44 +68,9 @@ static GuiData gui_data = {
.clock_mutliplier = 1.0,
.analog_clock_multiplier = 1.0,
};
GB_gameboy_t gb;
typedef enum {
INPUT_UP,
INPUT_DOWN,
INPUT_LEFT,
INPUT_RIGHT,
INPUT_A,
INPUT_B,
INPUT_START,
INPUT_SELECT,
INPUT_TURBO,
INPUT_REWIND,
INPUT_SLOWDOWN,
INPUT_FULLSCREEN,
} input_names_t;
static unsigned key_map[] = {
[INPUT_UP] = GDK_KEY_w,
[INPUT_LEFT] = GDK_KEY_a,
[INPUT_DOWN] = GDK_KEY_s,
[INPUT_RIGHT] = GDK_KEY_d,
[INPUT_A] = GDK_KEY_l,
[INPUT_B] = GDK_KEY_k,
[INPUT_START] = GDK_KEY_h,
[INPUT_SELECT] = GDK_KEY_g,
[INPUT_TURBO] = GDK_KEY_space,
[INPUT_REWIND] = GDK_KEY_Tab,
[INPUT_SLOWDOWN] = GDK_KEY_Shift_L,
[INPUT_FULLSCREEN] = GDK_KEY_F11,
};
// Forward declarations of the actions
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app);
static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer app);
@ -253,52 +118,6 @@ static const GActionEntry app_entries[] = {
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
};
static void replace_extension(const char *src, size_t length, char *dest, const char *ext) {
memcpy(dest, src, length);
dest[length] = 0;
/* Remove extension */
for (size_t i = length; i--;) {
if (dest[i] == '/') break;
if (dest[i] == '.') {
dest[i] = 0;
break;
}
}
/* Add new extension */
strcat(dest, ext);
}
static double clamp_double(double min, double max, double value) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static double max_double(double a, double b) {
if (a > b) return a;
return b;
}
static double min_double(double a, double b) {
if (a < b) return a;
return b;
}
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
return 0xFF000000 | (r << 16) | (g << 8) | b;
}
static uint32_t convert_color(uint16_t color) {
const uint8_t r = ((uint16_t)(color & 0x1F) * 255) / 31;
const uint8_t g = ((uint16_t)((color >> 5) & 0x1F) * 255) / 31;
const uint8_t b = ((uint16_t)((color >> 10) & 0x1F) * 255) / 31;
return (r << 16) | (g << 8) | b;
}
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data_ptr) {
const gchar *title = gtk_tree_view_column_get_title(col);
const uint8_t color_index = g_ascii_strtoll(&title[6], NULL, 10);
@ -405,51 +224,6 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
return -1;
}
// The main function for the OpenGL version check workaround
void gl_check_realize(GtkWidget *w, gpointer user_data_ptr) {
gboolean *result = (gboolean *) user_data_ptr;
GError *error = NULL;
GdkWindow *gdk_window = gtk_widget_get_window(w);
GdkGLContext *context = gdk_window_create_gl_context(gdk_window, &error);
if (error != NULL) {
g_warning("Failed to create context: %s", error->message);
g_error_free(error);
*result = false;
}
else {
gdk_gl_context_make_current(context);
int version = epoxy_gl_version();
g_object_run_dispose(G_OBJECT(context));
g_object_unref(context);
context = NULL;
gdk_gl_context_clear_current();
g_debug("OpenGL version: %d", version);
*result = version >= 32;
}
}
// Workaround to figure out if we have proper OpenGL support.
// Otherwise the application would crash after our GtkGlArea is realized
// and the context it uses is a legacy OpenGL 1.4 context because
// GTK3 calls OpenGL 2.0+ functions on it.
gboolean test_gl_support(void) {
gboolean result = false;
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "realize", G_CALLBACK(gl_check_realize), &result);
gtk_widget_realize(window);
gtk_widget_destroy(window);
window = NULL;
return result;
}
static gboolean init_controllers(void) {
SDL_version compiled;
SDL_version linked;
@ -564,14 +338,6 @@ static gboolean init_audio(void) {
return gui_data.audio_initialized = true;
}
static GB_model_t config_get_model_type(void) {
if (gui_data.cli_options.model != -1) {
return gui_data.cli_options.model;
}
return config_get_model();
}
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) {
if (gui_data.turbo_down) {
static unsigned skip = 0;
@ -806,19 +572,6 @@ static char *async_console_input(GB_gameboy_t *gb) {
return input;
}
GtkWidget *menubar_to_menu(GtkMenuBar *menubar) {
GtkWidget *menu = gtk_menu_new();
g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar));
while (iter) {
GtkWidget *item = GTK_WIDGET(iter->data);
gtk_widget_reparent(item, menu);
iter = iter->next;
}
return menu;
}
// Creating these items in the UI defintion files was buggy in some desktop
// environments and the manual call of `g_signal_connect` was needed anyway
// because the UI definition cant define string arguments for signal handlers.
@ -1460,7 +1213,7 @@ static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type) {
static void init(void) {
if (GB_is_inited(&gb)) return;
GB_init(&gb, config_get_model_type());
GB_init(&gb, config_get_model_type(&gui_data));
GB_set_vblank_callback(&gb, vblank);
GB_set_pixels_output(&gb, get_current_buffer());
@ -1487,8 +1240,8 @@ static void init(void) {
}
static void reset(void) {
g_debug("Reset: %d == %d", config_get_model_type(), gui_data.prev_model);
GB_model_t current_model = config_get_model_type();
g_debug("Reset: %d == %d", config_get_model_type(&gui_data), gui_data.prev_model);
GB_model_t current_model = config_get_model_type(&gui_data);
if (gui_data.prev_model == -1 || gui_data.prev_model == current_model) {
GB_reset(&gb);
@ -1499,7 +1252,7 @@ static void reset(void) {
GB_set_palette(&gb, config_get_monochrome_palette());
gui_data.prev_model = config_get_model_type();
gui_data.prev_model = config_get_model_type(&gui_data);
// Check SGB -> non-SGB and non-SGB to SGB transitions
if (GB_get_screen_width(&gb) != gui_data.last_screen_width || GB_get_screen_height(&gb) != gui_data.last_screen_height) {

144
gtk3/types.h Normal file
View File

@ -0,0 +1,144 @@
#ifndef types_h
#define types_h
#include "SDL.h"
#include "shader.h"
#define tileset_buffer_length 256 * 192 * 4
#define tilemap_buffer_length 256 * 256 * 4
typedef struct{
int16_t x, y;
uint16_t w, h;
} Rect;
typedef struct GuiData {
struct CliOptionData {
gchar *config_path;
gchar *boot_rom_path;
gboolean fullscreen;
GB_model_t model;
gboolean force_software_renderer;
} cli_options;
GFile *file;
gint sample_rate;
GDateTime *config_modification_date;
char *battery_save_path;
char *cheats_save_path;
GB_model_t prev_model;
const GThread *main_thread;
volatile bool running;
volatile bool stopping;
volatile bool stopped;
// GTK pointers
GtkApplication *main_application;
GtkBuilder *builder;
GtkApplicationWindow *main_window;
GtkBox *main_window_container;
GtkGLArea *gl_area;
GtkDrawingArea *fallback_canvas;
GtkWindow *preferences;
GtkWindow *vram_viewer;
GtkWindow *memory_viewer;
GtkWindow *console;
GtkWindow *printer;
// Debugger state
GtkTextBuffer *pending_console_output;
gboolean in_sync_input;
gchar *last_console_input;
gboolean log_to_sidebar;
gboolean should_clear_sidebar;
GMutex debugger_input_mutex;
GCond debugger_input_cond;
GRecMutex console_output_lock;
GPtrArray *debugger_input_queue;
bool vram_viewer_visible;
bool vram_viewer_updating;
gchar *vram_viewer_active_tab;
gboolean vram_viewer_is_cgb;
uint8_t vram_viewer_palette_data[16][0x40];
GB_oam_info_t oam_info[40];
uint16_t oam_count;
uint8_t oam_height;
uint32_t tileset_buffer[tileset_buffer_length];
uint32_t tilemap_buffer[tilemap_buffer_length];
GMutex tileset_buffer_mutex;
GMutex tilemap_buffer_mutex;
Rect scroll_rect;
// Audio and video
bool audio_initialized;
uint32_t *image_buffers[3];
unsigned char current_buffer;
Rect viewport;
bool border_mode_changed;
bool is_fullscreen;
bool supports_gl;
shader_t shader;
unsigned last_screen_width;
unsigned last_screen_height;
// Fast forward / slow motion
bool underclock_down;
bool rewind_down;
bool do_rewind;
bool rewind_paused;
bool turbo_down;
double clock_mutliplier;
double analog_clock_multiplier;
bool analog_clock_multiplier_valid;
// Input
uint8_t pressed_buttons;
struct Controller_t {
SDL_GameController *controller;
SDL_Haptic *haptic;
bool ignore_rumble;
} *controllers;
unsigned controller_count;
struct Controller_t *last_used_controller; // Used for rumble
} GuiData;
typedef enum {
INPUT_UP,
INPUT_DOWN,
INPUT_LEFT,
INPUT_RIGHT,
INPUT_A,
INPUT_B,
INPUT_START,
INPUT_SELECT,
INPUT_TURBO,
INPUT_REWIND,
INPUT_SLOWDOWN,
INPUT_FULLSCREEN,
} input_names_t;
static unsigned key_map[] = {
[INPUT_UP] = GDK_KEY_w,
[INPUT_LEFT] = GDK_KEY_a,
[INPUT_DOWN] = GDK_KEY_s,
[INPUT_RIGHT] = GDK_KEY_d,
[INPUT_A] = GDK_KEY_l,
[INPUT_B] = GDK_KEY_k,
[INPUT_START] = GDK_KEY_h,
[INPUT_SELECT] = GDK_KEY_g,
[INPUT_TURBO] = GDK_KEY_space,
[INPUT_REWIND] = GDK_KEY_Tab,
[INPUT_SLOWDOWN] = GDK_KEY_Shift_L,
[INPUT_FULLSCREEN] = GDK_KEY_F11,
};
#endif

114
gtk3/util.c Normal file
View File

@ -0,0 +1,114 @@
#include "util.h"
#include "config.h"
#include <epoxy/gl.h>
// Workaround to figure out if we have proper OpenGL support.
// Otherwise the application would crash after our GtkGlArea is realized
// and the context it uses is a legacy OpenGL 1.4 context because
// GTK3 calls OpenGL 2.0+ functions on it.
bool test_gl_support(void) {
gboolean result = false;
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "realize", G_CALLBACK(gl_check_realize), &result);
gtk_widget_realize(window);
gtk_widget_destroy(window);
window = NULL;
return result;
}
// The main function for the OpenGL version check workaround
void gl_check_realize(GtkWidget *w, gpointer user_data_ptr) {
gboolean *result = (gboolean *) user_data_ptr;
GError *error = NULL;
GdkWindow *gdk_window = gtk_widget_get_window(w);
GdkGLContext *context = gdk_window_create_gl_context(gdk_window, &error);
if (error != NULL) {
g_warning("Failed to create context: %s", error->message);
g_error_free(error);
*result = false;
}
else {
gdk_gl_context_make_current(context);
int version = epoxy_gl_version();
g_object_run_dispose(G_OBJECT(context));
g_object_unref(context);
context = NULL;
gdk_gl_context_clear_current();
g_debug("OpenGL version: %d", version);
*result = version >= 32;
}
}
void replace_extension(const char *src, size_t length, char *dest, const char *ext) {
memcpy(dest, src, length);
dest[length] = 0;
/* Remove extension */
for (size_t i = length; i--;) {
if (dest[i] == '/') break;
if (dest[i] == '.') {
dest[i] = 0;
break;
}
}
/* Add new extension */
strcat(dest, ext);
}
double clamp_double(double min, double max, double value) {
if (value < min) return min;
if (value > max) return max;
return value;
}
double max_double(double a, double b) {
if (a > b) return a;
return b;
}
double min_double(double a, double b) {
if (a < b) return a;
return b;
}
uint32_t convert_color(uint16_t color) {
const uint8_t r = ((uint16_t)(color & 0x1F) * 255) / 31;
const uint8_t g = ((uint16_t)((color >> 5) & 0x1F) * 255) / 31;
const uint8_t b = ((uint16_t)((color >> 10) & 0x1F) * 255) / 31;
return (r << 16) | (g << 8) | b;
}
uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
return 0xFF000000 | (r << 16) | (g << 8) | b;
}
GB_model_t config_get_model_type(GuiData *gui_data) {
if (gui_data->cli_options.model != -1) {
return gui_data->cli_options.model;
}
return config_get_model();
}
GtkWidget *menubar_to_menu(GtkMenuBar *menubar) {
GtkWidget *menu = gtk_menu_new();
g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar));
while (iter) {
GtkWidget *item = GTK_WIDGET(iter->data);
gtk_widget_reparent(item, menu);
iter = iter->next;
}
return menu;
}

25
gtk3/util.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef util_h
#define util_h
#include <stdbool.h>
#include <gtk/gtk.h>
#include <Core/gb.h>
#include "types.h"
bool test_gl_support(void);
void gl_check_realize(GtkWidget *w, gpointer user_data_ptr);
void replace_extension(const char *src, size_t length, char *dest, const char *ext);
double clamp_double(double min, double max, double value);
double max_double(double a, double b);
double min_double(double a, double b);
uint32_t convert_color(uint16_t color);
uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
GB_model_t config_get_model_type(GuiData *gui_data);
GtkWidget *menubar_to_menu(GtkMenuBar *menubar);
#endif