diff --git a/gtk3/main.c b/gtk3/main.c index 1b4a61d..bcd01a7 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -46,7 +46,6 @@ typedef struct GuiData { struct CliOptionData { gchar *config_path; gchar *boot_rom_path; - gchar *prefix; gboolean fullscreen; GB_model_t model; gboolean force_software_renderer; @@ -358,8 +357,6 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin // TODO: Synchronize with GB_model_t (Core/gb.h) if (g_str_has_prefix(model_name, "DMG")) { - gui_data.cli_options.prefix = "DMG"; - if (g_str_has_suffix(model_name, "-B") || g_strcmp0(model_name, "DMG") == 0) { gui_data.cli_options.model = GB_MODEL_DMG_B; } @@ -369,8 +366,6 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin } } else if (g_str_has_prefix(model_name, "SGB")) { - gui_data.cli_options.prefix = "SGB"; - if (g_str_has_suffix(model_name, "-NTSC") || g_strcmp0(model_name, "SGB") == 0) { gui_data.cli_options.model = GB_MODEL_SGB; } @@ -386,8 +381,6 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin } } else if (g_str_has_prefix(model_name, "CGB")) { - gui_data.cli_options.prefix = "CGB"; - if (g_str_has_suffix(model_name, "-C")) { gui_data.cli_options.model = GB_MODEL_CGB_C; } @@ -400,13 +393,9 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin } } else if (g_str_has_prefix(model_name, "AGB")) { - gui_data.cli_options.prefix = "AGB"; - gui_data.cli_options.model = GB_MODEL_AGB; } else { - gui_data.cli_options.prefix = NULL; - g_warning("Unknown model: %s", model_name); exit(EXIT_FAILURE); } @@ -818,17 +807,87 @@ static char *async_console_input(GB_gameboy_t *gb) { GtkWidget *menubar_to_menu(GtkMenuBar *menubar) { GtkWidget *menu = gtk_menu_new(); - g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER (menubar)); + 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 = g_list_next(iter); + 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 can’t define string arguments for signal handlers. +static void create_model_menu_items() { + void on_change_model(GtkCheckMenuItem *check_menu_item, const gchar *model_str); + + static const char *const model_names[] = { + "Game Boy", + "Super Game Boy", + "Game Boy Color", + "Game Boy Advance" + }; + + static const char *const model_codes[] = { + "DMG", + "SGB", + "CGB", + "GBA" + }; + + // Find the menu item index of the previous sibling of the new menu items + GtkWidget *before = builder_get(GTK_WIDGET, "before_model_changer"); + GtkContainer *parent = GTK_CONTAINER(gtk_widget_get_parent(before)); + g_autoptr(GList) list = gtk_container_get_children(parent); + gint position = g_list_index(list, before); + + GSList *group = NULL; + for (int i = 0; i < sizeof(model_names) / sizeof(const char*); i++) { + // Create a new menu item + GtkWidget *item = gtk_radio_menu_item_new_with_label(group, model_names[i]); + + // Add it to the existing menu + gtk_menu_shell_insert(GTK_MENU_SHELL(parent), item, ++position); + g_signal_connect(item, "toggled", G_CALLBACK(on_change_model), (gpointer) model_codes[i]); + + if (g_strcmp0(config.emulation.model, model_codes[i]) == 0) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), true); + } + + if (i == 0) group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + } + + static const char *const peripheral_names[] = { + "None", + "Game Boy Printer", + }; + + static const char *const peripheral_codes[] = { + "NONE", + "PRINTER", + }; + + GtkMenuShell *link_menu = builder_get(GTK_MENU_SHELL, "link_menu"); + group = NULL; + position = 0; + for (int i = 0; i < sizeof(peripheral_names) / sizeof(const char*); i++) { + // Create a new menu item + GtkWidget *item = gtk_radio_menu_item_new_with_label(group, peripheral_names[i]); + + // Add it to the existing menu + gtk_menu_shell_insert(link_menu, item, position++); + // g_signal_connect(item, "toggled", G_CALLBACK(on_change_linked_device, (gpointer) peripheral_codes[i]); + + if (i == 0) { + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), true); + } + } +} + // Create our application’s menu. // // This function tries to stick to the desktop environment’s conventions. @@ -836,72 +895,10 @@ GtkWidget *menubar_to_menu(GtkMenuBar *menubar) { // the desktop environment shell handle the menu if it signals support for it // or uses a standard menubar inside the window. static void setup_menu(GApplication *app) { + create_model_menu_items(); + GtkMenuBar *menubar = builder_get(GTK_MENU_BAR, "main_menu"); - enum menubar_type_t menubar_type = get_show_menubar(); - - // Try to use a sane default - if (menubar_type == MENUBAR_AUTO) { - GtkSettings *settings = gtk_settings_get_default(); - gboolean show_in_shell; - g_object_get(settings, "gtk-shell-shows-menubar", &show_in_shell, NULL); - - const gchar *xdg_current_desktop = g_getenv("XDG_CURRENT_DESKTOP"); - const gchar *gdm_session = g_getenv("GDMSESSION"); - const gchar *desktop_session = g_getenv("DESKTOP_SESSION"); - - gchar *desktop = (gchar *)xdg_current_desktop; - if (desktop == NULL || g_str_equal(desktop, "")) desktop = (gchar *)gdm_session; - if (desktop == NULL || g_str_equal(desktop, "")) desktop = (gchar *)desktop_session; - - g_debug("XDG_CURRENT_DESKTOP: %s\nGDMSESSION: %s\nDESKTOP_SESSION: %s\nChosen value: %s\nShow menu in shell: %d", xdg_current_desktop, gdm_session, desktop_session, desktop, show_in_shell); - - if (desktop != NULL && show_in_shell) { - menubar_type = MENUBAR_SHOW_IN_SHELL; - } - else if (desktop != NULL && g_str_match_string("GNOME", desktop, false)) { - if (g_str_match_string("GNOME-Flashback", desktop, false) || g_str_match_string("GNOME-Classic", desktop, false)) { - menubar_type = MENUBAR_SHOW_IN_WINDOW; - } - else if (gdm_session != NULL && (g_str_match_string("gnome-classic", gdm_session, false) || g_str_match_string("gnome-flashback", gdm_session, false))) { - menubar_type = MENUBAR_SHOW_IN_WINDOW; - } - else { - menubar_type = MENUBAR_SHOW_HAMBURGER; - } - } - else { - menubar_type = MENUBAR_SHOW_IN_WINDOW; - } - } - - switch (menubar_type) { - case MENUBAR_AUTO: - g_warning("Unreachable"); - break; - - case MENUBAR_SHOW_IN_SHELL: - case MENUBAR_SHOW_IN_WINDOW: { - g_debug("Showing menu in the window"); - gtk_box_pack_start(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(menubar), false, false, 0); - break; - } - - case MENUBAR_SHOW_HAMBURGER: { - g_debug("Showing hamburger"); - // Attach a custom title bar - GtkWidget *titlebar = builder_get(GTK_WIDGET, "main_header_bar"); - gtk_header_bar_set_title(GTK_HEADER_BAR(titlebar), gtk_window_get_title(GTK_WINDOW(gui_data.main_window))); - gtk_window_set_titlebar(GTK_WINDOW(gui_data.main_window), titlebar); - - // Disable menubar - gtk_application_set_menubar(GTK_APPLICATION(app), NULL); - - // Hook menubar up to the hamburger button - GtkMenuButton *hamburger_button = GTK_MENU_BUTTON(get_object("hamburger_button")); - gtk_menu_button_set_popup(hamburger_button, GTK_WIDGET(menubar_to_menu(menubar))); - break; - } - } + gtk_box_pack_start(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(menubar), false, false, 0); } // Determines if a ComboBox entry should be converted into a separator. @@ -963,6 +960,42 @@ static void flip(void) { gui_data.current_buffer = (gui_data.current_buffer + 1) % number_of_buffers(); } +static void update_viewport(void) { + GtkWidget *w = gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.gl_area); + + int win_width = gtk_widget_get_allocated_width(w); + int win_height = gtk_widget_get_allocated_height(w); + + double x_factor = win_width / (double) GB_get_screen_width(&gb); + double y_factor = win_height / (double) GB_get_screen_height(&gb); + + if (config.video.use_integer_scaling) { + x_factor = (int)(x_factor); + y_factor = (int)(y_factor); + } + + if (config.video.keep_aspect_ratio) { + if (x_factor > y_factor) { + x_factor = y_factor; + } + else { + y_factor = x_factor; + } + } + + unsigned new_width = x_factor * GB_get_screen_width(&gb); + unsigned new_height = y_factor * GB_get_screen_height(&gb); + + gui_data.viewport = (Rect){ + (win_width - new_width) / 2, + (win_height - new_height) / 2, + new_width, + new_height + }; + + if (!gui_data.fallback_canvas) glViewport(gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.w, gui_data.viewport.h); +} + // WHY DO WE NEED SUCH AN UGLY METHOD, GTK?! static void action_entries_set_enabled(const GActionEntry *entries, unsigned n_entries, bool value) { // Assumes null-terminated if n_entries == -1 @@ -978,15 +1011,20 @@ static void update_window_geometry(void) { g_debug("update_window_geometry: %u×%u → %u×%u", gui_data.last_screen_width, gui_data.last_screen_height, GB_get_screen_width(&gb), GB_get_screen_height(&gb)); GtkWidget *w = gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.gl_area); - int win_width = gtk_widget_get_allocated_width(w); - int win_height = gtk_widget_get_allocated_height(w); - unsigned new_width = GB_get_screen_width(&gb) * 2; - unsigned new_height = GB_get_screen_height(&gb) * 2; + signed win_width = gtk_widget_get_allocated_width(w); + signed win_height = gtk_widget_get_allocated_height(w); + signed menu_height = gtk_widget_get_allocated_height(builder_get(GTK_WIDGET, "main_menu")); + + unsigned _factor = win_width > win_height ? win_width / GB_get_screen_width(&gb) : win_height / GB_get_screen_height(&gb); + unsigned factor = _factor < 2 ? 2 : _factor; + + unsigned new_width = GB_get_screen_width(&gb) * factor; + unsigned new_height = GB_get_screen_height(&gb) * factor + menu_height; // Set size hints GdkGeometry hints; hints.min_width = GB_get_screen_width(&gb); - hints.min_height = GB_get_screen_height(&gb); + hints.min_height = GB_get_screen_height(&gb) + menu_height; gtk_window_set_geometry_hints( GTK_WINDOW(gui_data.main_window), @@ -1016,6 +1054,8 @@ static void update_window_geometry(void) { if (GB_is_inited(&gb)) { GB_set_pixels_output(&gb, get_pixels()); } + + update_viewport(); } static void stop(void) { @@ -1989,20 +2029,9 @@ static void startup(GApplication *app, gpointer null_ptr) { create_action_groups(app); - if (gui_data.cli_options.prefix != NULL) { - // TODO - - //GAction *action = g_action_map_lookup_action(G_ACTION_MAP(gui_data.main_application), "change_model"); - //g_action_change_state(action, g_variant_new_string(gui_data.cli_options.prefix)); - } - #if NDEBUG // Disable when not compiled in debug mode action_set_enabled(app, "open_gtk_debugger", false); - - // Remove the menubar override - gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector_label")); - gtk_widget_destroy(builder_get(GTK_WIDGET, "menubar_override_selector")); #endif gui_data.preferences = GTK_WINDOW(get_object("preferences")); @@ -2138,48 +2167,6 @@ static void connect_signal_handlers(GApplication *app) { g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "motion_notify_event", G_CALLBACK(on_motion_vram_viewer_tilemap), NULL); g_signal_connect(get_object("vram_viewer_stack"), "notify::visible-child", G_CALLBACK(on_vram_tab_change), NULL); - - // We can’t set these values in the UI definition file - g_signal_connect(get_object("change_model_dmg"), "toggled", G_CALLBACK(on_change_model), (gpointer) "DMG"); - g_signal_connect(get_object("change_model_sgb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "SGB"); - g_signal_connect(get_object("change_model_cgb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "CGB"); - g_signal_connect(get_object("change_model_agb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "AGB"); -} - -static void update_viewport(void) { - GtkWidget *w = gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.gl_area); - - int win_width = gtk_widget_get_allocated_width(w); - int win_height = gtk_widget_get_allocated_height(w); - - double x_factor = win_width / (double) GB_get_screen_width(&gb); - double y_factor = win_height / (double) GB_get_screen_height(&gb); - - if (config.video.use_integer_scaling) { - x_factor = (int)(x_factor); - y_factor = (int)(y_factor); - } - - if (config.video.keep_aspect_ratio) { - if (x_factor > y_factor) { - x_factor = y_factor; - } - else { - y_factor = x_factor; - } - } - - unsigned new_width = x_factor * GB_get_screen_width(&gb); - unsigned new_height = y_factor * GB_get_screen_height(&gb); - - gui_data.viewport = (Rect){ - (win_width - new_width) / 2, - (win_height - new_height) / 2, - new_width, - new_height - }; - - if (!gui_data.fallback_canvas) glViewport(gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.w, gui_data.viewport.h); } // TODO: Comment @@ -2362,6 +2349,8 @@ static void activate(GApplication *app, gpointer null_ptr) { gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(gui_data.main_window)); gtk_widget_show_all(GTK_WIDGET(gui_data.main_window)); + update_window_geometry(); + // Start the emulation thread run(); } @@ -2623,10 +2612,6 @@ G_MODULE_EXPORT void on_monochrome_palette_changed(GtkWidget *w, gpointer user_d GB_set_palette(&gb, get_monochrome_palette()); } -G_MODULE_EXPORT void on_color_menubar_override_changed(GtkWidget *w, gpointer user_data_ptr) { - config.window.menubar_override = (gchar *)gtk_combo_box_get_active_id(GTK_COMBO_BOX(w)); -} - G_MODULE_EXPORT void on_dmg_model_changed(GtkWidget *w, gpointer user_data_ptr) { GtkComboBox *box = GTK_COMBO_BOX(w); config.emulation.dmg_revision_name = (gchar *)gtk_combo_box_get_active_id(box); diff --git a/gtk3/resources/ui/window.ui b/gtk3/resources/ui/window.ui index 645ed51..b275a65 100644 --- a/gtk3/resources/ui/window.ui +++ b/gtk3/resources/ui/window.ui @@ -1206,26 +1206,6 @@ Maximilian Mader https://github.com/max-m - - True - False - False - True - - - True - True - True - none - - - - - - end - - - False Memory Viewer @@ -1496,46 +1476,11 @@ Maximilian Mader https://github.com/max-m - + True False - - - True - False - Game Boy - True - - - - - True - False - Super Game Boy - True - change_model_dmg - - - - - True - False - Game Boy Color - True - change_model_dmg - - - - - True - False - Game Boy Advance - True - change_model_dmg - - True @@ -1562,26 +1507,9 @@ Maximilian Mader https://github.com/max-m _Link True - + True False - - - True - False - None - True - - - - - True - False - Game Boy Printer - True - change_serial_device_none - - diff --git a/gtk3/settings.c b/gtk3/settings.c index c399b45..639a1cc 100644 --- a/gtk3/settings.c +++ b/gtk3/settings.c @@ -146,15 +146,6 @@ void on_preferences_realize(GtkWidget *w, gpointer builder_ptr) { gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "analog_speed_controls_toggle"), config.controls.analog_speed_controls); gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "rumble_mode_selector"), config.controls.rumble_mode); - - #if ! NDEBUG - gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "menubar_override_selector"), config.window.menubar_override); - #else - if (builder_get(GTK_COMBO_BOX, "menubar_override_selector") != NULL) { - gtk_widget_destroy(GTK_WIDGET(builder_get(GTK_COMBO_BOX, "menubar_override_selector"))); - gtk_widget_destroy(GTK_WIDGET(builder_get(GTK_COMBO_BOX, "menubar_override_selector_label"))); - } - #endif } void init_settings(GApplication *app, gchar *path, GDateTime **modification_date, GtkWindow *preferences) { @@ -259,44 +250,6 @@ void update_boot_rom_selector(GtkBuilder *builder) { gtk_combo_box_text_append(combo_box, "other", "Other"); } -enum menubar_type_t get_show_menubar(void) { - if (config.window.menubar_override == NULL) goto default_value; - - if (g_strcmp0(config.window.menubar_override, "auto") == 0) { - return MENUBAR_AUTO; - } - else if (g_strcmp0(config.window.menubar_override, "show_in_shell") == 0) { - return MENUBAR_SHOW_IN_SHELL; - } - else if (g_strcmp0(config.window.menubar_override, "show_in_window") == 0) { - return MENUBAR_SHOW_IN_WINDOW; - } - else if (g_strcmp0(config.window.menubar_override, "show_hamburger") == 0) { - return MENUBAR_SHOW_HAMBURGER; - } - - // This should not happen - g_warning("Unknown menubar setting: %s\nFalling back to “Auto”", config.window.menubar_override); - default_value: return MENUBAR_AUTO; -} - -void set_show_menubar(enum menubar_type_t value) { - switch (value) { - case MENUBAR_AUTO: - config.window.menubar_override = "auto"; - break; - case MENUBAR_SHOW_IN_SHELL: - config.window.menubar_override = "show_in_shell"; - break; - case MENUBAR_SHOW_IN_WINDOW: - config.window.menubar_override = "show_in_window"; - break; - case MENUBAR_SHOW_HAMBURGER: - config.window.menubar_override = "show_hamburger"; - break; - } -} - GB_color_correction_mode_t get_color_correction_mode(void) { if (config.video.color_correction_id == NULL) goto default_value; diff --git a/gtk3/settings.h b/gtk3/settings.h index 0caf1a4..dcea76c 100644 --- a/gtk3/settings.h +++ b/gtk3/settings.h @@ -54,7 +54,7 @@ EXPAND_GROUP_MEMBER(rumble_mode, string, "Never") \ ) \ EXPAND_GROUP(window, \ - EXPAND_GROUP_MEMBER(menubar_override, string, "auto") \ + \ ) typedef struct config_t { @@ -69,13 +69,6 @@ typedef struct config_t { #undef EXPAND_GROUP_MEMBER } config_t; -enum menubar_type_t { - MENUBAR_AUTO, - MENUBAR_SHOW_IN_SHELL, - MENUBAR_SHOW_IN_WINDOW, - MENUBAR_SHOW_HAMBURGER -}; - config_t config; void on_preferences_realize(GtkWidget *w, gpointer builder_ptr); @@ -90,9 +83,6 @@ void free_settings(void); void update_boot_rom_selector(GtkBuilder *builder); -enum menubar_type_t get_show_menubar(void); -void set_show_menubar(enum menubar_type_t); - GB_color_correction_mode_t get_color_correction_mode(void); void set_color_correction_mode(GB_color_correction_mode_t);