[GTK3] WIP! Start refactoring of the spaghetti main.c into a subclass of GtkApplication
This commit is contained in:
parent
cc4be1f903
commit
39af396004
1433
gtk3/main.c
1433
gtk3/main.c
File diff suppressed because it is too large
Load Diff
74
gtk3/resources/ui/about_dialog.ui
Normal file
74
gtk3/resources/ui/about_dialog.ui
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2021 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="AboutDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">SameBoy</property>
|
||||
<property name="copyright" translatable="yes">Copyright © 2015-2021 Lior Halphon</property>
|
||||
<property name="website">https://sameboy.github.io</property>
|
||||
<property name="website_label" translatable="yes">sameboy.github.io</property>
|
||||
<property name="authors">Lior Halphon https://github.com/LIJI32
|
||||
|
||||
GTK3 frontend by
|
||||
Maximilian Mader https://github.com/max-m</property>
|
||||
<property name="logo_icon_name"/>
|
||||
<property name="license_type">mit-x11</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
@ -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/about_dialog.ui</file>
|
||||
<file preprocess="xml-stripblanks" compressed="true">ui/main_window.ui</file>
|
||||
<file preprocess="xml-stripblanks" compressed="true">ui/main_menu.ui</file>
|
||||
<file preprocess="xml-stripblanks" compressed="true">ui/console_window.ui</file>
|
||||
|
388
gtk3/sameboy_application.c
Normal file
388
gtk3/sameboy_application.c
Normal file
@ -0,0 +1,388 @@
|
||||
#include "sameboy_application.h"
|
||||
#include "widgets/main_window.h"
|
||||
#include "widgets/about_dialog.h"
|
||||
#include "widgets/preferences_window.h"
|
||||
|
||||
#define str(x) #x
|
||||
#define xstr(x) str(x)
|
||||
|
||||
#define action_set_enabled(map, name, value) \
|
||||
g_simple_action_set_enabled( \
|
||||
G_SIMPLE_ACTION( \
|
||||
g_action_map_lookup_action(G_ACTION_MAP(map), name) \
|
||||
), \
|
||||
value \
|
||||
);
|
||||
|
||||
struct _SameBoyApplication {
|
||||
GtkApplication parent;
|
||||
|
||||
const GThread *main_thread;
|
||||
PreferencesWindow *preferences;
|
||||
AboutDialog *about_dialog;
|
||||
|
||||
struct CliOptionData {
|
||||
gchar *config_path;
|
||||
gchar *boot_rom_path;
|
||||
gboolean fullscreen;
|
||||
GB_model_t model;
|
||||
gboolean force_software_renderer;
|
||||
} cli_options;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(SameBoyApplication, sameboy_application, GTK_TYPE_APPLICATION);
|
||||
|
||||
static void sameboy_application_init(SameBoyApplication *app) {
|
||||
g_debug("sameboy_application_init");
|
||||
|
||||
// Define our command line parameters
|
||||
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, &app->cli_options.fullscreen, "Start in fullscreen mode", NULL },
|
||||
{ "bootrom", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &app->cli_options.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>" },
|
||||
{ "config", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &app->cli_options.config_path, "Override the path of the configuration file", "<file path>" },
|
||||
{ "no-gl", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &app->cli_options.force_software_renderer, "Do not use OpenGL for rendering", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
// Setup our command line information
|
||||
g_application_add_main_option_entries(G_APPLICATION(app), entries);
|
||||
g_application_set_option_context_parameter_string(G_APPLICATION(app), "[FILE…]");
|
||||
g_application_set_option_context_summary(G_APPLICATION(app), "SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator.");
|
||||
}
|
||||
|
||||
static gint sameboy_application_handle_local_options(GApplication *gapp, GVariantDict *options) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
|
||||
g_debug("sameboy_application_handle_local_options");
|
||||
|
||||
guint32 count;
|
||||
if (g_variant_dict_lookup(options, "version", "b", &count)) {
|
||||
g_message("SameBoy v" xstr(VERSION));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
app->cli_options.model = GB_MODEL_DMG_B;
|
||||
}
|
||||
else {
|
||||
app->cli_options.model = GB_MODEL_DMG_B;
|
||||
g_warning("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) {
|
||||
app->cli_options.model = GB_MODEL_SGB;
|
||||
}
|
||||
else if (g_str_has_suffix(model_name, "-PAL")) {
|
||||
app->cli_options.model = GB_MODEL_SGB | GB_MODEL_PAL_BIT;
|
||||
}
|
||||
else if (g_str_has_suffix(model_name, "2")) {
|
||||
app->cli_options.model = GB_MODEL_SGB2;
|
||||
}
|
||||
else {
|
||||
app->cli_options.model = GB_MODEL_SGB2;
|
||||
g_warning("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")) {
|
||||
app->cli_options.model = GB_MODEL_CGB_C;
|
||||
}
|
||||
else if (g_str_has_suffix(model_name, "-E") || g_strcmp0(model_name, "CGB") == 0) {
|
||||
app->cli_options.model = GB_MODEL_CGB_E;
|
||||
}
|
||||
else {
|
||||
app->cli_options.model = GB_MODEL_CGB_E;
|
||||
g_warning("Unsupported revision: %s\nFalling back to CGB-E", model_name);
|
||||
}
|
||||
}
|
||||
else if (g_str_has_prefix(model_name, "AGB")) {
|
||||
app->cli_options.model = GB_MODEL_AGB;
|
||||
}
|
||||
else {
|
||||
g_warning("Unknown model: %s", model_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
return G_APPLICATION_CLASS(sameboy_application_parent_class)->handle_local_options(G_APPLICATION(app), options);
|
||||
}
|
||||
|
||||
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void activate_reset(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void on_pause_changed(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void activate_show_console(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
main_window_open_console_window(window);
|
||||
}
|
||||
|
||||
static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
main_window_open_memory_viewer_window(window);
|
||||
}
|
||||
|
||||
static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
main_window_open_vram_viewer_window(window);
|
||||
}
|
||||
|
||||
static void activate_break_debugger(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void on_developer_mode_changed(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void activate_clear_console(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
gtk_window_set_interactive_debugging(true);
|
||||
}
|
||||
|
||||
static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the global about dialog.
|
||||
*/
|
||||
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
gtk_dialog_run(GTK_DIALOG(app->about_dialog));
|
||||
gtk_widget_hide(GTK_WIDGET(app->about_dialog));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the global preferences menu.
|
||||
*/
|
||||
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
gtk_widget_show_all(GTK_WIDGET(app->preferences));
|
||||
}
|
||||
|
||||
static void on_mute_changed(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(user_data);
|
||||
MainWindow *window = SAMEBOY_MAIN_WINDOW(gtk_application_get_active_window(GTK_APPLICATION(app)));
|
||||
}
|
||||
|
||||
static const GActionEntry file_entries[] = {
|
||||
{ "open", activate_open, NULL, NULL, NULL },
|
||||
{ "close", activate_close, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static const GActionEntry emulation_entries[] = {
|
||||
{ "reset", activate_reset, NULL, NULL, NULL },
|
||||
{ "pause", NULL, NULL, "false", on_pause_changed },
|
||||
{ "save_state", NULL, NULL, NULL, NULL },
|
||||
{ "load_state", NULL, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static const GActionEntry developer_entries[] = {
|
||||
{ "show_console", activate_show_console, NULL, NULL, NULL },
|
||||
{ "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
|
||||
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
|
||||
{ "break_debugger", activate_break_debugger, NULL, NULL, NULL },
|
||||
{ "toggle_developer_mode", NULL, NULL, "false", on_developer_mode_changed },
|
||||
{ "clear_console", activate_clear_console, NULL, NULL, NULL },
|
||||
{ "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static GActionEntry app_entries[] = {
|
||||
{ "quit", activate_quit, NULL, NULL, NULL },
|
||||
{ "about", activate_about, NULL, NULL, NULL },
|
||||
{ "preferences", activate_preferences, NULL, NULL, NULL },
|
||||
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
|
||||
};
|
||||
|
||||
// WHY DO WE NEED SUCH AN UGLY METHOD, GTK?!
|
||||
static void action_entries_set_enabled(SameBoyApplication *app, const GActionEntry *entries, unsigned n_entries, bool value) {
|
||||
// Assumes null-terminated if n_entries == -1
|
||||
for (unsigned i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++) {
|
||||
const GActionEntry *entry = &entries[i];
|
||||
if (entry->name == NULL) continue;
|
||||
|
||||
action_set_enabled(app, entry->name, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_action_groups(SameBoyApplication *app) {
|
||||
g_action_map_add_action_entries(G_ACTION_MAP(app), emulation_entries, G_N_ELEMENTS(emulation_entries), app);
|
||||
g_action_map_add_action_entries(G_ACTION_MAP(app), developer_entries, G_N_ELEMENTS(developer_entries), app);
|
||||
g_action_map_add_action_entries(G_ACTION_MAP(app), app_entries, G_N_ELEMENTS(app_entries), app);
|
||||
g_action_map_add_action_entries(G_ACTION_MAP(app), file_entries, G_N_ELEMENTS(file_entries), app);
|
||||
|
||||
action_set_enabled(app, "close", false);
|
||||
action_entries_set_enabled(app, emulation_entries, G_N_ELEMENTS(emulation_entries), false);
|
||||
}
|
||||
|
||||
static void sameboy_application_startup(GApplication *gapp) {
|
||||
G_APPLICATION_CLASS(sameboy_application_parent_class)->startup(gapp);
|
||||
|
||||
// TODO:
|
||||
// signal(SIGINT, quit_interrupt);
|
||||
|
||||
g_debug("GTK version %u.%u.%u", gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
|
||||
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
|
||||
g_debug("sameboy_application_startup");
|
||||
|
||||
app->preferences = preferences_window_new();
|
||||
app->about_dialog = about_dialog_new();
|
||||
|
||||
gtk_application_add_window(GTK_APPLICATION(gapp), GTK_WINDOW(app->preferences));
|
||||
|
||||
create_action_groups(app);
|
||||
|
||||
#if NDEBUG
|
||||
// Disable when not compiled in debug mode
|
||||
action_set_enabled(app, "open_gtk_debugger", false);
|
||||
#endif
|
||||
|
||||
GdkScreen *screen = gdk_screen_get_default();
|
||||
GtkCssProvider *provider = gtk_css_provider_new();
|
||||
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);
|
||||
}
|
||||
|
||||
static void open_file(SameBoyApplication *app, GFile *file) {
|
||||
g_debug("config_path: %s", app->cli_options.config_path);
|
||||
g_debug("boot_rom_path: %s", app->cli_options.boot_rom_path);
|
||||
g_debug("fullscreen: %d", app->cli_options.fullscreen);
|
||||
g_debug("model: %d", app->cli_options.model);
|
||||
g_debug("force_software_renderer: %d", app->cli_options.force_software_renderer);
|
||||
|
||||
if (file != NULL) {
|
||||
gchar *path = g_file_get_path(file);
|
||||
g_debug("File path: %s", path);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
MainWindow *window = main_window_new(SAMEBOY_APPLICATION(app), false /* force_software_renderer */);
|
||||
|
||||
// Define a set of window icons
|
||||
GList *icon_list = NULL;
|
||||
static char* icons[] = {
|
||||
RESOURCE_PREFIX "logo_256.png",
|
||||
RESOURCE_PREFIX "logo_128.png",
|
||||
RESOURCE_PREFIX "logo_64.png",
|
||||
RESOURCE_PREFIX "logo_48.png",
|
||||
RESOURCE_PREFIX "logo_32.png",
|
||||
RESOURCE_PREFIX "logo_16.png"
|
||||
};
|
||||
|
||||
GdkPixbuf *icon = gdk_pixbuf_new_from_resource(icons[5], NULL);
|
||||
if (icon) {
|
||||
gtk_window_set_icon(GTK_WINDOW(window), icon);
|
||||
gtk_window_set_default_icon(icon);
|
||||
}
|
||||
|
||||
// Create list of GdkPixbufs
|
||||
for (int i = 0; i < (sizeof(icons) / sizeof(const char*)); ++i) {
|
||||
GdkPixbuf *icon = gdk_pixbuf_new_from_resource(icons[i], NULL);
|
||||
if (!icon) continue;
|
||||
|
||||
icon_list = g_list_prepend(icon_list, icon);
|
||||
}
|
||||
|
||||
// Let GTK choose the proper icon
|
||||
gtk_window_set_icon_list(GTK_WINDOW(window), icon_list);
|
||||
gtk_window_set_default_icon_list(icon_list);
|
||||
|
||||
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(app->about_dialog), gdk_pixbuf_new_from_resource(icons[2], NULL)); // reuse the 64x64 icon
|
||||
gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(app->about_dialog), "v" xstr(VERSION));
|
||||
g_list_free_full(icon_list, g_object_unref);
|
||||
|
||||
if (app->cli_options.fullscreen) {
|
||||
main_window_fullscreen(window, true);
|
||||
}
|
||||
|
||||
gtk_window_present(GTK_WINDOW(window));
|
||||
}
|
||||
|
||||
static void sameboy_application_activate(GApplication *gapp) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
|
||||
g_debug("sameboy_application_activate");
|
||||
|
||||
open_file(app, NULL);
|
||||
|
||||
G_APPLICATION_CLASS(sameboy_application_parent_class)->activate(gapp);
|
||||
}
|
||||
|
||||
static void sameboy_application_open(GApplication *gapp, GFile **files, int n_files, const char *hint) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
|
||||
g_debug("sameboy_application_open(hint = \"%s\")", hint);
|
||||
|
||||
if (n_files >= 1) {
|
||||
if (n_files > 1) {
|
||||
g_warning("More than one file specified");
|
||||
}
|
||||
|
||||
open_file(app, files[0]);
|
||||
}
|
||||
|
||||
G_APPLICATION_CLASS(sameboy_application_parent_class)->open(gapp, files, n_files, hint);
|
||||
}
|
||||
|
||||
static void sameboy_application_shutdown(GApplication *gapp) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
|
||||
g_debug("sameboy_application_shutdown");
|
||||
|
||||
G_APPLICATION_CLASS(sameboy_application_parent_class)->shutdown(gapp);
|
||||
}
|
||||
|
||||
static void sameboy_application_class_init(SameBoyApplicationClass *class) {
|
||||
G_APPLICATION_CLASS(class)->handle_local_options = sameboy_application_handle_local_options;
|
||||
G_APPLICATION_CLASS(class)->startup = sameboy_application_startup;
|
||||
G_APPLICATION_CLASS(class)->activate = sameboy_application_activate;
|
||||
G_APPLICATION_CLASS(class)->open = sameboy_application_open;
|
||||
G_APPLICATION_CLASS(class)->shutdown = sameboy_application_shutdown;
|
||||
}
|
||||
|
||||
SameBoyApplication *sameboy_application_new(void) {
|
||||
return g_object_new(
|
||||
SAMEBOY_APPLICATION_TYPE,
|
||||
"application-id", APP_ID,
|
||||
// "flags", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN,
|
||||
"flags", G_APPLICATION_HANDLES_OPEN,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
void sameboy_application_preferences_signal_connect(SameBoyApplication *app, const gchar *detailed_signal, GCallback c_handler, gpointer data) {
|
||||
g_signal_connect(app->preferences, detailed_signal, c_handler, data);
|
||||
}
|
10
gtk3/sameboy_application.h
Normal file
10
gtk3/sameboy_application.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef sameboy_application_h
|
||||
#define sameboy_application_h
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SAMEBOY_APPLICATION_TYPE (sameboy_application_get_type())
|
||||
G_DECLARE_FINAL_TYPE(SameBoyApplication, sameboy_application, SAMEBOY, APPLICATION, GtkApplication)
|
||||
|
||||
SameBoyApplication *sameboy_application_new(void);
|
||||
void sameboy_application_preferences_signal_connect(SameBoyApplication *app, const gchar *detailed_signal, GCallback c_handler, gpointer data);
|
||||
#endif
|
@ -45,7 +45,6 @@ typedef struct GuiData {
|
||||
PreferencesWindow *preferences;
|
||||
VramViewerWindow *vram_viewer;
|
||||
PrinterWindow *printer;
|
||||
|
||||
GtkWindow *memory_viewer;
|
||||
|
||||
// Audio and video
|
||||
|
19
gtk3/widgets/about_dialog.c
Normal file
19
gtk3/widgets/about_dialog.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "about_dialog.h"
|
||||
|
||||
struct _AboutDialog {
|
||||
GtkMenuBarClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(AboutDialog, about_dialog, GTK_TYPE_ABOUT_DIALOG);
|
||||
|
||||
static void about_dialog_init(AboutDialog *self) {
|
||||
gtk_widget_init_template(GTK_WIDGET(self));
|
||||
}
|
||||
|
||||
static void about_dialog_class_init(AboutDialogClass *class) {
|
||||
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/about_dialog.ui");
|
||||
}
|
||||
|
||||
AboutDialog *about_dialog_new() {
|
||||
return g_object_new(ABOUT_DIALOG_TYPE, NULL);
|
||||
}
|
11
gtk3/widgets/about_dialog.h
Normal file
11
gtk3/widgets/about_dialog.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef about_dialog_h
|
||||
#define about_dialog_h
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define ABOUT_DIALOG_TYPE (about_dialog_get_type())
|
||||
G_DECLARE_FINAL_TYPE(AboutDialog, about_dialog, SAMEBOY, ABOUT_DIALOG, GtkAboutDialog)
|
||||
|
||||
AboutDialog *about_dialog_new();
|
||||
|
||||
#endif
|
@ -451,7 +451,7 @@ ConsoleWindow *console_window_new(GB_gameboy_t *gb) {
|
||||
}
|
||||
|
||||
// This function gets called every VBlank while the emulation is running.
|
||||
char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
char *console_window_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
self->clear_sidebar = true;
|
||||
|
||||
char *command = (char *)g_async_queue_try_pop(self->input_queue);
|
||||
@ -466,7 +466,7 @@ char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
|
||||
// This will only be called if the debugger is in stopped mode (after a breakpoint hit for example),
|
||||
// thus we block the emulation thread until input is available.
|
||||
char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
char *console_window_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
update_sidebar(self, gb);
|
||||
|
||||
char *command = (char *)g_async_queue_pop(self->input_queue);
|
||||
@ -479,17 +479,17 @@ char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb) {
|
||||
return command;
|
||||
}
|
||||
|
||||
void focus(ConsoleWindow *self) {
|
||||
void console_window_focus(ConsoleWindow *self) {
|
||||
gtk_window_present_with_time(GTK_WINDOW(self), time(NULL));
|
||||
gtk_widget_grab_focus(GTK_WIDGET(self->input));
|
||||
}
|
||||
|
||||
// Queues a message to be logged to the console
|
||||
void console_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes) {
|
||||
void console_window_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes) {
|
||||
if (!message || g_str_equal("", message)) return;
|
||||
|
||||
if (self->developer_mode) {
|
||||
focus(self);
|
||||
console_window_focus(self);
|
||||
}
|
||||
|
||||
AttributedMessage *attr_msg = g_new(AttributedMessage, 1);
|
||||
@ -504,27 +504,27 @@ void console_log(ConsoleWindow *self, const char *message, GB_log_attributes att
|
||||
}
|
||||
|
||||
// Marks the console as to be cleared on the next redraw
|
||||
void console_clear(ConsoleWindow *self) {
|
||||
void console_window_clear(ConsoleWindow *self) {
|
||||
self->should_clear = true;
|
||||
|
||||
// mark as dirty
|
||||
gtk_widget_queue_draw(GTK_WIDGET(self));
|
||||
}
|
||||
|
||||
void break_debugger(ConsoleWindow *self, bool forced) {
|
||||
void console_window_break_debugger(ConsoleWindow *self, bool forced) {
|
||||
if (!forced && !self->developer_mode) return;
|
||||
|
||||
GB_debugger_break(self->gb);
|
||||
focus(self);
|
||||
console_window_focus(self);
|
||||
}
|
||||
|
||||
// Hack to avoid deadlocking on queue reads ...
|
||||
void abort_debugger(ConsoleWindow *self) {
|
||||
void console_window_abort_debugger(ConsoleWindow *self) {
|
||||
g_async_queue_push(self->input_queue, g_strdup("c\0"));
|
||||
g_async_queue_push(self->output_queue, g_strdup("c\0"));
|
||||
console_clear(self);
|
||||
console_window_clear(self);
|
||||
}
|
||||
|
||||
void set_developer_mode(ConsoleWindow *self, bool value) {
|
||||
void console_window_set_developer_mode(ConsoleWindow *self, bool value) {
|
||||
self->developer_mode = value;
|
||||
}
|
||||
|
@ -9,11 +9,12 @@
|
||||
G_DECLARE_FINAL_TYPE(ConsoleWindow, console_window, SAMEBOY, CONSOLE_WINDOW, GtkWindow)
|
||||
|
||||
ConsoleWindow *console_window_new(GB_gameboy_t *gb);
|
||||
char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb);
|
||||
char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb);
|
||||
void console_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes);
|
||||
void console_clear(ConsoleWindow *self);
|
||||
void break_debugger(ConsoleWindow *self, bool forced);
|
||||
void abort_debugger(ConsoleWindow *self);
|
||||
void set_developer_mode(ConsoleWindow *self, bool value);
|
||||
char *console_window_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb);
|
||||
char *console_window_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb);
|
||||
void console_window_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes);
|
||||
void console_window_clear(ConsoleWindow *self);
|
||||
void console_window_focus(ConsoleWindow *self);
|
||||
void console_window_break_debugger(ConsoleWindow *self, bool forced);
|
||||
void console_window_abort_debugger(ConsoleWindow *self);
|
||||
void console_window_set_developer_mode(ConsoleWindow *self, bool value);
|
||||
#endif
|
@ -64,7 +64,7 @@ void main_menu_setup(MainMenu *self, char *model_string) {
|
||||
|
||||
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_connect_toggle_signal(model_group, on_change_model);
|
||||
check_menu_item_group_activate(model_group, model_string);
|
||||
|
||||
static const char *const peripheral_names[] = {
|
||||
@ -81,6 +81,6 @@ void main_menu_setup(MainMenu *self, char *model_string) {
|
||||
|
||||
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_connect_toggle_signal(link_group, on_change_linked_device);
|
||||
check_menu_item_group_activate(link_group, "NONE");
|
||||
}
|
||||
|
@ -1,14 +1,30 @@
|
||||
#include "main_window.h"
|
||||
#include <Core/gb.h>
|
||||
|
||||
#include "gb_screen.h"
|
||||
#include "main_menu.h"
|
||||
#include "console_window.h"
|
||||
#include "printer_window.h"
|
||||
#include "preferences_window.h"
|
||||
#include "vram_viewer_window.h"
|
||||
|
||||
struct _MainWindow {
|
||||
GtkApplicationWindowClass parent_class;
|
||||
|
||||
// Child nodes
|
||||
GtkBox *container;
|
||||
GbScreen *screen;
|
||||
bool force_software_renderer;
|
||||
MainMenu *main_menu;
|
||||
|
||||
// The local SameBoy core instance
|
||||
GB_gameboy_t *gb;
|
||||
|
||||
// Local sub-windows
|
||||
ConsoleWindow *console;
|
||||
GtkWindow *memory_viewer; // TODO
|
||||
PrinterWindow *printer;
|
||||
VramViewerWindow *vram_viewer;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(MainWindow, main_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||
@ -39,11 +55,98 @@ static void main_window_get_property(GObject *object, guint property_id, GValue
|
||||
}
|
||||
}
|
||||
|
||||
static void on_update_color_correction(PreferencesWindow *pref, const GB_color_correction_mode_t mode, MainWindow *self) {
|
||||
g_debug("on_update_color_correction(%d)", mode);
|
||||
}
|
||||
|
||||
static void on_update_video_color_temperature(PreferencesWindow *pref, const gint light_temperature, MainWindow *self) {
|
||||
g_debug("on_update_video_color_temperature(%d)", light_temperature);
|
||||
|
||||
if (GB_is_inited(self->gb)) {
|
||||
// wouldn’t it be nice to use the value set in the GtkAdjustment of the slider instead of 256.0 here?
|
||||
GB_set_light_temperature(self->gb, (double) light_temperature / 256.0);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_update_monochrome_palette(PreferencesWindow *pref, const GB_palette_t *palette, MainWindow *self) {
|
||||
g_debug(
|
||||
"on_update_monochrome_palette(\n\trgb(%d, %d, %d),\n\trgb(%d, %d, %d),\n\trgb(%d, %d, %d),\n\trgb(%d, %d, %d),\n\trgb(%d, %d, %d)\n)",
|
||||
palette->colors[0].r, palette->colors[0].g, palette->colors[0].b,
|
||||
palette->colors[1].r, palette->colors[1].g, palette->colors[1].b,
|
||||
palette->colors[2].r, palette->colors[2].g, palette->colors[2].b,
|
||||
palette->colors[3].r, palette->colors[3].g, palette->colors[3].b,
|
||||
palette->colors[4].r, palette->colors[4].g, palette->colors[4].b
|
||||
);
|
||||
}
|
||||
|
||||
static void on_update_highpass(PreferencesWindow *pref, const GB_highpass_mode_t mode, MainWindow *self) {
|
||||
g_debug("on_update_highpass(%d)", mode);
|
||||
}
|
||||
|
||||
static void on_update_rewind_duration(PreferencesWindow *pref, const guint rewind_duration, MainWindow *self) {
|
||||
g_debug("on_update_rewind_duration(%d)", rewind_duration);
|
||||
}
|
||||
|
||||
static void on_update_rumble_mode(PreferencesWindow *pref, const GB_rumble_mode_t mode, MainWindow *self) {
|
||||
g_debug("on_update_rumble_mode(%d)", mode);
|
||||
}
|
||||
|
||||
static void on_update_video_display_border_mode(PreferencesWindow *pref, const gchar *name, MainWindow *self) {
|
||||
g_debug("on_update_video_display_border_mode(%s)", name);
|
||||
// gui_data.border_mode_changed = true;
|
||||
}
|
||||
|
||||
static void on_update_video_shader(PreferencesWindow *pref, const gchar *name, MainWindow *self) {
|
||||
g_debug("on_update_video_shader(%s)", name);
|
||||
main_window_set_shader(self, name);
|
||||
}
|
||||
|
||||
static void on_update_audio_sample_rate(PreferencesWindow *pref, const guint sample_rate, MainWindow *self) {
|
||||
g_debug("on_update_audio_sample_rate(%d)", sample_rate);
|
||||
|
||||
if (sample_rate == -1) {
|
||||
// gui_data.sample_rate = GB_audio_default_sample_rate();
|
||||
}
|
||||
else {
|
||||
// gui_data.sample_rate = *sample_rate;
|
||||
}
|
||||
|
||||
// init_audio();
|
||||
}
|
||||
|
||||
static void on_update_audio_interference_volume(PreferencesWindow *pref, const guint *interference_volume, MainWindow *self) {
|
||||
g_debug("on_update_audio_interference_volume(%d)", *interference_volume);
|
||||
|
||||
if (GB_is_inited(self->gb)) {
|
||||
// wouldn’t it be nice to use the value set in the GtkAdjustment of the slider instead of 100.0 here?
|
||||
GB_set_interference_volume(self->gb, (double) *interference_volume / 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_application_set(MainWindow *self, GObject *object) {
|
||||
SameBoyApplication *app = SAMEBOY_APPLICATION(gtk_window_get_application(GTK_WINDOW(self)));
|
||||
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::color-correction", G_CALLBACK(on_update_color_correction), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::video-color-temperature", G_CALLBACK(on_update_video_color_temperature), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::monochrome-palette", G_CALLBACK(on_update_monochrome_palette), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::highpass", G_CALLBACK(on_update_highpass), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::rewind-duration", G_CALLBACK(on_update_rewind_duration), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::rumble-mode", G_CALLBACK(on_update_rumble_mode), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::video-display-border-mode", G_CALLBACK(on_update_video_display_border_mode), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::video-shader", G_CALLBACK(on_update_video_shader), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::audio-sample-rate", G_CALLBACK(on_update_audio_sample_rate), self);
|
||||
sameboy_application_preferences_signal_connect(app, "pref-update::audio-interference-volume", G_CALLBACK(on_update_audio_interference_volume), self);
|
||||
}
|
||||
|
||||
static void main_window_constructed(GObject *object) {
|
||||
G_OBJECT_CLASS(main_window_parent_class)->constructed(object);
|
||||
|
||||
MainWindow *self = (MainWindow *) object;
|
||||
|
||||
self->screen = gb_screen_new(self->force_software_renderer);
|
||||
gtk_box_pack_end(GTK_BOX(self->container), GTK_WIDGET(self->screen), true, true, 0);
|
||||
|
||||
g_signal_connect(self, "notify::application", G_CALLBACK(on_application_set), self);
|
||||
}
|
||||
|
||||
static void main_window_init(MainWindow *self) {
|
||||
@ -52,9 +155,40 @@ static void main_window_init(MainWindow *self) {
|
||||
gtk_window_set_title(GTK_WINDOW(self), "SameBoy");
|
||||
gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(self), false);
|
||||
|
||||
// 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);
|
||||
|
||||
self->gb = g_malloc(sizeof(GB_gameboy_t));
|
||||
if (self->gb == NULL) {
|
||||
g_warning("Out of memory!");
|
||||
// TODO: Try to stop gracefully
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
self->console = console_window_new(self->gb);
|
||||
gtk_window_set_attached_to(GTK_WINDOW(self->console), GTK_WIDGET(self));
|
||||
|
||||
self->vram_viewer = vram_viewer_window_new();
|
||||
gtk_window_set_attached_to(GTK_WINDOW(self->vram_viewer), GTK_WIDGET(self));
|
||||
|
||||
self->printer = printer_window_new();
|
||||
gtk_window_set_attached_to(GTK_WINDOW(self->printer), GTK_WIDGET(self));
|
||||
}
|
||||
|
||||
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, container);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, main_menu);
|
||||
|
||||
g_signal_new(
|
||||
"break-debugger-keyboard", // signal name
|
||||
G_TYPE_FROM_INSTANCE(self), // itype
|
||||
G_TYPE_FROM_CLASS(class), // itype
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_ACTION, // signal_flags
|
||||
0, // class_offset
|
||||
NULL, // accumulator
|
||||
@ -64,21 +198,6 @@ static void main_window_init(MainWindow *self) {
|
||||
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_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, container);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, main_menu);
|
||||
|
||||
obj_properties[PROP_FORCE_SOFTWARE_RENDERER] = g_param_spec_boolean(
|
||||
"force_software_renderer", "Software Renderer", "Forces the use of software rendering via Cairo",
|
||||
false,
|
||||
@ -92,16 +211,21 @@ static void main_window_class_init(MainWindowClass *class) {
|
||||
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);
|
||||
MainWindow *main_window_new(SameBoyApplication *application, bool force_software_renderer) {
|
||||
return g_object_new(
|
||||
MAIN_WINDOW_TYPE,
|
||||
"application", G_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));
|
||||
gtk_window_fullscreen(GTK_WINDOW(self));
|
||||
}
|
||||
else {
|
||||
gtk_window_fullscreen(GTK_WINDOW(self));
|
||||
gtk_window_unfullscreen(GTK_WINDOW(self));
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +233,22 @@ void main_window_setup_menu(MainWindow *self, char *model_string) {
|
||||
main_menu_setup(self->main_menu, model_string);
|
||||
}
|
||||
|
||||
void main_window_open_console_window(MainWindow *self) {
|
||||
gtk_widget_show_all(GTK_WIDGET(self->console));
|
||||
}
|
||||
|
||||
void main_window_open_memory_viewer_window(MainWindow *self) {
|
||||
g_warning("Not yet implemented!");
|
||||
}
|
||||
|
||||
void main_window_open_vram_viewer_window(MainWindow *self) {
|
||||
gtk_widget_show_all(GTK_WIDGET(self->vram_viewer));
|
||||
}
|
||||
|
||||
void main_window_open_printer_window(MainWindow *self) {
|
||||
gtk_widget_show_all(GTK_WIDGET(self->printer));
|
||||
}
|
||||
|
||||
// GbScreen wrappers
|
||||
void main_window_clear(MainWindow *self) {
|
||||
return gb_screen_clear(self->screen);
|
||||
|
@ -4,15 +4,21 @@
|
||||
#include <stdbool.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <Core/gb.h>
|
||||
#include "../sameboy_application.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);
|
||||
MainWindow *main_window_new(SameBoyApplication *app, bool force_software_renderer);
|
||||
void main_window_setup_menu(MainWindow *self, char *model_string);
|
||||
|
||||
void main_window_fullscreen(MainWindow *self, bool make_fullscreen);
|
||||
void main_window_open_console_window(MainWindow *self);
|
||||
void main_window_open_memory_viewer_window(MainWindow *self);
|
||||
void main_window_open_vram_viewer_window(MainWindow *self);
|
||||
void main_window_open_printer_window(MainWindow *self);
|
||||
|
||||
// GbScreen wrappers
|
||||
void main_window_clear(MainWindow *self);
|
||||
uint32_t *main_window_get_pixels(MainWindow *self);
|
||||
|
@ -30,14 +30,6 @@ struct _PreferencesWindow {
|
||||
|
||||
G_DEFINE_TYPE(PreferencesWindow, preferences_window, GTK_TYPE_WINDOW);
|
||||
|
||||
typedef enum {
|
||||
PROP_GB_PTR = 1,
|
||||
|
||||
N_PROPERTIES
|
||||
} PreferencesWindowProperty;
|
||||
|
||||
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
||||
|
||||
typedef enum {
|
||||
PREF_UPDATE,
|
||||
N_SIGNALS
|
||||
@ -45,24 +37,6 @@ typedef enum {
|
||||
|
||||
static guint preferences_signals[N_SIGNALS] = { 0, };
|
||||
|
||||
static void preferences_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
|
||||
PreferencesWindow *self = (PreferencesWindow *) object;
|
||||
|
||||
switch ((PreferencesWindowProperty) property_id) {
|
||||
case PROP_GB_PTR: self->gb = g_value_get_pointer(value); break;
|
||||
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void preferences_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
|
||||
PreferencesWindow *self = (PreferencesWindow *) object;
|
||||
|
||||
switch ((PreferencesWindowProperty) property_id) {
|
||||
case PROP_GB_PTR: g_value_set_pointer(value, self->gb); break;
|
||||
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void preferences_window_init(PreferencesWindow *self) {
|
||||
gtk_widget_init_template(GTK_WIDGET(self));
|
||||
|
||||
@ -139,9 +113,12 @@ static void on_color_correction_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||
config.video.color_correction_id = (gchar *)gtk_combo_box_get_active_id(box);
|
||||
|
||||
if (self->gb) {
|
||||
GB_set_color_correction_mode(self->gb, config_get_color_correction_mode());
|
||||
}
|
||||
g_signal_emit(
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("color-correction"),
|
||||
config_get_color_correction_mode()
|
||||
);
|
||||
}
|
||||
|
||||
static void on_light_temperature_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
@ -153,7 +130,7 @@ static void on_light_temperature_changed(GtkWidget *w, PreferencesWindow *self)
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("video-color-temperature"),
|
||||
&config.video.light_temperature
|
||||
config.video.light_temperature
|
||||
);
|
||||
}
|
||||
|
||||
@ -161,35 +138,47 @@ static void on_monochrome_palette_changed(GtkWidget *w, PreferencesWindow *self)
|
||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||
config.video.monochrome_palette_id = (gchar *)gtk_combo_box_get_active_id(box);
|
||||
|
||||
if (self->gb) {
|
||||
GB_set_palette(self->gb, config_get_monochrome_palette());
|
||||
}
|
||||
g_signal_emit(
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("monochrome-palette"),
|
||||
config_get_monochrome_palette()
|
||||
);
|
||||
}
|
||||
|
||||
static void on_highpass_filter_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
config.audio.high_pass_filter_id = (gchar *)gtk_combo_box_get_active_id(GTK_COMBO_BOX(w));
|
||||
|
||||
if (self->gb) {
|
||||
GB_set_highpass_filter_mode(self->gb, config_get_highpass_mode());
|
||||
}
|
||||
g_signal_emit(
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("highpass"),
|
||||
config_get_highpass_mode()
|
||||
);
|
||||
}
|
||||
|
||||
static void on_rewind_duration_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||
config.emulation.rewind_duration = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10);
|
||||
|
||||
if (self->gb) {
|
||||
GB_set_rewind_length(self->gb, config.emulation.rewind_duration);
|
||||
}
|
||||
g_signal_emit(
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("rewind-duration"),
|
||||
config.emulation.rewind_duration
|
||||
);
|
||||
}
|
||||
|
||||
static void on_rumble_mode_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||
config.controls.rumble_mode = (gchar *)gtk_combo_box_get_active_id(box);
|
||||
|
||||
if (self->gb) {
|
||||
GB_set_rumble_mode(self->gb, config_get_rumble_mode());
|
||||
}
|
||||
g_signal_emit(
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("rumble-mode"),
|
||||
config_get_rumble_mode()
|
||||
);
|
||||
}
|
||||
|
||||
static void on_display_border_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
@ -224,7 +213,7 @@ static void on_sample_rate_changed(GtkWidget *w, PreferencesWindow *self) {
|
||||
self,
|
||||
preferences_signals[PREF_UPDATE],
|
||||
g_quark_from_static_string("audio-sample-rate"),
|
||||
&config.audio.sample_rate
|
||||
config.audio.sample_rate
|
||||
);
|
||||
}
|
||||
|
||||
@ -319,16 +308,6 @@ static void preferences_window_class_init(PreferencesWindowClass *class) {
|
||||
|
||||
GTK_WIDGET_CLASS(class)->realize = preferences_window_realize;
|
||||
|
||||
obj_properties[PROP_GB_PTR] = g_param_spec_pointer(
|
||||
"gb", "SameBoy core pointer", "SameBoy Core pointer (GB_gameboy_t)",
|
||||
G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
||||
);
|
||||
|
||||
G_OBJECT_CLASS(class)->set_property = preferences_window_set_property;
|
||||
G_OBJECT_CLASS(class)->get_property = preferences_window_get_property;
|
||||
|
||||
g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties);
|
||||
|
||||
preferences_signals[PREF_UPDATE] = g_signal_new(
|
||||
"pref-update", // signal name
|
||||
G_TYPE_FROM_CLASS(G_OBJECT_CLASS(class)), // itype
|
||||
@ -343,8 +322,8 @@ static void preferences_window_class_init(PreferencesWindowClass *class) {
|
||||
);
|
||||
}
|
||||
|
||||
PreferencesWindow *preferences_window_new(GB_gameboy_t *gb) {
|
||||
return g_object_new(PREFERENCES_WINDOW_TYPE, "gb", gb, NULL);
|
||||
PreferencesWindow *preferences_window_new(void) {
|
||||
return g_object_new(PREFERENCES_WINDOW_TYPE, NULL);
|
||||
}
|
||||
|
||||
void preferences_window_update_boot_rom_selector(PreferencesWindow *self) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
#define PREFERENCES_WINDOW_TYPE (preferences_window_get_type())
|
||||
G_DECLARE_FINAL_TYPE(PreferencesWindow, preferences_window, SAMEBOY, PREFERENCES_WINDOW, GtkWindow)
|
||||
|
||||
PreferencesWindow *preferences_window_new(GB_gameboy_t *gb);
|
||||
PreferencesWindow *preferences_window_new(void);
|
||||
void preferences_window_update_boot_rom_selector(PreferencesWindow *self);
|
||||
|
||||
#endif
|
@ -43,11 +43,11 @@ static gboolean on_printer_draw(GtkWidget *widget, cairo_t *cr, PrinterWindow *w
|
||||
|
||||
static void on_printer_save(GtkWidget *w, PrinterWindow *self) {
|
||||
// This function is defined in `main.c` ...
|
||||
gpointer perform_atomic(gpointer (*fn)(gpointer args), gpointer args);
|
||||
// gpointer perform_atomic(gpointer (*fn)(gpointer args), gpointer args);
|
||||
|
||||
// This is ugly, yup.
|
||||
bool success = perform_atomic((gpointer)(*printer_window_save), self);
|
||||
g_debug("File saving status: %d", success);
|
||||
// bool success = perform_atomic((gpointer)(*printer_window_save), self);
|
||||
// g_debug("File saving status: %d", success);
|
||||
}
|
||||
|
||||
static void on_printer_clear(GtkWidget *w, PrinterWindow *self) {
|
||||
|
Loading…
Reference in New Issue
Block a user