2020-05-16 15:48:29 +00:00
|
|
|
#include "util.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include <epoxy/gl.h>
|
|
|
|
|
|
|
|
// Workaround to figure out if we have proper OpenGL support.
|
|
|
|
// Otherwise the application would crash after our GtkGlArea is realized
|
|
|
|
// and the context it uses is a legacy OpenGL 1.4 context because
|
|
|
|
// GTK3 calls OpenGL 2.0+ functions on it.
|
|
|
|
bool test_gl_support(void) {
|
|
|
|
gboolean result = false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The main function for the OpenGL version check workaround
|
|
|
|
void gl_check_realize(GtkWidget *w, gpointer user_data_ptr) {
|
|
|
|
gboolean *result = (gboolean *) user_data_ptr;
|
|
|
|
|
|
|
|
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_warning("Failed to create context: %s", 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_debug("OpenGL version: %d", version);
|
|
|
|
|
|
|
|
*result = version >= 32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void replace_extension(const char *src, size_t length, char *dest, const char *ext) {
|
|
|
|
memcpy(dest, src, length);
|
|
|
|
dest[length] = 0;
|
|
|
|
|
|
|
|
/* Remove extension */
|
|
|
|
for (size_t i = length; i--;) {
|
|
|
|
if (dest[i] == '/') break;
|
|
|
|
if (dest[i] == '.') {
|
|
|
|
dest[i] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add new extension */
|
|
|
|
strcat(dest, ext);
|
|
|
|
}
|
|
|
|
|
|
|
|
double clamp_double(double min, double max, double value) {
|
|
|
|
if (value < min) return min;
|
|
|
|
if (value > max) return max;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
double max_double(double a, double b) {
|
|
|
|
if (a > b) return a;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
double min_double(double a, double b) {
|
|
|
|
if (a < b) return a;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t convert_color(uint16_t color) {
|
|
|
|
const uint8_t r = ((uint16_t)(color & 0x1F) * 255) / 31;
|
|
|
|
const uint8_t g = ((uint16_t)((color >> 5) & 0x1F) * 255) / 31;
|
|
|
|
const uint8_t b = ((uint16_t)((color >> 10) & 0x1F) * 255) / 31;
|
|
|
|
|
|
|
|
return (r << 16) | (g << 8) | b;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
|
|
|
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
|
|
|
}
|
|
|
|
|
|
|
|
GB_model_t config_get_model_type(GuiData *gui_data) {
|
|
|
|
if (gui_data->cli_options.model != -1) {
|
|
|
|
return gui_data->cli_options.model;
|
|
|
|
}
|
|
|
|
|
|
|
|
return config_get_model();
|
|
|
|
}
|
|
|
|
|
|
|
|
GtkWidget *menubar_to_menu(GtkMenuBar *menubar) {
|
|
|
|
GtkWidget *menu = gtk_menu_new();
|
|
|
|
g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER(menubar));
|
|
|
|
|
|
|
|
while (iter) {
|
|
|
|
GtkWidget *item = GTK_WIDGET(iter->data);
|
|
|
|
gtk_widget_reparent(item, menu);
|
|
|
|
iter = iter->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
}
|
2020-05-20 01:41:33 +00:00
|
|
|
|
|
|
|
// Determines if a ComboBox entry should be converted into a separator.
|
|
|
|
// Each element with a text value of `<separator>` will be converted into a separator element.
|
|
|
|
gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) {
|
|
|
|
gchar *text = NULL;
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, iter, 0, &text, -1);
|
|
|
|
gboolean result = g_strcmp0("<separator>", text) == 0;
|
|
|
|
g_free(text);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recursively goes through all children of the given container and sets
|
|
|
|
// our `is_separator` function to all children of type`GtkComboBox`
|
|
|
|
void set_combo_box_row_separator_func(GtkContainer *container) {
|
|
|
|
GList *children = gtk_container_get_children(container);
|
|
|
|
|
|
|
|
for (GList *l = children; l; l = l->next) {
|
|
|
|
if (GTK_IS_COMBO_BOX(l->data)) {
|
|
|
|
gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(l->data), is_separator, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GTK_IS_CONTAINER(l->data)) {
|
|
|
|
set_combo_box_row_separator_func(GTK_CONTAINER(l->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free(children);
|
|
|
|
}
|
2020-05-21 20:37:25 +00:00
|
|
|
|
|
|
|
gboolean scroll_to_bottom(GtkTextView *textview, GtkTextMark *mark) {
|
|
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview);
|
|
|
|
GtkTextIter iter;
|
|
|
|
|
|
|
|
gtk_text_buffer_get_end_iter(buffer, &iter);
|
|
|
|
gtk_text_iter_set_line_offset(&iter, 0);
|
|
|
|
|
|
|
|
gtk_text_buffer_move_mark(buffer, mark, &iter);
|
|
|
|
gtk_text_view_scroll_to_mark(textview, mark, 0.0, true, 0.0, 0.10);
|
|
|
|
|
|
|
|
gtk_text_buffer_delete_mark(buffer, mark);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|