diff --git a/gtk3/main.c b/gtk3/main.c index 01cd548..4578169 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -39,8 +39,10 @@ static void run(GApplication *app, UserData *user_data); static GtkApplication *main_application; static GtkBuilder *builder; static GtkGLArea *gl_area; +static GtkDrawingArea *fallback_canvas; static GtkApplicationWindow *main_window; +static GtkBox *main_window_container; static GtkWindow *preferences; static GtkWindow *vram_viewer; static GtkWindow *memory_viewer; @@ -54,6 +56,8 @@ static GB_gameboy_t gb; static uint32_t *image_buffers[3]; static unsigned char current_buffer; +static bool supports_gl; + static bool paused = false; static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false; 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 unsigned char number_of_buffers(void) { - bool should_blend = true; + bool should_blend = !fallback_canvas; return should_blend? 3 : 2; } @@ -122,7 +126,12 @@ static void vblank(GB_gameboy_t *gb) { GB_set_pixels_output(gb, get_pixels()); // 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) { // TODO: Only update what is needed @@ -135,8 +144,10 @@ static void vblank(GB_gameboy_t *gb) { } static void update_viewport(void) { - int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area)); - int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area)); + GtkWidget *w = fallback_canvas ? GTK_WIDGET(fallback_canvas) : 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 y_factor = win_height / (double) GB_get_screen_height(&gb); @@ -165,7 +176,7 @@ static void update_viewport(void) { 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. @@ -199,22 +210,6 @@ static void set_combo_box_row_separator_func(GtkContainer *container) { 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 // 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) { @@ -332,32 +327,86 @@ G_MODULE_EXPORT void on_show_window(GtkWidget *w, gpointer window) { gtk_widget_show_all(GTK_WIDGET(window)); } -G_MODULE_EXPORT void gl_init() { - const char *renderer; +G_MODULE_EXPORT gboolean on_draw_fallback(GtkWidget *widget, cairo_t *cr, gpointer data) { + 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); - if (gtk_gl_area_get_error(gl_area) != NULL) { - return; - } + guint screen_width = GB_get_screen_width(&gb); + guint screen_height = GB_get_screen_height(&gb); - renderer = (char *) glGetString(GL_RENDERER); - g_print("GtkGLArea on %s\n", renderer ? renderer : "Unknown"); + gtk_render_background(context, cr, 0, 0, width, height); + + 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)) { - init_shader_with_name(&shader, "NearestNeighbor"); - } + cairo_translate(cr, rect.x, rect.y); + 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(); } +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() { render_texture(get_current_buffer(), get_previous_buffer()); } 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() { vram_viewer_visible = true; } @@ -390,7 +439,7 @@ G_MODULE_EXPORT gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t 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; 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)); } +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 static void startup(GApplication *app, gpointer 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"); gtk_builder_connect_signals(builder, NULL); @@ -529,65 +701,19 @@ static void startup(GApplication *app, gpointer user_data_gptr) { // setup main window 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 - gl_area = GTK_GL_AREA(gtk_gl_area_new()); - gtk_gl_area_set_auto_render(gl_area, false); + gtk_window_set_title(GTK_WINDOW(main_window), "SameBoy"); + gtk_application_window_set_show_menubar(main_window, false); + gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(main_window_container)); + + setup_menu(app); // Insert separators into `GtkComboBox`es 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(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 GList *icon_list = NULL; static char* icons[] = { @@ -625,7 +751,41 @@ static void activate(GApplication *app, gpointer user_data_gptr) { 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, "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_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; } +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() { // Set size 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_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_color_correction_mode(&gb, get_color_correction_mode()); GB_set_highpass_filter_mode(&gb, get_highpass_mode()); diff --git a/gtk3/settings.c b/gtk3/settings.c index a583d31..95a7b93 100644 --- a/gtk3/settings.c +++ b/gtk3/settings.c @@ -190,17 +190,20 @@ void update_boot_rom_selector(GtkBuilder *builder) { 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 (g_strcmp0(config.menubar_override, "auto") == 0) { return MENUBAR_AUTO; } - else if (g_strcmp0(config.menubar_override, "show") == 0) { - return MENUBAR_SHOW; + else if (g_strcmp0(config.menubar_override, "show_in_shell") == 0) { + return MENUBAR_SHOW_IN_SHELL; } - else if (g_strcmp0(config.menubar_override, "hide") == 0) { - return MENUBAR_HIDE; + else if (g_strcmp0(config.menubar_override, "show_in_window") == 0) { + return MENUBAR_SHOW_IN_WINDOW; + } + else if (g_strcmp0(config.menubar_override, "show_hamburger") == 0) { + return MENUBAR_SHOW_HAMBURGER; } // This should not happen @@ -208,16 +211,19 @@ enum menubar_override get_show_menubar(void) { default_value: return MENUBAR_AUTO; } -void set_show_menubar(enum menubar_override value) { +void set_show_menubar(enum menubar_type_t value) { switch (value) { case MENUBAR_AUTO: config.menubar_override = "auto"; break; - case MENUBAR_SHOW: - config.menubar_override = "show"; + case MENUBAR_SHOW_IN_SHELL: + config.menubar_override = "show_in_shell"; break; - case MENUBAR_HIDE: - config.menubar_override = "hide"; + case MENUBAR_SHOW_IN_WINDOW: + config.menubar_override = "show_in_window"; + break; + case MENUBAR_SHOW_HAMBURGER: + config.menubar_override = "show_hamburger"; break; } } diff --git a/gtk3/settings.h b/gtk3/settings.h index 75988e5..2dd2e58 100644 --- a/gtk3/settings.h +++ b/gtk3/settings.h @@ -58,10 +58,11 @@ typedef struct config_t { #undef EXPAND_GROUP_MEMBER } config_t; -enum menubar_override { +enum menubar_type_t { MENUBAR_AUTO, - MENUBAR_SHOW, - MENUBAR_HIDE + MENUBAR_SHOW_IN_SHELL, + MENUBAR_SHOW_IN_WINDOW, + MENUBAR_SHOW_HAMBURGER }; gchar* settings_file_path; @@ -80,8 +81,8 @@ void free_settings(void); void update_boot_rom_selector(GtkBuilder *builder); -enum menubar_override get_show_menubar(void); -void set_show_menubar(enum menubar_override); +enum menubar_type_t get_show_menubar(void); +void set_show_menubar(enum menubar_type_t); GB_color_correction_mode_t get_color_correction_mode(void); void set_color_correction_mode(GB_color_correction_mode_t); diff --git a/gtk3/shader.c b/gtk3/shader.c index a6bd860..402e5b9 100644 --- a/gtk3/shader.c +++ b/gtk3/shader.c @@ -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) { - GLint major = 0, minor = 0; - glGetIntegerv(GL_MAJOR_VERSION, &major); - glGetIntegerv(GL_MINOR_VERSION, &minor); - - if (major * 0x100 + minor < 0x302) { + if (epoxy_gl_version() < 32) { return false; } diff --git a/gtk3/shader.h b/gtk3/shader.h index ebd3433..d276f73 100644 --- a/gtk3/shader.h +++ b/gtk3/shader.h @@ -14,6 +14,8 @@ typedef struct shader_s { GLuint texture; GLuint previous_texture; GLuint program; + + bool compat_mode; } shader_t; bool init_shader_with_name(shader_t *shader, const char *name);