[GTK3] Implement more menu handlers
Also fix some memory leaks.
This commit is contained in:
parent
10ac1bd0a5
commit
df9189448d
471
gtk3/main.c
471
gtk3/main.c
@ -38,21 +38,23 @@ static GtkWindow *printer;
|
|||||||
|
|
||||||
static shader_t shader;
|
static shader_t shader;
|
||||||
|
|
||||||
static UserData user_data = { NULL };
|
static GuiData gui_data = { NULL };
|
||||||
static GB_gameboy_t gb;
|
static GB_gameboy_t gb;
|
||||||
static uint32_t *image_buffers[3];
|
static uint32_t *image_buffers[3];
|
||||||
static unsigned char current_buffer;
|
static unsigned char current_buffer;
|
||||||
|
|
||||||
static bool supports_gl;
|
static bool supports_gl;
|
||||||
|
static bool is_fullscreen;
|
||||||
|
|
||||||
static bool paused = false;
|
|
||||||
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
||||||
static double clock_mutliplier = 1.0;
|
static double clock_mutliplier = 1.0;
|
||||||
static char *battery_save_path_ptr;
|
static char *battery_save_path_ptr;
|
||||||
static Rect viewport = {0};
|
static Rect viewport = {0};
|
||||||
static Rect scrollRect = {0};
|
static Rect scrollRect = {0};
|
||||||
static bool vram_viewer_visible = false;
|
static bool vram_viewer_visible = false;
|
||||||
static bool running = true;
|
|
||||||
|
static bool running = false;
|
||||||
|
static bool stopping = false;
|
||||||
|
|
||||||
#define tileset_buffer_length 256 * 192 * 4
|
#define tileset_buffer_length 256 * 192 * 4
|
||||||
static uint32_t tileset_buffer[tileset_buffer_length] = {0};
|
static uint32_t tileset_buffer[tileset_buffer_length] = {0};
|
||||||
@ -81,6 +83,12 @@ static const GActionEntry app_entries[] = {
|
|||||||
{ "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
|
{ "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
|
||||||
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
|
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
|
||||||
{ "preferences", activate_preferences, NULL, NULL, NULL },
|
{ "preferences", activate_preferences, NULL, NULL, NULL },
|
||||||
|
{ "reset", activate_reset, NULL, NULL, NULL },
|
||||||
|
{ "toggle_blend_frames", NULL, NULL, "true", NULL },
|
||||||
|
{ "toggle_developer_mode", NULL, NULL, "false", NULL },
|
||||||
|
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
|
||||||
|
{ "change_model", NULL, "s", "@s 'CGB'", on_model_changed },
|
||||||
|
{ "pause", NULL, NULL, "false", on_pause_changed },
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@ -91,9 +99,9 @@ int main(int argc, char *argv[]) {
|
|||||||
GOptionEntry entries[] = {
|
GOptionEntry entries[] = {
|
||||||
{ "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the application version", NULL },
|
{ "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the application version", NULL },
|
||||||
{ "fullscreen", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Start in fullscreen mode", NULL },
|
{ "fullscreen", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Start in fullscreen mode", NULL },
|
||||||
{ "bootrom", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &user_data.boot_rom_path, "Path to the boot ROM to use", "<file path>" },
|
{ "bootrom", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &gui_data.boot_rom_path, "Path to the boot ROM to use", "<file path>" },
|
||||||
{ "model", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "Override the model type to emulate", "<model type>" },
|
{ "model", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "Override the model type to emulate", "<model type>" },
|
||||||
{ "config", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &user_data.config_path, "Override the path of the configuration file", "<file path>" },
|
{ "config", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &gui_data.config_path, "Override the path of the configuration file", "<file path>" },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
// Setup our command line information
|
// Setup our command line information
|
||||||
@ -102,11 +110,11 @@ int main(int argc, char *argv[]) {
|
|||||||
g_application_set_option_context_summary(G_APPLICATION(main_application), "SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator.");
|
g_application_set_option_context_summary(G_APPLICATION(main_application), "SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator.");
|
||||||
|
|
||||||
// Add signal handlers
|
// Add signal handlers
|
||||||
g_signal_connect(main_application, "handle-local-options", G_CALLBACK(handle_local_options), &user_data);
|
g_signal_connect(main_application, "handle-local-options", G_CALLBACK(handle_local_options), &gui_data);
|
||||||
g_signal_connect(main_application, "startup", G_CALLBACK(startup), &user_data);
|
g_signal_connect(main_application, "startup", G_CALLBACK(startup), &gui_data);
|
||||||
g_signal_connect(main_application, "activate", G_CALLBACK(activate), &user_data);
|
g_signal_connect(main_application, "activate", G_CALLBACK(activate), &gui_data);
|
||||||
g_signal_connect(main_application, "open", G_CALLBACK(open), &user_data);
|
g_signal_connect(main_application, "open", G_CALLBACK(open), &gui_data);
|
||||||
g_signal_connect(main_application, "shutdown", G_CALLBACK(shutdown), &user_data);
|
g_signal_connect(main_application, "shutdown", G_CALLBACK(shutdown), &gui_data);
|
||||||
|
|
||||||
// Start our GApplication main loop
|
// Start our GApplication main loop
|
||||||
int status = g_application_run(G_APPLICATION(main_application), argc, argv);
|
int status = g_application_run(G_APPLICATION(main_application), argc, argv);
|
||||||
@ -116,8 +124,8 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 user_data_gptr) {
|
static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer gui_data_gptr) {
|
||||||
UserData *user_data = user_data_gptr;
|
GuiData *gui_data = gui_data_gptr;
|
||||||
guint32 count;
|
guint32 count;
|
||||||
|
|
||||||
if (g_variant_dict_lookup(options, "version", "b", &count)) {
|
if (g_variant_dict_lookup(options, "version", "b", &count)) {
|
||||||
@ -126,7 +134,7 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g_variant_dict_lookup(options, "fullscreen", "b", &count)) {
|
if (g_variant_dict_lookup(options, "fullscreen", "b", &count)) {
|
||||||
user_data->fullscreen = true;
|
gui_data->fullscreen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle model override
|
// Handle model override
|
||||||
@ -137,42 +145,42 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
|
|||||||
// TODO: Synchronize with GB_model_t (Core/gb.h)
|
// TODO: Synchronize with GB_model_t (Core/gb.h)
|
||||||
if (g_str_has_prefix(model_name, "DMG")) {
|
if (g_str_has_prefix(model_name, "DMG")) {
|
||||||
if (g_str_has_suffix(model_name, "-B") || g_strcmp0(model_name, "DMG") == 0) {
|
if (g_str_has_suffix(model_name, "-B") || g_strcmp0(model_name, "DMG") == 0) {
|
||||||
user_data->model = GB_MODEL_DMG_B;
|
gui_data->model = GB_MODEL_DMG_B;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
user_data->model = GB_MODEL_DMG_B;
|
gui_data->model = GB_MODEL_DMG_B;
|
||||||
g_printerr("Unsupported revision: %s\nFalling back to DMG-B", model_name);
|
g_printerr("Unsupported revision: %s\nFalling back to DMG-B", model_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (g_str_has_prefix(model_name, "SGB")) {
|
else if (g_str_has_prefix(model_name, "SGB")) {
|
||||||
if (g_str_has_suffix(model_name, "-NTSC") || g_strcmp0(model_name, "SGB") == 0) {
|
if (g_str_has_suffix(model_name, "-NTSC") || g_strcmp0(model_name, "SGB") == 0) {
|
||||||
user_data->model = GB_MODEL_SGB;
|
gui_data->model = GB_MODEL_SGB;
|
||||||
}
|
}
|
||||||
else if (g_str_has_suffix(model_name, "-PAL")) {
|
else if (g_str_has_suffix(model_name, "-PAL")) {
|
||||||
user_data->model = GB_MODEL_SGB | GB_MODEL_PAL_BIT;
|
gui_data->model = GB_MODEL_SGB | GB_MODEL_PAL_BIT;
|
||||||
}
|
}
|
||||||
else if (g_str_has_suffix(model_name, "2")) {
|
else if (g_str_has_suffix(model_name, "2")) {
|
||||||
user_data->model = GB_MODEL_SGB2;
|
gui_data->model = GB_MODEL_SGB2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
user_data->model = GB_MODEL_SGB2;
|
gui_data->model = GB_MODEL_SGB2;
|
||||||
g_printerr("Unsupported revision: %s\nFalling back to SGB2", model_name);
|
g_printerr("Unsupported revision: %s\nFalling back to SGB2", model_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (g_str_has_prefix(model_name, "CGB")) {
|
else if (g_str_has_prefix(model_name, "CGB")) {
|
||||||
if (g_str_has_suffix(model_name, "-C")) {
|
if (g_str_has_suffix(model_name, "-C")) {
|
||||||
user_data->model = GB_MODEL_CGB_C;
|
gui_data->model = GB_MODEL_CGB_C;
|
||||||
}
|
}
|
||||||
else if (g_str_has_suffix(model_name, "-E") || g_strcmp0(model_name, "CGB") == 0) {
|
else if (g_str_has_suffix(model_name, "-E") || g_strcmp0(model_name, "CGB") == 0) {
|
||||||
user_data->model = GB_MODEL_CGB_E;
|
gui_data->model = GB_MODEL_CGB_E;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
user_data->model = GB_MODEL_CGB_E;
|
gui_data->model = GB_MODEL_CGB_E;
|
||||||
g_printerr("Unsupported revision: %s\nFalling back to CGB-E", model_name);
|
g_printerr("Unsupported revision: %s\nFalling back to CGB-E", model_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (g_str_has_prefix(model_name, "AGB")) {
|
else if (g_str_has_prefix(model_name, "AGB")) {
|
||||||
user_data->model = GB_MODEL_AGB;
|
gui_data->model = GB_MODEL_AGB;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
g_printerr("Unknown model: %s\n", model_name);
|
g_printerr("Unknown model: %s\n", model_name);
|
||||||
@ -190,7 +198,7 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
|
|||||||
gboolean test_gl_support(void) {
|
gboolean test_gl_support(void) {
|
||||||
gboolean result = FALSE;
|
gboolean result = FALSE;
|
||||||
|
|
||||||
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
g_signal_connect(window, "realize", G_CALLBACK(gl_check_realize), &result);
|
g_signal_connect(window, "realize", G_CALLBACK(gl_check_realize), &result);
|
||||||
gtk_widget_realize(window);
|
gtk_widget_realize(window);
|
||||||
gtk_widget_destroy(window);
|
gtk_widget_destroy(window);
|
||||||
@ -271,13 +279,18 @@ static gboolean init_controllers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean init_audio() {
|
static gboolean init_audio() {
|
||||||
|
bool audio_playing = SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
|
||||||
|
SDL_PauseAudioDevice(device_id, 1);
|
||||||
|
SDL_ClearQueuedAudio(device_id);
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||||
g_print("Failed to initialize audio: %s\n", SDL_GetError());
|
g_print("Failed to initialize audio: %s\n", SDL_GetError());
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&want_aspec, 0, sizeof(want_aspec));
|
memset(&want_aspec, 0, sizeof(want_aspec));
|
||||||
want_aspec.freq = DEFAULT_AUDIO_SAMPLE_RATE;
|
want_aspec.freq = gui_data.sample_rate;
|
||||||
want_aspec.format = AUDIO_S16SYS;
|
want_aspec.format = AUDIO_S16SYS;
|
||||||
want_aspec.channels = 2;
|
want_aspec.channels = 2;
|
||||||
want_aspec.samples = 512;
|
want_aspec.samples = 512;
|
||||||
@ -302,7 +315,10 @@ static gboolean init_audio() {
|
|||||||
|
|
||||||
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
|
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
|
||||||
|
|
||||||
SDL_PauseAudioDevice(device_id, 0);
|
g_print("Requested Sample Rate: %d Hz\nUsed Sample Rate: %d Hz\n", want_aspec.freq, have_aspec.freq);
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(device_id, audio_playing? 0 : 1);
|
||||||
|
GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -327,6 +343,8 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char *sync_console_input(GB_gameboy_t *gb) {
|
static char *sync_console_input(GB_gameboy_t *gb) {
|
||||||
|
console_log(gb, ">", 0);
|
||||||
|
|
||||||
g_mutex_lock(&debugger_input_mutex);
|
g_mutex_lock(&debugger_input_mutex);
|
||||||
g_cond_wait(&debugger_input_cond, &debugger_input_mutex);
|
g_cond_wait(&debugger_input_cond, &debugger_input_mutex);
|
||||||
|
|
||||||
@ -359,12 +377,6 @@ static char *async_console_input(GB_gameboy_t *gb) {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct LogData {
|
|
||||||
GB_gameboy_t *gb;
|
|
||||||
const char *string;
|
|
||||||
GB_log_attributes attributes;
|
|
||||||
} LogData;
|
|
||||||
|
|
||||||
static void on_console_log(gpointer user_data_gptr) {
|
static void on_console_log(gpointer user_data_gptr) {
|
||||||
LogData *log_data = (LogData *)user_data_gptr;
|
LogData *log_data = (LogData *)user_data_gptr;
|
||||||
GB_gameboy_t *gb = log_data->gb;
|
GB_gameboy_t *gb = log_data->gb;
|
||||||
@ -512,21 +524,19 @@ static void setup_menu(GApplication *app) {
|
|||||||
// Recursively goes through all children of the given container and sets
|
// Recursively goes through all children of the given container and sets
|
||||||
// our `is_separator` function to all children of type`GtkComboBox`
|
// our `is_separator` function to all children of type`GtkComboBox`
|
||||||
static void set_combo_box_row_separator_func(GtkContainer *container) {
|
static void set_combo_box_row_separator_func(GtkContainer *container) {
|
||||||
GList *list = gtk_container_get_children(container);
|
GList *children = gtk_container_get_children(container);
|
||||||
|
|
||||||
while (list) {
|
for (GList *l = children; l; l = l->next) {
|
||||||
if (GTK_IS_COMBO_BOX(list->data)) {
|
if (GTK_IS_COMBO_BOX(l->data)) {
|
||||||
gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(list->data), is_separator, NULL, NULL);
|
gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(l->data), is_separator, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GTK_IS_CONTAINER(list->data)) {
|
if (GTK_IS_CONTAINER(l->data)) {
|
||||||
set_combo_box_row_separator_func(GTK_CONTAINER(list->data));
|
set_combo_box_row_separator_func(GTK_CONTAINER(l->data));
|
||||||
}
|
}
|
||||||
|
|
||||||
list = list->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_list_free_full(list, NULL);
|
g_list_free(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if a ComboBox entry should be converted into a separator.
|
// Determines if a ComboBox entry should be converted into a separator.
|
||||||
@ -536,14 +546,19 @@ static gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer da
|
|||||||
|
|
||||||
gtk_tree_model_get(model, iter, 0, &text, -1);
|
gtk_tree_model_get(model, iter, 0, &text, -1);
|
||||||
gboolean result = g_strcmp0("<separator>", text) == 0;
|
gboolean result = g_strcmp0("<separator>", text) == 0;
|
||||||
|
g_free(text);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines how many frame buffers to use
|
// Determines how many frame buffers to use
|
||||||
static unsigned char number_of_buffers(void) {
|
static unsigned char number_of_buffers(void) {
|
||||||
// TODO
|
if (fallback_canvas) return 2;
|
||||||
bool should_blend = !fallback_canvas;
|
|
||||||
|
// TODO: Should we cache the action?
|
||||||
|
GAction *action = g_action_map_lookup_action(G_ACTION_MAP(main_application), "toggle_blend_frames");
|
||||||
|
GVariant *value = g_action_get_state(action);
|
||||||
|
gboolean should_blend = g_variant_get_boolean(value);
|
||||||
|
|
||||||
return should_blend? 3 : 2;
|
return should_blend? 3 : 2;
|
||||||
}
|
}
|
||||||
@ -573,8 +588,8 @@ static void quit_interrupt(int ignored) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This functions gets called immediately after registration of the GApplication
|
// This functions gets called immediately after registration of the GApplication
|
||||||
static void startup(GApplication *app, gpointer user_data_gptr) {
|
static void startup(GApplication *app, gpointer gui_data_gptr) {
|
||||||
UserData *user_data = user_data_gptr;
|
GuiData *gui_data = gui_data_gptr;
|
||||||
|
|
||||||
signal(SIGINT, quit_interrupt);
|
signal(SIGINT, quit_interrupt);
|
||||||
|
|
||||||
@ -605,7 +620,7 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
preferences = GTK_WINDOW(get_object("preferences"));
|
preferences = GTK_WINDOW(get_object("preferences"));
|
||||||
|
|
||||||
g_signal_connect(preferences, "realize", G_CALLBACK(on_preferences_realize), (gpointer) builder);
|
g_signal_connect(preferences, "realize", G_CALLBACK(on_preferences_realize), (gpointer) builder);
|
||||||
init_settings(user_data->config_path, preferences);
|
init_settings(gui_data->config_path, preferences);
|
||||||
|
|
||||||
vram_viewer = GTK_WINDOW(get_object("vram_viewer"));
|
vram_viewer = GTK_WINDOW(get_object("vram_viewer"));
|
||||||
memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
|
memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
|
||||||
@ -613,6 +628,13 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
console = GTK_WINDOW(get_object("console"));
|
console = GTK_WINDOW(get_object("console"));
|
||||||
printer = GTK_WINDOW(get_object("printer"));
|
printer = GTK_WINDOW(get_object("printer"));
|
||||||
|
|
||||||
|
if (config.sample_rate == -1) {
|
||||||
|
gui_data->sample_rate = DEFAULT_AUDIO_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gui_data->sample_rate = config.sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
// setup main window
|
// setup main window
|
||||||
main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
|
main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
|
||||||
main_window_container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
|
main_window_container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
|
||||||
@ -652,18 +674,20 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
|
|
||||||
// Add missing information to the about dialog
|
// Add missing information to the about dialog
|
||||||
GtkAboutDialog *about_dialog = GTK_ABOUT_DIALOG(get_object("about_dialog"));
|
GtkAboutDialog *about_dialog = GTK_ABOUT_DIALOG(get_object("about_dialog"));
|
||||||
gtk_about_dialog_set_logo(about_dialog, g_list_nth_data(icon_list, 3)); // reuse the 64x64 icon
|
gtk_about_dialog_set_logo(about_dialog, gdk_pixbuf_new_from_resource(icons[2], NULL)); // reuse the 64x64 icon
|
||||||
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
||||||
g_list_free(icon_list);
|
g_list_free_full(icon_list, g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets called when the GApplication gets activated, i.e. it is ready to show widgets.
|
// This function gets called when the GApplication gets activated, i.e. it is ready to show widgets.
|
||||||
static void activate(GApplication *app, gpointer user_data_gptr) {
|
static void activate(GApplication *app, gpointer gui_data_gptr) {
|
||||||
UserData *user_data = user_data_gptr;
|
GuiData *gui_data = gui_data_gptr;
|
||||||
|
|
||||||
if (user_data->fullscreen) {
|
// initialize SameBoy core
|
||||||
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
init(gui_data);
|
||||||
}
|
|
||||||
|
init_audio();
|
||||||
|
init_controllers();
|
||||||
|
|
||||||
// Connect signal handlers
|
// Connect signal handlers
|
||||||
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
|
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
|
||||||
@ -672,6 +696,7 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
g_signal_connect(main_window, "destroy", G_CALLBACK(on_quit), app);
|
g_signal_connect(main_window, "destroy", G_CALLBACK(on_quit), app);
|
||||||
g_signal_connect(main_window, "key_press_event", G_CALLBACK(on_key_press), NULL);
|
g_signal_connect(main_window, "key_press_event", G_CALLBACK(on_key_press), NULL);
|
||||||
g_signal_connect(main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
|
g_signal_connect(main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
|
||||||
|
g_signal_connect(main_window, "window-state-event", G_CALLBACK(on_window_state_change), NULL);
|
||||||
|
|
||||||
g_signal_connect(vram_viewer, "realize", G_CALLBACK(on_vram_viewer_realize), NULL);
|
g_signal_connect(vram_viewer, "realize", G_CALLBACK(on_vram_viewer_realize), NULL);
|
||||||
g_signal_connect(vram_viewer, "unrealize", G_CALLBACK(on_vram_viewer_unrealize), NULL);
|
g_signal_connect(vram_viewer, "unrealize", G_CALLBACK(on_vram_viewer_unrealize), NULL);
|
||||||
@ -712,7 +737,7 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
GdkScreen *screen = gdk_screen_get_default();
|
GdkScreen *screen = gdk_screen_get_default();
|
||||||
GtkCssProvider *provider = gtk_css_provider_new();
|
GtkCssProvider *provider = gtk_css_provider_new();
|
||||||
gtk_css_provider_load_from_resource(provider, RESOURCE_PREFIX "css/main.css");
|
gtk_css_provider_load_from_resource(provider, RESOURCE_PREFIX "css/main.css");
|
||||||
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
|
||||||
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
|
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
|
||||||
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
|
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
|
||||||
@ -720,35 +745,61 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
gtk_text_buffer_create_tag(text_buf, "underline", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
|
gtk_text_buffer_create_tag(text_buf, "underline", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
|
||||||
gtk_text_buffer_create_tag(text_buf, "dashed_underline", "underline", PANGO_UNDERLINE_DOUBLE, "underline-set", TRUE, NULL);
|
gtk_text_buffer_create_tag(text_buf, "dashed_underline", "underline", PANGO_UNDERLINE_DOUBLE, "underline-set", TRUE, NULL);
|
||||||
|
|
||||||
|
if (gui_data->fullscreen) {
|
||||||
|
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
||||||
|
}
|
||||||
|
|
||||||
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window));
|
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window));
|
||||||
gtk_widget_show_all(GTK_WIDGET(main_window));
|
gtk_widget_show_all(GTK_WIDGET(main_window));
|
||||||
|
|
||||||
g_thread_new("CoreLoop", run, user_data);
|
g_mutex_init(&debugger_input_mutex);
|
||||||
|
g_cond_init(&debugger_input_cond);
|
||||||
|
g_mutex_init(&console_output_lock);
|
||||||
|
|
||||||
|
if (!debugger_input_queue) {
|
||||||
|
debugger_input_queue = g_ptr_array_sized_new(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the emulation thread
|
||||||
|
run(gui_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets called when the application is closed.
|
// This function gets called when the application is closed.
|
||||||
static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr) {
|
static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer gui_data_gptr) {
|
||||||
g_print("SHUTDOWN\n");
|
g_print("SHUTDOWN\n");
|
||||||
|
|
||||||
|
stop(&gui_data);
|
||||||
|
while (stopping);
|
||||||
|
|
||||||
|
g_object_unref(builder);
|
||||||
|
|
||||||
save_settings();
|
save_settings();
|
||||||
free_settings();
|
free_settings();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
|
if (image_buffers[0]) g_free(image_buffers[0]);
|
||||||
|
if (image_buffers[1]) g_free(image_buffers[1]);
|
||||||
|
if (image_buffers[2]) g_free(image_buffers[2]);
|
||||||
|
free_shader(&shader);
|
||||||
|
free_master_shader();
|
||||||
|
|
||||||
|
GB_free(&gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets called when there are files to open.
|
// This function gets called when there are files to open.
|
||||||
// Note: When `open` gets called `activate` won’t fire unless we call it ourselves.
|
// Note: When `open` gets called `activate` won’t fire unless we call it ourselves.
|
||||||
static void open(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr) {
|
static void open(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer gui_data_gptr) {
|
||||||
UserData *user_data = user_data_gptr;
|
GuiData *gui_data = gui_data_gptr;
|
||||||
|
|
||||||
if (n_files > 1) {
|
if (n_files > 1) {
|
||||||
g_printerr("More than one file specified\n");
|
g_printerr("More than one file specified\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
user_data->file = g_file_dup(files[0]);
|
gui_data->file = g_file_dup(files[0]);
|
||||||
|
|
||||||
// We have handled the files, now activate the application
|
// We have handled the files, now activate the application
|
||||||
activate(app, user_data_gptr);
|
activate(app, gui_data_gptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell our application to quit.
|
// Tell our application to quit.
|
||||||
@ -782,6 +833,17 @@ static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data) {
|
|||||||
|
|
||||||
case GDK_KEY_k: mask = BUTTON_MASK_B; break;
|
case GDK_KEY_k: mask = BUTTON_MASK_B; break;
|
||||||
case GDK_KEY_l: mask = BUTTON_MASK_A; break;
|
case GDK_KEY_l: mask = BUTTON_MASK_A; break;
|
||||||
|
|
||||||
|
case GDK_KEY_F11: {
|
||||||
|
if (event->type == GDK_KEY_RELEASE) {
|
||||||
|
if (is_fullscreen) {
|
||||||
|
gtk_window_unfullscreen(GTK_WINDOW(main_window));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->type == GDK_KEY_PRESS) {
|
if (event->type == GDK_KEY_PRESS) {
|
||||||
@ -794,6 +856,10 @@ static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data) {
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_window_state_change(GtkWidget *w, GdkEventWindowState *event, gpointer data) {
|
||||||
|
is_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
// app.about GAction
|
// app.about GAction
|
||||||
// Opens the about dialog
|
// Opens the about dialog
|
||||||
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app) {
|
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app) {
|
||||||
@ -858,6 +924,72 @@ static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer a
|
|||||||
quit(G_APPLICATION(app));
|
quit(G_APPLICATION(app));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// app.reset GAction
|
||||||
|
// Resets the emulation
|
||||||
|
static void activate_reset(GSimpleAction *action, GVariant *parameter, gpointer app) {
|
||||||
|
if (gui_data.stopped) {
|
||||||
|
reset(&gui_data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
stop(&gui_data);
|
||||||
|
reset(&gui_data);
|
||||||
|
run(&gui_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_model_changed(GSimpleAction *action, GVariant *value, gpointer user_data) {
|
||||||
|
const gchar *model_str = g_variant_get_string(value, NULL);
|
||||||
|
|
||||||
|
GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(
|
||||||
|
GTK_WINDOW(main_window),
|
||||||
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_MESSAGE_QUESTION,
|
||||||
|
GTK_BUTTONS_YES_NO,
|
||||||
|
"Changing the emulated model requires a reset.\nChange model and reset game?"
|
||||||
|
));
|
||||||
|
|
||||||
|
stop(&gui_data);
|
||||||
|
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case GTK_RESPONSE_YES:
|
||||||
|
g_print("TODO: RESET!\n");
|
||||||
|
g_simple_action_set_state(action, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Action has been canceled
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(&gui_data);
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data) {
|
||||||
|
gboolean do_mute = g_variant_get_boolean(value);
|
||||||
|
|
||||||
|
if (do_mute) {
|
||||||
|
SDL_PauseAudioDevice(device_id, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SDL_ClearQueuedAudio(device_id);
|
||||||
|
SDL_PauseAudioDevice(device_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_simple_action_set_state(action, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_pause_changed(GSimpleAction *action, GVariant *value, gpointer user_data) {
|
||||||
|
if (g_variant_get_boolean(value)) {
|
||||||
|
stop(&gui_data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
run(&gui_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_simple_action_set_state(action, value);
|
||||||
|
}
|
||||||
|
|
||||||
// `destroy` signal GCallback
|
// `destroy` signal GCallback
|
||||||
// Exits the application
|
// Exits the application
|
||||||
static void on_quit(GtkWidget *w, gpointer app) {
|
static void on_quit(GtkWidget *w, gpointer app) {
|
||||||
@ -1225,6 +1357,7 @@ G_MODULE_EXPORT void on_graphic_filter_changed(GtkWidget *w, gpointer user_data_
|
|||||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||||
config.shader = (gchar *)gtk_combo_box_get_active_id(box);
|
config.shader = (gchar *)gtk_combo_box_get_active_id(box);
|
||||||
|
|
||||||
|
free_shader(&shader);
|
||||||
init_shader_with_name(&shader, config.shader);
|
init_shader_with_name(&shader, config.shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1248,6 +1381,20 @@ G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data
|
|||||||
config.rewind_duration = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10);
|
config.rewind_duration = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void on_sample_rate_changed(GtkWidget *w, gpointer user_data_gptr) {
|
||||||
|
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||||
|
config.sample_rate = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10);
|
||||||
|
|
||||||
|
if (config.sample_rate == -1) {
|
||||||
|
gui_data.sample_rate = DEFAULT_AUDIO_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gui_data.sample_rate = config.sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_audio();
|
||||||
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr) {
|
G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr) {
|
||||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||||
config.sgb_revision_name = (gchar *)gtk_combo_box_get_active_id(box);
|
config.sgb_revision_name = (gchar *)gtk_combo_box_get_active_id(box);
|
||||||
@ -1343,15 +1490,19 @@ static void update_window_geometry() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Setup our image buffers
|
// Setup our image buffers
|
||||||
if (image_buffers[0]) free(image_buffers[0]);
|
if (image_buffers[0]) g_free(image_buffers[0]);
|
||||||
if (image_buffers[1]) free(image_buffers[1]);
|
if (image_buffers[1]) g_free(image_buffers[1]);
|
||||||
if (image_buffers[2]) free(image_buffers[2]);
|
if (image_buffers[2]) g_free(image_buffers[2]);
|
||||||
|
|
||||||
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(&gb) * GB_get_screen_height(&gb);
|
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(&gb) * GB_get_screen_height(&gb);
|
||||||
|
|
||||||
image_buffers[0] = malloc(buffer_size);
|
image_buffers[0] = g_malloc0(buffer_size);
|
||||||
image_buffers[1] = malloc(buffer_size);
|
image_buffers[1] = g_malloc0(buffer_size);
|
||||||
image_buffers[2] = malloc(buffer_size);
|
image_buffers[2] = g_malloc0(buffer_size);
|
||||||
|
|
||||||
|
if (GB_is_inited(&gb)) {
|
||||||
|
GB_set_pixels_output(&gb, get_pixels());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_events(GB_gameboy_t *gb) {
|
static void handle_events(GB_gameboy_t *gb) {
|
||||||
@ -1630,52 +1781,53 @@ static void vblank(GB_gameboy_t *gb) {
|
|||||||
g_idle_add((GSourceFunc) on_vblank, NULL);
|
g_idle_add((GSourceFunc) on_vblank, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer run(gpointer user_data_gptr) {
|
static void run(GuiData *gui_data) {
|
||||||
UserData *user_data = user_data_gptr;
|
if (running) return;
|
||||||
|
while (stopping);
|
||||||
|
|
||||||
GB_model_t prev_model = GB_get_model(&gb);
|
g_thread_new("CoreLoop", run_thread, gui_data);
|
||||||
GB_model_t model = user_data->model? user_data->model : GB_MODEL_CGB_E; // TODO: Model from config
|
}
|
||||||
|
|
||||||
if (!debugger_input_queue) {
|
static gpointer run_thread(gpointer gui_data_gptr) {
|
||||||
g_mutex_init(&debugger_input_mutex);
|
GuiData *gui_data = gui_data_gptr;
|
||||||
g_cond_init(&debugger_input_cond);
|
|
||||||
g_mutex_init(&console_output_lock);
|
|
||||||
debugger_input_queue = g_ptr_array_sized_new(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GB_is_inited(&gb)) {
|
if (gui_data->stopped) {
|
||||||
GB_switch_model_and_reset(&gb, model);
|
start(gui_data);
|
||||||
|
|
||||||
GtkRequisition minimum_size;
|
|
||||||
GtkRequisition natural_size;
|
|
||||||
gtk_widget_get_preferred_size(GTK_WIDGET(main_window), &minimum_size, &natural_size);
|
|
||||||
|
|
||||||
// Check SGB -> non-SGB and non-SGB to SGB transitions
|
|
||||||
if (GB_get_screen_width(&gb) != minimum_size.width || GB_get_screen_height(&gb) != minimum_size.height) {
|
|
||||||
update_window_geometry();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
init_audio();
|
init(gui_data);
|
||||||
init_controllers();
|
reset(gui_data);
|
||||||
|
start(gui_data);
|
||||||
GB_init(&gb, model);
|
|
||||||
update_window_geometry();
|
|
||||||
|
|
||||||
GB_set_vblank_callback(&gb, vblank);
|
|
||||||
GB_set_pixels_output(&gb, get_current_buffer());
|
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
|
||||||
GB_set_sample_rate(&gb, DEFAULT_AUDIO_SAMPLE_RATE);
|
|
||||||
GB_set_color_correction_mode(&gb, get_color_correction_mode());
|
|
||||||
GB_set_highpass_filter_mode(&gb, get_highpass_mode());
|
|
||||||
GB_set_rewind_length(&gb, config.rewind_duration);
|
|
||||||
GB_set_update_input_hint_callback(&gb, handle_events);
|
|
||||||
GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
|
||||||
GB_set_input_callback(&gb, sync_console_input);
|
|
||||||
GB_set_async_input_callback(&gb, async_console_input);
|
|
||||||
GB_set_log_callback(&gb, console_log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(GuiData *gui_data) {
|
||||||
|
if (GB_is_inited(&gb)) return;
|
||||||
|
g_print("init: %p\n", g_thread_self());
|
||||||
|
|
||||||
|
GB_model_t prev_model = GB_get_model(&gb);
|
||||||
|
gui_data->model = gui_data->model? gui_data->model : GB_MODEL_CGB_E; // TODO: Model from config
|
||||||
|
|
||||||
|
GB_init(&gb, gui_data->model);
|
||||||
|
update_window_geometry();
|
||||||
|
|
||||||
|
GB_set_vblank_callback(&gb, vblank);
|
||||||
|
GB_set_pixels_output(&gb, get_current_buffer());
|
||||||
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
|
GB_set_sample_rate(&gb, gui_data->sample_rate);
|
||||||
|
GB_set_color_correction_mode(&gb, get_color_correction_mode());
|
||||||
|
GB_set_highpass_filter_mode(&gb, get_highpass_mode());
|
||||||
|
GB_set_rewind_length(&gb, config.rewind_duration);
|
||||||
|
GB_set_update_input_hint_callback(&gb, handle_events);
|
||||||
|
GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
||||||
|
GB_set_input_callback(&gb, sync_console_input);
|
||||||
|
GB_set_async_input_callback(&gb, async_console_input);
|
||||||
|
GB_set_log_callback(&gb, console_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_boot_rom(GuiData *gui_data) {
|
||||||
GError *error;
|
GError *error;
|
||||||
char *boot_rom_path;
|
char *boot_rom_path;
|
||||||
char *boot_rom_name;
|
char *boot_rom_name;
|
||||||
@ -1683,15 +1835,15 @@ static gpointer run(gpointer user_data_gptr) {
|
|||||||
const guchar *boot_rom_data;
|
const guchar *boot_rom_data;
|
||||||
gsize boot_rom_size;
|
gsize boot_rom_size;
|
||||||
|
|
||||||
if (user_data->boot_rom_path != NULL) {
|
if (gui_data->boot_rom_path != NULL) {
|
||||||
g_print("Trying to load boot ROM from %s\n", user_data->boot_rom_path);
|
g_print("Trying to load boot ROM from %s\n", gui_data->boot_rom_path);
|
||||||
if (GB_load_boot_rom(&gb, user_data->boot_rom_path)) {
|
if (GB_load_boot_rom(&gb, gui_data->boot_rom_path)) {
|
||||||
g_printerr("Falling back to boot ROM from config\n");
|
g_printerr("Falling back to boot ROM from config\n");
|
||||||
goto config_boot_rom;
|
goto config_boot_rom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { config_boot_rom:
|
else { config_boot_rom:
|
||||||
switch (model) {
|
switch (gui_data->model) {
|
||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
boot_rom_name = "dmg_boot.bin";
|
boot_rom_name = "dmg_boot.bin";
|
||||||
break;
|
break;
|
||||||
@ -1722,13 +1874,17 @@ static gpointer run(gpointer user_data_gptr) {
|
|||||||
g_print("Trying to load boot ROM from %s\n", boot_rom_path);
|
g_print("Trying to load boot ROM from %s\n", boot_rom_path);
|
||||||
|
|
||||||
if (GB_load_boot_rom(&gb, boot_rom_path)) {
|
if (GB_load_boot_rom(&gb, boot_rom_path)) {
|
||||||
|
g_free(boot_rom_path);
|
||||||
g_printerr("Falling back to internal boot ROM\n");
|
g_printerr("Falling back to internal boot ROM\n");
|
||||||
goto internal_boot_rom;
|
goto internal_boot_rom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(boot_rom_path);
|
||||||
}
|
}
|
||||||
else { internal_boot_rom:
|
else { internal_boot_rom:
|
||||||
boot_rom_path = g_build_filename(RESOURCE_PREFIX "bootroms/", boot_rom_name, NULL);
|
boot_rom_path = g_build_filename(RESOURCE_PREFIX "bootroms/", boot_rom_name, NULL);
|
||||||
boot_rom_f = g_resources_lookup_data(boot_rom_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
boot_rom_f = g_resources_lookup_data(boot_rom_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
||||||
|
g_free(boot_rom_path);
|
||||||
|
|
||||||
if (boot_rom_f == NULL) {
|
if (boot_rom_f == NULL) {
|
||||||
g_printerr("Failed to load internal boot ROM: %s\n", boot_rom_path);
|
g_printerr("Failed to load internal boot ROM: %s\n", boot_rom_path);
|
||||||
@ -1738,30 +1894,79 @@ static gpointer run(gpointer user_data_gptr) {
|
|||||||
|
|
||||||
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
|
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
|
||||||
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
|
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
|
||||||
|
|
||||||
|
g_bytes_unref(boot_rom_f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (user_data->file != NULL && GB_load_rom(&gb, g_file_get_path(user_data->file)) == 0) {
|
|
||||||
/* Run emulation */
|
static void stop(GuiData *gui_data) {
|
||||||
while (running) {
|
if (!running) return;
|
||||||
if (paused || rewind_paused) {
|
|
||||||
handle_events(&gb);
|
SDL_PauseAudioDevice(device_id, 1);
|
||||||
}
|
GB_debugger_set_disabled(&gb, true);
|
||||||
else {
|
|
||||||
if (do_rewind) {
|
if (GB_debugger_is_stopped(&gb)) {
|
||||||
GB_rewind_pop(&gb);
|
// [self interruptDebugInputRead];
|
||||||
if (turbo_down) {
|
}
|
||||||
GB_rewind_pop(&gb);
|
|
||||||
}
|
stopping = true;
|
||||||
if (!GB_rewind_pop(&gb)) {
|
running = false;
|
||||||
rewind_paused = true;
|
while (stopping);
|
||||||
}
|
|
||||||
do_rewind = false;
|
GB_debugger_set_disabled(&gb, false);
|
||||||
}
|
gui_data->stopped = true;
|
||||||
GB_run(&gb);
|
}
|
||||||
}
|
|
||||||
}
|
static void reset(GuiData *gui_data) {
|
||||||
}
|
GB_switch_model_and_reset(&gb, gui_data->model);
|
||||||
|
|
||||||
return NULL;
|
GtkRequisition minimum_size;
|
||||||
|
GtkRequisition natural_size;
|
||||||
|
gtk_widget_get_preferred_size(GTK_WIDGET(main_window), &minimum_size, &natural_size);
|
||||||
|
|
||||||
|
// Check SGB -> non-SGB and non-SGB to SGB transitions
|
||||||
|
if (GB_get_screen_width(&gb) != minimum_size.width || GB_get_screen_height(&gb) != minimum_size.height) {
|
||||||
|
update_window_geometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
load_boot_rom(gui_data);
|
||||||
|
|
||||||
|
char *path = g_file_get_path(gui_data->file);
|
||||||
|
|
||||||
|
if (GB_load_rom(&gb, path) != 0) {
|
||||||
|
g_print("Failed to load ROM: %s", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(GuiData *gui_data) {
|
||||||
|
running = true;
|
||||||
|
gui_data->stopped = false;
|
||||||
|
|
||||||
|
SDL_ClearQueuedAudio(device_id);
|
||||||
|
SDL_PauseAudioDevice(device_id, 0);
|
||||||
|
|
||||||
|
/* Run emulation */
|
||||||
|
while (running) {
|
||||||
|
if (rewind_paused) {
|
||||||
|
handle_events(&gb);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (do_rewind) {
|
||||||
|
GB_rewind_pop(&gb);
|
||||||
|
if (turbo_down) {
|
||||||
|
GB_rewind_pop(&gb);
|
||||||
|
}
|
||||||
|
if (!GB_rewind_pop(&gb)) {
|
||||||
|
rewind_paused = true;
|
||||||
|
}
|
||||||
|
do_rewind = false;
|
||||||
|
}
|
||||||
|
GB_run(&gb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopping = false;
|
||||||
}
|
}
|
||||||
|
58
gtk3/main.h
58
gtk3/main.h
@ -16,19 +16,39 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
|
|
||||||
typedef struct UserData {
|
enum generic_model {
|
||||||
|
MODEL_NONE,
|
||||||
|
MODEL_DMG,
|
||||||
|
MODEL_CGB,
|
||||||
|
MODEL_AGB,
|
||||||
|
MODEL_SGB,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct GuiData {
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
|
gint sample_rate;
|
||||||
|
|
||||||
GFile *file;
|
GFile *file;
|
||||||
gchar *boot_rom_path;
|
gchar *boot_rom_path;
|
||||||
gchar *config_path;
|
gchar *config_path;
|
||||||
|
|
||||||
|
enum generic_model generic_model;
|
||||||
GB_model_t model;
|
GB_model_t model;
|
||||||
} UserData;
|
|
||||||
|
bool stopped;
|
||||||
|
} GuiData;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
int16_t x, y;
|
int16_t x, y;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
} Rect;
|
} Rect;
|
||||||
|
|
||||||
|
typedef struct LogData {
|
||||||
|
GB_gameboy_t *gb;
|
||||||
|
const char *string;
|
||||||
|
GB_log_attributes attributes;
|
||||||
|
} LogData;
|
||||||
|
|
||||||
#define JOYSTICK_HIGH 0x4000
|
#define JOYSTICK_HIGH 0x4000
|
||||||
#define JOYSTICK_LOW 0x3800
|
#define JOYSTICK_LOW 0x3800
|
||||||
|
|
||||||
@ -46,8 +66,16 @@ int main(int argc, char *argv[]);
|
|||||||
static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer user_data_gptr);
|
static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer user_data_gptr);
|
||||||
|
|
||||||
// GtkGlArea crash workaround
|
// GtkGlArea crash workaround
|
||||||
void gl_check_realize(GtkWidget *w, gpointer user_data_gptr);
|
|
||||||
gboolean test_gl_support(void);
|
gboolean test_gl_support(void);
|
||||||
|
void gl_check_realize(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
|
||||||
|
static gboolean init_controllers();
|
||||||
|
static gboolean init_audio();
|
||||||
|
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample);
|
||||||
|
static char *sync_console_input(GB_gameboy_t *gb);
|
||||||
|
static char *async_console_input(GB_gameboy_t *gb);
|
||||||
|
static void on_console_log(gpointer user_data_gptr);
|
||||||
|
static void console_log(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
|
||||||
|
|
||||||
static GMenuModel *get_menu_model(GApplication *app, const char *id);
|
static GMenuModel *get_menu_model(GApplication *app, const char *id);
|
||||||
static void create_fallback_canvas(void);
|
static void create_fallback_canvas(void);
|
||||||
@ -62,6 +90,8 @@ static uint32_t *get_current_buffer(void);
|
|||||||
static uint32_t *get_previous_buffer(void);
|
static uint32_t *get_previous_buffer(void);
|
||||||
static void flip(void);
|
static void flip(void);
|
||||||
|
|
||||||
|
static void quit_interrupt(int ignored);
|
||||||
|
|
||||||
// GApplication signals
|
// GApplication signals
|
||||||
static void startup(GApplication *app, gpointer user_data_gptr);
|
static void startup(GApplication *app, gpointer user_data_gptr);
|
||||||
static void activate(GApplication *app, gpointer user_data_gptr);
|
static void activate(GApplication *app, gpointer user_data_gptr);
|
||||||
@ -69,6 +99,7 @@ static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar
|
|||||||
static void open(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr);
|
static void open(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr);
|
||||||
static void quit(GApplication *app);
|
static void quit(GApplication *app);
|
||||||
static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data);
|
static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data);
|
||||||
|
static void on_window_state_change(GtkWidget *w, GdkEventWindowState *event, gpointer data);
|
||||||
|
|
||||||
// App actions
|
// App actions
|
||||||
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app);
|
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
@ -79,6 +110,13 @@ static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter
|
|||||||
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app);
|
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer app);
|
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer app);
|
static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_reset(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
|
||||||
|
static void on_model_changed(GSimpleAction *action, GVariant *value, gpointer user_data);
|
||||||
|
static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data);
|
||||||
|
static void on_pause_changed(GSimpleAction *action, GVariant *value, gpointer user_data);
|
||||||
|
|
||||||
|
static void on_quit(GtkWidget *w, gpointer app);
|
||||||
|
|
||||||
// Signal callback
|
// Signal callback
|
||||||
static void on_quit(GtkWidget *w, gpointer app);
|
static void on_quit(GtkWidget *w, gpointer app);
|
||||||
@ -93,8 +131,8 @@ static void resize();
|
|||||||
// VRAM viewer bindings
|
// VRAM viewer bindings
|
||||||
static void on_vram_viewer_realize();
|
static void on_vram_viewer_realize();
|
||||||
static void on_vram_viewer_unrealize();
|
static void on_vram_viewer_unrealize();
|
||||||
static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data);
|
|
||||||
static gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t *cr, gpointer data);
|
static gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t *cr, gpointer data);
|
||||||
|
static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data);
|
||||||
static gboolean on_motion_vram_viewer_tileset(GtkWidget *widget, GdkEventMotion *event);
|
static gboolean on_motion_vram_viewer_tileset(GtkWidget *widget, GdkEventMotion *event);
|
||||||
static gboolean on_motion_vram_viewer_tilemap(GtkWidget *widget, GdkEventMotion *event);
|
static gboolean on_motion_vram_viewer_tilemap(GtkWidget *widget, GdkEventMotion *event);
|
||||||
static void on_vram_tab_change(GtkWidget *widget, GParamSpec *pspec, GtkStackSwitcher *self);
|
static void on_vram_tab_change(GtkWidget *widget, GParamSpec *pspec, GtkStackSwitcher *self);
|
||||||
@ -111,16 +149,26 @@ G_MODULE_EXPORT void on_keep_aspect_ratio_changed(GtkWidget *w, gpointer user_da
|
|||||||
G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data_gptr);
|
G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr);
|
G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
G_MODULE_EXPORT void on_use_integer_scaling_changed(GtkWidget *w, gpointer user_data_gptr);
|
G_MODULE_EXPORT void on_use_integer_scaling_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void console_on_enter(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
|
||||||
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
||||||
static void render_texture(void *pixels, void *previous);
|
static void render_texture(void *pixels, void *previous);
|
||||||
static void update_viewport(void);
|
static void update_viewport(void);
|
||||||
static void update_window_geometry();
|
static void update_window_geometry();
|
||||||
|
|
||||||
|
static void run(GuiData *gui_data);
|
||||||
|
static gpointer run_thread(gpointer user_data_gptr);
|
||||||
|
static void init(GuiData *gui_data);
|
||||||
|
static void load_boot_rom(GuiData *gui_data);
|
||||||
|
|
||||||
static void handle_events(GB_gameboy_t *gb);
|
static void handle_events(GB_gameboy_t *gb);
|
||||||
static uint32_t convert_color(uint16_t color);
|
static uint32_t convert_color(uint16_t color);
|
||||||
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
|
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
|
||||||
|
static void on_vblank(gpointer data);
|
||||||
static void vblank(GB_gameboy_t *gb);
|
static void vblank(GB_gameboy_t *gb);
|
||||||
static gpointer run(gpointer user_data_gptr);
|
|
||||||
|
static void reset(GuiData *gui_data);
|
||||||
|
static void stop(GuiData *gui_data);
|
||||||
|
static void start(GuiData *gui_data);
|
||||||
|
|
||||||
#endif /* main_h */
|
#endif /* main_h */
|
||||||
|
@ -288,22 +288,22 @@ Author: Maximilian Mader
|
|||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Game Boy</attribute>
|
<attribute name="label" translatable="yes">Game Boy</attribute>
|
||||||
<attribute name="action">app.change_model</attribute>
|
<attribute name="action">app.change_model</attribute>
|
||||||
<attribute name="model">DMG</attribute>
|
<attribute name="target">DMG</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Super Game Boy</attribute>
|
<attribute name="label" translatable="yes">Super Game Boy</attribute>
|
||||||
<attribute name="action">app.change_model</attribute>
|
<attribute name="action">app.change_model</attribute>
|
||||||
<attribute name="model">SGB</attribute>
|
<attribute name="target">SGB</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Game Boy Color</attribute>
|
<attribute name="label" translatable="yes">Game Boy Color</attribute>
|
||||||
<attribute name="action">app.change_model</attribute>
|
<attribute name="action">app.change_model</attribute>
|
||||||
<attribute name="model">CGB</attribute>
|
<attribute name="target">CGB</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Game Boy Advance</attribute>
|
<attribute name="label" translatable="yes">Game Boy Advance</attribute>
|
||||||
<attribute name="action">app.change_model</attribute>
|
<attribute name="action">app.change_model</attribute>
|
||||||
<attribute name="model">AGB</attribute>
|
<attribute name="target">AGB</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -195,9 +195,10 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTextView">
|
<object class="GtkTextView" id="console_sidebar_input">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
|
<property name="wrap_mode">word</property>
|
||||||
<property name="left_margin">5</property>
|
<property name="left_margin">5</property>
|
||||||
<property name="right_margin">5</property>
|
<property name="right_margin">5</property>
|
||||||
<property name="top_margin">5</property>
|
<property name="top_margin">5</property>
|
||||||
@ -235,10 +236,11 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTextView">
|
<object class="GtkTextView" id="console_sidebar_output">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="editable">False</property>
|
<property name="editable">False</property>
|
||||||
|
<property name="wrap_mode">word</property>
|
||||||
<property name="left_margin">5</property>
|
<property name="left_margin">5</property>
|
||||||
<property name="right_margin">5</property>
|
<property name="right_margin">5</property>
|
||||||
<property name="top_margin">5</property>
|
<property name="top_margin">5</property>
|
||||||
@ -653,23 +655,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">3</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkCheckButton" id="aspect_ratio_toggle">
|
|
||||||
<property name="label" translatable="yes">Keep Aspect Ratio</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="margin_top">5</property>
|
|
||||||
<property name="margin_bottom">10</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<signal name="toggled" handler="on_keep_aspect_ratio_changed" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">4</property>
|
|
||||||
</packing>
|
|
||||||
</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>
|
||||||
@ -687,6 +672,23 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">4</property>
|
<property name="position">4</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="aspect_ratio_toggle">
|
||||||
|
<property name="label" translatable="yes">Keep Aspect Ratio</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="margin_top">5</property>
|
||||||
|
<property name="margin_bottom">10</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_keep_aspect_ratio_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</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>
|
||||||
@ -797,6 +799,37 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Sample Rate:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBoxText" id="sample_rate_selector">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<items>
|
||||||
|
<item id="-1" translatable="yes">Default</item>
|
||||||
|
<item translatable="yes"><separator></item>
|
||||||
|
<item id="44100" translatable="yes">44.1 kHz</item>
|
||||||
|
<item id="48000" translatable="yes">48 kHz</item>
|
||||||
|
<item id="96000" translatable="yes">96 kHz</item>
|
||||||
|
</items>
|
||||||
|
<signal name="changed" handler="on_sample_rate_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="position">2</property>
|
<property name="position">2</property>
|
||||||
|
@ -107,6 +107,7 @@ void on_preferences_realize(GtkWidget *w, gpointer builder_ptr) {
|
|||||||
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "integer_scaling_toggle"), config.use_integer_scaling);
|
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "integer_scaling_toggle"), config.use_integer_scaling);
|
||||||
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "aspect_ratio_toggle"), config.keep_aspect_ratio);
|
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "aspect_ratio_toggle"), config.keep_aspect_ratio);
|
||||||
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "highpass_filter_selector"), config.high_pass_filter_id);
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "highpass_filter_selector"), config.high_pass_filter_id);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "sample_rate_selector"), itoa(config.sample_rate));
|
||||||
|
|
||||||
#if ! NDEBUG
|
#if ! NDEBUG
|
||||||
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "menubar_override_selector"), config.menubar_override);
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "menubar_override_selector"), config.menubar_override);
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
) \
|
) \
|
||||||
EXPAND_GROUP(Audio, \
|
EXPAND_GROUP(Audio, \
|
||||||
EXPAND_GROUP_MEMBER(high_pass_filter_id, string, "emulate_hardware") \
|
EXPAND_GROUP_MEMBER(high_pass_filter_id, string, "emulate_hardware") \
|
||||||
|
EXPAND_GROUP_MEMBER(sample_rate, integer, -1) \
|
||||||
) \
|
) \
|
||||||
EXPAND_GROUP(Controls, \
|
EXPAND_GROUP(Controls, \
|
||||||
\
|
\
|
||||||
|
@ -8,6 +8,10 @@ gl_Position = aPosition;\n\
|
|||||||
}\n\
|
}\n\
|
||||||
";
|
";
|
||||||
|
|
||||||
|
static GBytes *master_shader_f = NULL;
|
||||||
|
static const gchar *master_shader_code;
|
||||||
|
static gsize master_shader_code_size = 0;
|
||||||
|
|
||||||
static GLuint create_shader(const char *source, GLenum type)
|
static GLuint create_shader(const char *source, GLenum type)
|
||||||
{
|
{
|
||||||
// Create the shader object
|
// Create the shader object
|
||||||
@ -66,9 +70,6 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GBytes *master_shader_f = NULL;
|
|
||||||
static const gchar *master_shader_code;
|
|
||||||
static gsize master_shader_code_size;
|
|
||||||
static char final_shader_code[0x10801] = {0,};
|
static char final_shader_code[0x10801] = {0,};
|
||||||
static signed long filter_token_location = 0;
|
static signed long filter_token_location = 0;
|
||||||
|
|
||||||
@ -191,11 +192,7 @@ void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
|||||||
|
|
||||||
void free_shader(shader_t *shader)
|
void free_shader(shader_t *shader)
|
||||||
{
|
{
|
||||||
GLint major = 0, minor = 0;
|
if (epoxy_gl_version() < 32) {
|
||||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
||||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
||||||
|
|
||||||
if (major * 0x100 + minor < 0x302) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,3 +200,8 @@ void free_shader(shader_t *shader)
|
|||||||
glDeleteTextures(1, &shader->texture);
|
glDeleteTextures(1, &shader->texture);
|
||||||
glDeleteTextures(1, &shader->previous_texture);
|
glDeleteTextures(1, &shader->previous_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void free_master_shader(void) {
|
||||||
|
g_bytes_unref(master_shader_f);
|
||||||
|
master_shader_code_size = 0;
|
||||||
|
}
|
||||||
|
@ -26,5 +26,6 @@ void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
|||||||
unsigned source_width, unsigned source_height,
|
unsigned source_width, unsigned source_height,
|
||||||
unsigned x, unsigned y, unsigned w, unsigned h);
|
unsigned x, unsigned y, unsigned w, unsigned h);
|
||||||
void free_shader(struct shader_s *shader);
|
void free_shader(struct shader_s *shader);
|
||||||
|
void free_master_shader(void);
|
||||||
|
|
||||||
#endif /* shader_h */
|
#endif /* shader_h */
|
||||||
|
Loading…
Reference in New Issue
Block a user