Compare commits
35 Commits
master
...
gtk3-OpenA
Author | SHA1 | Date | |
---|---|---|---|
3b4f81ca78 | |||
5d68673843 | |||
b23399c9ca | |||
13d849cb41 | |||
5020be359e | |||
d75a261663 | |||
6090bbf384 | |||
6844ad31e8 | |||
efb87f7a36 | |||
d9a9baae1b | |||
e7cbc71fd6 | |||
952934a3d7 | |||
714665f832 | |||
5476f45eb8 | |||
be63e5b87d | |||
9614b47cd7 | |||
ade14530e4 | |||
5641f26651 | |||
21adec8f30 | |||
8fb33900ec | |||
fd7370fc29 | |||
2d8aced75c | |||
ef682ec0c5 | |||
f68472f1c3 | |||
eca563dfde | |||
d60df680bb | |||
3709a88e6e | |||
64ac3690e7 | |||
511d65fa45 | |||
4caa1d0266 | |||
557bfd117e | |||
56308f8b73 | |||
3c29a33043 | |||
2ea8858dfc | |||
1b2317e3c6 |
8
.gitignore
vendored
@ -1 +1,9 @@
|
|||||||
build
|
build
|
||||||
|
|
||||||
|
# intermediate source files generated at build time
|
||||||
|
gtk3/sameboy-gtk3-resources.c
|
||||||
|
gtk3/resources/gtk3/
|
||||||
|
|
||||||
|
# temporary backup file
|
||||||
|
*.*~
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
||||||
gb ->screen[i] = color;
|
gb->screen[i] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
Makefile
@ -1,5 +1,5 @@
|
|||||||
# Make hacks
|
# Make hacks
|
||||||
.INTERMEDIATE:
|
.INTERMEDIATE: gtk3/sameboy-gtk3-resources.c
|
||||||
|
|
||||||
# Set target, configuration, version and destination folders
|
# Set target, configuration, version and destination folders
|
||||||
|
|
||||||
@ -83,12 +83,19 @@ endif
|
|||||||
|
|
||||||
CFLAGS += -Werror -Wall -Wno-unused-result -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
CFLAGS += -Werror -Wall -Wno-unused-result -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
||||||
SDL_LDFLAGS := -lSDL2 -lGL
|
SDL_LDFLAGS := -lSDL2 -lGL
|
||||||
|
GTK3_CFLAGS := `pkg-config --cflags gio-2.0 gtk+-3.0 epoxy openal` -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\"
|
||||||
|
GTK3_LDFLAGS := `pkg-config --libs gio-2.0 gtk+-3.0 epoxy openal`
|
||||||
|
|
||||||
|
# TODO: REMOVE DISABLE UNUSED WARNINGS
|
||||||
|
GTK3_CFLAGS += -Wno-unused
|
||||||
|
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
CFLAGS += -IWindows -Drandom=rand
|
CFLAGS += -IWindows -Drandom=rand
|
||||||
LDFLAGS += -lmsvcrt -lcomdlg32 -lSDL2main -Wl,/MANIFESTFILE:NUL
|
LDFLAGS += -lmsvcrt -lcomdlg32 -lSDL2main -Wl,/MANIFESTFILE:NUL
|
||||||
SDL_LDFLAGS := -lSDL2 -lopengl32
|
SDL_LDFLAGS := -lSDL2 -lopengl32
|
||||||
else
|
else
|
||||||
LDFLAGS += -lc -lm -ldl
|
LDFLAGS += -lc -lm -ldl
|
||||||
|
GTK3_LDFLAGS += -Wl -rdynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),Darwin)
|
ifeq ($(PLATFORM),Darwin)
|
||||||
@ -129,6 +136,7 @@ endif
|
|||||||
cocoa: $(BIN)/SameBoy.app
|
cocoa: $(BIN)/SameBoy.app
|
||||||
quicklook: $(BIN)/SameBoy.qlgenerator
|
quicklook: $(BIN)/SameBoy.qlgenerator
|
||||||
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
|
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
|
||||||
|
gtk3: $(BIN)/gtk3/sameboy
|
||||||
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin
|
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin
|
||||||
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin
|
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin
|
||||||
all: cocoa sdl tester libretro
|
all: cocoa sdl tester libretro
|
||||||
@ -137,6 +145,7 @@ all: cocoa sdl tester libretro
|
|||||||
|
|
||||||
CORE_SOURCES := $(shell ls Core/*.c)
|
CORE_SOURCES := $(shell ls Core/*.c)
|
||||||
SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG)
|
SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG)
|
||||||
|
GTK3_SOURCES := $(shell ls gtk3/*.c) gtk3/sameboy-gtk3-resources.c
|
||||||
TESTER_SOURCES := $(shell ls Tester/*.c)
|
TESTER_SOURCES := $(shell ls Tester/*.c)
|
||||||
|
|
||||||
ifeq ($(PLATFORM),Darwin)
|
ifeq ($(PLATFORM),Darwin)
|
||||||
@ -152,6 +161,7 @@ CORE_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(CORE_SOURCES))
|
|||||||
COCOA_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(COCOA_SOURCES))
|
COCOA_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(COCOA_SOURCES))
|
||||||
QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES))
|
QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES))
|
||||||
SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES))
|
SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES))
|
||||||
|
GTK3_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(GTK3_SOURCES))
|
||||||
TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES))
|
TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES))
|
||||||
|
|
||||||
# Automatic dependency generation
|
# Automatic dependency generation
|
||||||
@ -161,6 +171,9 @@ ifneq ($(filter-out clean bootroms libretro %.bin, $(MAKECMDGOALS)),)
|
|||||||
ifneq ($(filter $(MAKECMDGOALS),sdl),)
|
ifneq ($(filter $(MAKECMDGOALS),sdl),)
|
||||||
-include $(SDL_OBJECTS:.o=.dep)
|
-include $(SDL_OBJECTS:.o=.dep)
|
||||||
endif
|
endif
|
||||||
|
ifneq ($(filter $(MAKECMDGOALS),gtk3),)
|
||||||
|
-include $(GTK3_OBJECTS:.o=.gtk3dep)
|
||||||
|
endif
|
||||||
ifneq ($(filter $(MAKECMDGOALS),tester),)
|
ifneq ($(filter $(MAKECMDGOALS),tester),)
|
||||||
-include $(TESTER_OBJECTS:.o=.dep)
|
-include $(TESTER_OBJECTS:.o=.dep)
|
||||||
endif
|
endif
|
||||||
@ -169,6 +182,16 @@ ifneq ($(filter $(MAKECMDGOALS),cocoa),)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
gtk3/sameboy-gtk3-resources.c: gtk3/sameboy.gresource.xml $(shell glib-compile-resources --sourcedir=gtk3/resources --generate-dependencies gtk3/sameboy.gresource.xml)
|
||||||
|
glib-compile-resources --sourcedir=gtk3/resources --target=$@ --generate-source $<
|
||||||
|
# Resource hack cleanup
|
||||||
|
rm -rf gtk3/resources/gtk3
|
||||||
|
|
||||||
|
# GTK3 requires special CFLAGS
|
||||||
|
$(OBJ)/gtk3/%.gtk3dep: %
|
||||||
|
-@$(MKDIR) -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
||||||
|
|
||||||
$(OBJ)/%.dep: %
|
$(OBJ)/%.dep: %
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
||||||
@ -179,6 +202,11 @@ $(OBJ)/Core/%.c.o: Core/%.c
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@
|
$(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@
|
||||||
|
|
||||||
|
# GTK3 requires special CFLAGS
|
||||||
|
$(OBJ)/gtk3/%.c.o: gtk3/%.c
|
||||||
|
-@$(MKDIR) -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
$(OBJ)/%.c.o: %.c
|
$(OBJ)/%.c.o: %.c
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
@ -261,7 +289,7 @@ ifeq ($(CONF), release)
|
|||||||
strip $@
|
strip $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Windows version builds two, one with a conole and one without it
|
# Windows version builds two, one with a console and one without it
|
||||||
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) -Wl,/subsystem:windows
|
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) -Wl,/subsystem:windows
|
||||||
@ -288,6 +316,15 @@ $(BIN)/SDL/SDL2.dll:
|
|||||||
@$(eval MATCH := $(shell where $$LIB:SDL2.dll))
|
@$(eval MATCH := $(shell where $$LIB:SDL2.dll))
|
||||||
cp "$(MATCH)" $@
|
cp "$(MATCH)" $@
|
||||||
|
|
||||||
|
# GTK3
|
||||||
|
|
||||||
|
$(BIN)/gtk3/sameboy: $(CORE_OBJECTS) $(GTK3_OBJECTS)
|
||||||
|
-@$(MKDIR) -p $(dir $@)
|
||||||
|
$(CC) $^ -o $@ $(LDFLAGS) $(GTK3_LDFLAGS)
|
||||||
|
ifeq ($(CONF), release)
|
||||||
|
strip $@
|
||||||
|
endif
|
||||||
|
|
||||||
# Tester
|
# Tester
|
||||||
|
|
||||||
$(BIN)/tester/sameboy_tester: $(CORE_OBJECTS) $(TESTER_OBJECTS)
|
$(BIN)/tester/sameboy_tester: $(CORE_OBJECTS) $(TESTER_OBJECTS)
|
||||||
@ -325,6 +362,15 @@ $(BIN)/SDL/Shaders: Shaders
|
|||||||
-@$(MKDIR) -p $@
|
-@$(MKDIR) -p $@
|
||||||
cp -rf Shaders/*.fsh $@
|
cp -rf Shaders/*.fsh $@
|
||||||
|
|
||||||
|
# Fugly resource hack
|
||||||
|
gtk3/resources/bootroms/%.bin: $(BOOTROMS_DIR)/%.bin
|
||||||
|
-@$(MKDIR) -p gtk3/resources/gtk3/resources/bootroms/
|
||||||
|
cp -f $< gtk3/resources/$@
|
||||||
|
|
||||||
|
gtk3/resources/Shaders/%.fsh: Shaders/%.fsh
|
||||||
|
-@$(MKDIR) -p gtk3/resources/gtk3/resources/Shaders/
|
||||||
|
cp -rf $< gtk3/resources/$@
|
||||||
|
|
||||||
# Boot ROMs
|
# Boot ROMs
|
||||||
|
|
||||||
$(OBJ)/%.1bpp: %.png
|
$(OBJ)/%.1bpp: %.png
|
||||||
|
10
gtk3/macros.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef macros_h
|
||||||
|
#define macros_h
|
||||||
|
|
||||||
|
#define str(x) #x
|
||||||
|
#define xstr(x) str(x)
|
||||||
|
#define get_object(id) gtk_builder_get_object(builder, id)
|
||||||
|
#define builder_get(type, id) type(gtk_builder_get_object(builder, id))
|
||||||
|
#define itoa(val) g_strdup_printf("%i", val)
|
||||||
|
|
||||||
|
#endif /* macros_h */
|
1584
gtk3/main.c
Normal file
164
gtk3/main.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#ifndef main_h
|
||||||
|
#define main_h
|
||||||
|
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <Core/gb.h>
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "shader.h"
|
||||||
|
|
||||||
|
#define OPENAL_ERROR openal_error_quark()
|
||||||
|
|
||||||
|
GQuark openal_error_quark (void) {
|
||||||
|
return g_quark_from_static_string("openal-error-quark");
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *openal_error(const gchar *user_msg) {
|
||||||
|
ALCenum error = alGetError();
|
||||||
|
gchar *description;
|
||||||
|
gchar *msg;
|
||||||
|
|
||||||
|
switch (error) {
|
||||||
|
case AL_NO_ERROR:
|
||||||
|
return NULL;
|
||||||
|
case AL_INVALID_NAME:
|
||||||
|
description = "A bad name (ID) was passed to an OpenAL function";
|
||||||
|
break;
|
||||||
|
case AL_INVALID_ENUM:
|
||||||
|
description = "An invalid enum value was passed to an OpenAL function";
|
||||||
|
break;
|
||||||
|
case AL_INVALID_VALUE:
|
||||||
|
description = "An invalid value was passed to an OpenAL function";
|
||||||
|
break;
|
||||||
|
case AL_INVALID_OPERATION:
|
||||||
|
description = "The requested operation is not valid";
|
||||||
|
break;
|
||||||
|
case AL_OUT_OF_MEMORY:
|
||||||
|
description = "The requested operation resulted in OpenAL running out of memory";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_msg != NULL) {
|
||||||
|
msg = g_strdup_printf("%s: %s", user_msg, description);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_error_new_literal(OPENAL_ERROR, error, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct UserData {
|
||||||
|
bool fullscreen;
|
||||||
|
GFile *file;
|
||||||
|
gchar *boot_rom_path;
|
||||||
|
gchar *config_path;
|
||||||
|
GB_model_t model;
|
||||||
|
} UserData;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t w, h;
|
||||||
|
} Rect;
|
||||||
|
|
||||||
|
#define BUTTON_MASK_A 0x01
|
||||||
|
#define BUTTON_MASK_B 0x02
|
||||||
|
#define BUTTON_MASK_START 0x04
|
||||||
|
#define BUTTON_MASK_SELECT 0x08
|
||||||
|
#define BUTTON_MASK_UP 0x10
|
||||||
|
#define BUTTON_MASK_DOWN 0x20
|
||||||
|
#define BUTTON_MASK_LEFT 0x40
|
||||||
|
#define BUTTON_MASK_RIGHT 0x80
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
static gint handle_local_options(GApplication *app, GVariantDict *options, gpointer user_data_gptr);
|
||||||
|
|
||||||
|
// GtkGlArea crash workaround
|
||||||
|
void gl_check_realize(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
gboolean test_gl_support(void);
|
||||||
|
|
||||||
|
static void setup_audio();
|
||||||
|
|
||||||
|
static GMenuModel *get_menu_model(GApplication *app, const char *id);
|
||||||
|
static void create_fallback_canvas(void);
|
||||||
|
static void setup_menu(GApplication *app);
|
||||||
|
static void set_combo_box_row_separator_func(GtkContainer *container);
|
||||||
|
static gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
|
||||||
|
|
||||||
|
// Rendering support functions
|
||||||
|
static unsigned char number_of_buffers(void);
|
||||||
|
static uint32_t *get_pixels(void);
|
||||||
|
static uint32_t *get_current_buffer(void);
|
||||||
|
static uint32_t *get_previous_buffer(void);
|
||||||
|
static void flip(void);
|
||||||
|
|
||||||
|
// GApplication signals
|
||||||
|
static void startup(GApplication *app, gpointer user_data_gptr);
|
||||||
|
static void activate(GApplication *app, gpointer user_data_gptr);
|
||||||
|
static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr);
|
||||||
|
static void open(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr);
|
||||||
|
static void quit(GApplication *app);
|
||||||
|
static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data);
|
||||||
|
|
||||||
|
// App actions
|
||||||
|
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer app);
|
||||||
|
|
||||||
|
// Signal callback
|
||||||
|
static void on_quit(GtkWidget *w, gpointer app);
|
||||||
|
|
||||||
|
// Renderer area bindings
|
||||||
|
static void gl_init(GtkWidget *w);
|
||||||
|
static void gl_draw();
|
||||||
|
static void gl_finish();
|
||||||
|
static gboolean on_draw_fallback(GtkWidget *widget, cairo_t *cr, gpointer data);
|
||||||
|
static void resize();
|
||||||
|
|
||||||
|
// VRAM viewer bindings
|
||||||
|
static void on_vram_viewer_realize();
|
||||||
|
static void on_vram_viewer_unrealize();
|
||||||
|
static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data);
|
||||||
|
static gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t *cr, gpointer data);
|
||||||
|
static gboolean on_motion_vram_viewer_tileset(GtkWidget *widget, GdkEventMotion *event);
|
||||||
|
static gboolean on_motion_vram_viewer_tilemap(GtkWidget *widget, GdkEventMotion *event);
|
||||||
|
static void on_vram_tab_change(GtkWidget *widget, GParamSpec *pspec, GtkStackSwitcher *self);
|
||||||
|
|
||||||
|
// Option bindings
|
||||||
|
G_MODULE_EXPORT void on_boot_rom_location_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_cgb_model_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_color_correction_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_color_menubar_override_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_dmg_model_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_graphic_filter_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_highpass_filter_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_keep_aspect_ratio_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_rewind_duration_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
G_MODULE_EXPORT void on_use_integer_scaling_changed(GtkWidget *w, gpointer user_data_gptr);
|
||||||
|
|
||||||
|
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
static void render_texture(void *pixels, void *previous);
|
||||||
|
static void update_viewport(void);
|
||||||
|
static void update_window_geometry();
|
||||||
|
|
||||||
|
static void handle_events(GB_gameboy_t *gb);
|
||||||
|
static uint32_t convert_color(uint16_t color);
|
||||||
|
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
|
||||||
|
static void vblank(GB_gameboy_t *gb);
|
||||||
|
static gpointer run(gpointer user_data_gptr);
|
||||||
|
|
||||||
|
#endif /* main_h */
|
10
gtk3/resources/css/main.css
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.vram-viewer-button-bar {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vram-viewer-button-bar button {
|
||||||
|
padding: 2px 4px;
|
||||||
|
min-height: 11px;
|
||||||
|
min-width: 12px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
431
gtk3/resources/gtk/menus-common.ui
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2019 Lior Halphon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
Author: Maximilian Mader
|
||||||
|
|
||||||
|
-->
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
<!-- menubar is in common since on ubuntu would be picked from menus-traditional, and on osx from menus -->
|
||||||
|
<menu id="menubar">
|
||||||
|
<section>
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_SameBoy</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">sameboy-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">About SameBoy</attribute>
|
||||||
|
<attribute name="action">app.about</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Preferences</attribute>
|
||||||
|
<attribute name="action">app.preferences</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">sameboy-section-1</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Hide SameBoy</attribute>
|
||||||
|
<attribute name="action">app.hide_app</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Hide Others</attribute>
|
||||||
|
<attribute name="action">app.hide_others</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Show All</attribute>
|
||||||
|
<attribute name="action">app.show_all</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Quit SameBoy</attribute>
|
||||||
|
<attribute name="action">app.quit</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_File</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">file-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Open</attribute>
|
||||||
|
<attribute name="action">app.open</attribute>
|
||||||
|
</item>
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">Open _Recent</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">recent-files-section</attribute>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">file-section-1</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Close</attribute>
|
||||||
|
<attribute name="action">app.close</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_Edit</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">edit-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Undo</attribute>
|
||||||
|
<attribute name="action">app.undo</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Redo</attribute>
|
||||||
|
<attribute name="action">app.redo</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">edit-section-1</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Cut</attribute>
|
||||||
|
<attribute name="action">app.cut</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Copy</attribute>
|
||||||
|
<attribute name="action">app.copy</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Paste</attribute>
|
||||||
|
<attribute name="action">app.paste</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Delete</attribute>
|
||||||
|
<attribute name="action">app.delete</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Select All</attribute>
|
||||||
|
<attribute name="action">app.select_all</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">Find</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">find-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Find…</attribute>
|
||||||
|
<attribute name="action">app.find</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Find Next</attribute>
|
||||||
|
<attribute name="action">app.find_next</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Find Previous</attribute>
|
||||||
|
<attribute name="action">app.find_previous</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Use Selection for Find</attribute>
|
||||||
|
<attribute name="action">app.find_selection</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Jump to Selection</attribute>
|
||||||
|
<attribute name="action">app.jump_selection</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">E_mulation</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">emulation-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Reset</attribute>
|
||||||
|
<attribute name="action">app.reset</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Pause</attribute>
|
||||||
|
<attribute name="action">app.pause</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">emulation-section-1</attribute>
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">Save State</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 1</attribute>
|
||||||
|
<attribute name="save-slot">0</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 2</attribute>
|
||||||
|
<attribute name="save-slot">1</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 3</attribute>
|
||||||
|
<attribute name="save-slot">2</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 4</attribute>
|
||||||
|
<attribute name="save-slot">3</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 5</attribute>
|
||||||
|
<attribute name="save-slot">4</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 6</attribute>
|
||||||
|
<attribute name="save-slot">5</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 7</attribute>
|
||||||
|
<attribute name="save-slot">6</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 8</attribute>
|
||||||
|
<attribute name="save-slot">7</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 9</attribute>
|
||||||
|
<attribute name="save-slot">8</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 10</attribute>
|
||||||
|
<attribute name="save-slot">9</attribute>
|
||||||
|
<attribute name="action">app.save_state</attribute>
|
||||||
|
</item>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">Save State</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 1</attribute>
|
||||||
|
<attribute name="save-slot">0</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 2</attribute>
|
||||||
|
<attribute name="save-slot">1</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 3</attribute>
|
||||||
|
<attribute name="save-slot">2</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 4</attribute>
|
||||||
|
<attribute name="save-slot">3</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 5</attribute>
|
||||||
|
<attribute name="save-slot">4</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 6</attribute>
|
||||||
|
<attribute name="save-slot">5</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 7</attribute>
|
||||||
|
<attribute name="save-slot">6</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 8</attribute>
|
||||||
|
<attribute name="save-slot">7</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 9</attribute>
|
||||||
|
<attribute name="save-slot">8</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Slot 10</attribute>
|
||||||
|
<attribute name="save-slot">9</attribute>
|
||||||
|
<attribute name="action">app.load_state</attribute>
|
||||||
|
</item>
|
||||||
|
</submenu>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">emulation-section-2</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Game Boy</attribute>
|
||||||
|
<attribute name="action">app.change_model</attribute>
|
||||||
|
<attribute name="model">DMG</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Super Game Boy</attribute>
|
||||||
|
<attribute name="action">app.change_model</attribute>
|
||||||
|
<attribute name="model">SGB</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Game Boy Color</attribute>
|
||||||
|
<attribute name="action">app.change_model</attribute>
|
||||||
|
<attribute name="model">CGB</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Game Boy Advance</attribute>
|
||||||
|
<attribute name="action">app.change_model</attribute>
|
||||||
|
<attribute name="model">AGB</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">emulation-section-3</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Mute Sound</attribute>
|
||||||
|
<attribute name="action">app.toggle_mute</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">emulation-section-4</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Blend Frames</attribute>
|
||||||
|
<attribute name="action">app.toggle_blend_frames</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_Connectivity</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">connectivity-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">None</attribute>
|
||||||
|
<attribute name="action">app.change_serial_device</attribute>
|
||||||
|
<attribute name="serial-device">none</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Game Boy Printer</attribute>
|
||||||
|
<attribute name="action">app.change_serial_device</attribute>
|
||||||
|
<attribute name="serial-device">gb_printer</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_Developer</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">developer-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Developer Mode</attribute>
|
||||||
|
<attribute name="action">app.toggle_developer_mode</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">developer-section-1</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Show Console</attribute>
|
||||||
|
<attribute name="action">app.show_console</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Clear Console</attribute>
|
||||||
|
<attribute name="action">app.clear_console</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">developer-section-2</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Break Debugger</attribute>
|
||||||
|
<attribute name="action">app.break_debugger</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">developer-section-3</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Show Memory Viewer</attribute>
|
||||||
|
<attribute name="action">app.open_memory_viewer</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Show VRAM Viewer</attribute>
|
||||||
|
<attribute name="action">app.open_vram_viewer</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">developer-section-4</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Show GTK Debugger</attribute>
|
||||||
|
<attribute name="action">app.open_gtk_debugger</attribute>
|
||||||
|
<attribute name="hidden-when">action-disabled</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_Window</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">window-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Minimize</attribute>
|
||||||
|
<attribute name="action">app.minimize</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Zoom</attribute>
|
||||||
|
<attribute name="action">app.zoom</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<attribute name="id">window-section-1</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Bring All to Front</attribute>
|
||||||
|
<attribute name="action">app.bring_to_front</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
|
||||||
|
<submenu>
|
||||||
|
<attribute name="label" translatable="yes">_Help</attribute>
|
||||||
|
<section>
|
||||||
|
<attribute name="id">help-section-0</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">SameBoy Help</attribute>
|
||||||
|
<attribute name="action">app.help</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</submenu>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
|
</interface>
|
30
gtk3/resources/gtk/menus-traditional.ui
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2019 Lior Halphon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
Author: Maximilian Mader
|
||||||
|
|
||||||
|
-->
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
</interface>
|
30
gtk3/resources/gtk/menus.ui
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2019 Lior Halphon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
Author: Maximilian Mader
|
||||||
|
|
||||||
|
-->
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
</interface>
|
BIN
gtk3/resources/pixmaps/CPU.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
gtk3/resources/pixmaps/Display.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
gtk3/resources/pixmaps/Joypad.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
gtk3/resources/pixmaps/Speaker.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
gtk3/resources/pixmaps/logo/logo_128.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
gtk3/resources/pixmaps/logo/logo_16.png
Normal file
After Width: | Height: | Size: 974 B |
BIN
gtk3/resources/pixmaps/logo/logo_256.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
gtk3/resources/pixmaps/logo/logo_32.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
gtk3/resources/pixmaps/logo/logo_48.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
gtk3/resources/pixmaps/logo/logo_64.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
1600
gtk3/resources/ui/window.ui
Normal file
48
gtk3/sameboy.gresource.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/io/github/sameboy">
|
||||||
|
<file preprocess="xml-stripblanks" compressed="true">ui/window.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">gtk/menus.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">gtk/menus-common.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">gtk/menus-traditional.ui</file>
|
||||||
|
|
||||||
|
<file compressed="true">css/main.css</file>
|
||||||
|
|
||||||
|
<file alias="logo_256.png">pixmaps/logo/logo_256.png</file>
|
||||||
|
<file alias="logo_128.png">pixmaps/logo/logo_128.png</file>
|
||||||
|
<file alias="logo_64.png">pixmaps/logo/logo_64.png</file>
|
||||||
|
<file alias="logo_48.png">pixmaps/logo/logo_48.png</file>
|
||||||
|
<file alias="logo_32.png">pixmaps/logo/logo_32.png</file>
|
||||||
|
<file alias="logo_16.png">pixmaps/logo/logo_16.png</file>
|
||||||
|
|
||||||
|
<!-- Boot ROMs, generated at compile time -->
|
||||||
|
<!-- The file path is a hack to get automatic dependency generation working-->
|
||||||
|
<file compressed="true" alias="bootroms/dmg_boot.bin">gtk3/resources/bootroms/dmg_boot.bin</file>
|
||||||
|
<file compressed="true" alias="bootroms/cgb_boot.bin">gtk3/resources/bootroms/cgb_boot.bin</file>
|
||||||
|
<file compressed="true" alias="bootroms/agb_boot.bin">gtk3/resources/bootroms/agb_boot.bin</file>
|
||||||
|
<file compressed="true" alias="bootroms/sgb_boot.bin">gtk3/resources/bootroms/sgb_boot.bin</file>
|
||||||
|
<file compressed="true" alias="bootroms/sgb2_boot.bin">gtk3/resources/bootroms/sgb2_boot.bin</file>
|
||||||
|
|
||||||
|
<!-- Shaders -->
|
||||||
|
<!-- The file path is a hack to get automatic dependency generation working-->
|
||||||
|
<file compressed="true" alias="Shaders/AAOmniScaleLegacy.fsh">gtk3/resources/Shaders/AAOmniScaleLegacy.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/AAScale2x.fsh">gtk3/resources/Shaders/AAScale2x.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/AAScale4x.fsh">gtk3/resources/Shaders/AAScale4x.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/Bilinear.fsh">gtk3/resources/Shaders/Bilinear.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/CRT.fsh">gtk3/resources/Shaders/CRT.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/HQ2x.fsh">gtk3/resources/Shaders/HQ2x.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/LCD.fsh">gtk3/resources/Shaders/LCD.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/MasterShader.fsh">gtk3/resources/Shaders/MasterShader.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/NearestNeighbor.fsh">gtk3/resources/Shaders/NearestNeighbor.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/OmniScale.fsh">gtk3/resources/Shaders/OmniScale.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/OmniScaleLegacy.fsh">gtk3/resources/Shaders/OmniScaleLegacy.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/Scale2x.fsh">gtk3/resources/Shaders/Scale2x.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/Scale4x.fsh">gtk3/resources/Shaders/Scale4x.fsh</file>
|
||||||
|
<file compressed="true" alias="Shaders/SmoothBilinear.fsh">gtk3/resources/Shaders/SmoothBilinear.fsh</file>
|
||||||
|
|
||||||
|
<file>pixmaps/CPU.png</file>
|
||||||
|
<file>pixmaps/Display.png</file>
|
||||||
|
<file>pixmaps/Joypad.png</file>
|
||||||
|
<file>pixmaps/Speaker.png</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
301
gtk3/settings.c
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
static void print_config_error(GError *error) {
|
||||||
|
if (error == NULL) return;
|
||||||
|
|
||||||
|
if (!g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) && !g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
|
||||||
|
g_printerr("Config error: %s\n", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_config(config_t *config) {
|
||||||
|
#define EXPAND_GROUP(group_name, members) \
|
||||||
|
g_print("[%s]\n", #group_name); \
|
||||||
|
members
|
||||||
|
|
||||||
|
#define EXPAND_GROUP_MEMBER(member, key_type, default_value) \
|
||||||
|
g_print("%s="FORMAT_FOR_KEY_TYPE(key_type)"\n", #member, config->member);
|
||||||
|
|
||||||
|
EXPAND_CONFIG
|
||||||
|
|
||||||
|
#undef EXPAND_GROUP
|
||||||
|
#undef EXPAND_GROUP_MEMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_config_from_key_file(config_t *config, GKeyFile *key_file) {
|
||||||
|
g_print("Loading config from key file\n");
|
||||||
|
GError *error = NULL;
|
||||||
|
gchar *group_name;
|
||||||
|
|
||||||
|
#define EXPAND_GROUP(name, members) \
|
||||||
|
group_name = #name; \
|
||||||
|
members
|
||||||
|
|
||||||
|
#define EXPAND_GROUP_MEMBER(member, key_type, default_value) \
|
||||||
|
config->member = g_key_file_get_##key_type(key_file, group_name, #member, &error); \
|
||||||
|
if (error != NULL) { \
|
||||||
|
config->member = default_value; \
|
||||||
|
print_config_error(error); \
|
||||||
|
g_clear_error(&error); \
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPAND_CONFIG
|
||||||
|
|
||||||
|
if (config->rewind_duration > 600) {
|
||||||
|
g_warning("Setting Emulation.rewind_duration too high might affect performance.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef EXPAND_GROUP
|
||||||
|
#undef EXPAND_GROUP_MEMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_config_to_key_file(config_t *config, GKeyFile *key_file) {
|
||||||
|
g_print("Saving config to key file\n");
|
||||||
|
GError *error = NULL;
|
||||||
|
gchar *group_name;
|
||||||
|
|
||||||
|
#define EXPAND_GROUP(name, members) \
|
||||||
|
group_name = #name; \
|
||||||
|
members
|
||||||
|
|
||||||
|
#define EXPAND_GROUP_MEMBER_IF_0(member, key_type, default_value) \
|
||||||
|
g_key_file_set_##key_type(key_file, group_name, #member, config->member);
|
||||||
|
|
||||||
|
#define EXPAND_GROUP_MEMBER_IF_1(member, key_type, default_value) \
|
||||||
|
if (config->member != NULL) { \
|
||||||
|
g_key_file_set_##key_type(key_file, group_name, #member, config->member); \
|
||||||
|
} \
|
||||||
|
else if (g_key_file_has_key(key_file, group_name, #member, &error)) { \
|
||||||
|
if (error != NULL) { \
|
||||||
|
g_printerr("%s\n", error->message); \
|
||||||
|
g_clear_error(&error); \
|
||||||
|
} \
|
||||||
|
g_key_file_remove_key(key_file, group_name, #member, &error); \
|
||||||
|
if (error != NULL) { \
|
||||||
|
g_printerr("%s\n", error->message); \
|
||||||
|
g_clear_error(&error); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXPAND_GROUP_MEMBER_IF_EVAL(y, member, key_type, default_value) EXPAND_GROUP_MEMBER_IF_ ## y(member, key_type, default_value)
|
||||||
|
#define EXPAND_GROUP_MEMBER_IF(member, key_type, is_pointer, default_value) EXPAND_GROUP_MEMBER_IF_EVAL(is_pointer, member, key_type, default_value)
|
||||||
|
#define EXPAND_GROUP_MEMBER(member, key_type, default_value) EXPAND_GROUP_MEMBER_IF(member, key_type, GTYPE_IS_POINTER(key_type), default_value)
|
||||||
|
|
||||||
|
EXPAND_CONFIG
|
||||||
|
|
||||||
|
#undef EXPAND_GROUP
|
||||||
|
#undef EXPAND_GROUP_MEMBER
|
||||||
|
#undef EXPAND_GROUP_MEMBER_IF
|
||||||
|
#undef EXPAND_GROUP_MEMBER_IF_EVAL
|
||||||
|
#undef EXPAND_GROUP_MEMBER_IF_0
|
||||||
|
#undef EXPAND_GROUP_MEMBER_IF_1
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_preferences_realize(GtkWidget *w, gpointer builder_ptr) {
|
||||||
|
GtkWindow *preferences = GTK_WINDOW(w);
|
||||||
|
GtkBuilder *builder = (GtkBuilder *) builder_ptr;
|
||||||
|
|
||||||
|
update_boot_rom_selector(builder);
|
||||||
|
|
||||||
|
// Hook up the static preferences
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "rewind_duration_selector"), itoa(config.rewind_duration));
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "dmg_revision_selector"), config.dmg_revision_name);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "sgb_revision_selector"), config.sgb_revision_name);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "cgb_revision_selector"), config.cgb_revision_name);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "shader_selector"), config.shader);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "color_correction_selector"), config.color_correction_id);
|
||||||
|
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "integer_scaling_toggle"), config.use_integer_scaling);
|
||||||
|
gtk_toggle_button_set_active(builder_get(GTK_TOGGLE_BUTTON, "aspect_ratio_toggle"), config.keep_aspect_ratio);
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "highpass_filter_selector"), config.high_pass_filter_id);
|
||||||
|
|
||||||
|
#if ! NDEBUG
|
||||||
|
gtk_combo_box_set_active_id(builder_get(GTK_COMBO_BOX, "menubar_override_selector"), config.menubar_override);
|
||||||
|
#else
|
||||||
|
if (builder_get(GTK_COMBO_BOX, "menubar_override_selector") != NULL) {
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(builder_get(GTK_COMBO_BOX, "menubar_override_selector")));
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(builder_get(GTK_COMBO_BOX, "menubar_override_selector_label")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_settings(gchar *path, GtkWindow *preferences) {
|
||||||
|
free_settings();
|
||||||
|
key_file = g_key_file_new();
|
||||||
|
|
||||||
|
if (path != NULL) {
|
||||||
|
settings_file_path = path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
settings_file_path = g_build_filename(g_get_user_config_dir(), SETTINGS_FILE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
load_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_settings(void) {
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
g_print("Trying to load settings from %s\n", settings_file_path);
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file(key_file, settings_file_path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
|
||||||
|
if (error->domain == G_FILE_ERROR) {
|
||||||
|
g_warning("Unable to load %s: %s", settings_file_path, error->message);
|
||||||
|
}
|
||||||
|
else if (error->domain == G_KEY_FILE_ERROR) {
|
||||||
|
g_warning("Failed to parse %s: %s", settings_file_path, error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
load_config_from_key_file(&config, key_file);
|
||||||
|
print_config(&config);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_settings(void) {
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
g_print("Trying to save settings to %s\n", settings_file_path);
|
||||||
|
|
||||||
|
save_config_to_key_file(&config, key_file);
|
||||||
|
|
||||||
|
if (!g_key_file_save_to_file(key_file, settings_file_path, &error)) {
|
||||||
|
g_warning ("Failed to save %s: %s", settings_file_path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_settings(void) {
|
||||||
|
if (key_file != NULL) {
|
||||||
|
g_key_file_free(key_file);
|
||||||
|
key_file = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_boot_rom_selector(GtkBuilder *builder) {
|
||||||
|
GtkComboBoxText *combo_box = builder_get(GTK_COMBO_BOX_TEXT, "boot_rom_selector");
|
||||||
|
gtk_combo_box_text_remove_all(combo_box);
|
||||||
|
gtk_combo_box_text_append(combo_box, "auto", "Use Built-in Boot ROMs");
|
||||||
|
if (config.boot_rom_path != NULL && !g_str_equal(config.boot_rom_path, "auto") && !g_str_equal(config.boot_rom_path, "other")) {
|
||||||
|
gtk_combo_box_text_append(combo_box, config.boot_rom_path, config.boot_rom_path);
|
||||||
|
gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), config.boot_rom_path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), "auto");
|
||||||
|
}
|
||||||
|
gtk_combo_box_text_append_text(combo_box, "<separator>");
|
||||||
|
gtk_combo_box_text_append(combo_box, "other", "Other");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_in_shell") == 0) {
|
||||||
|
return MENUBAR_SHOW_IN_SHELL;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
g_warning("Unknown menubar setting: %s\nFalling back to “Auto”\n", config.menubar_override);
|
||||||
|
default_value: return MENUBAR_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_show_menubar(enum menubar_type_t value) {
|
||||||
|
switch (value) {
|
||||||
|
case MENUBAR_AUTO:
|
||||||
|
config.menubar_override = "auto";
|
||||||
|
break;
|
||||||
|
case MENUBAR_SHOW_IN_SHELL:
|
||||||
|
config.menubar_override = "show_in_shell";
|
||||||
|
break;
|
||||||
|
case MENUBAR_SHOW_IN_WINDOW:
|
||||||
|
config.menubar_override = "show_in_window";
|
||||||
|
break;
|
||||||
|
case MENUBAR_SHOW_HAMBURGER:
|
||||||
|
config.menubar_override = "show_hamburger";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_color_correction_mode_t get_color_correction_mode(void) {
|
||||||
|
if (config.color_correction_id == NULL) goto default_value;
|
||||||
|
|
||||||
|
if (g_strcmp0(config.color_correction_id, "disabled") == 0) {
|
||||||
|
return GB_COLOR_CORRECTION_DISABLED;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.color_correction_id, "correct_color_curves") == 0) {
|
||||||
|
return GB_COLOR_CORRECTION_CORRECT_CURVES;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.color_correction_id, "emulate_hardware") == 0) {
|
||||||
|
return GB_COLOR_CORRECTION_EMULATE_HARDWARE;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.color_correction_id, "preserve_brightness") == 0) {
|
||||||
|
return GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should not happen
|
||||||
|
g_warning("Unknown color correction mode: %s\nFalling back to “Emulate Hardware”\n", config.color_correction_id);
|
||||||
|
default_value: return GB_COLOR_CORRECTION_EMULATE_HARDWARE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_color_correction_mode(GB_color_correction_mode_t mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case GB_COLOR_CORRECTION_DISABLED:
|
||||||
|
config.color_correction_id = "disabled";
|
||||||
|
break;
|
||||||
|
case GB_COLOR_CORRECTION_CORRECT_CURVES:
|
||||||
|
config.color_correction_id = "correct_color_curves";
|
||||||
|
break;
|
||||||
|
case GB_COLOR_CORRECTION_EMULATE_HARDWARE:
|
||||||
|
config.color_correction_id = "emulate_hardware";
|
||||||
|
break;
|
||||||
|
case GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS:
|
||||||
|
config.color_correction_id = "preserve_brightness";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_highpass_mode_t get_highpass_mode(void) {
|
||||||
|
if (config.high_pass_filter_id == NULL) goto default_value;
|
||||||
|
|
||||||
|
if (g_strcmp0(config.high_pass_filter_id, "disabled") == 0) {
|
||||||
|
return GB_HIGHPASS_OFF;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.high_pass_filter_id, "emulate_hardware") == 0) {
|
||||||
|
return GB_HIGHPASS_ACCURATE;
|
||||||
|
}
|
||||||
|
else if (g_strcmp0(config.high_pass_filter_id, "preserve_waveform") == 0) {
|
||||||
|
return GB_HIGHPASS_REMOVE_DC_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should not happen
|
||||||
|
g_warning("Unknown highpass mode: %s\nFalling back to “Accurate”\n", config.high_pass_filter_id);
|
||||||
|
default_value: return GB_HIGHPASS_ACCURATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_highpass_mode(GB_highpass_mode_t mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case GB_HIGHPASS_OFF:
|
||||||
|
config.high_pass_filter_id = "disabled";
|
||||||
|
break;
|
||||||
|
case GB_HIGHPASS_MAX:
|
||||||
|
g_warning("GB_HIGHPASS_MAX is not a valid highpass mode, falling back to “Accurate”.\n");
|
||||||
|
case GB_HIGHPASS_ACCURATE:
|
||||||
|
config.high_pass_filter_id = "emulate_hardware";
|
||||||
|
break;
|
||||||
|
case GB_HIGHPASS_REMOVE_DC_OFFSET:
|
||||||
|
config.high_pass_filter_id = "preserve_waveform";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
93
gtk3/settings.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#ifndef settings_h
|
||||||
|
#define settings_h
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <Core/gb.h>
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#define SETTINGS_FILE "sameboy-gtk3-settings.ini"
|
||||||
|
|
||||||
|
#define FORMAT_FOR_KEY_TYPE(type) FORMAT_FOR_##type
|
||||||
|
#define FORMAT_FOR_string "%s"
|
||||||
|
#define FORMAT_FOR_integer "%d"
|
||||||
|
#define FORMAT_FOR_boolean "%d"
|
||||||
|
|
||||||
|
#define GTYPE_OF_KEY_TYPE(type) GTYPE_OF_##type
|
||||||
|
#define GTYPE_OF_string gchar *
|
||||||
|
#define GTYPE_OF_integer gint
|
||||||
|
#define GTYPE_OF_boolean gboolean
|
||||||
|
|
||||||
|
#define GTYPE_IS_POINTER(type) GTYPE_IS_POINTER_##type
|
||||||
|
#define GTYPE_IS_POINTER_string 1
|
||||||
|
#define GTYPE_IS_POINTER_integer 0
|
||||||
|
#define GTYPE_IS_POINTER_boolean 0
|
||||||
|
|
||||||
|
// Note: Make sure to use a member name only once for the whole config struct
|
||||||
|
//
|
||||||
|
// Macro usage:
|
||||||
|
// EXPAND_CONFIG(EXPAND_GROUP(…) EXPAND_GROUP(…) …)
|
||||||
|
// EXPAND_GROUP(group_name, group_members)
|
||||||
|
// EXPAND_GROUP_MEMBER(member_name, key_type, default_value)
|
||||||
|
#define EXPAND_CONFIG \
|
||||||
|
EXPAND_GROUP(Emulation, \
|
||||||
|
EXPAND_GROUP_MEMBER(boot_rom_path, string, "auto") /* overrides search location for boot ROMs by name */ \
|
||||||
|
EXPAND_GROUP_MEMBER(rewind_duration, integer, 10) \
|
||||||
|
EXPAND_GROUP_MEMBER(dmg_revision_name, string, "DMG_CPU_C") \
|
||||||
|
EXPAND_GROUP_MEMBER(sgb_revision_name, string, "SGB2") \
|
||||||
|
EXPAND_GROUP_MEMBER(cgb_revision_name, string, "CPU_CGB_E") \
|
||||||
|
) \
|
||||||
|
EXPAND_GROUP(Video, \
|
||||||
|
EXPAND_GROUP_MEMBER(shader, string, "NearestNeighbor") \
|
||||||
|
EXPAND_GROUP_MEMBER(color_correction_id, string, "emulate_hardware") \
|
||||||
|
EXPAND_GROUP_MEMBER(keep_aspect_ratio, boolean, true) \
|
||||||
|
EXPAND_GROUP_MEMBER(use_integer_scaling, boolean, true) \
|
||||||
|
EXPAND_GROUP_MEMBER(menubar_override, string, "auto") \
|
||||||
|
) \
|
||||||
|
EXPAND_GROUP(Audio, \
|
||||||
|
EXPAND_GROUP_MEMBER(high_pass_filter_id, string, "emulate_hardware") \
|
||||||
|
) \
|
||||||
|
EXPAND_GROUP(Controls, \
|
||||||
|
\
|
||||||
|
)
|
||||||
|
|
||||||
|
typedef struct config_t {
|
||||||
|
#define EXPAND_GROUP(group_name, members) members
|
||||||
|
#define EXPAND_GROUP_MEMBER(member, key_type, default_value) GTYPE_OF_KEY_TYPE(key_type) member;
|
||||||
|
EXPAND_CONFIG
|
||||||
|
#undef EXPAND_GROUP
|
||||||
|
#undef EXPAND_GROUP_MEMBER
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
enum menubar_type_t {
|
||||||
|
MENUBAR_AUTO,
|
||||||
|
MENUBAR_SHOW_IN_SHELL,
|
||||||
|
MENUBAR_SHOW_IN_WINDOW,
|
||||||
|
MENUBAR_SHOW_HAMBURGER
|
||||||
|
};
|
||||||
|
|
||||||
|
gchar* settings_file_path;
|
||||||
|
GKeyFile *key_file;
|
||||||
|
config_t config;
|
||||||
|
|
||||||
|
void on_preferences_realize(GtkWidget *w, gpointer builder_ptr);
|
||||||
|
|
||||||
|
void print_config(config_t *config);
|
||||||
|
void load_config_from_key_file(config_t *config, GKeyFile *key_file);
|
||||||
|
|
||||||
|
void init_settings(gchar *path, GtkWindow *preferences);
|
||||||
|
int load_settings(void);
|
||||||
|
void save_settings(void);
|
||||||
|
void free_settings(void);
|
||||||
|
|
||||||
|
void update_boot_rom_selector(GtkBuilder *builder);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
GB_highpass_mode_t get_highpass_mode(void);
|
||||||
|
void set_highpass_mode(GB_highpass_mode_t);
|
||||||
|
|
||||||
|
#endif /* settings_h */
|
205
gtk3/shader.c
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include "shader.h"
|
||||||
|
|
||||||
|
static const char *vertex_shader = "\n\
|
||||||
|
#version 150 \n\
|
||||||
|
in vec4 aPosition;\n\
|
||||||
|
void main(void) {\n\
|
||||||
|
gl_Position = aPosition;\n\
|
||||||
|
}\n\
|
||||||
|
";
|
||||||
|
|
||||||
|
static GLuint create_shader(const char *source, GLenum type)
|
||||||
|
{
|
||||||
|
// Create the shader object
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
// Load the shader source
|
||||||
|
glShaderSource(shader, 1, &source, 0);
|
||||||
|
// Compile the shader
|
||||||
|
glCompileShader(shader);
|
||||||
|
// Check for errors
|
||||||
|
GLint status = 0;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status == GL_FALSE) {
|
||||||
|
GLchar messages[1024];
|
||||||
|
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
|
||||||
|
g_printerr("GLSL Shader Error: %s", messages);
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint create_program(const char *vsh, const char *fsh)
|
||||||
|
{
|
||||||
|
// Build shaders
|
||||||
|
GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER);
|
||||||
|
GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER);
|
||||||
|
|
||||||
|
// Create program
|
||||||
|
GLuint program = glCreateProgram();
|
||||||
|
|
||||||
|
// Attach shaders
|
||||||
|
glAttachShader(program, vertex_shader);
|
||||||
|
glAttachShader(program, fragment_shader);
|
||||||
|
|
||||||
|
// Link program
|
||||||
|
glLinkProgram(program);
|
||||||
|
// Check for errors
|
||||||
|
GLint status;
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||||
|
|
||||||
|
if (status == GL_FALSE) {
|
||||||
|
GLchar messages[1024];
|
||||||
|
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
|
||||||
|
g_printerr("GLSL Program Error: %s", messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete shaders
|
||||||
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(fragment_shader);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init_shader_with_name(shader_t *shader, const char *name)
|
||||||
|
{
|
||||||
|
if (epoxy_gl_version() < 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *error = NULL;
|
||||||
|
GBytes *master_shader_f = NULL;
|
||||||
|
static const gchar *master_shader_code;
|
||||||
|
static gsize master_shader_code_size;
|
||||||
|
static char final_shader_code[0x10801] = {0,};
|
||||||
|
static signed long filter_token_location = 0;
|
||||||
|
|
||||||
|
if (!master_shader_code_size) {
|
||||||
|
master_shader_f = g_resources_lookup_data(RESOURCE_PREFIX "Shaders/MasterShader.fsh", G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
||||||
|
master_shader_code = g_bytes_get_data(master_shader_f, &master_shader_code_size);
|
||||||
|
|
||||||
|
if (!master_shader_f) {
|
||||||
|
g_printerr("Failed to load master shader: %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
||||||
|
|
||||||
|
if (filter_token_location < 0) {
|
||||||
|
g_error_free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char shader_path[1024];
|
||||||
|
g_snprintf(shader_path, sizeof(shader_path), RESOURCE_PREFIX "Shaders/%s.fsh", name);
|
||||||
|
|
||||||
|
GBytes *shader_f = g_resources_lookup_data(shader_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
||||||
|
if (!shader_f) {
|
||||||
|
g_printerr("Failed to load shader \"%s\": %s", shader_path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsize shader_code_size;
|
||||||
|
const gchar *shader_code = g_bytes_get_data(shader_f, &shader_code_size);
|
||||||
|
|
||||||
|
memset(final_shader_code, 0, sizeof(final_shader_code));
|
||||||
|
memcpy(final_shader_code, master_shader_code, filter_token_location);
|
||||||
|
strcpy(final_shader_code + filter_token_location, shader_code);
|
||||||
|
strcat(final_shader_code + filter_token_location,
|
||||||
|
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
|
||||||
|
|
||||||
|
g_bytes_unref(shader_f);
|
||||||
|
|
||||||
|
shader->program = create_program(vertex_shader, final_shader_code);
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
shader->position_attribute = glGetAttribLocation(shader->program, "aPosition");
|
||||||
|
// Uniforms
|
||||||
|
shader->resolution_uniform = glGetUniformLocation(shader->program, "output_resolution");
|
||||||
|
shader->origin_uniform = glGetUniformLocation(shader->program, "origin");
|
||||||
|
|
||||||
|
glGenTextures(1, &shader->texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
shader->texture_uniform = glGetUniformLocation(shader->program, "image");
|
||||||
|
|
||||||
|
glGenTextures(1, &shader->previous_texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image");
|
||||||
|
|
||||||
|
shader->mix_previous_uniform = glGetUniformLocation(shader->program, "mix_previous");
|
||||||
|
|
||||||
|
// Program
|
||||||
|
|
||||||
|
glUseProgram(shader->program);
|
||||||
|
|
||||||
|
GLuint vao;
|
||||||
|
glGenVertexArrays(1, &vao);
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
|
||||||
|
GLuint vbo;
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
|
||||||
|
static GLfloat const quad[16] = {
|
||||||
|
-1.f, -1.f, 0, 1,
|
||||||
|
-1.f, +1.f, 0, 1,
|
||||||
|
+1.f, -1.f, 0, 1,
|
||||||
|
+1.f, +1.f, 0, 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(shader->position_attribute);
|
||||||
|
glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
||||||
|
unsigned source_width, unsigned source_height,
|
||||||
|
unsigned x, unsigned y, unsigned w, unsigned h)
|
||||||
|
{
|
||||||
|
glUseProgram(shader->program);
|
||||||
|
glUniform2f(shader->origin_uniform, x, y);
|
||||||
|
glUniform2f(shader->resolution_uniform, w, h);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_width, source_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, bitmap);
|
||||||
|
glUniform1i(shader->texture_uniform, 0);
|
||||||
|
glUniform1i(shader->mix_previous_uniform, previous != NULL);
|
||||||
|
if (previous) {
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_width, source_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, previous);
|
||||||
|
glUniform1i(shader->previous_texture_uniform, 1);
|
||||||
|
}
|
||||||
|
glBindFragDataLocation(shader->program, 0, "frag_color");
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_shader(shader_t *shader)
|
||||||
|
{
|
||||||
|
GLint major = 0, minor = 0;
|
||||||
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||||
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||||
|
|
||||||
|
if (major * 0x100 + minor < 0x302) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteProgram(shader->program);
|
||||||
|
glDeleteTextures(1, &shader->texture);
|
||||||
|
glDeleteTextures(1, &shader->previous_texture);
|
||||||
|
}
|
30
gtk3/shader.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef shader_h
|
||||||
|
#define shader_h
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
typedef struct shader_s {
|
||||||
|
GLuint resolution_uniform;
|
||||||
|
GLuint origin_uniform;
|
||||||
|
GLuint texture_uniform;
|
||||||
|
GLuint previous_texture_uniform;
|
||||||
|
GLuint mix_previous_uniform;
|
||||||
|
|
||||||
|
GLuint position_attribute;
|
||||||
|
GLuint texture;
|
||||||
|
GLuint previous_texture;
|
||||||
|
GLuint program;
|
||||||
|
|
||||||
|
bool compat_mode;
|
||||||
|
} shader_t;
|
||||||
|
|
||||||
|
bool init_shader_with_name(shader_t *shader, const char *name);
|
||||||
|
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
||||||
|
unsigned source_width, unsigned source_height,
|
||||||
|
unsigned x, unsigned y, unsigned w, unsigned h);
|
||||||
|
void free_shader(struct shader_s *shader);
|
||||||
|
|
||||||
|
#endif /* shader_h */
|