[GTK3] Move some functions into util.c
This commit is contained in:
parent
7dbd0e18f9
commit
accaedbdac
261
gtk3/main.c
261
gtk3/main.c
@ -1,7 +1,6 @@
|
||||
#define G_LOG_USE_STRUCTURED
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <epoxy/gl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
@ -9,7 +8,9 @@
|
||||
|
||||
#include <Core/gb.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "shader.h"
|
||||
#include "check_menu_radio_group.h"
|
||||
|
||||
@ -29,113 +30,12 @@
|
||||
#define BUTTON_MASK_LEFT 0x40
|
||||
#define BUTTON_MASK_RIGHT 0x80
|
||||
|
||||
#define tileset_buffer_length 256 * 192 * 4
|
||||
#define tilemap_buffer_length 256 * 256 * 4
|
||||
|
||||
#define str(x) #x
|
||||
#define xstr(x) str(x)
|
||||
#define get_object(id) gtk_builder_get_object(gui_data.builder, id)
|
||||
#define builder_get(type, id) type(get_object(id))
|
||||
#define action_set_enabled(map, name, value) g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(map), name)), value);
|
||||
|
||||
typedef struct{
|
||||
int16_t x, y;
|
||||
uint16_t w, h;
|
||||
} Rect;
|
||||
|
||||
typedef struct GuiData {
|
||||
struct CliOptionData {
|
||||
gchar *config_path;
|
||||
gchar *boot_rom_path;
|
||||
gboolean fullscreen;
|
||||
GB_model_t model;
|
||||
gboolean force_software_renderer;
|
||||
} cli_options;
|
||||
|
||||
GFile *file;
|
||||
gint sample_rate;
|
||||
GDateTime *config_modification_date;
|
||||
|
||||
char *battery_save_path;
|
||||
char *cheats_save_path;
|
||||
|
||||
GB_model_t prev_model;
|
||||
|
||||
const GThread *main_thread;
|
||||
volatile bool running;
|
||||
volatile bool stopping;
|
||||
volatile bool stopped;
|
||||
|
||||
// GTK pointers
|
||||
GtkApplication *main_application;
|
||||
GtkBuilder *builder;
|
||||
GtkApplicationWindow *main_window;
|
||||
GtkBox *main_window_container;
|
||||
GtkGLArea *gl_area;
|
||||
GtkDrawingArea *fallback_canvas;
|
||||
GtkWindow *preferences;
|
||||
GtkWindow *vram_viewer;
|
||||
GtkWindow *memory_viewer;
|
||||
GtkWindow *console;
|
||||
GtkWindow *printer;
|
||||
|
||||
// Debugger state
|
||||
GtkTextBuffer *pending_console_output;
|
||||
gboolean in_sync_input;
|
||||
gchar *last_console_input;
|
||||
gboolean log_to_sidebar;
|
||||
gboolean should_clear_sidebar;
|
||||
GMutex debugger_input_mutex;
|
||||
GCond debugger_input_cond;
|
||||
GRecMutex console_output_lock;
|
||||
GPtrArray *debugger_input_queue;
|
||||
bool vram_viewer_visible;
|
||||
bool vram_viewer_updating;
|
||||
gchar *vram_viewer_active_tab;
|
||||
gboolean vram_viewer_is_cgb;
|
||||
uint8_t vram_viewer_palette_data[16][0x40];
|
||||
GB_oam_info_t oam_info[40];
|
||||
uint16_t oam_count;
|
||||
uint8_t oam_height;
|
||||
uint32_t tileset_buffer[tileset_buffer_length];
|
||||
uint32_t tilemap_buffer[tilemap_buffer_length];
|
||||
GMutex tileset_buffer_mutex;
|
||||
GMutex tilemap_buffer_mutex;
|
||||
Rect scroll_rect;
|
||||
|
||||
// Audio and video
|
||||
bool audio_initialized;
|
||||
uint32_t *image_buffers[3];
|
||||
unsigned char current_buffer;
|
||||
Rect viewport;
|
||||
bool border_mode_changed;
|
||||
bool is_fullscreen;
|
||||
bool supports_gl;
|
||||
shader_t shader;
|
||||
unsigned last_screen_width;
|
||||
unsigned last_screen_height;
|
||||
|
||||
// Fast forward / slow motion
|
||||
bool underclock_down;
|
||||
bool rewind_down;
|
||||
bool do_rewind;
|
||||
bool rewind_paused;
|
||||
bool turbo_down;
|
||||
double clock_mutliplier;
|
||||
double analog_clock_multiplier;
|
||||
bool analog_clock_multiplier_valid;
|
||||
|
||||
// Input
|
||||
uint8_t pressed_buttons;
|
||||
struct Controller_t {
|
||||
SDL_GameController *controller;
|
||||
SDL_Haptic *haptic;
|
||||
bool ignore_rumble;
|
||||
} *controllers;
|
||||
unsigned controller_count;
|
||||
struct Controller_t *last_used_controller; // Used for rumble
|
||||
} GuiData;
|
||||
|
||||
// Initialize the GuiData
|
||||
static GuiData gui_data = {
|
||||
.cli_options = {
|
||||
@ -168,44 +68,9 @@ static GuiData gui_data = {
|
||||
.clock_mutliplier = 1.0,
|
||||
.analog_clock_multiplier = 1.0,
|
||||
};
|
||||
|
||||
GB_gameboy_t gb;
|
||||
|
||||
typedef enum {
|
||||
INPUT_UP,
|
||||
INPUT_DOWN,
|
||||
INPUT_LEFT,
|
||||
INPUT_RIGHT,
|
||||
INPUT_A,
|
||||
INPUT_B,
|
||||
INPUT_START,
|
||||
INPUT_SELECT,
|
||||
|
||||
INPUT_TURBO,
|
||||
INPUT_REWIND,
|
||||
INPUT_SLOWDOWN,
|
||||
|
||||
INPUT_FULLSCREEN,
|
||||
} input_names_t;
|
||||
|
||||
static unsigned key_map[] = {
|
||||
[INPUT_UP] = GDK_KEY_w,
|
||||
[INPUT_LEFT] = GDK_KEY_a,
|
||||
[INPUT_DOWN] = GDK_KEY_s,
|
||||
[INPUT_RIGHT] = GDK_KEY_d,
|
||||
|
||||
[INPUT_A] = GDK_KEY_l,
|
||||
[INPUT_B] = GDK_KEY_k,
|
||||
|
||||
[INPUT_START] = GDK_KEY_h,
|
||||
[INPUT_SELECT] = GDK_KEY_g,
|
||||
|
||||
[INPUT_TURBO] = GDK_KEY_space,
|
||||
[INPUT_REWIND] = GDK_KEY_Tab,
|
||||
[INPUT_SLOWDOWN] = GDK_KEY_Shift_L,
|
||||
|
||||
[INPUT_FULLSCREEN] = GDK_KEY_F11,
|
||||
};
|
||||
|
||||
// Forward declarations of the actions
|
||||
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||
static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||
@ -253,52 +118,6 @@ static const GActionEntry app_entries[] = {
|
||||
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
|
||||
};
|
||||
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static double clamp_double(double min, double max, double value) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
static double max_double(double a, double b) {
|
||||
if (a > b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
static double min_double(double a, double b) {
|
||||
if (a < b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
||||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data_ptr) {
|
||||
const gchar *title = gtk_tree_view_column_get_title(col);
|
||||
const uint8_t color_index = g_ascii_strtoll(&title[6], NULL, 10);
|
||||
@ -405,51 +224,6 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
gboolean 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;
|
||||
}
|
||||
|
||||
static gboolean init_controllers(void) {
|
||||
SDL_version compiled;
|
||||
SDL_version linked;
|
||||
@ -564,14 +338,6 @@ static gboolean init_audio(void) {
|
||||
return gui_data.audio_initialized = true;
|
||||
}
|
||||
|
||||
static GB_model_t config_get_model_type(void) {
|
||||
if (gui_data.cli_options.model != -1) {
|
||||
return gui_data.cli_options.model;
|
||||
}
|
||||
|
||||
return config_get_model();
|
||||
}
|
||||
|
||||
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) {
|
||||
if (gui_data.turbo_down) {
|
||||
static unsigned skip = 0;
|
||||
@ -806,19 +572,6 @@ static char *async_console_input(GB_gameboy_t *gb) {
|
||||
return input;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Creating these items in the UI defintion files was buggy in some desktop
|
||||
// environments and the manual call of `g_signal_connect` was needed anyway
|
||||
// because the UI definition can’t define string arguments for signal handlers.
|
||||
@ -1460,7 +1213,7 @@ static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type) {
|
||||
static void init(void) {
|
||||
if (GB_is_inited(&gb)) return;
|
||||
|
||||
GB_init(&gb, config_get_model_type());
|
||||
GB_init(&gb, config_get_model_type(&gui_data));
|
||||
|
||||
GB_set_vblank_callback(&gb, vblank);
|
||||
GB_set_pixels_output(&gb, get_current_buffer());
|
||||
@ -1487,8 +1240,8 @@ static void init(void) {
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
g_debug("Reset: %d == %d", config_get_model_type(), gui_data.prev_model);
|
||||
GB_model_t current_model = config_get_model_type();
|
||||
g_debug("Reset: %d == %d", config_get_model_type(&gui_data), gui_data.prev_model);
|
||||
GB_model_t current_model = config_get_model_type(&gui_data);
|
||||
|
||||
if (gui_data.prev_model == -1 || gui_data.prev_model == current_model) {
|
||||
GB_reset(&gb);
|
||||
@ -1499,7 +1252,7 @@ static void reset(void) {
|
||||
|
||||
GB_set_palette(&gb, config_get_monochrome_palette());
|
||||
|
||||
gui_data.prev_model = config_get_model_type();
|
||||
gui_data.prev_model = config_get_model_type(&gui_data);
|
||||
|
||||
// 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) {
|
||||
|
144
gtk3/types.h
Normal file
144
gtk3/types.h
Normal file
@ -0,0 +1,144 @@
|
||||
#ifndef types_h
|
||||
#define types_h
|
||||
|
||||
#include "SDL.h"
|
||||
#include "shader.h"
|
||||
|
||||
#define tileset_buffer_length 256 * 192 * 4
|
||||
#define tilemap_buffer_length 256 * 256 * 4
|
||||
|
||||
typedef struct{
|
||||
int16_t x, y;
|
||||
uint16_t w, h;
|
||||
} Rect;
|
||||
|
||||
typedef struct GuiData {
|
||||
struct CliOptionData {
|
||||
gchar *config_path;
|
||||
gchar *boot_rom_path;
|
||||
gboolean fullscreen;
|
||||
GB_model_t model;
|
||||
gboolean force_software_renderer;
|
||||
} cli_options;
|
||||
|
||||
GFile *file;
|
||||
gint sample_rate;
|
||||
GDateTime *config_modification_date;
|
||||
|
||||
char *battery_save_path;
|
||||
char *cheats_save_path;
|
||||
|
||||
GB_model_t prev_model;
|
||||
|
||||
const GThread *main_thread;
|
||||
volatile bool running;
|
||||
volatile bool stopping;
|
||||
volatile bool stopped;
|
||||
|
||||
// GTK pointers
|
||||
GtkApplication *main_application;
|
||||
GtkBuilder *builder;
|
||||
GtkApplicationWindow *main_window;
|
||||
GtkBox *main_window_container;
|
||||
GtkGLArea *gl_area;
|
||||
GtkDrawingArea *fallback_canvas;
|
||||
GtkWindow *preferences;
|
||||
GtkWindow *vram_viewer;
|
||||
GtkWindow *memory_viewer;
|
||||
GtkWindow *console;
|
||||
GtkWindow *printer;
|
||||
|
||||
// Debugger state
|
||||
GtkTextBuffer *pending_console_output;
|
||||
gboolean in_sync_input;
|
||||
gchar *last_console_input;
|
||||
gboolean log_to_sidebar;
|
||||
gboolean should_clear_sidebar;
|
||||
GMutex debugger_input_mutex;
|
||||
GCond debugger_input_cond;
|
||||
GRecMutex console_output_lock;
|
||||
GPtrArray *debugger_input_queue;
|
||||
bool vram_viewer_visible;
|
||||
bool vram_viewer_updating;
|
||||
gchar *vram_viewer_active_tab;
|
||||
gboolean vram_viewer_is_cgb;
|
||||
uint8_t vram_viewer_palette_data[16][0x40];
|
||||
GB_oam_info_t oam_info[40];
|
||||
uint16_t oam_count;
|
||||
uint8_t oam_height;
|
||||
uint32_t tileset_buffer[tileset_buffer_length];
|
||||
uint32_t tilemap_buffer[tilemap_buffer_length];
|
||||
GMutex tileset_buffer_mutex;
|
||||
GMutex tilemap_buffer_mutex;
|
||||
Rect scroll_rect;
|
||||
|
||||
// Audio and video
|
||||
bool audio_initialized;
|
||||
uint32_t *image_buffers[3];
|
||||
unsigned char current_buffer;
|
||||
Rect viewport;
|
||||
bool border_mode_changed;
|
||||
bool is_fullscreen;
|
||||
bool supports_gl;
|
||||
shader_t shader;
|
||||
unsigned last_screen_width;
|
||||
unsigned last_screen_height;
|
||||
|
||||
// Fast forward / slow motion
|
||||
bool underclock_down;
|
||||
bool rewind_down;
|
||||
bool do_rewind;
|
||||
bool rewind_paused;
|
||||
bool turbo_down;
|
||||
double clock_mutliplier;
|
||||
double analog_clock_multiplier;
|
||||
bool analog_clock_multiplier_valid;
|
||||
|
||||
// Input
|
||||
uint8_t pressed_buttons;
|
||||
struct Controller_t {
|
||||
SDL_GameController *controller;
|
||||
SDL_Haptic *haptic;
|
||||
bool ignore_rumble;
|
||||
} *controllers;
|
||||
unsigned controller_count;
|
||||
struct Controller_t *last_used_controller; // Used for rumble
|
||||
} GuiData;
|
||||
|
||||
typedef enum {
|
||||
INPUT_UP,
|
||||
INPUT_DOWN,
|
||||
INPUT_LEFT,
|
||||
INPUT_RIGHT,
|
||||
INPUT_A,
|
||||
INPUT_B,
|
||||
INPUT_START,
|
||||
INPUT_SELECT,
|
||||
|
||||
INPUT_TURBO,
|
||||
INPUT_REWIND,
|
||||
INPUT_SLOWDOWN,
|
||||
|
||||
INPUT_FULLSCREEN,
|
||||
} input_names_t;
|
||||
|
||||
static unsigned key_map[] = {
|
||||
[INPUT_UP] = GDK_KEY_w,
|
||||
[INPUT_LEFT] = GDK_KEY_a,
|
||||
[INPUT_DOWN] = GDK_KEY_s,
|
||||
[INPUT_RIGHT] = GDK_KEY_d,
|
||||
|
||||
[INPUT_A] = GDK_KEY_l,
|
||||
[INPUT_B] = GDK_KEY_k,
|
||||
|
||||
[INPUT_START] = GDK_KEY_h,
|
||||
[INPUT_SELECT] = GDK_KEY_g,
|
||||
|
||||
[INPUT_TURBO] = GDK_KEY_space,
|
||||
[INPUT_REWIND] = GDK_KEY_Tab,
|
||||
[INPUT_SLOWDOWN] = GDK_KEY_Shift_L,
|
||||
|
||||
[INPUT_FULLSCREEN] = GDK_KEY_F11,
|
||||
};
|
||||
|
||||
#endif
|
114
gtk3/util.c
Normal file
114
gtk3/util.c
Normal file
@ -0,0 +1,114 @@
|
||||
#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;
|
||||
}
|
25
gtk3/util.h
Normal file
25
gtk3/util.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef util_h
|
||||
#define util_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <Core/gb.h>
|
||||
#include "types.h"
|
||||
|
||||
bool test_gl_support(void);
|
||||
void gl_check_realize(GtkWidget *w, gpointer user_data_ptr);
|
||||
|
||||
void replace_extension(const char *src, size_t length, char *dest, const char *ext);
|
||||
|
||||
double clamp_double(double min, double max, double value);
|
||||
double max_double(double a, double b);
|
||||
double min_double(double a, double b);
|
||||
|
||||
uint32_t convert_color(uint16_t color);
|
||||
uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
GB_model_t config_get_model_type(GuiData *gui_data);
|
||||
|
||||
GtkWidget *menubar_to_menu(GtkMenuBar *menubar);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user