[GTK3] Move main window code into a proper MainWindow widget

This commit is contained in:
Maximilian Mader 2021-01-07 00:47:14 +01:00
parent 1201b8928f
commit db325ad44d
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
9 changed files with 634 additions and 448 deletions

View File

@ -69,7 +69,6 @@ static void console_window_get_property(GObject *object, guint property_id, GVal
}
}
static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleWindow *self) {
switch (event->keyval) {
case GDK_KEY_Up:

View File

@ -277,7 +277,7 @@ static void gb_screen_class_init(GbScreenClass *class) {
G_OBJECT_CLASS(class)->finalize = gb_screen_finalize;
G_OBJECT_CLASS(class)->set_property = gb_screen_set_property;
G_OBJECT_CLASS(class)->get_property = gb_screen_get_property;
G_OBJECT_CLASS(class)->constructed = gb_screen_constructed;
G_OBJECT_CLASS(class)->constructed = gb_screen_constructed;
GTK_WIDGET_CLASS(class)->draw = gb_screen_draw;
GTK_WIDGET_CLASS(class)->get_preferred_width = gb_screen_get_preferred_width;

View File

@ -11,9 +11,8 @@
#include "types.h"
#include "config.h"
#include "util.h"
#include "check_menu_radio_group.h"
#include "gb_screen.h"
#include "main_window.h"
#include "console_window.h"
#include "preferences_window.h"
#include "vram_viewer_window.h"
@ -46,6 +45,7 @@ static GuiData gui_data = {
.cli_options = {
.fullscreen = false,
.model = -1,
.force_software_renderer = false,
},
.prev_model = -1,
@ -356,71 +356,6 @@ static void rumble_callback(GB_gameboy_t *gb, double amp) {
}
}
// Creating these items in the UI defintion files was buggy in some desktop
// environments and the manual call of `g_signal_connect` was needed anyway
// because the UI definition cant define string arguments for signal handlers.
static void create_model_menu_items() {
bool on_change_model(GtkWidget *, gpointer);
bool on_change_linked_device(GtkWidget *, gpointer);
static const char *const model_names[] = {
"Game Boy",
"Super Game Boy",
"Game Boy Color",
"Game Boy Advance",
NULL
};
static const char *const model_codes[] = {
"DMG",
"SGB",
"CGB",
"GBA",
NULL
};
// 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);
CheckMenuItemGroup *model_group = check_menu_item_group_new((char **) model_names, (char **) model_codes);
check_menu_item_group_insert_into_menu_shell(model_group, GTK_MENU_SHELL(parent), position + 1);
check_menu_item_group_connect_toggle_signal(model_group, on_change_model);
check_menu_item_group_activate(model_group, config.emulation.model);
static const char *const peripheral_names[] = {
"None",
"Game Boy Printer",
NULL
};
static const char *const peripheral_codes[] = {
"NONE",
"PRINTER",
NULL,
};
CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes);
check_menu_item_group_insert_into_menu_shell(link_group, GTK_MENU_SHELL(builder_get(GTK_MENU_SHELL, "link_menu")), 0);
check_menu_item_group_connect_toggle_signal(link_group, on_change_linked_device);
check_menu_item_group_activate(link_group, "NONE");
}
// Create our applications menu.
//
// This function tries to stick to the desktop environments conventions.
// For the GNOME Shell it uses a hamburger menu, otherwise it either lets
// 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");
gtk_box_pack_start(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(menubar), false, false, 0);
}
// 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
@ -452,25 +387,25 @@ static void stop(void) {
}
static gboolean on_vblank(GB_gameboy_t *gb) {
gb_screen_queue_render(gui_data.screen);
main_window_queue_render(gui_data.main_window);
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
return false;
}
static void vblank(GB_gameboy_t *gb) {
gb_screen_flip(gui_data.screen);
main_window_flip(gui_data.main_window);
if (gui_data.border_mode_changed) {
GB_set_border_mode(gb, config_get_display_border_mode());
gb_screen_set_resolution(gui_data.screen, GB_get_screen_width(gb), GB_get_screen_height(gb));
GB_set_pixels_output(gb, gb_screen_get_pixels(gui_data.screen));
main_window_set_resolution(gui_data.main_window, GB_get_screen_width(gb), GB_get_screen_height(gb));
GB_set_pixels_output(gb, main_window_get_pixels(gui_data.main_window));
gui_data.border_mode_changed = false;
}
GB_set_pixels_output(gb, gb_screen_get_pixels(gui_data.screen));
GB_set_pixels_output(gb, main_window_get_pixels(gui_data.main_window));
// Handle the speed modifiers:
// The binary slowdown is limited to half speed.
@ -499,7 +434,7 @@ static void vblank(GB_gameboy_t *gb) {
GB_frame_blending_mode_t mode = config_get_frame_blending_mode();
if (!gb_screen_get_previous_buffer(gui_data.screen)) {
if (!main_window_get_previous_buffer(gui_data.main_window)) {
mode = GB_FRAME_BLENDING_MODE_DISABLED;
}
else if (mode == GB_FRAME_BLENDING_MODE_ACCURATE) {
@ -513,7 +448,7 @@ static void vblank(GB_gameboy_t *gb) {
}
}
gb_screen_set_blending_mode(gui_data.screen, mode);
main_window_set_blending_mode(gui_data.main_window, mode);
g_idle_add((GSourceFunc) on_vblank, gb);
}
@ -714,7 +649,7 @@ static void init(void) {
GB_set_vblank_callback(&gb, vblank);
GB_set_rgb_encode_callback(&gb, rgb_encode);
GB_set_pixels_output(&gb, gb_screen_get_current_buffer(gui_data.screen));
GB_set_pixels_output(&gb, main_window_get_current_buffer(gui_data.main_window));
GB_set_color_correction_mode(&gb, config_get_color_correction_mode());
GB_set_light_temperature(&gb, (double) config.video.light_temperature / 256.0);
if (config_get_display_border_mode() <= GB_BORDER_ALWAYS) {
@ -756,8 +691,8 @@ static void reset(void) {
// Check SGB -> non-SGB and non-SGB to SGB transitions
if (GB_get_screen_width(&gb) != gui_data.last_screen_width || GB_get_screen_height(&gb) != gui_data.last_screen_height) {
gb_screen_set_resolution(gui_data.screen, GB_get_screen_width(&gb), GB_get_screen_height(&gb));
GB_set_pixels_output(&gb, gb_screen_get_pixels(gui_data.screen));
main_window_set_resolution(gui_data.main_window, GB_get_screen_width(&gb), GB_get_screen_height(&gb));
GB_set_pixels_output(&gb, main_window_get_pixels(gui_data.main_window));
}
bool success = false;
@ -995,12 +930,7 @@ static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data) {
if (event->keyval == key_map[INPUT_FULLSCREEN]) {
if (event->type == GDK_KEY_RELEASE) {
if (gui_data.is_fullscreen) {
gtk_window_unfullscreen(GTK_WINDOW(gui_data.main_window));
}
else {
gtk_window_fullscreen(GTK_WINDOW(gui_data.main_window));
}
main_window_fullscreen(gui_data.main_window, !gui_data.is_fullscreen);
}
}
@ -1036,11 +966,11 @@ static void startup(GApplication *app, gpointer null_ptr) {
init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date);
gui_data.screen = gb_screen_new(gui_data.cli_options.force_software_renderer);
gui_data.console = console_window_new(&gb);
gui_data.main_window = main_window_new(app, gui_data.cli_options.force_software_renderer);
gui_data.console = console_window_new(&gb);
gui_data.preferences = preferences_window_new(&gb);
gui_data.vram_viewer = vram_viewer_window_new();
gui_data.printer = printer_window_new();
gui_data.printer = printer_window_new();
gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
@ -1051,16 +981,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
gui_data.sample_rate = config.audio.sample_rate;
}
// setup main window
gui_data.main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
gui_data.main_window_container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
gtk_window_set_title(GTK_WINDOW(gui_data.main_window), "SameBoy");
gtk_application_window_set_show_menubar(gui_data.main_window, false);
gtk_container_add(GTK_CONTAINER(gui_data.main_window), GTK_WIDGET(gui_data.main_window_container));
gtk_box_pack_end(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(gui_data.screen), true, true, 0);
setup_menu(app);
main_window_setup_menu(gui_data.main_window, config.emulation.model);
// Insert separators into `GtkComboBox`es
set_combo_box_row_separator_func(GTK_CONTAINER(gui_data.memory_viewer));
@ -1210,7 +1131,7 @@ void on_preferences_notify_border(PreferencesWindow *pref, const gchar *name) {
}
void on_preferences_notify_shader(PreferencesWindow *pref, const gchar *name) {
gb_screen_set_shader(gui_data.screen, name);
main_window_set_shader(gui_data.main_window, name);
}
void on_preferences_notify_light_temperature(PreferencesWindow *pref, const gint *light_temperature) {
@ -1239,22 +1160,6 @@ void on_preferences_notify_interference_volume(PreferencesWindow *pref, const gu
}
static void connect_signal_handlers(GApplication *app) {
g_signal_new(
"break-debugger-keyboard", // signal name
G_TYPE_FROM_INSTANCE(gui_data.main_window), // itype
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_ACTION, // signal_flags
0, // class_offset
NULL, // accumulator
NULL, // accumulator_data
NULL, // c_marshaller,
G_TYPE_NONE, // return_type
0 // n_params
);
// Connect signal handlers
gtk_widget_add_events(GTK_WIDGET(gui_data.main_window), GDK_KEY_PRESS_MASK);
gtk_widget_add_events(GTK_WIDGET(gui_data.main_window), GDK_KEY_RELEASE_MASK);
g_signal_connect(gui_data.main_window, "destroy", G_CALLBACK(on_quit_activate), app);
g_signal_connect(gui_data.main_window, "key-press-event", G_CALLBACK(on_key_press), NULL);
g_signal_connect(gui_data.main_window, "key-release-event", G_CALLBACK(on_key_press), NULL);
@ -1283,13 +1188,9 @@ static void activate(GApplication *app, gpointer null_ptr) {
connect_signal_handlers(app);
if (gui_data.cli_options.fullscreen) {
gtk_window_fullscreen(GTK_WINDOW(gui_data.main_window));
main_window_fullscreen(gui_data.main_window, true);
}
GtkAccelGroup *accelGroup = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(gui_data.main_window), accelGroup);
gtk_widget_add_accelerator(GTK_WIDGET(gui_data.main_window), "break-debugger-keyboard", accelGroup, GDK_KEY_C, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(gui_data.main_window));
gtk_widget_show_all(GTK_WIDGET(gui_data.main_window));
@ -1377,8 +1278,8 @@ static void close_rom(void) {
stop();
GB_free(&gb);
gb_screen_clear(gui_data.screen);
gb_screen_queue_render(gui_data.screen);
main_window_clear(gui_data.main_window);
main_window_queue_render(gui_data.main_window);
vram_viewer_clear(gui_data.vram_viewer);
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));

222
gtk3/main_window.c Normal file
View File

@ -0,0 +1,222 @@
#include "main_window.h"
#include <stdbool.h>
#include "util.h"
#include "gb_screen.h"
#include "check_menu_radio_group.h"
struct _MainWindow {
GtkApplicationWindow parent_instance;
GtkApplicationWindowClass parent_class;
GtkBox *container;
GbScreen *screen;
bool force_software_renderer;
GtkMenuBar *main_menu;
GtkSeparatorMenuItem *before_model_changer;
GtkMenu *link_menu;
};
G_DEFINE_TYPE(MainWindow, main_window, GTK_TYPE_APPLICATION_WINDOW);
typedef enum {
PROP_FORCE_SOFTWARE_RENDERER = 1,
N_PROPERTIES
} MainWindowProperty;
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
static void main_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
MainWindow *self = (MainWindow *) object;
switch ((MainWindowProperty) property_id) {
case PROP_FORCE_SOFTWARE_RENDERER: self->force_software_renderer = g_value_get_boolean(value); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void main_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
MainWindow *self = (MainWindow *) object;
switch ((MainWindowProperty) property_id) {
case PROP_FORCE_SOFTWARE_RENDERER: g_value_set_boolean(value, self->force_software_renderer); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void main_window_constructed(GObject *object) {
MainWindow *self = (MainWindow *) object;
self->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
self->screen = gb_screen_new(self->force_software_renderer);
gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(self->container));
gtk_box_pack_end(GTK_BOX(self->container), GTK_WIDGET(self->screen), true, true, 0);
}
static void main_window_init(MainWindow *self) {
gtk_widget_init_template(GTK_WIDGET(self));
gtk_window_set_title(GTK_WINDOW(self), "SameBoy");
gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(self), false);
g_signal_new(
"break-debugger-keyboard", // signal name
G_TYPE_FROM_INSTANCE(self), // itype
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_ACTION, // signal_flags
0, // class_offset
NULL, // accumulator
NULL, // accumulator_data
NULL, // c_marshaller,
G_TYPE_NONE, // return_type
0 // n_params
);
// Connect signal handlers
gtk_widget_add_events(GTK_WIDGET(self), GDK_KEY_PRESS_MASK);
gtk_widget_add_events(GTK_WIDGET(self), GDK_KEY_RELEASE_MASK);
GtkAccelGroup *accelGroup = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(self), accelGroup);
gtk_widget_add_accelerator(GTK_WIDGET(self), "break-debugger-keyboard", accelGroup, GDK_KEY_C, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
}
static void main_window_finalize(GObject *object) {
MainWindow *self = (MainWindow *) object;
G_OBJECT_CLASS(main_window_parent_class)->finalize(object);
}
static void main_window_class_init(MainWindowClass *class) {
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/main_window.ui");
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, main_menu);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, before_model_changer);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, link_menu);
G_OBJECT_CLASS(class)->finalize = main_window_finalize;
obj_properties[PROP_FORCE_SOFTWARE_RENDERER] = g_param_spec_boolean(
"force_software_renderer", "Software Renderer", "Forces the use of software rendering via Cairo",
false,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE
);
G_OBJECT_CLASS(class)->set_property = main_window_set_property;
G_OBJECT_CLASS(class)->get_property = main_window_get_property;
G_OBJECT_CLASS(class)->constructed = main_window_constructed;
g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties);
}
MainWindow *main_window_new(GApplication *application, bool force_software_renderer) {
return g_object_new(MAIN_WINDOW_TYPE, "application", application, "force_software_renderer", force_software_renderer, NULL);
}
void main_window_fullscreen(MainWindow *self, bool make_fullscreen) {
if (make_fullscreen) {
gtk_window_unfullscreen(GTK_WINDOW(self));
}
else {
gtk_window_fullscreen(GTK_WINDOW(self));
}
}
// Creating these items in the UI defintion files was buggy in some desktop
// environments and the manual call of `g_signal_connect` was needed anyway
// because the UI definition cant define string arguments for signal handlers.
static void create_model_menu_items(MainWindow *self, char *model_string) {
bool on_change_model(GtkWidget *, gpointer);
bool on_change_linked_device(GtkWidget *, gpointer);
static const char *const model_names[] = {
"Game Boy",
"Super Game Boy",
"Game Boy Color",
"Game Boy Advance",
NULL
};
static const char *const model_codes[] = {
"DMG",
"SGB",
"CGB",
"GBA",
NULL
};
// Find the menu item index of the previous sibling of the new menu items
GtkContainer *parent = GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(self->before_model_changer)));
g_autoptr(GList) list = gtk_container_get_children(parent);
gint position = g_list_index(list, self->before_model_changer);
CheckMenuItemGroup *model_group = check_menu_item_group_new((char **) model_names, (char **) model_codes);
check_menu_item_group_insert_into_menu_shell(model_group, GTK_MENU_SHELL(parent), position + 1);
check_menu_item_group_connect_toggle_signal(model_group, on_change_model);
check_menu_item_group_activate(model_group, model_string);
static const char *const peripheral_names[] = {
"None",
"Game Boy Printer",
NULL
};
static const char *const peripheral_codes[] = {
"NONE",
"PRINTER",
NULL,
};
CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes);
check_menu_item_group_insert_into_menu_shell(link_group, GTK_MENU_SHELL(self->link_menu), 0);
check_menu_item_group_connect_toggle_signal(link_group, on_change_linked_device);
check_menu_item_group_activate(link_group, "NONE");
}
// Create our applications menu.
//
// This function tries to stick to the desktop environments conventions.
// For the GNOME Shell it uses a hamburger menu, otherwise it either lets
// the desktop environment shell handle the menu if it signals support for it
// or uses a standard menubar inside the window.
void main_window_setup_menu(MainWindow *self, char *model_string) {
create_model_menu_items(self, model_string);
gtk_box_pack_start(GTK_BOX(self->container), GTK_WIDGET(self->main_menu), false, false, 0);
}
// GbScreen wrappers
void main_window_clear(MainWindow *self) {
return gb_screen_clear(self->screen);
}
uint32_t *main_window_get_pixels(MainWindow *self) {
return gb_screen_get_pixels(self->screen);
}
uint32_t *main_window_get_current_buffer(MainWindow *self) {
return gb_screen_get_current_buffer(self->screen);
}
uint32_t *main_window_get_previous_buffer(MainWindow *self) {
return gb_screen_get_previous_buffer(self->screen);
}
void main_window_flip(MainWindow *self) {
return gb_screen_flip(self->screen);
}
void main_window_set_resolution(MainWindow *self, unsigned width, unsigned height) {
return gb_screen_set_resolution(self->screen, width, height);
}
void main_window_set_blending_mode(MainWindow *self, GB_frame_blending_mode_t mode) {
return gb_screen_set_blending_mode(self->screen, mode);
}
void main_window_set_shader(MainWindow *self, const char *shader_name) {
return gb_screen_set_shader(self->screen, shader_name);
}
void main_window_queue_render(MainWindow *self) {
return gb_screen_queue_render(self->screen);
}

26
gtk3/main_window.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef main_window_h
#define main_window_h
#include <gtk/gtk.h>
#include <Core/gb.h>
#include "shader.h"
#define MAIN_WINDOW_TYPE (main_window_get_type())
G_DECLARE_FINAL_TYPE(MainWindow, main_window, SAMEBOY, MAIN_WINDOW, GtkApplicationWindow)
MainWindow *main_window_new(GApplication *app, bool force_software_renderer);
void main_window_fullscreen(MainWindow *self, bool make_fullscreen);
void main_window_setup_menu(MainWindow *self, char *model_string);
// GbScreen wrappers
void main_window_clear(MainWindow *self);
uint32_t *main_window_get_pixels(MainWindow *self);
uint32_t *main_window_get_current_buffer(MainWindow *self);
uint32_t *main_window_get_previous_buffer(MainWindow *self);
void main_window_flip(MainWindow *self);
void main_window_set_resolution(MainWindow *self, unsigned width, unsigned height);
void main_window_set_blending_mode(MainWindow *self, GB_frame_blending_mode_t mode);
void main_window_set_shader(MainWindow *self, const char *shader_name);
void main_window_queue_render(MainWindow *self);
#endif

View File

@ -0,0 +1,360 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License (MIT)
Copyright (c) 2015-2019 Lior Halphon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Author: Maximilian Mader
-->
<interface>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type mit -->
<!-- interface-name SameBoy -->
<!-- interface-description SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator, written in portable C. -->
<!-- interface-copyright 2015-2021 Lior Halphon -->
<!-- interface-authors Maximilian Mader -->
<template class="MainWindow">
<object class="GtkMenuBar" id="main_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open</property>
<property name="label" translatable="yes">_Open</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Recent files</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkRecentChooserMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="filter">recent_files_filter</property>
<property name="limit">10</property>
<property name="sort_type">mru</property>
<signal name="item-activated" handler="on_open_recent_activate" swapped="no"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.close</property>
<property name="label" translatable="yes">_Close</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.quit</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.preferences</property>
<property name="label" translatable="yes">_Preferences</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">E_mulation</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.reset</property>
<property name="label" translatable="yes">_Reset</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.pause</property>
<property name="label" translatable="yes">Pause</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.save_state</property>
<property name="label" translatable="yes">Save State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.load_state</property>
<property name="label" translatable="yes">Load State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="before_model_changer">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.toggle_mute</property>
<property name="label" translatable="yes">Mute Sound</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Link</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="link_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Develop</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.toggle_developer_mode</property>
<property name="label" translatable="yes">Developer Mode</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.show_console</property>
<property name="label" translatable="yes">Show Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.clear_console</property>
<property name="label" translatable="yes">Clear Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.break_debugger</property>
<property name="label" translatable="yes">Break Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_memory_viewer</property>
<property name="label" translatable="yes">Show Memory Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_vram_viewer</property>
<property name="label" translatable="yes">Show VRAM Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_gtk_debugger</property>
<property name="label" translatable="yes">Show GTK Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.about</property>
<property name="label">_About</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</template>
<object class="GtkRecentFilter" id="recent_files_filter">
<mime-types>
<mime-type>application/x-gameboy-rom</mime-type>
<mime-type>application/x-gameboy-color-rom</mime-type>
</mime-types>
<patterns>
<pattern>*.gb</pattern>
<pattern>*.gbc</pattern>
<pattern>*.isx</pattern>
</patterns>
<applications>
<application>sameboy</application>
</applications>
</object>
</interface>

View File

@ -123,325 +123,4 @@ Maximilian Mader https://github.com/max-m</property>
<placeholder/>
</child>
</object>
<object class="GtkRecentFilter" id="recent_files_filter">
<mime-types>
<mime-type>application/x-gameboy-rom</mime-type>
<mime-type>application/x-gameboy-color-rom</mime-type>
</mime-types>
<patterns>
<pattern>*.gb</pattern>
<pattern>*.gbc</pattern>
<pattern>*.isx</pattern>
</patterns>
<applications>
<application>sameboy</application>
</applications>
</object>
<object class="GtkMenuBar" id="main_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open</property>
<property name="label" translatable="yes">_Open</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Recent files</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkRecentChooserMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="filter">recent_files_filter</property>
<property name="limit">10</property>
<property name="sort_type">mru</property>
<signal name="item-activated" handler="on_open_recent_activate" swapped="no"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.close</property>
<property name="label" translatable="yes">_Close</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.quit</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.preferences</property>
<property name="label" translatable="yes">_Preferences</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">E_mulation</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.reset</property>
<property name="label" translatable="yes">_Reset</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.pause</property>
<property name="label" translatable="yes">Pause</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.save_state</property>
<property name="label" translatable="yes">Save State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.load_state</property>
<property name="label" translatable="yes">Load State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="before_model_changer">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.toggle_mute</property>
<property name="label" translatable="yes">Mute Sound</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Link</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="link_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Develop</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.toggle_developer_mode</property>
<property name="label" translatable="yes">Developer Mode</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.show_console</property>
<property name="label" translatable="yes">Show Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.clear_console</property>
<property name="label" translatable="yes">Clear Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.break_debugger</property>
<property name="label" translatable="yes">Break Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_memory_viewer</property>
<property name="label" translatable="yes">Show Memory Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_vram_viewer</property>
<property name="label" translatable="yes">Show VRAM Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_gtk_debugger</property>
<property name="label" translatable="yes">Show GTK Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.about</property>
<property name="label">_About</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/io/github/sameboy">
<file preprocess="xml-stripblanks" compressed="true">ui/window.ui</file>
<file preprocess="xml-stripblanks" compressed="true">ui/main_window.ui</file>
<file preprocess="xml-stripblanks" compressed="true">ui/console_window.ui</file>
<file preprocess="xml-stripblanks" compressed="true">ui/preferences_window.ui</file>
<file preprocess="xml-stripblanks" compressed="true">ui/vram_viewer_window.ui</file>

View File

@ -2,7 +2,7 @@
#define types_h
#include "SDL.h"
#include "gb_screen.h"
#include "main_window.h"
#include "console_window.h"
#include "preferences_window.h"
#include "vram_viewer_window.h"
@ -39,10 +39,8 @@ typedef struct GuiData {
// GTK pointers
GtkApplication *main_application;
GtkBuilder *builder;
GtkApplicationWindow *main_window;
GtkBox *main_window_container;
MainWindow *main_window;
GbScreen *screen;
ConsoleWindow *console;
PreferencesWindow *preferences;
VramViewerWindow *vram_viewer;