diff --git a/gtk3/main.c b/gtk3/main.c index 165af4e..cf72e67 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -10,9 +10,13 @@ #define str(x) #x #define xstr(x) str(x) +#define SETTINGS_FILE "sameboy-gtk3-settings.ini" + typedef struct UserData { bool fullscreen; GFile *file; + const gchar* bootrom_path; + GB_model_t model; } UserData; typedef struct{ @@ -22,13 +26,15 @@ typedef struct{ static void run(UserData *user_data); -GtkApplication *main_application; -GtkBuilder *builder; -GtkApplicationWindow *main_window; -GtkGLArea *gl_area; -shader_t shader; +static GKeyFile *key_file; +static GtkApplication *main_application; +static GtkBuilder *builder; +static GtkApplicationWindow *main_window; +static GtkGLArea *gl_area; +static shader_t shader; +static gchar* settings_file_path; -GB_gameboy_t gb; +static GB_gameboy_t gb; static uint32_t *image_buffers[3]; static unsigned char current_buffer; @@ -40,29 +46,40 @@ static Rect rect; static bool running = true; -unsigned char number_of_buffers(void) { +static void save_settings(void) { + GError *error = NULL; + + // Save as a file. + if (!g_key_file_save_to_file(key_file, settings_file_path, &error)) { + g_warning ("Error saving %s: %s", settings_file_path, error->message); + g_error_free(error); + return; + } +} + +static unsigned char number_of_buffers(void) { bool should_blend = true; return should_blend? 3 : 2; } -void flip(void) { +static void flip(void) { current_buffer = (current_buffer + 1) % number_of_buffers(); } -uint32_t *get_pixels(void) { +static uint32_t *get_pixels(void) { return image_buffers[(current_buffer + 1) % number_of_buffers()]; } -uint32_t *get_current_buffer(void) { +static uint32_t *get_current_buffer(void) { return image_buffers[current_buffer]; } -uint32_t *get_previous_buffer(void) { +static uint32_t *get_previous_buffer(void) { return image_buffers[(current_buffer + 2) % number_of_buffers()]; } -void render_texture(void *pixels, void *previous) { +static void render_texture(void *pixels, void *previous) { static void *_pixels = NULL; if (pixels) { _pixels = pixels; @@ -86,7 +103,7 @@ static void vblank(GB_gameboy_t *gb) { } } -void update_viewport(void) { +static void update_viewport(void) { int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area)); int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area)); @@ -190,10 +207,24 @@ static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer gtk_widget_hide(GTK_WIDGET(dialog)); } +// app.open_gtk_debugger GAction +// Opens the GTK debugger +static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer user_data) { + gtk_window_set_interactive_debugging(true); +} + +// app.preferences GAction +// Opens the preferences window +static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer user_data) { + gtk_widget_show_all(GTK_WIDGET(get_object("preferences"))); +} + // List of GActions for the `app` prefix static GActionEntry app_entries[] = { { "quit", activate_quit, NULL, NULL, NULL }, { "about", activate_about, NULL, NULL, NULL }, + { "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL }, + { "preferences", activate_preferences, NULL, NULL, NULL }, }; G_MODULE_EXPORT void on_quit(GtkWidget *w, gpointer app) { @@ -264,6 +295,20 @@ static void startup(GApplication *app, gpointer user_data_gptr) { g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL); gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(gl_area)); + GError *error = NULL; + settings_file_path = g_build_filename(g_get_user_config_dir(), SETTINGS_FILE, NULL); + key_file = g_key_file_new(); + + g_print("Trying to load settings from %s\n", settings_file_path); + + if (!g_key_file_load_from_file(key_file, settings_file_path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error)) { + if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_warning("Error loading %s: %s", settings_file_path, error->message); + } + + g_error_free(error); + } + // Handle the whole menubar situation … if (show_menubar()) { // Show a classic menubar @@ -340,8 +385,7 @@ static void open(GApplication *app, GFile **files, gint n_files, const gchar *hi if (n_files > 1) { g_printerr("More than one file specified\n"); - g_application_quit(app); - return; + exit(EXIT_FAILURE); } user_data->file = files[0]; @@ -352,6 +396,8 @@ static void open(GApplication *app, GFile **files, gint n_files, const gchar *hi static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr) { g_print("SHUTDOWN\n"); + + save_settings(); } // This function gets called after the parsing of the commandline options has occurred. @@ -368,6 +414,57 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin user_data->fullscreen = true; } + // Handle model override + GVariant *model_name_var = g_variant_dict_lookup_value(options, "model", G_VARIANT_TYPE_STRING); + if (model_name_var != NULL) { + const gchar *model_name = g_variant_get_string(model_name_var, NULL); + + // TODO: Synchronize with GB_model_t (Core/gb.h) + if (g_str_has_prefix(model_name, "DMG")) { + if (g_str_has_suffix(model_name, "-B") || g_strcmp0(model_name, "DMG") == 0) { + user_data->model = GB_MODEL_DMG_B; + } + else { + user_data->model = GB_MODEL_DMG_B; + g_printerr("Unsupported revision: %s\nFalling back to DMG-B", model_name); + } + } + else if (g_str_has_prefix(model_name, "SGB")) { + if (g_str_has_suffix(model_name, "-NTSC") || g_strcmp0(model_name, "SGB") == 0) { + user_data->model = GB_MODEL_SGB; + } + else if (g_str_has_suffix(model_name, "-PAL")) { + user_data->model = GB_MODEL_SGB | GB_MODEL_PAL_BIT; + } + else if (g_str_has_suffix(model_name, "2")) { + user_data->model = GB_MODEL_SGB2; + } + else { + user_data->model = GB_MODEL_SGB2; + g_printerr("Unsupported revision: %s\nFalling back to SGB2", model_name); + } + } + else if (g_str_has_prefix(model_name, "CGB")) { + if (g_str_has_suffix(model_name, "-C")) { + user_data->model = GB_MODEL_CGB_C; + } + else if (g_str_has_suffix(model_name, "-E") || g_strcmp0(model_name, "CGB") == 0) { + user_data->model = GB_MODEL_CGB_E; + } + else { + user_data->model = GB_MODEL_CGB_E; + g_printerr("Unsupported revision: %s\nFalling back to CGB-E", model_name); + } + } + else if (g_str_has_prefix(model_name, "AGB")) { + user_data->model = GB_MODEL_AGB; + } + else { + g_printerr("Unknown model: %s\n", model_name); + exit(EXIT_FAILURE); + } + } + return -1; } @@ -417,7 +514,7 @@ static void update_window_geometry() { static void run(UserData *user_data) { GB_model_t prev_model = GB_get_model(&gb); - GB_model_t model = GB_MODEL_CGB_E; + GB_model_t model = user_data->model? user_data->model : GB_MODEL_CGB_E; // TODO: Model from config if (GB_is_inited(&gb)) { GB_switch_model_and_reset(&gb, model); @@ -446,15 +543,57 @@ static void run(UserData *user_data) { // GB_apu_set_sample_callback(&gb, gb_audio_callback); } - GError *gerror; + GError *error; + char *boot_rom_path; GBytes *boot_rom_f; const guchar *boot_rom_data; gsize boot_rom_size; - - boot_rom_f = g_resources_lookup_data(RESOURCE_PREFIX "bootroms/cgb_boot.bin", G_RESOURCE_LOOKUP_FLAGS_NONE, &gerror); - 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); + if (user_data->bootrom_path) { + g_print("Trying to load boot ROM from %s\n", user_data->bootrom_path); + if (GB_load_boot_rom(&gb, user_data->bootrom_path)) { + g_printerr("Falling back to internal boot ROM\n"); + goto internal_bootrom; + } + } + else { internal_bootrom: + switch (model) { + case GB_MODEL_DMG_B: + boot_rom_path = RESOURCE_PREFIX "bootroms/dmg_boot.bin"; + break; + + case GB_MODEL_SGB: + case GB_MODEL_SGB_PAL: + case GB_MODEL_SGB_NO_SFC: + boot_rom_path = RESOURCE_PREFIX "bootroms/sgb_boot.bin"; + break; + + case GB_MODEL_SGB2: + case GB_MODEL_SGB2_NO_SFC: + boot_rom_path = RESOURCE_PREFIX "bootroms/sgb2_boot.bin"; + break; + + case GB_MODEL_CGB_C: + case GB_MODEL_CGB_E: + boot_rom_path = RESOURCE_PREFIX "bootroms/cgb_boot.bin"; + break; + + case GB_MODEL_AGB: + boot_rom_path = RESOURCE_PREFIX "bootroms/agb_boot.bin"; + break; + } + + boot_rom_f = g_resources_lookup_data(boot_rom_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); + + if (boot_rom_f == NULL) { + g_printerr("Failed to load internal boot ROM: %s\n", boot_rom_path); + g_error_free(error); + exit(EXIT_FAILURE); + } + + 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); + } if (user_data->file != NULL) { GB_load_rom(&gb, g_file_get_path(user_data->file)); @@ -493,6 +632,8 @@ int main(int argc, char *argv[]) { GOptionEntry entries[] = { { "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 }, + { "bootrom", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &user_data.bootrom_path, "Path to the boot ROM to use", "" }, + { "model", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "Override the model type to emulate", "" }, { NULL } }; // Setup our command line information @@ -507,10 +648,6 @@ int main(int argc, char *argv[]) { g_signal_connect(main_application, "open", G_CALLBACK(open), &user_data); g_signal_connect(main_application, "shutdown", G_CALLBACK(shutdown), &user_data); -#ifndef NDEBUG - //gtk_window_set_interactive_debugging(true); -#endif - // Start our GApplication main loop int status = g_application_run(G_APPLICATION(main_application), argc, argv); g_object_unref(main_application); diff --git a/gtk3/resources/gtk/menus-common.ui b/gtk3/resources/gtk/menus-common.ui index 07c5b3b..5adb447 100644 --- a/gtk3/resources/gtk/menus-common.ui +++ b/gtk3/resources/gtk/menus-common.ui @@ -382,6 +382,14 @@ Author: Maximilian Mader win.open_vram_viewer + +
+ developer-section-4 + + Show GTK Debugger + app.open_gtk_debugger + +
diff --git a/gtk3/resources/ui/window.ui b/gtk3/resources/ui/window.ui index 8879d96..0c24227 100644 --- a/gtk3/resources/ui/window.ui +++ b/gtk3/resources/ui/window.ui @@ -500,7 +500,7 @@ Maximilian Mader https://github.com/max-m True False 2 - /io/github/sameboy/CPU.png + /io/github/sameboy/pixmaps/CPU.png 3 @@ -645,7 +645,7 @@ Maximilian Mader https://github.com/max-m True False 2 - /io/github/sameboy/Display.png + /io/github/sameboy/pixmaps/Display.png 3 @@ -728,7 +728,7 @@ Maximilian Mader https://github.com/max-m True False 2 - /io/github/sameboy/Speaker.png + /io/github/sameboy/pixmaps/Speaker.png 3 @@ -935,7 +935,7 @@ Maximilian Mader https://github.com/max-m True False 2 - /io/github/sameboy/Joypad.png + /io/github/sameboy/pixmaps/Joypad.png 3 diff --git a/gtk3/shader.c b/gtk3/shader.c index af9912b..e7da2e4 100644 --- a/gtk3/shader.c +++ b/gtk3/shader.c @@ -71,8 +71,8 @@ bool init_shader_with_name(shader_t *shader, const char *name) return false; } - GError *error; - GBytes *master_shader_f; + 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,};