diff --git a/gtk3/check_menu_radio_group.c b/gtk3/check_menu_radio_group.c index baf264a..f33f6a8 100644 --- a/gtk3/check_menu_radio_group.c +++ b/gtk3/check_menu_radio_group.c @@ -4,7 +4,7 @@ static void check_menu_item_group_handler(GtkCheckMenuItem *item, CheckMenuItemG bool cancel = false; if (data->handler) { - cancel = data->handler(GTK_WIDGET(item), (gpointer) data->arg); + cancel = data->handler(data->group->parent, GTK_WIDGET(item), (gpointer) data->arg); } GValue value = G_VALUE_INIT; @@ -23,7 +23,7 @@ static void check_menu_item_group_handler(GtkCheckMenuItem *item, CheckMenuItemG } } -CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args) { +CheckMenuItemGroup *check_menu_item_group_new(GtkWidget *parent, char **names, char **args) { unsigned name_count = 0; if (names != NULL) { @@ -31,6 +31,7 @@ CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args) { } CheckMenuItemGroup *group = g_malloc0(sizeof(CheckMenuItemGroup)); + group->parent = parent; group->count = name_count; group->items = g_malloc0(sizeof(GtkWidget*) * name_count); group->handlers = g_malloc0(sizeof(CheckMenuItemGroupHandlerData*) * name_count); @@ -66,7 +67,7 @@ void check_menu_item_group_activate(CheckMenuItemGroup *group, char *arg) { } } -void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, gpointer)) { +void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, GtkWidget *, gpointer)) { for (unsigned i = 0; i < group->count; i++) { group->handlers[i]->handler = handler; } diff --git a/gtk3/check_menu_radio_group.h b/gtk3/check_menu_radio_group.h index de12df9..a8113d8 100644 --- a/gtk3/check_menu_radio_group.h +++ b/gtk3/check_menu_radio_group.h @@ -7,18 +7,19 @@ typedef struct CheckMenuItemGroupHandlerData { struct CheckMenuItemGroup *group; char *arg; - bool (*handler)(GtkWidget *, void *); + bool (*handler)(GtkWidget *, GtkWidget *, void *); } CheckMenuItemGroupHandlerData; typedef struct CheckMenuItemGroup { + GtkWidget *parent; unsigned count; GtkWidget **items; CheckMenuItemGroupHandlerData **handlers; } CheckMenuItemGroup; -CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args); +CheckMenuItemGroup *check_menu_item_group_new(GtkWidget *parent, char **names, char **args); void check_menu_item_group_activate(CheckMenuItemGroup *group, char *arg); -void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, gpointer)); +void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, GtkWidget *, gpointer)); void check_menu_item_group_insert_into_menu_shell(CheckMenuItemGroup *group, GtkMenuShell *menu_shell, gint position); #endif \ No newline at end of file diff --git a/gtk3/config.c b/gtk3/config.c index f9f1b43..c67c02a 100644 --- a/gtk3/config.c +++ b/gtk3/config.c @@ -466,6 +466,14 @@ GB_model_t config_get_model(void) { return config_get_cgb_model(); } +GB_model_t config_get_model_type(struct CliOptionData *cli_options) { + if (cli_options->model != -1) { + return cli_options->model; + } + + return config_get_model(); +} + void config_set_model(GB_model_t model) { switch (model & GB_MODEL_FAMILY_MASK) { case GB_MODEL_DMG_FAMILY: diff --git a/gtk3/config.h b/gtk3/config.h index 3f68e6c..2cd662c 100644 --- a/gtk3/config.h +++ b/gtk3/config.h @@ -4,6 +4,7 @@ #include #include #include "shader.h" +#include "util.h" #define CONFIG_FILE "sameboy-gtk3.ini" @@ -103,6 +104,7 @@ void config_set_rumble_mode(const GB_rumble_mode_t); void config_set_model(GB_model_t model); GB_model_t config_get_model(void); +GB_model_t config_get_model_type(struct CliOptionData *cli_options); GB_model_t config_get_dmg_model(void); GB_model_t config_get_sgb_model(void); diff --git a/gtk3/sameboy_application.c b/gtk3/sameboy_application.c index af9cf76..0009ab8 100644 --- a/gtk3/sameboy_application.c +++ b/gtk3/sameboy_application.c @@ -1,4 +1,5 @@ #include "sameboy_application.h" +#include "config.h" #include "widgets/main_window.h" #include "widgets/about_dialog.h" #include "widgets/preferences_window.h" @@ -17,23 +18,19 @@ struct _SameBoyApplication { GtkApplication parent; + struct CliOptionData cli_options; + 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; + GDateTime *config_modification_date; }; G_DEFINE_TYPE(SameBoyApplication, sameboy_application, GTK_TYPE_APPLICATION); static void sameboy_application_init(SameBoyApplication *app) { - g_debug("sameboy_application_init"); + g_debug("sameboy_application_init(%p)", app); // Define our command line parameters GOptionEntry entries[] = { @@ -64,6 +61,8 @@ static gint sameboy_application_handle_local_options(GApplication *gapp, GVarian // Handle model override GVariant *model_name_var = g_variant_dict_lookup_value(options, "model", G_VARIANT_TYPE_STRING); + app->cli_options.model = -1; + if (model_name_var != NULL) { const gchar *model_name = g_variant_get_string(model_name_var, NULL); @@ -260,8 +259,15 @@ static void sameboy_application_startup(GApplication *gapp) { SameBoyApplication *app = SAMEBOY_APPLICATION(gapp); g_debug("sameboy_application_startup"); + 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); - app->preferences = preferences_window_new(); + init_config(G_APPLICATION(app), app->cli_options.config_path, &app->config_modification_date); + + app->preferences = preferences_window_new(); app->about_dialog = about_dialog_new(); gtk_application_add_window(GTK_APPLICATION(gapp), GTK_WINDOW(app->preferences)); @@ -277,22 +283,20 @@ static void sameboy_application_startup(GApplication *gapp) { 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); + + // Just hide our sub-windows when closing them + g_signal_connect(app->preferences, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); } 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 */); + MainWindow *window = main_window_new(SAMEBOY_APPLICATION(app), app->cli_options.force_software_renderer); + main_window_setup_menu(window, config.emulation.model); // Define a set of window icons GList *icon_list = NULL; @@ -386,3 +390,7 @@ SameBoyApplication *sameboy_application_new(void) { 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); } + +struct CliOptionData *sameboy_application_get_cli_options(SameBoyApplication *self) { + return &self->cli_options; +} diff --git a/gtk3/sameboy_application.h b/gtk3/sameboy_application.h index 690ad6b..bf1c002 100644 --- a/gtk3/sameboy_application.h +++ b/gtk3/sameboy_application.h @@ -1,10 +1,20 @@ #ifndef sameboy_application_h #define sameboy_application_h #include + #include + + struct CliOptionData { + gchar *config_path; + gchar *boot_rom_path; + gboolean fullscreen; + GB_model_t model; + gboolean force_software_renderer; + }; #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); + struct CliOptionData *sameboy_application_get_cli_options(SameBoyApplication *self); #endif diff --git a/gtk3/types.h b/gtk3/types.h index cef73bf..3ed3107 100644 --- a/gtk3/types.h +++ b/gtk3/types.h @@ -13,6 +13,7 @@ typedef struct{ uint16_t width, height; } Rect; +/* typedef struct GuiData { struct CliOptionData { gchar *config_path; @@ -74,6 +75,7 @@ typedef struct GuiData { unsigned controller_count; struct Controller_t *last_used_controller; // Used for rumble } GuiData; +*/ typedef enum { INPUT_UP, diff --git a/gtk3/util.c b/gtk3/util.c index 508b2fc..7124404 100644 --- a/gtk3/util.c +++ b/gtk3/util.c @@ -92,14 +92,6 @@ uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) { return 0xFF000000 | (r << 16) | (g << 8) | b; } -GB_model_t config_get_model_type(GuiData *gui_data) { - if (gui_data->cli_options.model != -1) { - return gui_data->cli_options.model; - } - - return config_get_model(); -} - GtkWidget *menubar_to_menu(GtkMenuBar *menubar) { GtkWidget *menu = gtk_menu_new(); g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar)); diff --git a/gtk3/util.h b/gtk3/util.h index 35fd398..1c78200 100644 --- a/gtk3/util.h +++ b/gtk3/util.h @@ -18,8 +18,6 @@ double min_double(double a, double b); uint32_t convert_color(uint16_t color); uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b); -GB_model_t config_get_model_type(GuiData *gui_data); - GtkWidget *menubar_to_menu(GtkMenuBar *menubar); gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data); diff --git a/gtk3/widgets/main_menu.c b/gtk3/widgets/main_menu.c index 045e126..bd69182 100644 --- a/gtk3/widgets/main_menu.c +++ b/gtk3/widgets/main_menu.c @@ -1,6 +1,7 @@ #include "main_menu.h" #include #include "gb_screen.h" +#include "main_window.h" #include "../check_menu_radio_group.h" struct _MainMenu { @@ -34,13 +35,15 @@ MainMenu *main_menu_new() { // 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 menu. -void main_menu_setup(MainMenu *self, char *model_string) { +void main_menu_setup( MainMenu *self + , char *model_string + , MainWindow *main_window + , bool (*on_change_model)(GtkWidget *, GtkWidget *, gpointer) + , bool (*on_change_linked_device)(GtkWidget *, GtkWidget *, gpointer) +) { // 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. - bool on_change_model(GtkWidget *, gpointer); - bool on_change_linked_device(GtkWidget *, gpointer); - static const char *const model_names[] = { "Game Boy", "Super Game Boy", @@ -62,25 +65,27 @@ void main_menu_setup(MainMenu *self, char *model_string) { 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); + CheckMenuItemGroup *model_group = check_menu_item_group_new(GTK_WIDGET(main_window), (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[] = { "None", "Game Boy Printer", + "WorkBoy", NULL }; static const char *const peripheral_codes[] = { "NONE", "PRINTER", + "WORKBOY", NULL, }; - CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes); + CheckMenuItemGroup *link_group = check_menu_item_group_new(GTK_WIDGET(main_window), (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"); } diff --git a/gtk3/widgets/main_menu.h b/gtk3/widgets/main_menu.h index dc85cce..badaf17 100644 --- a/gtk3/widgets/main_menu.h +++ b/gtk3/widgets/main_menu.h @@ -3,11 +3,17 @@ #include #include +#include "main_window.h" #define MAIN_MENU_TYPE (main_menu_get_type()) G_DECLARE_FINAL_TYPE(MainMenu, main_menu, SAMEBOY, MAIN_MENU, GtkMenuBar) MainMenu *main_menu_new(); -void main_menu_setup(MainMenu *self, char *model_string); +void main_menu_setup( MainMenu *self + , char *model_string + , MainWindow *main_window + , bool (*on_change_model)(GtkWidget *, GtkWidget *, gpointer) + , bool (*on_change_linked_device)(GtkWidget *, GtkWidget *, gpointer) + ); #endif diff --git a/gtk3/widgets/main_window.c b/gtk3/widgets/main_window.c index 08434f7..091991d 100644 --- a/gtk3/widgets/main_window.c +++ b/gtk3/widgets/main_window.c @@ -1,6 +1,8 @@ #include "main_window.h" #include +#include "../../SDL/audio/audio.h" +#include "../config.h" #include "gb_screen.h" #include "main_menu.h" #include "console_window.h" @@ -14,11 +16,29 @@ struct _MainWindow { // Child nodes GtkBox *container; GbScreen *screen; - bool force_software_renderer; MainMenu *main_menu; + bool force_software_renderer; + // The local SameBoy core instance GB_gameboy_t *gb; + unsigned sample_rate; + bool running; // Indicates that the emulation thread is running + bool stopping; // Indicates that the emulation thread is about to be stopped + + GFile *file; + char *battery_save_path; + char *cheats_save_path; + + // Fast forward / slow motion + bool underclock_down; + bool rewind_down; + bool do_rewind; + bool rewind_paused; + bool turbo_down; + double clock_mutliplier; + double analog_clock_multiplier; + bool analog_clock_multiplier_valid; // Local sub-windows ConsoleWindow *console; @@ -37,6 +57,14 @@ typedef enum { static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; +#define perform_atomic(self, block) { \ + while (!GB_is_inited(self->gb)); \ + bool was_running = self->running && !GB_debugger_is_stopped(self->gb); \ + if (was_running) { main_window_stop(self); } \ + block; \ + if (was_running) { main_window_start(self); } \ +} + static void main_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MainWindow *self = (MainWindow *) object; @@ -93,7 +121,7 @@ static void on_update_rumble_mode(PreferencesWindow *pref, const GB_rumble_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; + // self->border_mode_changed = true; } static void on_update_video_shader(PreferencesWindow *pref, const gchar *name, MainWindow *self) { @@ -105,10 +133,10 @@ static void on_update_audio_sample_rate(PreferencesWindow *pref, const guint sam g_debug("on_update_audio_sample_rate(%d)", sample_rate); if (sample_rate == -1) { - // gui_data.sample_rate = GB_audio_default_sample_rate(); + self->sample_rate = GB_audio_default_sample_rate(); } else { - // gui_data.sample_rate = *sample_rate; + self->sample_rate = sample_rate; } // init_audio(); @@ -123,8 +151,196 @@ static void on_update_audio_interference_volume(PreferencesWindow *pref, const g } } +static bool on_change_model(GtkWidget *parent, GtkWidget *widget, gpointer user_data) { + MainWindow *self = SAMEBOY_MAIN_WINDOW(parent); + g_debug("on_change_model"); + return false; +} + +struct PrinterCallbackData { + PrinterWindow *printer_window; + uint32_t *image; + uint8_t height; + uint8_t top_margin; + uint8_t bottom_margin; + uint8_t exposure; +}; + +static gboolean draw_printer_image(struct PrinterCallbackData *data) { + printer_window_update(data->printer_window, data->image, data->height, data->top_margin, data->bottom_margin, data->exposure); + + g_free(data->image); + g_slice_free(struct PrinterCallbackData, data); + return false; +} + +static void emu_thread_on_print_image(GB_gameboy_t *gb, uint32_t *image, uint8_t height, uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure) { + MainWindow *self = SAMEBOY_MAIN_WINDOW(GB_get_user_data(gb)); + + struct PrinterCallbackData *data = g_slice_alloc(sizeof(struct PrinterCallbackData)); + + data->printer_window = self->printer; + data->image = g_malloc0(160 * height * sizeof(image[0])); + memcpy(data->image, image, 160 * height * sizeof(image[0])); + + data->height = height; + data->top_margin = top_margin; + data->bottom_margin = bottom_margin; + data->exposure = exposure; + + // We must make sure to run this function on the GTK thread + g_idle_add((GSourceFunc) draw_printer_image, data); +} + +static bool on_change_linked_device(GtkWidget *parent, GtkWidget *widget, gpointer user_data) { + MainWindow *self = SAMEBOY_MAIN_WINDOW(parent); + g_debug("on_change_linked_device"); + + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM(widget); + gchar *device_id = (gchar *) user_data; + + if (!gtk_check_menu_item_get_active(check_menu_item)) { + return true; + } + else if (!GB_is_inited(self->gb)) { + return false; + } + + perform_atomic(self, { + if (g_strcmp0(device_id, "NONE") == 0) { + g_debug("Disconnecting serial device"); + GB_disconnect_serial(self->gb); + } + else if (g_strcmp0(device_id, "PRINTER") == 0) { + g_debug("Connecting printer"); + GB_connect_printer(self->gb, emu_thread_on_print_image); + } + else if (g_strcmp0(device_id, "WORKBOY") == 0) { + g_debug("Connecting WorkBoy"); + + g_warning("Not yet implemented!") + GB_disconnect_serial(self->gb); + } + }); + + return false; +} + +static void emu_thread_on_load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type) { + g_debug("on_load_boot_rom(%p, %d)", gb, type); + + MainWindow *self = SAMEBOY_MAIN_WINDOW(GB_get_user_data(gb)); + SameBoyApplication *app = SAMEBOY_APPLICATION(gtk_window_get_application(GTK_WINDOW(self))); + struct CliOptionData *cli_options = sameboy_application_get_cli_options(app); + + GError *error = NULL; + char *boot_rom_path = NULL; + GBytes *boot_rom_f = NULL; + const guchar *boot_rom_data; + gsize boot_rom_size; + + static const char *const names[] = { + [GB_BOOT_ROM_DMG0] = "dmg0_boot.bin", + [GB_BOOT_ROM_DMG] = "dmg_boot.bin", + [GB_BOOT_ROM_MGB] = "mgb_boot.bin", + [GB_BOOT_ROM_SGB] = "sgb_boot.bin", + [GB_BOOT_ROM_SGB2] = "sgb2_boot.bin", + [GB_BOOT_ROM_CGB0] = "cgb0_boot.bin", + [GB_BOOT_ROM_CGB] = "cgb_boot.bin", + [GB_BOOT_ROM_AGB] = "agb_boot.bin", + }; + + const char *const boot_rom_name = names[type]; + + if (cli_options->boot_rom_path != NULL) { + g_message("[CLI override] Trying to load boot ROM from %s", cli_options->boot_rom_path); + if (GB_load_boot_rom(gb, cli_options->boot_rom_path)) { + g_warning("Falling back to boot ROM from config"); + goto config_boot_rom; + } + } + else { config_boot_rom: + if (config.emulation.boot_rom_path != NULL && g_strcmp0(config.emulation.boot_rom_path, "other") != 0 && g_strcmp0(config.emulation.boot_rom_path, "auto") != 0) { + boot_rom_path = g_build_filename(config.emulation.boot_rom_path, boot_rom_name, NULL); + g_message("Trying to load boot ROM from %s", boot_rom_path); + + if (GB_load_boot_rom(gb, boot_rom_path)) { + g_free(boot_rom_path); + g_warning("Falling back to internal boot ROM"); + goto internal_boot_rom; + } + + g_free(boot_rom_path); + } + else { internal_boot_rom: + boot_rom_path = g_build_filename(RESOURCE_PREFIX "bootroms/", boot_rom_name, NULL); + boot_rom_f = g_resources_lookup_data(boot_rom_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); + + g_message("Loading internal boot ROM: %s", boot_rom_path); + + if (boot_rom_f == NULL) { + g_warning("Failed to load internal boot ROM: %s", boot_rom_path); + g_error_free(error); + // exit(EXIT_FAILURE); + } + else { + boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size); + GB_load_boot_rom_from_buffer(gb, boot_rom_data, boot_rom_size); + g_bytes_unref(boot_rom_f); + } + + g_free(boot_rom_path); + } + } +} + +static void main_window_init_core(MainWindow *self) { + if (GB_is_inited(self->gb)) return; + + SameBoyApplication *app = SAMEBOY_APPLICATION(gtk_window_get_application(GTK_WINDOW(self))); + struct CliOptionData *cli_options = sameboy_application_get_cli_options(app); + + GB_init(self->gb, config_get_model_type(cli_options)); + GB_set_user_data(self->gb, self); + + if (config.audio.sample_rate == -1) { + self->sample_rate = GB_audio_default_sample_rate(); + } + else { + self->sample_rate = config.audio.sample_rate; + } + + // GB_set_vblank_callback(self->gb, emu_thread_on_vblank); + // GB_set_rgb_encode_callback(self->gb, emu_thread_rgb_encode); + + GB_set_pixels_output(self->gb, main_window_get_current_buffer(self)); + GB_set_color_correction_mode(self->gb, config_get_color_correction_mode()); + GB_set_light_temperature(self->gb, (double) config.video.light_temperature / 256.0); + if (config_get_display_border_mode() <= GB_BORDER_ALWAYS) { + GB_set_border_mode(self->gb, config_get_display_border_mode()); + } + + // GB_apu_set_sample_callback(self->gb, emu_thread_audio_callback); + + GB_set_sample_rate(self->gb, GB_audio_get_sample_rate()); + GB_set_highpass_filter_mode(self->gb, config_get_highpass_mode()); + GB_set_interference_volume(self->gb, (double) config.audio.interference_volume / 100.0); + + // GB_set_log_callback(self->gb, emu_thread_console_log); + // GB_set_input_callback(self->gb, emu_thread_get_sync_input); + // GB_set_async_input_callback(self->gb, emu_thread_get_async_input); + + GB_set_boot_rom_load_callback(self->gb, emu_thread_on_load_boot_rom); + // GB_set_update_input_hint_callback(self->gb, emu_thread_handle_events); + // GB_set_rumble_callback(self->gb, emu_thread_rumble_callback); + + GB_set_rumble_mode(self->gb, config_get_rumble_mode()); + GB_set_rewind_length(self->gb, config.emulation.rewind_duration); +} + static void on_application_set(MainWindow *self, GObject *object) { SameBoyApplication *app = SAMEBOY_APPLICATION(gtk_window_get_application(GTK_WINDOW(self))); + g_debug("on_application_set(%p, %p) => %p", self, object, app); 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); @@ -136,6 +352,8 @@ static void on_application_set(MainWindow *self, GObject *object) { 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); + + main_window_init_core(self); } static void main_window_constructed(GObject *object) { @@ -178,6 +396,12 @@ static void main_window_init(MainWindow *self) { self->printer = printer_window_new(); gtk_window_set_attached_to(GTK_WINDOW(self->printer), GTK_WIDGET(self)); + + // Just hide our sub-windows when closing them + g_signal_connect(self->console, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); + // g_signal_connect(self->memory_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); + g_signal_connect(self->printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); + g_signal_connect(self->vram_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); } static void main_window_class_init(MainWindowClass *class) { @@ -212,6 +436,8 @@ static void main_window_class_init(MainWindowClass *class) { } MainWindow *main_window_new(SameBoyApplication *application, bool force_software_renderer) { + g_debug("main_window_new(%p, %d)", application, force_software_renderer); + return g_object_new( MAIN_WINDOW_TYPE, "application", G_APPLICATION(application), @@ -230,7 +456,7 @@ void main_window_fullscreen(MainWindow *self, bool make_fullscreen) { } void main_window_setup_menu(MainWindow *self, char *model_string) { - main_menu_setup(self->main_menu, model_string); + main_menu_setup(self->main_menu, model_string, self, on_change_model, on_change_linked_device); } void main_window_open_console_window(MainWindow *self) { @@ -285,3 +511,72 @@ void main_window_set_shader(MainWindow *self, const char *shader_name) { void main_window_queue_render(MainWindow *self) { return gb_screen_queue_render(self->screen); } + +// Core functions +static void emulation_thread(MainWindow *self) { + +} + +static void main_window_start_emulation_thread(MainWindow *self) { + if (self->running) return; + while (self->stopping); + + g_thread_new("EmulationThread", (GThreadFunc)emulation_thread, self); +} + +void main_window_start(MainWindow *self) { + g_debug("main_window_start"); + self->running = true; + + // TODO: Clear audio queue + + while (self->running) { + if (self->rewind_paused) { + // handle_events(self->gb); + g_usleep(G_USEC_PER_SEC / 8); + } + else { + if (self->do_rewind) { + GB_rewind_pop(self->gb); + if (self->turbo_down) { + GB_rewind_pop(self->gb); + } + if (!GB_rewind_pop(self->gb)) { + self->rewind_paused = true; + } + self->do_rewind = false; + } + GB_run(self->gb); + } + } + + if (self->file) { + GB_save_battery(self->gb, self->battery_save_path); + GB_save_cheats(self->gb, self->cheats_save_path); + } + + self->stopping = false; +} + +void main_window_stop(MainWindow *self) { + g_debug("main_window_stop"); + if (self->running) return; + + // GB_audio_set_paused(true); + GB_debugger_set_disabled(self->gb, true); + + self->stopping = true; + self->running = false; + + if (GB_debugger_is_stopped(self->gb)) { + console_window_abort_debugger(self->console); + } + + while (self->stopping); + + GB_debugger_set_disabled(self->gb, false); +} + +void main_window_reset(MainWindow *self) { + g_debug("main_window_reset"); +} diff --git a/gtk3/widgets/main_window.h b/gtk3/widgets/main_window.h index 6c551e6..11d3048 100644 --- a/gtk3/widgets/main_window.h +++ b/gtk3/widgets/main_window.h @@ -30,4 +30,9 @@ void main_window_set_blending_mode(MainWindow *self, GB_frame_blending_mode_t mo void main_window_set_shader(MainWindow *self, const char *shader_name); void main_window_queue_render(MainWindow *self); +// Core functions +void main_window_start(MainWindow *self); +void main_window_stop(MainWindow *self); +void main_window_reset(MainWindow *self); + #endif diff --git a/gtk3/widgets/printer_window.c b/gtk3/widgets/printer_window.c index 55bbe0a..ae68e04 100644 --- a/gtk3/widgets/printer_window.c +++ b/gtk3/widgets/printer_window.c @@ -90,20 +90,20 @@ PrinterWindow *printer_window_new(void) { return g_object_new(PRINTER_WINDOW_TYPE, NULL); } -void printer_window_update(PrinterWindow *self, struct PrinterData *data) { +void printer_window_update(PrinterWindow *self, uint32_t *image, uint8_t height, uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure) { g_mutex_lock(&self->surface_mutex); size_t current_size = self->current_height * 160; size_t new_height = self->current_height - + data->top_margin * 8 - + data->height - + data->bottom_margin * 8; + + top_margin * 8 + + height + + bottom_margin * 8; size_t new_size = new_height * 160 * sizeof(uint32_t); uint32_t *new_image = g_malloc(new_size); memset(new_image, 0xFF, new_size); // fill with white memcpy(new_image, self->current_image, current_size * sizeof(uint32_t)); // copy old image - memcpy(new_image + current_size + (data->top_margin * 160 * 8), data->image, data->height * 160 * sizeof(uint32_t)); // copy new image + memcpy(new_image + current_size + (top_margin * 160 * 8), image, height * 160 * sizeof(uint32_t)); // copy new image g_free(self->current_image); self->current_image = new_image; diff --git a/gtk3/widgets/printer_window.h b/gtk3/widgets/printer_window.h index 20aa048..9624aed 100644 --- a/gtk3/widgets/printer_window.h +++ b/gtk3/widgets/printer_window.h @@ -4,21 +4,13 @@ #include #include -struct PrinterData { - uint32_t *image; - uint8_t height; - uint8_t top_margin; - uint8_t bottom_margin; - uint8_t exposure; -}; - #define PRINTER_WINDOW_TYPE (printer_window_get_type()) G_DECLARE_FINAL_TYPE(PrinterWindow, printer_window, SAMEBOY, PRINTER_WINDOW, GtkWindow) PrinterWindow *printer_window_new(void); void printer_window_clear(PrinterWindow *self); bool printer_window_save(PrinterWindow *self); -void printer_window_update(PrinterWindow *self, struct PrinterData *data); +void printer_window_update(PrinterWindow *self, uint32_t *image, uint8_t height, uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure); void printer_window_set_suggestion_prefix(PrinterWindow *self, char* prefix); #endif