[GTK3] Somewhat working GtkDrawingArea fallback
This commit is contained in:
parent
af7ed7b10e
commit
8385a18233
346
gtk3/main.c
346
gtk3/main.c
@ -39,8 +39,10 @@ static void run(GApplication *app, UserData *user_data);
|
|||||||
static GtkApplication *main_application;
|
static GtkApplication *main_application;
|
||||||
static GtkBuilder *builder;
|
static GtkBuilder *builder;
|
||||||
static GtkGLArea *gl_area;
|
static GtkGLArea *gl_area;
|
||||||
|
static GtkDrawingArea *fallback_canvas;
|
||||||
|
|
||||||
static GtkApplicationWindow *main_window;
|
static GtkApplicationWindow *main_window;
|
||||||
|
static GtkBox *main_window_container;
|
||||||
static GtkWindow *preferences;
|
static GtkWindow *preferences;
|
||||||
static GtkWindow *vram_viewer;
|
static GtkWindow *vram_viewer;
|
||||||
static GtkWindow *memory_viewer;
|
static GtkWindow *memory_viewer;
|
||||||
@ -54,6 +56,8 @@ static GB_gameboy_t gb;
|
|||||||
static uint32_t *image_buffers[3];
|
static uint32_t *image_buffers[3];
|
||||||
static unsigned char current_buffer;
|
static unsigned char current_buffer;
|
||||||
|
|
||||||
|
static bool supports_gl;
|
||||||
|
|
||||||
static bool paused = false;
|
static bool paused = false;
|
||||||
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
||||||
static double clock_mutliplier = 1.0;
|
static double clock_mutliplier = 1.0;
|
||||||
@ -69,7 +73,7 @@ static const size_t tilemap_buffer_length = 256 * 256 * 4;
|
|||||||
static uint32_t tilemap_buffer[tilemap_buffer_length] = {0};
|
static uint32_t tilemap_buffer[tilemap_buffer_length] = {0};
|
||||||
|
|
||||||
static unsigned char number_of_buffers(void) {
|
static unsigned char number_of_buffers(void) {
|
||||||
bool should_blend = true;
|
bool should_blend = !fallback_canvas;
|
||||||
|
|
||||||
return should_blend? 3 : 2;
|
return should_blend? 3 : 2;
|
||||||
}
|
}
|
||||||
@ -122,7 +126,12 @@ static void vblank(GB_gameboy_t *gb) {
|
|||||||
GB_set_pixels_output(gb, get_pixels());
|
GB_set_pixels_output(gb, get_pixels());
|
||||||
|
|
||||||
// Queue drawing of the current frame
|
// Queue drawing of the current frame
|
||||||
gtk_gl_area_queue_render(gl_area);
|
if (fallback_canvas) {
|
||||||
|
gtk_widget_queue_draw(GTK_WIDGET(main_window));
|
||||||
|
}
|
||||||
|
else if (gl_area) {
|
||||||
|
gtk_gl_area_queue_render(gl_area);
|
||||||
|
}
|
||||||
|
|
||||||
if (vram_viewer_visible) {
|
if (vram_viewer_visible) {
|
||||||
// TODO: Only update what is needed
|
// TODO: Only update what is needed
|
||||||
@ -135,8 +144,10 @@ static void vblank(GB_gameboy_t *gb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void update_viewport(void) {
|
static void update_viewport(void) {
|
||||||
int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area));
|
GtkWidget *w = fallback_canvas ? GTK_WIDGET(fallback_canvas) : GTK_WIDGET(gl_area);
|
||||||
int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area));
|
|
||||||
|
int win_width = gtk_widget_get_allocated_width(w);
|
||||||
|
int win_height = gtk_widget_get_allocated_height(w);
|
||||||
|
|
||||||
double x_factor = win_width / (double) GB_get_screen_width(&gb);
|
double x_factor = win_width / (double) GB_get_screen_width(&gb);
|
||||||
double y_factor = win_height / (double) GB_get_screen_height(&gb);
|
double y_factor = win_height / (double) GB_get_screen_height(&gb);
|
||||||
@ -165,7 +176,7 @@ static void update_viewport(void) {
|
|||||||
new_height
|
new_height
|
||||||
};
|
};
|
||||||
|
|
||||||
glViewport(rect.x, rect.y, rect.w, rect.h);
|
if (!fallback_canvas) glViewport(rect.x, rect.y, rect.w, rect.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if a ComboBox entry should be converted into a separator.
|
// Determines if a ComboBox entry should be converted into a separator.
|
||||||
@ -199,22 +210,6 @@ static void set_combo_box_row_separator_func(GtkContainer *container) {
|
|||||||
g_list_free_full(list, NULL);
|
g_list_free_full(list, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the application should show a menubar
|
|
||||||
static gboolean show_menubar(void) {
|
|
||||||
switch (get_show_menubar()) {
|
|
||||||
case MENUBAR_AUTO: {
|
|
||||||
GtkSettings *settings = gtk_settings_get_default();
|
|
||||||
gboolean result;
|
|
||||||
|
|
||||||
g_object_get(settings, "gtk-shell-shows-menubar", &result, NULL);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case MENUBAR_SHOW: return true;
|
|
||||||
case MENUBAR_HIDE: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a `GApplication`s `GMenuModel` by ID
|
// Returns a `GApplication`s `GMenuModel` by ID
|
||||||
// GApplication menus are loaded from `gtk/menus.ui`, `gtk/menus-traditional.ui` and `gtk/menus-common.ui`.
|
// GApplication menus are loaded from `gtk/menus.ui`, `gtk/menus-traditional.ui` and `gtk/menus-common.ui`.
|
||||||
static GMenuModel *get_menu_model(GApplication *app, const char *id) {
|
static GMenuModel *get_menu_model(GApplication *app, const char *id) {
|
||||||
@ -332,32 +327,86 @@ G_MODULE_EXPORT void on_show_window(GtkWidget *w, gpointer window) {
|
|||||||
gtk_widget_show_all(GTK_WIDGET(window));
|
gtk_widget_show_all(GTK_WIDGET(window));
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void gl_init() {
|
G_MODULE_EXPORT gboolean on_draw_fallback(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
||||||
const char *renderer;
|
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
||||||
|
guint width = gtk_widget_get_allocated_width(widget);
|
||||||
|
guint height = gtk_widget_get_allocated_height(widget);
|
||||||
|
|
||||||
gtk_gl_area_make_current(gl_area);
|
guint screen_width = GB_get_screen_width(&gb);
|
||||||
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
guint screen_height = GB_get_screen_height(&gb);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer = (char *) glGetString(GL_RENDERER);
|
gtk_render_background(context, cr, 0, 0, width, height);
|
||||||
g_print("GtkGLArea on %s\n", renderer ? renderer : "Unknown");
|
|
||||||
|
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||||
|
(unsigned char *) get_current_buffer(),
|
||||||
|
CAIRO_FORMAT_RGB24,
|
||||||
|
screen_width,
|
||||||
|
screen_height,
|
||||||
|
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, screen_width)
|
||||||
|
);
|
||||||
|
|
||||||
if (config.shader == NULL || !init_shader_with_name(&shader, config.shader)) {
|
cairo_translate(cr, rect.x, rect.y);
|
||||||
init_shader_with_name(&shader, "NearestNeighbor");
|
cairo_scale(cr, rect.w / screen_width, rect.h / screen_height);
|
||||||
}
|
cairo_set_source_surface(cr, surface, 0, 0);
|
||||||
|
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||||
|
cairo_paint(cr);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void gl_resize() {
|
G_MODULE_EXPORT void resize() {
|
||||||
update_viewport();
|
update_viewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_fallback_canvas(void) {
|
||||||
|
fallback_canvas = GTK_DRAWING_AREA(gtk_drawing_area_new());
|
||||||
|
g_signal_connect(fallback_canvas, "draw", G_CALLBACK(on_draw_fallback), NULL);
|
||||||
|
g_signal_connect(fallback_canvas, "size-allocate", G_CALLBACK(resize), NULL);
|
||||||
|
gtk_box_pack_end(GTK_BOX(main_window_container), GTK_WIDGET(fallback_canvas), TRUE, TRUE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void gl_draw() {
|
G_MODULE_EXPORT void gl_draw() {
|
||||||
render_texture(get_current_buffer(), get_previous_buffer());
|
render_texture(get_current_buffer(), get_previous_buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void gl_finish() { }
|
G_MODULE_EXPORT void gl_finish() { }
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_init(GtkWidget *w) {
|
||||||
|
GtkGLArea *gl_area = GTK_GL_AREA(w);
|
||||||
|
|
||||||
|
g_print("GL_INIT\n");
|
||||||
|
const char *renderer;
|
||||||
|
|
||||||
|
g_print("GL Context: %p\n", gtk_gl_area_get_context(gl_area));
|
||||||
|
|
||||||
|
gtk_gl_area_make_current(gl_area);
|
||||||
|
|
||||||
|
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer = (char *)glGetString(GL_RENDERER);
|
||||||
|
g_print("GtkGLArea on %s\n", renderer ? renderer : "Unknown");
|
||||||
|
|
||||||
|
if (config.shader == NULL || !init_shader_with_name(&shader, config.shader) || !init_shader_with_name(&shader, "NearestNeighbor")) {
|
||||||
|
GError *error = g_error_new_literal(g_quark_from_string("sameboy-gl-error"), 1, "Failed to initialize shaders");
|
||||||
|
gtk_gl_area_set_error(gl_area, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g_signal_connect(gl_area, "render", G_CALLBACK(gl_draw), NULL);
|
||||||
|
g_signal_connect(gl_area, "resize", G_CALLBACK(resize), NULL);
|
||||||
|
g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
||||||
|
g_printerr("GtkGLArea: %s\n", gtk_gl_area_get_error(gl_area)->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_fallback_canvas();
|
||||||
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void on_vram_viewer_realize() {
|
G_MODULE_EXPORT void on_vram_viewer_realize() {
|
||||||
vram_viewer_visible = true;
|
vram_viewer_visible = true;
|
||||||
}
|
}
|
||||||
@ -390,7 +439,7 @@ G_MODULE_EXPORT gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT gboolean on_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
G_MODULE_EXPORT gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
||||||
guint width, height;
|
guint width, height;
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
|
|
||||||
@ -497,10 +546,133 @@ G_MODULE_EXPORT void on_color_menubar_override_changed(GtkWidget *w, gpointer us
|
|||||||
config.menubar_override = (gchar *)gtk_combo_box_get_active_id(GTK_COMBO_BOX(w));
|
config.menubar_override = (gchar *)gtk_combo_box_get_active_id(GTK_COMBO_BOX(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setup_menu(GApplication *app) {
|
||||||
|
GMenuModel *menubar_model = get_menu_model(app, "menubar");
|
||||||
|
enum menubar_type_t menubar_type = get_show_menubar();
|
||||||
|
|
||||||
|
// Try to use a sane default
|
||||||
|
if (menubar_type == MENUBAR_AUTO) {
|
||||||
|
GtkSettings *settings = gtk_settings_get_default();
|
||||||
|
gboolean show_in_shell;
|
||||||
|
g_object_get(settings, "gtk-shell-shows-menubar", &show_in_shell, NULL);
|
||||||
|
|
||||||
|
const gchar *xdg_current_desktop = g_getenv("XDG_CURRENT_DESKTOP");
|
||||||
|
const gchar *gdm_session = g_getenv("GDMSESSION");
|
||||||
|
const gchar *desktop_session = g_getenv("DESKTOP_SESSION");
|
||||||
|
|
||||||
|
gchar *desktop = (gchar *)xdg_current_desktop;
|
||||||
|
if (desktop == NULL || g_str_equal(desktop, "")) desktop = (gchar *)gdm_session;
|
||||||
|
if (desktop == NULL || g_str_equal(desktop, "")) desktop = (gchar *)desktop_session;
|
||||||
|
|
||||||
|
g_print("XDG_CURRENT_DESKTOP: %s\nGDMSESSION: %s\nDESKTOP_SESSION: %s\nChosen value: %s\nShow menu in shell: %d\n", xdg_current_desktop, gdm_session, desktop_session, desktop, show_in_shell);
|
||||||
|
|
||||||
|
if (desktop != NULL && show_in_shell) {
|
||||||
|
menubar_type = MENUBAR_SHOW_IN_SHELL;
|
||||||
|
}
|
||||||
|
else if (desktop != NULL && g_str_match_string("GNOME", desktop, FALSE)) {
|
||||||
|
if (g_str_match_string("GNOME-Flashback", desktop, FALSE) || g_str_match_string("GNOME-Classic", desktop, FALSE)) {
|
||||||
|
menubar_type = MENUBAR_SHOW_IN_WINDOW;
|
||||||
|
}
|
||||||
|
else if (gdm_session != NULL && (g_str_match_string("gnome-classic", gdm_session, FALSE) || g_str_match_string("gnome-flashback", gdm_session, FALSE))) {
|
||||||
|
menubar_type = MENUBAR_SHOW_IN_WINDOW;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
menubar_type = MENUBAR_SHOW_HAMBURGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
menubar_type = MENUBAR_SHOW_IN_WINDOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (menubar_type) {
|
||||||
|
case MENUBAR_AUTO:
|
||||||
|
g_error("Unreachable\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MENUBAR_SHOW_IN_SHELL:
|
||||||
|
g_print("Showing menu in the shell\n");
|
||||||
|
gtk_application_set_menubar(GTK_APPLICATION(app), menubar_model);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MENUBAR_SHOW_IN_WINDOW: {
|
||||||
|
g_print("Showing menu in the window\n");
|
||||||
|
GtkMenuBar *menubar = GTK_MENU_BAR(gtk_menu_bar_new_from_model(menubar_model));
|
||||||
|
gtk_box_pack_start(GTK_BOX(main_window_container), GTK_WIDGET(menubar), FALSE, FALSE, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MENUBAR_SHOW_HAMBURGER: {
|
||||||
|
g_print("Showing hamburger\n");
|
||||||
|
// Attach a custom title bar
|
||||||
|
GtkWidget *titlebar = gtkget(GTK_WIDGET, "main_header_bar");
|
||||||
|
gtk_window_set_titlebar(GTK_WINDOW(main_window), titlebar);
|
||||||
|
|
||||||
|
// Disable menubar
|
||||||
|
gtk_application_set_menubar(GTK_APPLICATION(app), NULL);
|
||||||
|
|
||||||
|
// Hook menubar up to the hamburger button
|
||||||
|
GtkMenuButton *hamburger_button = GTK_MENU_BUTTON(get_object("hamburger_button"));
|
||||||
|
gtk_menu_button_set_menu_model(hamburger_button, menubar_model);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_check_realize(GtkWidget *w, gpointer user_data_gptr) {
|
||||||
|
gboolean *result = (gboolean *) user_data_gptr;
|
||||||
|
|
||||||
|
GError *error = NULL;
|
||||||
|
GdkWindow *gdk_window = gtk_widget_get_window(w);
|
||||||
|
GdkGLContext *context = gdk_window_create_gl_context(gdk_window, &error);
|
||||||
|
|
||||||
|
if (error != NULL) {
|
||||||
|
g_printerr("Failed to create context: %s\n", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
*result = FALSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gdk_gl_context_make_current(context);
|
||||||
|
int version = epoxy_gl_version();
|
||||||
|
|
||||||
|
g_object_run_dispose(G_OBJECT(context));
|
||||||
|
g_object_unref(context);
|
||||||
|
context = NULL;
|
||||||
|
|
||||||
|
gdk_gl_context_clear_current();
|
||||||
|
|
||||||
|
g_print("OpenGL version: %d\n", version);
|
||||||
|
|
||||||
|
*result = version >= 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean test_gl_support(void) {
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
|
gtk_init(NULL, NULL);
|
||||||
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
|
g_signal_connect(window, "realize", G_CALLBACK(gl_check_realize), &result);
|
||||||
|
gtk_widget_realize(window);
|
||||||
|
gtk_widget_destroy(window);
|
||||||
|
window = NULL;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// This functions gets called immediately after registration of the GApplication
|
// This functions gets called immediately after registration of the GApplication
|
||||||
static void startup(GApplication *app, gpointer user_data_gptr) {
|
static void startup(GApplication *app, gpointer user_data_gptr) {
|
||||||
UserData *user_data = user_data_gptr;
|
UserData *user_data = user_data_gptr;
|
||||||
|
|
||||||
|
// Very ugly workaround for GtkGlArea!
|
||||||
|
// When a GtkGlArea is realized and it creates a legacy GL 1.4 context
|
||||||
|
// it tries to use GL 2.0 functions to render the window which leads to the application crashing.
|
||||||
|
// So we initialize GTK, create a dummy GtkWindow object, attach a `realize` callback and
|
||||||
|
// in this callback create a GdkGLContext on this window. But instead of running the GTK main loop
|
||||||
|
// we just realize and destroy the dummy window and compare the context’s version in the realize callback.
|
||||||
|
supports_gl = test_gl_support();
|
||||||
|
g_print("OpenGL supported: %s\n", supports_gl? "Yes" : "No");
|
||||||
|
|
||||||
builder = gtk_builder_new_from_resource(RESOURCE_PREFIX "ui/window.ui");
|
builder = gtk_builder_new_from_resource(RESOURCE_PREFIX "ui/window.ui");
|
||||||
gtk_builder_connect_signals(builder, NULL);
|
gtk_builder_connect_signals(builder, NULL);
|
||||||
|
|
||||||
@ -529,65 +701,19 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
|
|
||||||
// setup main window
|
// setup main window
|
||||||
main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
|
main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
|
||||||
gtk_application_window_set_show_menubar(main_window, false);
|
main_window_container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
|
||||||
|
|
||||||
// create our renderer area
|
gtk_window_set_title(GTK_WINDOW(main_window), "SameBoy");
|
||||||
gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
gtk_application_window_set_show_menubar(main_window, false);
|
||||||
gtk_gl_area_set_auto_render(gl_area, false);
|
gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(main_window_container));
|
||||||
|
|
||||||
|
setup_menu(app);
|
||||||
|
|
||||||
// Insert separators into `GtkComboBox`es
|
// Insert separators into `GtkComboBox`es
|
||||||
set_combo_box_row_separator_func(GTK_CONTAINER(preferences));
|
set_combo_box_row_separator_func(GTK_CONTAINER(preferences));
|
||||||
set_combo_box_row_separator_func(GTK_CONTAINER(vram_viewer));
|
set_combo_box_row_separator_func(GTK_CONTAINER(vram_viewer));
|
||||||
set_combo_box_row_separator_func(GTK_CONTAINER(memory_viewer));
|
set_combo_box_row_separator_func(GTK_CONTAINER(memory_viewer));
|
||||||
|
|
||||||
// Connect signal handlers
|
|
||||||
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
|
|
||||||
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_RELEASE_MASK);
|
|
||||||
g_signal_connect(main_window, "key_press_event", G_CALLBACK(on_key_press), NULL);
|
|
||||||
g_signal_connect(main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
|
|
||||||
|
|
||||||
g_signal_connect(gl_area, "realize", G_CALLBACK(gl_init), NULL);
|
|
||||||
g_signal_connect(gl_area, "render", G_CALLBACK(gl_draw), NULL);
|
|
||||||
g_signal_connect(gl_area, "resize", G_CALLBACK(gl_resize), NULL);
|
|
||||||
g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL);
|
|
||||||
|
|
||||||
g_signal_connect(vram_viewer, "realize", G_CALLBACK(on_vram_viewer_realize), NULL);
|
|
||||||
g_signal_connect(vram_viewer, "unrealize", G_CALLBACK(on_vram_viewer_unrealize), NULL);
|
|
||||||
|
|
||||||
// Just hide our sub-windows when closing them
|
|
||||||
g_signal_connect(preferences, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
||||||
g_signal_connect(vram_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
||||||
g_signal_connect(memory_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
||||||
g_signal_connect(console, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
||||||
g_signal_connect(printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
||||||
|
|
||||||
g_signal_connect(get_object("vram_viewer_tileset_canvas"), "draw", G_CALLBACK(on_draw_vram_viewer_tileset), NULL);
|
|
||||||
g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "draw", G_CALLBACK(on_vram_viewer_tilemap), NULL);
|
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(gl_area));
|
|
||||||
|
|
||||||
// Handle the whole menubar situation …
|
|
||||||
GMenuModel *menubar = get_menu_model(app, "menubar");
|
|
||||||
|
|
||||||
if (show_menubar()) {
|
|
||||||
// Show a classic menubar
|
|
||||||
gtk_application_set_menubar(GTK_APPLICATION(app), menubar);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Attach a custom title bar
|
|
||||||
GtkWidget *titlebar = gtkget(GTK_WIDGET, "main_header_bar");
|
|
||||||
gtk_window_set_titlebar(GTK_WINDOW(main_window), titlebar);
|
|
||||||
|
|
||||||
// Disable menubar
|
|
||||||
gtk_application_set_menubar(GTK_APPLICATION(app), NULL);
|
|
||||||
|
|
||||||
// Hook menubar up to the hamburger button
|
|
||||||
GtkMenuButton *hamburger_button = GTK_MENU_BUTTON(get_object("hamburger_button"));
|
|
||||||
gtk_menu_button_set_menu_model(hamburger_button, menubar);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_window_set_title(GTK_WINDOW(main_window), "SameBoy");
|
|
||||||
|
|
||||||
// Define a set of window icons
|
// Define a set of window icons
|
||||||
GList *icon_list = NULL;
|
GList *icon_list = NULL;
|
||||||
static char* icons[] = {
|
static char* icons[] = {
|
||||||
@ -625,7 +751,41 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect signal handlers
|
||||||
|
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
|
||||||
|
gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_RELEASE_MASK);
|
||||||
|
|
||||||
g_signal_connect(main_window, "destroy", G_CALLBACK(on_quit), app);
|
g_signal_connect(main_window, "destroy", G_CALLBACK(on_quit), app);
|
||||||
|
g_signal_connect(main_window, "key_press_event", G_CALLBACK(on_key_press), NULL);
|
||||||
|
g_signal_connect(main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
|
||||||
|
|
||||||
|
g_signal_connect(vram_viewer, "realize", G_CALLBACK(on_vram_viewer_realize), NULL);
|
||||||
|
g_signal_connect(vram_viewer, "unrealize", G_CALLBACK(on_vram_viewer_unrealize), NULL);
|
||||||
|
|
||||||
|
// Just hide our sub-windows when closing them
|
||||||
|
g_signal_connect(preferences, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
|
g_signal_connect(vram_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
|
g_signal_connect(memory_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
|
g_signal_connect(console, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
|
g_signal_connect(printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
|
|
||||||
|
g_signal_connect(get_object("vram_viewer_tileset_canvas"), "draw", G_CALLBACK(on_draw_vram_viewer_tileset), NULL);
|
||||||
|
g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "draw", G_CALLBACK(on_draw_vram_viewer_tilemap), NULL);
|
||||||
|
|
||||||
|
// create our renderer area
|
||||||
|
if (supports_gl) {
|
||||||
|
gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
||||||
|
gtk_gl_area_set_required_version(gl_area, 3, 2);
|
||||||
|
gtk_gl_area_set_auto_render(gl_area, false);
|
||||||
|
gtk_gl_area_set_has_alpha(gl_area, false);
|
||||||
|
gtk_gl_area_set_has_depth_buffer(gl_area, false);
|
||||||
|
gtk_gl_area_set_has_stencil_buffer(gl_area, false);
|
||||||
|
g_signal_connect(gl_area, "realize", G_CALLBACK(gl_init), NULL);
|
||||||
|
gtk_box_pack_end(GTK_BOX(main_window_container), GTK_WIDGET(gl_area), TRUE, TRUE, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
create_fallback_canvas();
|
||||||
|
}
|
||||||
|
|
||||||
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window));
|
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window));
|
||||||
gtk_widget_show_all(GTK_WIDGET(main_window));
|
gtk_widget_show_all(GTK_WIDGET(main_window));
|
||||||
@ -741,6 +901,18 @@ static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t rgb_encode_fallback(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
#ifdef GB_LITTLE_ENDIAN
|
||||||
|
// ARGB
|
||||||
|
uint32_t color = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||||
|
#else
|
||||||
|
// BGRA
|
||||||
|
uint32_t color = (b << 24) | (g << 16) | (r << 8) | 0xFF;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
static void update_window_geometry() {
|
static void update_window_geometry() {
|
||||||
// Set size hints
|
// Set size hints
|
||||||
GdkGeometry hints;
|
GdkGeometry hints;
|
||||||
@ -793,7 +965,7 @@ static void run(GApplication *app, UserData *user_data) {
|
|||||||
|
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_pixels_output(&gb, get_current_buffer());
|
GB_set_pixels_output(&gb, get_current_buffer());
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
GB_set_rgb_encode_callback(&gb, fallback_canvas? rgb_encode_fallback : rgb_encode);
|
||||||
// GB_set_sample_rate(&gb, have_aspec.freq);
|
// GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
GB_set_color_correction_mode(&gb, get_color_correction_mode());
|
GB_set_color_correction_mode(&gb, get_color_correction_mode());
|
||||||
GB_set_highpass_filter_mode(&gb, get_highpass_mode());
|
GB_set_highpass_filter_mode(&gb, get_highpass_mode());
|
||||||
|
@ -190,17 +190,20 @@ void update_boot_rom_selector(GtkBuilder *builder) {
|
|||||||
gtk_combo_box_text_append(combo_box, "other", "Other");
|
gtk_combo_box_text_append(combo_box, "other", "Other");
|
||||||
}
|
}
|
||||||
|
|
||||||
enum menubar_override get_show_menubar(void) {
|
enum menubar_type_t get_show_menubar(void) {
|
||||||
if (config.menubar_override == NULL) goto default_value;
|
if (config.menubar_override == NULL) goto default_value;
|
||||||
|
|
||||||
if (g_strcmp0(config.menubar_override, "auto") == 0) {
|
if (g_strcmp0(config.menubar_override, "auto") == 0) {
|
||||||
return MENUBAR_AUTO;
|
return MENUBAR_AUTO;
|
||||||
}
|
}
|
||||||
else if (g_strcmp0(config.menubar_override, "show") == 0) {
|
else if (g_strcmp0(config.menubar_override, "show_in_shell") == 0) {
|
||||||
return MENUBAR_SHOW;
|
return MENUBAR_SHOW_IN_SHELL;
|
||||||
}
|
}
|
||||||
else if (g_strcmp0(config.menubar_override, "hide") == 0) {
|
else if (g_strcmp0(config.menubar_override, "show_in_window") == 0) {
|
||||||
return MENUBAR_HIDE;
|
return MENUBAR_SHOW_IN_WINDOW;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.menubar_override, "show_hamburger") == 0) {
|
||||||
|
return MENUBAR_SHOW_HAMBURGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should not happen
|
// This should not happen
|
||||||
@ -208,16 +211,19 @@ enum menubar_override get_show_menubar(void) {
|
|||||||
default_value: return MENUBAR_AUTO;
|
default_value: return MENUBAR_AUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_show_menubar(enum menubar_override value) {
|
void set_show_menubar(enum menubar_type_t value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case MENUBAR_AUTO:
|
case MENUBAR_AUTO:
|
||||||
config.menubar_override = "auto";
|
config.menubar_override = "auto";
|
||||||
break;
|
break;
|
||||||
case MENUBAR_SHOW:
|
case MENUBAR_SHOW_IN_SHELL:
|
||||||
config.menubar_override = "show";
|
config.menubar_override = "show_in_shell";
|
||||||
break;
|
break;
|
||||||
case MENUBAR_HIDE:
|
case MENUBAR_SHOW_IN_WINDOW:
|
||||||
config.menubar_override = "hide";
|
config.menubar_override = "show_in_window";
|
||||||
|
break;
|
||||||
|
case MENUBAR_SHOW_HAMBURGER:
|
||||||
|
config.menubar_override = "show_hamburger";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,10 +58,11 @@ typedef struct config_t {
|
|||||||
#undef EXPAND_GROUP_MEMBER
|
#undef EXPAND_GROUP_MEMBER
|
||||||
} config_t;
|
} config_t;
|
||||||
|
|
||||||
enum menubar_override {
|
enum menubar_type_t {
|
||||||
MENUBAR_AUTO,
|
MENUBAR_AUTO,
|
||||||
MENUBAR_SHOW,
|
MENUBAR_SHOW_IN_SHELL,
|
||||||
MENUBAR_HIDE
|
MENUBAR_SHOW_IN_WINDOW,
|
||||||
|
MENUBAR_SHOW_HAMBURGER
|
||||||
};
|
};
|
||||||
|
|
||||||
gchar* settings_file_path;
|
gchar* settings_file_path;
|
||||||
@ -80,8 +81,8 @@ void free_settings(void);
|
|||||||
|
|
||||||
void update_boot_rom_selector(GtkBuilder *builder);
|
void update_boot_rom_selector(GtkBuilder *builder);
|
||||||
|
|
||||||
enum menubar_override get_show_menubar(void);
|
enum menubar_type_t get_show_menubar(void);
|
||||||
void set_show_menubar(enum menubar_override);
|
void set_show_menubar(enum menubar_type_t);
|
||||||
|
|
||||||
GB_color_correction_mode_t get_color_correction_mode(void);
|
GB_color_correction_mode_t get_color_correction_mode(void);
|
||||||
void set_color_correction_mode(GB_color_correction_mode_t);
|
void set_color_correction_mode(GB_color_correction_mode_t);
|
||||||
|
@ -63,11 +63,7 @@ static GLuint create_program(const char *vsh, const char *fsh)
|
|||||||
|
|
||||||
bool init_shader_with_name(shader_t *shader, const char *name)
|
bool init_shader_with_name(shader_t *shader, const char *name)
|
||||||
{
|
{
|
||||||
GLint major = 0, minor = 0;
|
if (epoxy_gl_version() < 32) {
|
||||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
||||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
||||||
|
|
||||||
if (major * 0x100 + minor < 0x302) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ typedef struct shader_s {
|
|||||||
GLuint texture;
|
GLuint texture;
|
||||||
GLuint previous_texture;
|
GLuint previous_texture;
|
||||||
GLuint program;
|
GLuint program;
|
||||||
|
|
||||||
|
bool compat_mode;
|
||||||
} shader_t;
|
} shader_t;
|
||||||
|
|
||||||
bool init_shader_with_name(shader_t *shader, const char *name);
|
bool init_shader_with_name(shader_t *shader, const char *name);
|
||||||
|
Loading…
Reference in New Issue
Block a user