[GTK3] Implement GbScreen widget for rendering
This commit is contained in:
parent
a39937aeb2
commit
d63560d3c1
339
gtk3/gb_screen.c
Normal file
339
gtk3/gb_screen.c
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
#include "gb_screen.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct _GbScreen {
|
||||||
|
GtkBin parent;
|
||||||
|
|
||||||
|
GtkGLArea *gl_area;
|
||||||
|
GtkDrawingArea *fallback;
|
||||||
|
|
||||||
|
bool use_gl;
|
||||||
|
shader_t shader;
|
||||||
|
|
||||||
|
uint32_t *image_buffers[3];
|
||||||
|
unsigned char current_buffer;
|
||||||
|
|
||||||
|
unsigned screen_width;
|
||||||
|
unsigned screen_height;
|
||||||
|
|
||||||
|
GB_frame_blending_mode_t blending_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(GbScreen, gb_screen, GTK_TYPE_BIN);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PROP_USE_GL = 1,
|
||||||
|
N_PROPERTIES
|
||||||
|
} GbScreenProperty;
|
||||||
|
|
||||||
|
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
||||||
|
|
||||||
|
static void gb_screen_finalize(GObject *object) {
|
||||||
|
GbScreen *self = (GbScreen *) object;
|
||||||
|
|
||||||
|
if (self->image_buffers[0]) g_free(self->image_buffers[0]);
|
||||||
|
if (self->image_buffers[1]) g_free(self->image_buffers[1]);
|
||||||
|
if (self->image_buffers[2]) g_free(self->image_buffers[2]);
|
||||||
|
|
||||||
|
free_shader(&self->shader);
|
||||||
|
free_master_shader();
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(gb_screen_parent_class)->finalize(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_get_natural_size(GbScreen *self, gint *natural_width, gint *natural_height, double *scale_x_ptr, double *scale_y_ptr) {
|
||||||
|
int width = gtk_widget_get_allocated_width(GTK_WIDGET(self));
|
||||||
|
int height = gtk_widget_get_allocated_height(GTK_WIDGET(self));
|
||||||
|
|
||||||
|
double scale_x = width / (double)self->screen_width;
|
||||||
|
double scale_y = height / (double)self->screen_height;
|
||||||
|
|
||||||
|
if (config.video.use_integer_scaling) {
|
||||||
|
scale_x = (unsigned)(scale_x);
|
||||||
|
scale_y = (unsigned)(scale_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.video.keep_aspect_ratio) {
|
||||||
|
if (scale_x > scale_y) {
|
||||||
|
scale_x = scale_y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scale_y = scale_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scale_x = max_double(1.0, scale_x);
|
||||||
|
scale_y = max_double(1.0, scale_y);
|
||||||
|
|
||||||
|
if (natural_width) *natural_width = self->screen_width * scale_x;
|
||||||
|
if (natural_height) *natural_height = self->screen_height * scale_y;
|
||||||
|
if (scale_x_ptr) *scale_x_ptr = scale_x;
|
||||||
|
if (scale_y_ptr) *scale_y_ptr = scale_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean gb_screen_draw(GtkWidget *widget, cairo_t *cr) {
|
||||||
|
GTK_WIDGET_CLASS(gb_screen_parent_class)->draw(widget, cr);
|
||||||
|
|
||||||
|
static gint scaled_width, scaled_height;
|
||||||
|
static double scale_x, scale_y;
|
||||||
|
|
||||||
|
GbScreen *self = (GbScreen *) widget;
|
||||||
|
|
||||||
|
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
||||||
|
int width = gtk_widget_get_allocated_width(widget);
|
||||||
|
int height = gtk_widget_get_allocated_height(widget);
|
||||||
|
|
||||||
|
gtk_render_background(context, cr, 0, 0, width, height);
|
||||||
|
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||||
|
gb_screen_get_natural_size(self, &scaled_width, &scaled_height, &scale_x, &scale_y);
|
||||||
|
|
||||||
|
Rect viewport = {
|
||||||
|
(width - scaled_width) / 2,
|
||||||
|
(height - scaled_height) / 2,
|
||||||
|
scaled_width,
|
||||||
|
scaled_height
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self->use_gl) {
|
||||||
|
glViewport(viewport.x, viewport.y, scaled_width, scaled_height);
|
||||||
|
|
||||||
|
uint32_t *pixels = gb_screen_get_current_buffer(self);
|
||||||
|
uint32_t *previous = gb_screen_get_previous_buffer(self);
|
||||||
|
|
||||||
|
static void *_pixels = NULL;
|
||||||
|
|
||||||
|
if (pixels) {
|
||||||
|
_pixels = pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
render_bitmap_with_shader(
|
||||||
|
&self->shader, _pixels, previous,
|
||||||
|
self->screen_width, self->screen_height,
|
||||||
|
viewport.x, viewport.y, viewport.width, viewport.height,
|
||||||
|
self->blending_mode
|
||||||
|
);
|
||||||
|
|
||||||
|
// gtk_gl_area_queue_render(self->gl_area);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||||
|
(unsigned char *) gb_screen_get_current_buffer(self),
|
||||||
|
CAIRO_FORMAT_RGB24,
|
||||||
|
self->screen_width,
|
||||||
|
self->screen_height,
|
||||||
|
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, self->screen_width)
|
||||||
|
);
|
||||||
|
|
||||||
|
cairo_translate(cr, viewport.x, viewport.y);
|
||||||
|
cairo_scale(cr, scale_x, scale_y);
|
||||||
|
cairo_set_source_surface(cr, surface, 0, 0);
|
||||||
|
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||||
|
cairo_paint(cr);
|
||||||
|
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width) {
|
||||||
|
GbScreen *self = (GbScreen *)widget;
|
||||||
|
|
||||||
|
*minimum_width = self->screen_width;
|
||||||
|
gb_screen_get_natural_size(self, natural_width, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height) {
|
||||||
|
GbScreen *self = (GbScreen *)widget;
|
||||||
|
|
||||||
|
*minimum_height = self->screen_height;
|
||||||
|
gb_screen_get_natural_size(self, NULL, natural_height, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
|
||||||
|
GbScreen *self = (GbScreen *) object;
|
||||||
|
|
||||||
|
switch ((GbScreenProperty) property_id) {
|
||||||
|
case PROP_USE_GL:
|
||||||
|
self->use_gl = g_value_get_boolean(value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
|
||||||
|
GbScreen *self = (GbScreen *) object;
|
||||||
|
|
||||||
|
switch ((GbScreenProperty) property_id) {
|
||||||
|
case PROP_USE_GL:
|
||||||
|
g_value_set_boolean(value, self->use_gl);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_gl_area_realized(GtkWidget *widget, GObject *object) {
|
||||||
|
GbScreen *self = (GbScreen *)object;
|
||||||
|
GtkGLArea *gl_area = GTK_GL_AREA(widget);
|
||||||
|
|
||||||
|
g_debug("GL Context: %p", gtk_gl_area_get_context(self->gl_area));
|
||||||
|
|
||||||
|
gtk_gl_area_make_current(self->gl_area);
|
||||||
|
if (gtk_gl_area_get_error(self->gl_area) != NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *renderer = (char *)glGetString(GL_RENDERER);
|
||||||
|
g_debug("GtkGLArea on %s", renderer ? renderer : "Unknown");
|
||||||
|
|
||||||
|
if (config.video.shader == NULL || (!init_shader_with_name(&self->shader, config.video.shader) && !init_shader_with_name(&self->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(self->gl_area, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g_info("Using OpenGL for rendering");
|
||||||
|
G_OBJECT_CLASS(gb_screen_parent_class)->constructed(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (gtk_gl_area_get_error(self->gl_area) != NULL) {
|
||||||
|
g_warning("GtkGLArea: %s", gtk_gl_area_get_error(self->gl_area)->message);
|
||||||
|
}
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(self->gl_area));
|
||||||
|
self->gl_area = NULL;
|
||||||
|
self->use_gl = false;
|
||||||
|
|
||||||
|
g_info("Using Cairo for rendering");
|
||||||
|
G_OBJECT_CLASS(gb_screen_parent_class)->constructed(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_constructed(GObject *object) {
|
||||||
|
GbScreen *self = (GbScreen *)object;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
self->use_gl = self->use_gl && test_gl_support();
|
||||||
|
|
||||||
|
if (self->use_gl) {
|
||||||
|
self->gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
||||||
|
g_signal_connect(self->gl_area, "realize", G_CALLBACK(gb_screen_gl_area_realized), object);
|
||||||
|
|
||||||
|
gtk_gl_area_set_required_version(self->gl_area, 3, 2);
|
||||||
|
gtk_gl_area_set_auto_render(self->gl_area, false);
|
||||||
|
gtk_gl_area_set_has_alpha(self->gl_area, false);
|
||||||
|
gtk_gl_area_set_has_depth_buffer(self->gl_area, false);
|
||||||
|
gtk_gl_area_set_has_stencil_buffer(self->gl_area, false);
|
||||||
|
gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(self->gl_area));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g_info("Using Cairo for rendering");
|
||||||
|
G_OBJECT_CLASS(gb_screen_parent_class)->constructed(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_class_init(GbScreenClass *class) {
|
||||||
|
obj_properties[PROP_USE_GL] = g_param_spec_boolean(
|
||||||
|
"use-gl", "Use OpenGL", "Whether to use OpenGL for rendering.",
|
||||||
|
true,
|
||||||
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_PRIVATE
|
||||||
|
);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(class)->finalize = gb_screen_finalize;
|
||||||
|
G_OBJECT_CLASS(class)->set_property = gb_screen_set_property;
|
||||||
|
G_OBJECT_CLASS(class)->get_property = gb_screen_get_property;
|
||||||
|
G_OBJECT_CLASS(class)->constructed = gb_screen_constructed;
|
||||||
|
|
||||||
|
GTK_WIDGET_CLASS(class)->draw = gb_screen_draw;
|
||||||
|
GTK_WIDGET_CLASS(class)->get_preferred_width = gb_screen_get_preferred_width;
|
||||||
|
GTK_WIDGET_CLASS(class)->get_preferred_height = gb_screen_get_preferred_height;
|
||||||
|
|
||||||
|
g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_screen_init(GbScreen *self) {
|
||||||
|
gb_screen_set_resolution(self, 160, 144);
|
||||||
|
gb_screen_clear(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
GbScreen *gb_screen_new(bool force_fallback) {
|
||||||
|
g_debug("force_fallback: %d", force_fallback);
|
||||||
|
return g_object_new(GB_SCREEN_TYPE, "use-gl", !force_fallback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_screen_clear(GbScreen *self) {
|
||||||
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
|
memset(self->image_buffers[i], 0, self->screen_width * self->screen_height * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gb_screen_uses_fallback(GbScreen *self) {
|
||||||
|
return !self->use_gl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines how many frame buffers to use
|
||||||
|
static uint8_t number_of_buffers(GbScreen *self) {
|
||||||
|
if (!self->use_gl) return 2;
|
||||||
|
|
||||||
|
bool should_blend = config_get_frame_blending_mode() != GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
|
|
||||||
|
return should_blend? 3 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the buffer that should be used by the Core to render a new frame to
|
||||||
|
uint32_t *gb_screen_get_pixels(GbScreen *self) {
|
||||||
|
return self->image_buffers[(self->current_buffer + 1) % number_of_buffers(self)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current finished frame
|
||||||
|
uint32_t *gb_screen_get_current_buffer(GbScreen *self) {
|
||||||
|
return self->image_buffers[self->current_buffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the previous finished frame
|
||||||
|
uint32_t *gb_screen_get_previous_buffer(GbScreen *self) {
|
||||||
|
return self->image_buffers[(self->current_buffer + 2) % number_of_buffers(self)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cycles the buffers
|
||||||
|
void gb_screen_flip(GbScreen *self) {
|
||||||
|
self->current_buffer = (self->current_buffer + 1) % number_of_buffers(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_screen_set_resolution(GbScreen *self, unsigned width, unsigned height) {
|
||||||
|
self->screen_width = width;
|
||||||
|
self->screen_height = height;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
|
self->image_buffers[i] = g_realloc_n(
|
||||||
|
self->image_buffers[i],
|
||||||
|
self->screen_width * self->screen_height, sizeof(uint32_t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_queue_resize(GTK_WIDGET(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_screen_set_blending_mode(GbScreen *self, GB_frame_blending_mode_t mode) {
|
||||||
|
self->blending_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_screen_set_shader(GbScreen *self, const char *shader_name) {
|
||||||
|
if (!self->use_gl) return;
|
||||||
|
|
||||||
|
free_shader(&self->shader);
|
||||||
|
init_shader_with_name(&self->shader, shader_name);
|
||||||
|
}
|
25
gtk3/gb_screen.h
Normal file
25
gtk3/gb_screen.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef gb_screen_h
|
||||||
|
#define gb_screen_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <Core/gb.h>
|
||||||
|
#include "shader.h"
|
||||||
|
|
||||||
|
#define GB_SCREEN_TYPE (gb_screen_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(GbScreen, gb_screen, SAMEBOY, BIN, GtkBin)
|
||||||
|
|
||||||
|
GbScreen *gb_screen_new(bool force_fallback);
|
||||||
|
void gb_screen_clear(GbScreen *self);
|
||||||
|
bool gb_screen_uses_fallback(GbScreen *self);
|
||||||
|
|
||||||
|
uint32_t *gb_screen_get_pixels(GbScreen *self);
|
||||||
|
uint32_t *gb_screen_get_current_buffer(GbScreen *self);
|
||||||
|
uint32_t *gb_screen_get_previous_buffer(GbScreen *self);
|
||||||
|
void gb_screen_flip(GbScreen *self);
|
||||||
|
void gb_screen_set_resolution(GbScreen *self, unsigned width, unsigned height);
|
||||||
|
void gb_screen_set_blending_mode(GbScreen *self, GB_frame_blending_mode_t mode);
|
||||||
|
void gb_screen_set_shader(GbScreen *self, const char *shader_name);
|
||||||
|
|
||||||
|
#endif
|
349
gtk3/main.c
349
gtk3/main.c
@ -11,9 +11,9 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "shader.h"
|
|
||||||
#include "check_menu_radio_group.h"
|
#include "check_menu_radio_group.h"
|
||||||
|
|
||||||
|
#include "gb_screen.h"
|
||||||
#include "vram_viewer_window.h"
|
#include "vram_viewer_window.h"
|
||||||
|
|
||||||
// used for audio and game controllers
|
// used for audio and game controllers
|
||||||
@ -641,73 +641,6 @@ static void set_combo_box_row_separator_func(GtkContainer *container) {
|
|||||||
g_list_free(children);
|
g_list_free(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines how many frame buffers to use
|
|
||||||
static unsigned char number_of_buffers(void) {
|
|
||||||
if (gui_data.fallback_canvas) return 2;
|
|
||||||
|
|
||||||
bool should_blend = config_get_frame_blending_mode() != GB_FRAME_BLENDING_MODE_DISABLED;
|
|
||||||
|
|
||||||
return should_blend? 3 : 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the buffer that should be used by the Core to render a new frame to
|
|
||||||
static uint32_t *get_pixels(void) {
|
|
||||||
return gui_data.image_buffers[(gui_data.current_buffer + 1) % number_of_buffers()];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the current finished frame
|
|
||||||
static uint32_t *get_current_buffer(void) {
|
|
||||||
return gui_data.image_buffers[gui_data.current_buffer];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the previous finished frame
|
|
||||||
static uint32_t *get_previous_buffer(void) {
|
|
||||||
return gui_data.image_buffers[(gui_data.current_buffer + 2) % number_of_buffers()];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cycles the buffers
|
|
||||||
static void flip(void) {
|
|
||||||
gui_data.current_buffer = (gui_data.current_buffer + 1) % number_of_buffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_viewport(void) {
|
|
||||||
GtkWidget *w = gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.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);
|
|
||||||
|
|
||||||
if (config.video.use_integer_scaling) {
|
|
||||||
x_factor = (int)(x_factor);
|
|
||||||
y_factor = (int)(y_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.video.keep_aspect_ratio) {
|
|
||||||
if (x_factor > y_factor) {
|
|
||||||
x_factor = y_factor;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
y_factor = x_factor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned new_width = x_factor * GB_get_screen_width(&gb);
|
|
||||||
unsigned new_height = y_factor * GB_get_screen_height(&gb);
|
|
||||||
|
|
||||||
gui_data.viewport = (Rect){
|
|
||||||
(win_width - new_width) / 2,
|
|
||||||
(win_height - new_height) / 2,
|
|
||||||
new_width,
|
|
||||||
new_height
|
|
||||||
};
|
|
||||||
|
|
||||||
if (gui_data.gl_area) {
|
|
||||||
glViewport(gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.width, gui_data.viewport.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WHY DO WE NEED SUCH AN UGLY METHOD, GTK?!
|
// WHY DO WE NEED SUCH AN UGLY METHOD, GTK?!
|
||||||
static void action_entries_set_enabled(const GActionEntry *entries, unsigned n_entries, bool value) {
|
static void action_entries_set_enabled(const GActionEntry *entries, unsigned n_entries, bool value) {
|
||||||
// Assumes null-terminated if n_entries == -1
|
// Assumes null-terminated if n_entries == -1
|
||||||
@ -719,55 +652,6 @@ static void action_entries_set_enabled(const GActionEntry *entries, unsigned n_e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_window_geometry(void) {
|
|
||||||
g_debug("update_window_geometry: %u×%u → %u×%u", gui_data.last_screen_width, gui_data.last_screen_height, GB_get_screen_width(&gb), GB_get_screen_height(&gb));
|
|
||||||
|
|
||||||
GtkWidget *w = gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.gl_area);
|
|
||||||
signed win_width = gtk_widget_get_allocated_width(w);
|
|
||||||
signed win_height = gtk_widget_get_allocated_height(w);
|
|
||||||
signed menu_height = gtk_widget_get_allocated_height(builder_get(GTK_WIDGET, "main_menu"));
|
|
||||||
|
|
||||||
unsigned _factor = win_width > win_height ? win_width / GB_get_screen_width(&gb) : win_height / GB_get_screen_height(&gb);
|
|
||||||
unsigned factor = _factor < 2 ? 2 : _factor;
|
|
||||||
|
|
||||||
unsigned new_width = GB_get_screen_width(&gb) * factor;
|
|
||||||
unsigned new_height = GB_get_screen_height(&gb) * factor + menu_height;
|
|
||||||
|
|
||||||
// Set size hints
|
|
||||||
GdkGeometry hints;
|
|
||||||
hints.min_width = GB_get_screen_width(&gb);
|
|
||||||
hints.min_height = GB_get_screen_height(&gb) + menu_height;
|
|
||||||
|
|
||||||
gtk_window_set_geometry_hints(
|
|
||||||
GTK_WINDOW(gui_data.main_window),
|
|
||||||
NULL,
|
|
||||||
&hints,
|
|
||||||
(GdkWindowHints)(GDK_HINT_MIN_SIZE)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (new_width > win_width || new_height > win_height) {
|
|
||||||
gtk_window_resize(GTK_WINDOW(gui_data.main_window), new_width, new_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup our image buffers
|
|
||||||
if (gui_data.image_buffers[0]) g_free(gui_data.image_buffers[0]);
|
|
||||||
if (gui_data.image_buffers[1]) g_free(gui_data.image_buffers[1]);
|
|
||||||
if (gui_data.image_buffers[2]) g_free(gui_data.image_buffers[2]);
|
|
||||||
|
|
||||||
size_t buffer_size = sizeof(gui_data.image_buffers[0][0]) * GB_get_screen_width(&gb) * GB_get_screen_height(&gb);
|
|
||||||
|
|
||||||
gui_data.image_buffers[0] = g_malloc0(buffer_size);
|
|
||||||
gui_data.image_buffers[1] = g_malloc0(buffer_size);
|
|
||||||
gui_data.image_buffers[2] = g_malloc0(buffer_size);
|
|
||||||
|
|
||||||
gui_data.last_screen_width = GB_get_screen_width(&gb);
|
|
||||||
gui_data.last_screen_height = GB_get_screen_height(&gb);
|
|
||||||
|
|
||||||
if (GB_is_inited(&gb)) {
|
|
||||||
GB_set_pixels_output(&gb, get_pixels());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stop(void) {
|
static void stop(void) {
|
||||||
if (!gui_data.running) return;
|
if (!gui_data.running) return;
|
||||||
|
|
||||||
@ -787,30 +671,25 @@ static void stop(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean on_vblank(GB_gameboy_t *gb) {
|
static gboolean on_vblank(GB_gameboy_t *gb) {
|
||||||
// Queue drawing of the current frame
|
gtk_widget_queue_draw(GTK_WIDGET(gui_data.screen));
|
||||||
if (gui_data.fallback_canvas) {
|
|
||||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.main_window));
|
|
||||||
}
|
|
||||||
else if (gui_data.gl_area) {
|
|
||||||
gtk_gl_area_queue_render(gui_data.gl_area);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb) {
|
static void vblank(GB_gameboy_t *gb) {
|
||||||
flip();
|
gb_screen_flip(gui_data.screen);
|
||||||
|
|
||||||
if (gui_data.border_mode_changed) {
|
if (gui_data.border_mode_changed) {
|
||||||
GB_set_border_mode(gb, config_get_display_border_mode());
|
GB_set_border_mode(gb, config_get_display_border_mode());
|
||||||
update_window_geometry();
|
|
||||||
|
gb_screen_set_resolution(gui_data.screen, GB_get_screen_width(gb), GB_get_screen_height(gb));
|
||||||
|
GB_set_pixels_output(gb, gb_screen_get_pixels(gui_data.screen));
|
||||||
|
|
||||||
gui_data.border_mode_changed = false;
|
gui_data.border_mode_changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_set_pixels_output(gb, get_pixels());
|
GB_set_pixels_output(gb, gb_screen_get_pixels(gui_data.screen));
|
||||||
|
|
||||||
// Handle the speed modifiers:
|
// Handle the speed modifiers:
|
||||||
// The binary slowdown is limited to half speed.
|
// The binary slowdown is limited to half speed.
|
||||||
@ -837,6 +716,24 @@ static void vblank(GB_gameboy_t *gb) {
|
|||||||
|
|
||||||
vram_viewer_update(gui_data.vram_viewer, gb);
|
vram_viewer_update(gui_data.vram_viewer, gb);
|
||||||
|
|
||||||
|
GB_frame_blending_mode_t mode = config_get_frame_blending_mode();
|
||||||
|
|
||||||
|
if (!gb_screen_get_previous_buffer(gui_data.screen)) {
|
||||||
|
mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
|
}
|
||||||
|
else if (mode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
||||||
|
mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
mode = GB_FRAME_BLENDING_MODE_SIMPLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mode = GB_is_odd_frame(gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_screen_set_blending_mode(gui_data.screen, mode);
|
||||||
|
|
||||||
g_idle_add((GSourceFunc) on_vblank, gb);
|
g_idle_add((GSourceFunc) on_vblank, gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,7 +902,7 @@ static void init(void) {
|
|||||||
GB_init(&gb, config_get_model_type(&gui_data));
|
GB_init(&gb, config_get_model_type(&gui_data));
|
||||||
|
|
||||||
GB_set_vblank_callback(&gb, vblank);
|
GB_set_vblank_callback(&gb, vblank);
|
||||||
GB_set_pixels_output(&gb, get_current_buffer());
|
GB_set_pixels_output(&gb, gb_screen_get_current_buffer(gui_data.screen));
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
GB_set_sample_rate(&gb, GB_audio_get_sample_rate());
|
GB_set_sample_rate(&gb, GB_audio_get_sample_rate());
|
||||||
GB_set_color_correction_mode(&gb, config_get_color_correction_mode());
|
GB_set_color_correction_mode(&gb, config_get_color_correction_mode());
|
||||||
@ -1024,8 +921,6 @@ static void init(void) {
|
|||||||
if (config_get_display_border_mode() <= GB_BORDER_ALWAYS) {
|
if (config_get_display_border_mode() <= GB_BORDER_ALWAYS) {
|
||||||
GB_set_border_mode(&gb, config_get_display_border_mode());
|
GB_set_border_mode(&gb, config_get_display_border_mode());
|
||||||
}
|
}
|
||||||
|
|
||||||
update_window_geometry();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset(void) {
|
static void reset(void) {
|
||||||
@ -1045,7 +940,8 @@ static void reset(void) {
|
|||||||
|
|
||||||
// Check SGB -> non-SGB and non-SGB to SGB transitions
|
// Check SGB -> non-SGB and non-SGB to SGB transitions
|
||||||
if (GB_get_screen_width(&gb) != gui_data.last_screen_width || GB_get_screen_height(&gb) != gui_data.last_screen_height) {
|
if (GB_get_screen_width(&gb) != gui_data.last_screen_width || GB_get_screen_height(&gb) != gui_data.last_screen_height) {
|
||||||
update_window_geometry();
|
gb_screen_set_resolution(gui_data.screen, GB_get_screen_width(&gb), GB_get_screen_height(&gb));
|
||||||
|
GB_set_pixels_output(&gb, gb_screen_get_pixels(gui_data.screen));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@ -1290,20 +1186,6 @@ static void startup(GApplication *app, gpointer null_ptr) {
|
|||||||
|
|
||||||
g_debug("GTK version %u.%u.%u", gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
|
g_debug("GTK version %u.%u.%u", gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
|
||||||
|
|
||||||
if (gui_data.cli_options.force_software_renderer) {
|
|
||||||
g_message("Forcing fallback renderer!");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 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.
|
|
||||||
gui_data.supports_gl = test_gl_support();
|
|
||||||
g_debug("OpenGL supported: %s", gui_data.supports_gl? "Yes" : "No");
|
|
||||||
}
|
|
||||||
|
|
||||||
gui_data.builder = gtk_builder_new_from_resource(RESOURCE_PREFIX "ui/window.ui");
|
gui_data.builder = gtk_builder_new_from_resource(RESOURCE_PREFIX "ui/window.ui");
|
||||||
gtk_builder_connect_signals(gui_data.builder, NULL);
|
gtk_builder_connect_signals(gui_data.builder, NULL);
|
||||||
|
|
||||||
@ -1319,6 +1201,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
|
|||||||
g_signal_connect(gui_data.preferences, "realize", G_CALLBACK(on_preferences_realize), (gpointer) gui_data.builder);
|
g_signal_connect(gui_data.preferences, "realize", G_CALLBACK(on_preferences_realize), (gpointer) gui_data.builder);
|
||||||
init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date, gui_data.preferences);
|
init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date, gui_data.preferences);
|
||||||
|
|
||||||
|
gui_data.screen = gb_screen_new(gui_data.cli_options.force_software_renderer);
|
||||||
gui_data.vram_viewer = vram_viewer_new();
|
gui_data.vram_viewer = vram_viewer_new();
|
||||||
gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
|
gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
|
||||||
|
|
||||||
@ -1339,6 +1222,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
|
|||||||
gtk_window_set_title(GTK_WINDOW(gui_data.main_window), "SameBoy");
|
gtk_window_set_title(GTK_WINDOW(gui_data.main_window), "SameBoy");
|
||||||
gtk_application_window_set_show_menubar(gui_data.main_window, false);
|
gtk_application_window_set_show_menubar(gui_data.main_window, false);
|
||||||
gtk_container_add(GTK_CONTAINER(gui_data.main_window), GTK_WIDGET(gui_data.main_window_container));
|
gtk_container_add(GTK_CONTAINER(gui_data.main_window), GTK_WIDGET(gui_data.main_window_container));
|
||||||
|
gtk_box_pack_end(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(gui_data.screen), true, true, 0);
|
||||||
|
|
||||||
setup_menu(app);
|
setup_menu(app);
|
||||||
|
|
||||||
@ -1374,6 +1258,11 @@ static void startup(GApplication *app, gpointer null_ptr) {
|
|||||||
gtk_about_dialog_set_logo(about_dialog, gdk_pixbuf_new_from_resource(icons[2], NULL)); // reuse the 64x64 icon
|
gtk_about_dialog_set_logo(about_dialog, gdk_pixbuf_new_from_resource(icons[2], NULL)); // reuse the 64x64 icon
|
||||||
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
||||||
g_list_free_full(icon_list, g_object_unref);
|
g_list_free_full(icon_list, g_object_unref);
|
||||||
|
|
||||||
|
GdkScreen *screen = gdk_screen_get_default();
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void on_quit_activate(GtkWidget *w, gpointer user_data_ptr) {
|
G_MODULE_EXPORT void on_quit_activate(GtkWidget *w, gpointer user_data_ptr) {
|
||||||
@ -1443,145 +1332,6 @@ static void connect_signal_handlers(GApplication *app) {
|
|||||||
g_signal_connect(gui_data.printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
g_signal_connect(gui_data.printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Comment
|
|
||||||
static void gl_draw(void) {
|
|
||||||
uint32_t *pixels = get_current_buffer();
|
|
||||||
uint32_t *previous = get_previous_buffer();
|
|
||||||
|
|
||||||
static void *_pixels = NULL;
|
|
||||||
|
|
||||||
if (pixels) {
|
|
||||||
_pixels = pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
glClearColor(0, 0, 0, 1);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
GB_frame_blending_mode_t mode = config_get_frame_blending_mode();
|
|
||||||
if (!previous) {
|
|
||||||
mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
|
||||||
}
|
|
||||||
else if (mode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
|
||||||
if (GB_is_sgb(&gb)) {
|
|
||||||
mode = GB_FRAME_BLENDING_MODE_SIMPLE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mode = GB_is_odd_frame(&gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_bitmap_with_shader(
|
|
||||||
&gui_data.shader, _pixels, previous,
|
|
||||||
GB_get_screen_width(&gb), GB_get_screen_height(&gb),
|
|
||||||
gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.width, gui_data.viewport.height,
|
|
||||||
mode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Comment
|
|
||||||
static void gl_finish(void) { }
|
|
||||||
|
|
||||||
// TODO: Comment
|
|
||||||
static void resize(void) {
|
|
||||||
update_viewport();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Comment
|
|
||||||
static 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);
|
|
||||||
|
|
||||||
guint screen_width = GB_get_screen_width(&gb);
|
|
||||||
guint screen_height = GB_get_screen_height(&gb);
|
|
||||||
|
|
||||||
gtk_render_background(context, cr, 0, 0, width, height);
|
|
||||||
gtk_render_frame(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)
|
|
||||||
);
|
|
||||||
|
|
||||||
cairo_translate(cr, gui_data.viewport.x, gui_data.viewport.y);
|
|
||||||
cairo_scale(cr, gui_data.viewport.width / screen_width, gui_data.viewport.height / 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a GtkDrawingArea as a fallback in case we can’t use OpenGL
|
|
||||||
static void create_fallback_canvas(void) {
|
|
||||||
gui_data.fallback_canvas = GTK_DRAWING_AREA(gtk_drawing_area_new());
|
|
||||||
g_signal_connect(gui_data.fallback_canvas, "draw", G_CALLBACK(on_draw_fallback), NULL);
|
|
||||||
g_signal_connect(gui_data.fallback_canvas, "size-allocate", G_CALLBACK(resize), NULL);
|
|
||||||
gtk_box_pack_end(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(gui_data.fallback_canvas), true, true, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Comment
|
|
||||||
static void gl_init(GtkWidget *w) {
|
|
||||||
GtkGLArea *gl_area = GTK_GL_AREA(w);
|
|
||||||
|
|
||||||
g_debug("GL_INIT");
|
|
||||||
const char *renderer;
|
|
||||||
|
|
||||||
g_debug("GL Context: %p", 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_debug("GtkGLArea on %s", renderer ? renderer : "Unknown");
|
|
||||||
|
|
||||||
if (config.video.shader == NULL || (!init_shader_with_name(&gui_data.shader, config.video.shader) && !init_shader_with_name(&gui_data.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_warning("GtkGLArea: %s", gtk_gl_area_get_error(gl_area)->message);
|
|
||||||
}
|
|
||||||
|
|
||||||
create_fallback_canvas();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void create_canvas(void) {
|
|
||||||
// create our renderer area
|
|
||||||
if (gui_data.supports_gl) {
|
|
||||||
gui_data.gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
|
||||||
gtk_gl_area_set_required_version(gui_data.gl_area, 3, 2);
|
|
||||||
gtk_gl_area_set_auto_render(gui_data.gl_area, false);
|
|
||||||
gtk_gl_area_set_has_alpha(gui_data.gl_area, false);
|
|
||||||
gtk_gl_area_set_has_depth_buffer(gui_data.gl_area, false);
|
|
||||||
gtk_gl_area_set_has_stencil_buffer(gui_data.gl_area, false);
|
|
||||||
g_signal_connect(gui_data.gl_area, "realize", G_CALLBACK(gl_init), NULL);
|
|
||||||
gtk_box_pack_end(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(gui_data.gl_area), true, true, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
create_fallback_canvas();
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkScreen *screen = gdk_screen_get_default();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_console(void) {
|
static void setup_console(void) {
|
||||||
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
|
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
|
||||||
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
|
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
|
||||||
@ -1614,7 +1364,7 @@ static void activate(GApplication *app, gpointer null_ptr) {
|
|||||||
init_controllers();
|
init_controllers();
|
||||||
|
|
||||||
connect_signal_handlers(app);
|
connect_signal_handlers(app);
|
||||||
create_canvas();
|
// create_canvas();
|
||||||
setup_console();
|
setup_console();
|
||||||
|
|
||||||
if (gui_data.cli_options.fullscreen) {
|
if (gui_data.cli_options.fullscreen) {
|
||||||
@ -1624,8 +1374,6 @@ static void activate(GApplication *app, gpointer null_ptr) {
|
|||||||
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(gui_data.main_window));
|
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(gui_data.main_window));
|
||||||
gtk_widget_show_all(GTK_WIDGET(gui_data.main_window));
|
gtk_widget_show_all(GTK_WIDGET(gui_data.main_window));
|
||||||
|
|
||||||
update_window_geometry();
|
|
||||||
|
|
||||||
// Start the emulation thread
|
// Start the emulation thread
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
@ -1637,12 +1385,7 @@ static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar
|
|||||||
stop();
|
stop();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
if (gui_data.image_buffers[0]) g_free(gui_data.image_buffers[0]);
|
gtk_widget_destroy(GTK_WIDGET(gui_data.screen));
|
||||||
if (gui_data.image_buffers[1]) g_free(gui_data.image_buffers[1]);
|
|
||||||
if (gui_data.image_buffers[2]) g_free(gui_data.image_buffers[2]);
|
|
||||||
free_shader(&gui_data.shader);
|
|
||||||
free_master_shader();
|
|
||||||
|
|
||||||
GB_free(&gb);
|
GB_free(&gb);
|
||||||
|
|
||||||
g_object_unref(gui_data.builder);
|
g_object_unref(gui_data.builder);
|
||||||
@ -1705,19 +1448,10 @@ static void close_rom(void) {
|
|||||||
stop();
|
stop();
|
||||||
GB_free(&gb);
|
GB_free(&gb);
|
||||||
|
|
||||||
// Clear the screen as side effect
|
gb_screen_clear(gui_data.screen);
|
||||||
update_window_geometry();
|
gtk_widget_queue_draw(GTK_WIDGET(gui_data.screen));
|
||||||
|
|
||||||
if (gui_data.fallback_canvas) {
|
|
||||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.main_window));
|
|
||||||
}
|
|
||||||
else if (gui_data.gl_area) {
|
|
||||||
gtk_gl_area_queue_render(gui_data.gl_area);
|
|
||||||
}
|
|
||||||
|
|
||||||
vram_viewer_clear(gui_data.vram_viewer);
|
vram_viewer_clear(gui_data.vram_viewer);
|
||||||
|
|
||||||
// Redraw the VRAM viewer
|
|
||||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
||||||
|
|
||||||
// Update menu action states
|
// Update menu action states
|
||||||
@ -1884,8 +1618,7 @@ G_MODULE_EXPORT void on_graphic_filter_changed(GtkWidget *w, gpointer user_data_
|
|||||||
GtkComboBox *box = GTK_COMBO_BOX(w);
|
GtkComboBox *box = GTK_COMBO_BOX(w);
|
||||||
config.video.shader = (gchar *)gtk_combo_box_get_active_id(box);
|
config.video.shader = (gchar *)gtk_combo_box_get_active_id(box);
|
||||||
|
|
||||||
free_shader(&gui_data.shader);
|
gb_screen_set_shader(gui_data.screen, config.video.shader);
|
||||||
init_shader_with_name(&gui_data.shader, config.video.shader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void on_highpass_filter_changed(GtkWidget *w, gpointer user_data_ptr) {
|
G_MODULE_EXPORT void on_highpass_filter_changed(GtkWidget *w, gpointer user_data_ptr) {
|
||||||
@ -1900,7 +1633,6 @@ G_MODULE_EXPORT void on_keep_aspect_ratio_changed(GtkWidget *w, gpointer user_da
|
|||||||
GtkCheckButton *button = GTK_CHECK_BUTTON(w);
|
GtkCheckButton *button = GTK_CHECK_BUTTON(w);
|
||||||
gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
||||||
config.video.keep_aspect_ratio = value;
|
config.video.keep_aspect_ratio = value;
|
||||||
update_viewport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data_ptr) {
|
G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data_ptr) {
|
||||||
@ -1932,7 +1664,6 @@ G_MODULE_EXPORT void on_use_integer_scaling_changed(GtkWidget *w, gpointer user_
|
|||||||
GtkCheckButton *button = GTK_CHECK_BUTTON(w);
|
GtkCheckButton *button = GTK_CHECK_BUTTON(w);
|
||||||
gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
||||||
config.video.use_integer_scaling = value;
|
config.video.use_integer_scaling = value;
|
||||||
update_viewport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
G_MODULE_EXPORT void console_on_enter(GtkWidget *w, gpointer user_data_ptr) {
|
G_MODULE_EXPORT void console_on_enter(GtkWidget *w, gpointer user_data_ptr) {
|
||||||
|
12
gtk3/types.h
12
gtk3/types.h
@ -2,7 +2,7 @@
|
|||||||
#define types_h
|
#define types_h
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "shader.h"
|
#include "gb_screen.h"
|
||||||
#include "vram_viewer_window.h"
|
#include "vram_viewer_window.h"
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
@ -38,8 +38,9 @@ typedef struct GuiData {
|
|||||||
GtkBuilder *builder;
|
GtkBuilder *builder;
|
||||||
GtkApplicationWindow *main_window;
|
GtkApplicationWindow *main_window;
|
||||||
GtkBox *main_window_container;
|
GtkBox *main_window_container;
|
||||||
GtkGLArea *gl_area;
|
|
||||||
GtkDrawingArea *fallback_canvas;
|
GbScreen *screen;
|
||||||
|
|
||||||
GtkWindow *preferences;
|
GtkWindow *preferences;
|
||||||
VramViewerWindow *vram_viewer;
|
VramViewerWindow *vram_viewer;
|
||||||
GtkWindow *memory_viewer;
|
GtkWindow *memory_viewer;
|
||||||
@ -59,13 +60,8 @@ typedef struct GuiData {
|
|||||||
|
|
||||||
// Audio and video
|
// Audio and video
|
||||||
bool audio_initialized;
|
bool audio_initialized;
|
||||||
uint32_t *image_buffers[3];
|
|
||||||
unsigned char current_buffer;
|
|
||||||
Rect viewport;
|
|
||||||
bool border_mode_changed;
|
bool border_mode_changed;
|
||||||
bool is_fullscreen;
|
bool is_fullscreen;
|
||||||
bool supports_gl;
|
|
||||||
shader_t shader;
|
|
||||||
unsigned last_screen_width;
|
unsigned last_screen_width;
|
||||||
unsigned last_screen_height;
|
unsigned last_screen_height;
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ static void visible_tab_changed(GObject *stack, GParamSpec *pspec, VramViewerWin
|
|||||||
|
|
||||||
static gboolean draw_tileset_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
static gboolean draw_tileset_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
||||||
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
||||||
guint width = gtk_widget_get_allocated_width(widget);
|
int width = gtk_widget_get_allocated_width(widget);
|
||||||
guint height = gtk_widget_get_allocated_height(widget);
|
int height = gtk_widget_get_allocated_height(widget);
|
||||||
|
|
||||||
gtk_render_background(context, cr, 0, 0, width, height);
|
gtk_render_background(context, cr, 0, 0, width, height);
|
||||||
gtk_render_frame(context, cr, 0, 0, width, height);
|
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||||
@ -100,8 +100,8 @@ static gboolean draw_tileset_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWi
|
|||||||
|
|
||||||
static gboolean draw_tilemap_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
static gboolean draw_tilemap_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
||||||
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
GtkStyleContext *context = gtk_widget_get_style_context(widget);
|
||||||
guint width = gtk_widget_get_allocated_width(widget);
|
int width = gtk_widget_get_allocated_width(widget);
|
||||||
guint height = gtk_widget_get_allocated_height(widget);
|
int height = gtk_widget_get_allocated_height(widget);
|
||||||
|
|
||||||
gtk_render_background(context, cr, 0, 0, width, height);
|
gtk_render_background(context, cr, 0, 0, width, height);
|
||||||
gtk_render_frame(context, cr, 0, 0, width, height);
|
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||||
|
Loading…
Reference in New Issue
Block a user