[GTK3] WIP! “Port” some more functions
This commit is contained in:
parent
39af396004
commit
32969c0959
@ -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;
|
||||
}
|
||||
|
@ -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
|
@ -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:
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <Core/gb.h>
|
||||
#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);
|
||||
|
@ -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,6 +259,13 @@ 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);
|
||||
|
||||
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();
|
||||
@ -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;
|
||||
}
|
||||
|
@ -1,10 +1,20 @@
|
||||
#ifndef sameboy_application_h
|
||||
#define sameboy_application_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())
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "main_menu.h"
|
||||
#include <stdbool.h>
|
||||
#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");
|
||||
}
|
||||
|
@ -3,11 +3,17 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <Core/gb.h>
|
||||
#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
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "main_window.h"
|
||||
#include <Core/gb.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -4,21 +4,13 @@
|
||||
#include <gtk/gtk.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())
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user