[GTK3] WIP! “Port” some more functions

This commit is contained in:
Maximilian Mader 2021-01-09 19:09:15 +01:00
parent 39af396004
commit 32969c0959
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
15 changed files with 385 additions and 60 deletions

View File

@ -4,7 +4,7 @@ static void check_menu_item_group_handler(GtkCheckMenuItem *item, CheckMenuItemG
bool cancel = false; bool cancel = false;
if (data->handler) { 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; 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; unsigned name_count = 0;
if (names != NULL) { if (names != NULL) {
@ -31,6 +31,7 @@ CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args) {
} }
CheckMenuItemGroup *group = g_malloc0(sizeof(CheckMenuItemGroup)); CheckMenuItemGroup *group = g_malloc0(sizeof(CheckMenuItemGroup));
group->parent = parent;
group->count = name_count; group->count = name_count;
group->items = g_malloc0(sizeof(GtkWidget*) * name_count); group->items = g_malloc0(sizeof(GtkWidget*) * name_count);
group->handlers = g_malloc0(sizeof(CheckMenuItemGroupHandlerData*) * 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++) { for (unsigned i = 0; i < group->count; i++) {
group->handlers[i]->handler = handler; group->handlers[i]->handler = handler;
} }

View File

@ -7,18 +7,19 @@
typedef struct CheckMenuItemGroupHandlerData { typedef struct CheckMenuItemGroupHandlerData {
struct CheckMenuItemGroup *group; struct CheckMenuItemGroup *group;
char *arg; char *arg;
bool (*handler)(GtkWidget *, void *); bool (*handler)(GtkWidget *, GtkWidget *, void *);
} CheckMenuItemGroupHandlerData; } CheckMenuItemGroupHandlerData;
typedef struct CheckMenuItemGroup { typedef struct CheckMenuItemGroup {
GtkWidget *parent;
unsigned count; unsigned count;
GtkWidget **items; GtkWidget **items;
CheckMenuItemGroupHandlerData **handlers; CheckMenuItemGroupHandlerData **handlers;
} CheckMenuItemGroup; } 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_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); void check_menu_item_group_insert_into_menu_shell(CheckMenuItemGroup *group, GtkMenuShell *menu_shell, gint position);
#endif #endif

View File

@ -466,6 +466,14 @@ GB_model_t config_get_model(void) {
return config_get_cgb_model(); 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) { void config_set_model(GB_model_t model) {
switch (model & GB_MODEL_FAMILY_MASK) { switch (model & GB_MODEL_FAMILY_MASK) {
case GB_MODEL_DMG_FAMILY: case GB_MODEL_DMG_FAMILY:

View File

@ -4,6 +4,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <Core/gb.h> #include <Core/gb.h>
#include "shader.h" #include "shader.h"
#include "util.h"
#define CONFIG_FILE "sameboy-gtk3.ini" #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); void config_set_model(GB_model_t model);
GB_model_t config_get_model(void); 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_dmg_model(void);
GB_model_t config_get_sgb_model(void); GB_model_t config_get_sgb_model(void);

View File

@ -1,4 +1,5 @@
#include "sameboy_application.h" #include "sameboy_application.h"
#include "config.h"
#include "widgets/main_window.h" #include "widgets/main_window.h"
#include "widgets/about_dialog.h" #include "widgets/about_dialog.h"
#include "widgets/preferences_window.h" #include "widgets/preferences_window.h"
@ -17,23 +18,19 @@
struct _SameBoyApplication { struct _SameBoyApplication {
GtkApplication parent; GtkApplication parent;
struct CliOptionData cli_options;
const GThread *main_thread; const GThread *main_thread;
PreferencesWindow *preferences; PreferencesWindow *preferences;
AboutDialog *about_dialog; AboutDialog *about_dialog;
struct CliOptionData { GDateTime *config_modification_date;
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); G_DEFINE_TYPE(SameBoyApplication, sameboy_application, GTK_TYPE_APPLICATION);
static void sameboy_application_init(SameBoyApplication *app) { static void sameboy_application_init(SameBoyApplication *app) {
g_debug("sameboy_application_init"); g_debug("sameboy_application_init(%p)", app);
// Define our command line parameters // Define our command line parameters
GOptionEntry entries[] = { GOptionEntry entries[] = {
@ -64,6 +61,8 @@ static gint sameboy_application_handle_local_options(GApplication *gapp, GVarian
// Handle model override // Handle model override
GVariant *model_name_var = g_variant_dict_lookup_value(options, "model", G_VARIANT_TYPE_STRING); 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) { if (model_name_var != NULL) {
const gchar *model_name = g_variant_get_string(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); SameBoyApplication *app = SAMEBOY_APPLICATION(gapp);
g_debug("sameboy_application_startup"); 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(); app->about_dialog = about_dialog_new();
gtk_application_add_window(GTK_APPLICATION(gapp), GTK_WINDOW(app->preferences)); 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(); GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_resource(provider, RESOURCE_PREFIX "css/main.css"); 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); 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) { 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) { if (file != NULL) {
gchar *path = g_file_get_path(file); gchar *path = g_file_get_path(file);
g_debug("File path: %s", path); g_debug("File path: %s", path);
g_free(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 // Define a set of window icons
GList *icon_list = NULL; 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) { 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); g_signal_connect(app->preferences, detailed_signal, c_handler, data);
} }
struct CliOptionData *sameboy_application_get_cli_options(SameBoyApplication *self) {
return &self->cli_options;
}

View File

@ -1,10 +1,20 @@
#ifndef sameboy_application_h #ifndef sameboy_application_h
#define sameboy_application_h #define sameboy_application_h
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <Core/gb.h>
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()) #define SAMEBOY_APPLICATION_TYPE (sameboy_application_get_type())
G_DECLARE_FINAL_TYPE(SameBoyApplication, sameboy_application, SAMEBOY, APPLICATION, GtkApplication) G_DECLARE_FINAL_TYPE(SameBoyApplication, sameboy_application, SAMEBOY, APPLICATION, GtkApplication)
SameBoyApplication *sameboy_application_new(void); SameBoyApplication *sameboy_application_new(void);
void sameboy_application_preferences_signal_connect(SameBoyApplication *app, const gchar *detailed_signal, GCallback c_handler, gpointer data); 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 #endif

View File

@ -13,6 +13,7 @@ typedef struct{
uint16_t width, height; uint16_t width, height;
} Rect; } Rect;
/*
typedef struct GuiData { typedef struct GuiData {
struct CliOptionData { struct CliOptionData {
gchar *config_path; gchar *config_path;
@ -74,6 +75,7 @@ typedef struct GuiData {
unsigned controller_count; unsigned controller_count;
struct Controller_t *last_used_controller; // Used for rumble struct Controller_t *last_used_controller; // Used for rumble
} GuiData; } GuiData;
*/
typedef enum { typedef enum {
INPUT_UP, INPUT_UP,

View File

@ -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; 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 *menubar_to_menu(GtkMenuBar *menubar) {
GtkWidget *menu = gtk_menu_new(); GtkWidget *menu = gtk_menu_new();
g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar)); g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar));

View File

@ -18,8 +18,6 @@ double min_double(double a, double b);
uint32_t convert_color(uint16_t color); uint32_t convert_color(uint16_t color);
uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b); 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); GtkWidget *menubar_to_menu(GtkMenuBar *menubar);
gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data); gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data);

View File

@ -1,6 +1,7 @@
#include "main_menu.h" #include "main_menu.h"
#include <stdbool.h> #include <stdbool.h>
#include "gb_screen.h" #include "gb_screen.h"
#include "main_window.h"
#include "../check_menu_radio_group.h" #include "../check_menu_radio_group.h"
struct _MainMenu { struct _MainMenu {
@ -34,13 +35,15 @@ MainMenu *main_menu_new() {
// For the GNOME Shell it uses a hamburger menu, otherwise it either lets // 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 // the desktop environment shell handle the menu if it signals support for it
// or uses a standard menubar inside the menu. // 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 // 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 // environments and the manual call of `g_signal_connect` was needed anyway
// because the UI definition cant define string arguments for signal handlers. // because the UI definition cant 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[] = { static const char *const model_names[] = {
"Game Boy", "Game Boy",
"Super 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); g_autoptr(GList) list = gtk_container_get_children(parent);
gint position = g_list_index(list, self->before_model_changer); 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_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); check_menu_item_group_activate(model_group, model_string);
static const char *const peripheral_names[] = { static const char *const peripheral_names[] = {
"None", "None",
"Game Boy Printer", "Game Boy Printer",
"WorkBoy",
NULL NULL
}; };
static const char *const peripheral_codes[] = { static const char *const peripheral_codes[] = {
"NONE", "NONE",
"PRINTER", "PRINTER",
"WORKBOY",
NULL, 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_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"); check_menu_item_group_activate(link_group, "NONE");
} }

View File

@ -3,11 +3,17 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <Core/gb.h> #include <Core/gb.h>
#include "main_window.h"
#define MAIN_MENU_TYPE (main_menu_get_type()) #define MAIN_MENU_TYPE (main_menu_get_type())
G_DECLARE_FINAL_TYPE(MainMenu, main_menu, SAMEBOY, MAIN_MENU, GtkMenuBar) G_DECLARE_FINAL_TYPE(MainMenu, main_menu, SAMEBOY, MAIN_MENU, GtkMenuBar)
MainMenu *main_menu_new(); 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 #endif

View File

@ -1,6 +1,8 @@
#include "main_window.h" #include "main_window.h"
#include <Core/gb.h> #include <Core/gb.h>
#include "../../SDL/audio/audio.h"
#include "../config.h"
#include "gb_screen.h" #include "gb_screen.h"
#include "main_menu.h" #include "main_menu.h"
#include "console_window.h" #include "console_window.h"
@ -14,11 +16,29 @@ struct _MainWindow {
// Child nodes // Child nodes
GtkBox *container; GtkBox *container;
GbScreen *screen; GbScreen *screen;
bool force_software_renderer;
MainMenu *main_menu; MainMenu *main_menu;
bool force_software_renderer;
// The local SameBoy core instance // The local SameBoy core instance
GB_gameboy_t *gb; 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 // Local sub-windows
ConsoleWindow *console; ConsoleWindow *console;
@ -37,6 +57,14 @@ typedef enum {
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; 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) { static void main_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
MainWindow *self = (MainWindow *) object; 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) { 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); 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) { 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); g_debug("on_update_audio_sample_rate(%d)", sample_rate);
if (sample_rate == -1) { if (sample_rate == -1) {
// gui_data.sample_rate = GB_audio_default_sample_rate(); self->sample_rate = GB_audio_default_sample_rate();
} }
else { else {
// gui_data.sample_rate = *sample_rate; self->sample_rate = sample_rate;
} }
// init_audio(); // 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) { static void on_application_set(MainWindow *self, GObject *object) {
SameBoyApplication *app = SAMEBOY_APPLICATION(gtk_window_get_application(GTK_WINDOW(self))); 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::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::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::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-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); 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) { static void main_window_constructed(GObject *object) {
@ -178,6 +396,12 @@ static void main_window_init(MainWindow *self) {
self->printer = printer_window_new(); self->printer = printer_window_new();
gtk_window_set_attached_to(GTK_WINDOW(self->printer), GTK_WIDGET(self)); 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) { 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) { 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( return g_object_new(
MAIN_WINDOW_TYPE, MAIN_WINDOW_TYPE,
"application", G_APPLICATION(application), "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) { 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) { 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) { void main_window_queue_render(MainWindow *self) {
return gb_screen_queue_render(self->screen); 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");
}

View File

@ -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_set_shader(MainWindow *self, const char *shader_name);
void main_window_queue_render(MainWindow *self); 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 #endif

View File

@ -90,20 +90,20 @@ PrinterWindow *printer_window_new(void) {
return g_object_new(PRINTER_WINDOW_TYPE, NULL); 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); g_mutex_lock(&self->surface_mutex);
size_t current_size = self->current_height * 160; size_t current_size = self->current_height * 160;
size_t new_height = self->current_height size_t new_height = self->current_height
+ data->top_margin * 8 + top_margin * 8
+ data->height + height
+ data->bottom_margin * 8; + bottom_margin * 8;
size_t new_size = new_height * 160 * sizeof(uint32_t); size_t new_size = new_height * 160 * sizeof(uint32_t);
uint32_t *new_image = g_malloc(new_size); uint32_t *new_image = g_malloc(new_size);
memset(new_image, 0xFF, new_size); // fill with white 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, 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); g_free(self->current_image);
self->current_image = new_image; self->current_image = new_image;

View File

@ -4,21 +4,13 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <Core/gb.h> #include <Core/gb.h>
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()) #define PRINTER_WINDOW_TYPE (printer_window_get_type())
G_DECLARE_FINAL_TYPE(PrinterWindow, printer_window, SAMEBOY, PRINTER_WINDOW, GtkWindow) G_DECLARE_FINAL_TYPE(PrinterWindow, printer_window, SAMEBOY, PRINTER_WINDOW, GtkWindow)
PrinterWindow *printer_window_new(void); PrinterWindow *printer_window_new(void);
void printer_window_clear(PrinterWindow *self); void printer_window_clear(PrinterWindow *self);
bool printer_window_save(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); void printer_window_set_suggestion_prefix(PrinterWindow *self, char* prefix);
#endif #endif