[GTK3] Convert VRAM viewer into proper GTK widget
This commit is contained in:
parent
accaedbdac
commit
825786210a
536
gtk3/main.c
536
gtk3/main.c
@ -14,6 +14,8 @@
|
||||
#include "shader.h"
|
||||
#include "check_menu_radio_group.h"
|
||||
|
||||
#include "vram_viewer.h"
|
||||
|
||||
// used for audio and game controllers
|
||||
#include "SDL.h"
|
||||
#include "../SDL/audio/audio.h"
|
||||
@ -53,11 +55,6 @@ static GuiData gui_data = {
|
||||
.log_to_sidebar = false,
|
||||
.should_clear_sidebar = false,
|
||||
|
||||
.vram_viewer_visible = false,
|
||||
.vram_viewer_updating = false,
|
||||
.vram_viewer_active_tab = "",
|
||||
.vram_viewer_is_cgb = false,
|
||||
|
||||
.audio_initialized = false,
|
||||
.border_mode_changed = false,
|
||||
|
||||
@ -118,34 +115,6 @@ static const GActionEntry app_entries[] = {
|
||||
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
|
||||
};
|
||||
|
||||
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);
|
||||
const uint8_t column_index = 2 + (2 * color_index);
|
||||
|
||||
GValue color_val = G_VALUE_INIT;
|
||||
gtk_tree_model_get_value(model, iter, column_index, &color_val);
|
||||
gint color = g_value_get_int(&color_val);
|
||||
gchar *color_string = g_strdup_printf("#%06x", color);
|
||||
|
||||
gint lightness = 0.299 * ((color >> 16) & 0xFF) + 0.587 * ((color >> 8) & 0xFF) + 0.114 * (color & 0xFF);
|
||||
|
||||
GValue color_str = G_VALUE_INIT;
|
||||
g_value_init(&color_str, G_TYPE_STRING);
|
||||
g_value_set_string(&color_str, color_string);
|
||||
g_object_set_property(G_OBJECT(renderer), "background", &color_str);
|
||||
|
||||
GValue fg_color_str = G_VALUE_INIT;
|
||||
g_value_init(&fg_color_str, G_TYPE_STRING);
|
||||
g_value_set_static_string(&fg_color_str, (lightness > 0x7F)? "#000000" : "#FFFFFF");
|
||||
g_object_set_property(G_OBJECT(renderer), "foreground", &fg_color_str);
|
||||
|
||||
g_value_unset(&color_val);
|
||||
g_value_unset(&color_str);
|
||||
g_value_unset(&fg_color_str);
|
||||
g_free(color_string);
|
||||
}
|
||||
|
||||
static const char* get_sdl_joystick_power_level_name(SDL_JoystickPowerLevel level) {
|
||||
switch (level) {
|
||||
case SDL_JOYSTICK_POWER_EMPTY: return "Empty";
|
||||
@ -718,17 +687,20 @@ static void update_viewport(void) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned new_width = x_factor * GB_get_screen_width(&gb);
|
||||
unsigned new_height = y_factor * GB_get_screen_height(&gb);
|
||||
|
||||
gui_data.viewport = (Rect){
|
||||
(win_width - new_width) / 2,
|
||||
(win_height - new_height) / 2,
|
||||
new_width,
|
||||
new_height
|
||||
};
|
||||
if (gui_data.gl_area) {
|
||||
unsigned new_width = x_factor * GB_get_screen_width(&gb);
|
||||
unsigned new_height = y_factor * GB_get_screen_height(&gb);
|
||||
|
||||
gui_data.viewport = (Rect){
|
||||
(win_width - new_width) / 2,
|
||||
(win_height - new_height) / 2,
|
||||
new_width,
|
||||
new_height
|
||||
};
|
||||
|
||||
if (!gui_data.fallback_canvas) glViewport(gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.w, gui_data.viewport.h);
|
||||
glViewport(gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.width, gui_data.viewport.height);
|
||||
}
|
||||
}
|
||||
|
||||
// WHY DO WE NEED SUCH AN UGLY METHOD, GTK?!
|
||||
@ -789,8 +761,6 @@ static void update_window_geometry(void) {
|
||||
if (GB_is_inited(&gb)) {
|
||||
GB_set_pixels_output(&gb, get_pixels());
|
||||
}
|
||||
|
||||
update_viewport();
|
||||
}
|
||||
|
||||
static void stop(void) {
|
||||
@ -811,140 +781,7 @@ static void stop(void) {
|
||||
gui_data.stopped = true;
|
||||
}
|
||||
|
||||
static void on_vblank(gpointer data) {
|
||||
if (!gui_data.vram_viewer_updating && gui_data.vram_viewer_visible) {
|
||||
gui_data.vram_viewer_updating = true;
|
||||
|
||||
if (g_strcmp0("vram_viewer_sprites", gui_data.vram_viewer_active_tab) == 0) {
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *tree_view = builder_get(GTK_TREE_VIEW, "vram_viewer_sprites");
|
||||
// gtk_tree_view_set_model(tree_view, NULL); // Do we need this?
|
||||
|
||||
GtkListStore *store = gtk_list_store_new(7,
|
||||
GDK_TYPE_PIXBUF, // Preview image
|
||||
G_TYPE_STRING, // X position
|
||||
G_TYPE_STRING, // Y position
|
||||
G_TYPE_STRING, // Tile
|
||||
G_TYPE_STRING, // Tile Address
|
||||
G_TYPE_STRING, // OAM Address
|
||||
G_TYPE_STRING // Attributes
|
||||
);
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
|
||||
|
||||
for (unsigned row = 0; row < gui_data.oam_count; ++row) {
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_bytes(
|
||||
g_bytes_new(gui_data.oam_info[row].image, 128 * sizeof(uint32_t)),
|
||||
GDK_COLORSPACE_RGB, true, 8, 8, gui_data.oam_height, 8 * sizeof(uint32_t)
|
||||
);
|
||||
|
||||
GdkPixbuf *dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, 8 * 2, gui_data.oam_height * 2);
|
||||
|
||||
gdk_pixbuf_scale(pixbuf, dest,
|
||||
0, 0, 8 * 2, gui_data.oam_height * 2,
|
||||
0, 0, 2.0, 2.0,
|
||||
GDK_INTERP_NEAREST
|
||||
);
|
||||
|
||||
gtk_list_store_insert_with_values(store, &iter, -1,
|
||||
0, dest,
|
||||
1, g_strdup_printf("%i", gui_data.oam_info[row].x - 8),
|
||||
2, g_strdup_printf("%i", gui_data.oam_info[row].y - 16),
|
||||
3, g_strdup_printf("$%02x", gui_data.oam_info[row].tile),
|
||||
4, g_strdup_printf("$%04x", 0x8000 + gui_data.oam_info[row].tile * 0x10),
|
||||
5, g_strdup_printf("$%04x", gui_data.oam_info[row].oam_addr),
|
||||
6, gui_data.vram_viewer_is_cgb
|
||||
? g_strdup_printf("%c%c%c%d%d",
|
||||
gui_data.oam_info[row].flags & 0x80? 'P' : '-',
|
||||
gui_data.oam_info[row].flags & 0x40? 'Y' : '-',
|
||||
gui_data.oam_info[row].flags & 0x20? 'X' : '-',
|
||||
gui_data.oam_info[row].flags & 0x08? 1 : 0,
|
||||
gui_data.oam_info[row].flags & 0x07)
|
||||
: g_strdup_printf("%c%c%c%d",
|
||||
gui_data.oam_info[row].flags & 0x80? 'P' : '-',
|
||||
gui_data.oam_info[row].flags & 0x40? 'Y' : '-',
|
||||
gui_data.oam_info[row].flags & 0x20? 'X' : '-',
|
||||
gui_data.oam_info[row].flags & 0x10? 1 : 0),
|
||||
-1
|
||||
);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
g_object_unref(dest);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(store));
|
||||
g_object_unref(store);
|
||||
}
|
||||
else if (g_strcmp0("vram_viewer_palettes", gui_data.vram_viewer_active_tab) == 0) {
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *tree_view = builder_get(GTK_TREE_VIEW, "vram_viewer_palettes");
|
||||
// gtk_tree_view_set_model(tree_view, NULL); // Do we need this?
|
||||
|
||||
GtkListStore *store = gtk_list_store_new(9,
|
||||
G_TYPE_STRING, // Name
|
||||
|
||||
G_TYPE_STRING, // Color 0 string
|
||||
G_TYPE_INT, // Color 0 integer
|
||||
|
||||
G_TYPE_STRING, // Color 1 string
|
||||
G_TYPE_INT, // Color 1 integer
|
||||
|
||||
G_TYPE_STRING, // Color 2 string
|
||||
G_TYPE_INT, // Color 2 integer
|
||||
|
||||
G_TYPE_STRING, // Color 3 string
|
||||
G_TYPE_INT // Color 3 integer
|
||||
);
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
|
||||
|
||||
for (unsigned row = 0; row < 16; ++row) {
|
||||
uint8_t offset = (row & 7) * 4;
|
||||
|
||||
uint16_t color_0 = (gui_data.vram_viewer_palette_data[row][((0 + offset) << 1) + 1] << 8) | gui_data.vram_viewer_palette_data[row][((0 + offset) << 1)];
|
||||
uint16_t color_1 = (gui_data.vram_viewer_palette_data[row][((1 + offset) << 1) + 1] << 8) | gui_data.vram_viewer_palette_data[row][((1 + offset) << 1)];
|
||||
uint16_t color_2 = (gui_data.vram_viewer_palette_data[row][((2 + offset) << 1) + 1] << 8) | gui_data.vram_viewer_palette_data[row][((2 + offset) << 1)];
|
||||
uint16_t color_3 = (gui_data.vram_viewer_palette_data[row][((3 + offset) << 1) + 1] << 8) | gui_data.vram_viewer_palette_data[row][((3 + offset) << 1)];
|
||||
|
||||
gtk_list_store_insert_with_values(store, &iter, -1,
|
||||
0, g_strdup_printf("%s %d", row >=8 ? "Object" : "Background", row & 7),
|
||||
1, g_strdup_printf("$%04x", color_0 & 0x7FFF),
|
||||
2, convert_color(color_0),
|
||||
3, g_strdup_printf("$%04x", color_1 & 0x7FFF),
|
||||
4, convert_color(color_1),
|
||||
5, g_strdup_printf("$%04x", color_2 & 0x7FFF),
|
||||
6, convert_color(color_2),
|
||||
7, g_strdup_printf("$%04x", color_3 & 0x7FFF),
|
||||
8, convert_color(color_3),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
GtkTreeViewColumn *column_0 = gtk_tree_view_get_column(tree_view, 1);
|
||||
GtkTreeViewColumn *column_1 = gtk_tree_view_get_column(tree_view, 2);
|
||||
GtkTreeViewColumn *column_2 = gtk_tree_view_get_column(tree_view, 3);
|
||||
GtkTreeViewColumn *column_3 = gtk_tree_view_get_column(tree_view, 4);
|
||||
|
||||
GtkCellRendererText *cell_renderer_0 = builder_get(GTK_CELL_RENDERER_TEXT, "vram_viewer_palette_cell_renderer_0");
|
||||
GtkCellRendererText *cell_renderer_1 = builder_get(GTK_CELL_RENDERER_TEXT, "vram_viewer_palette_cell_renderer_1");
|
||||
GtkCellRendererText *cell_renderer_2 = builder_get(GTK_CELL_RENDERER_TEXT, "vram_viewer_palette_cell_renderer_2");
|
||||
GtkCellRendererText *cell_renderer_3 = builder_get(GTK_CELL_RENDERER_TEXT, "vram_viewer_palette_cell_renderer_3");
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func(column_0, GTK_CELL_RENDERER(cell_renderer_0), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_1, GTK_CELL_RENDERER(cell_renderer_1), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_2, GTK_CELL_RENDERER(cell_renderer_2), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_3, GTK_CELL_RENDERER(cell_renderer_3), palette_color_data_func, NULL, NULL);
|
||||
|
||||
gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(store));
|
||||
g_object_unref(store);
|
||||
}
|
||||
|
||||
// Queue a redraw of the VRAM viewer
|
||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
||||
|
||||
gui_data.vram_viewer_updating = false;
|
||||
}
|
||||
|
||||
static void on_vblank(GB_gameboy_t *gb) {
|
||||
// Queue drawing of the current frame
|
||||
if (gui_data.fallback_canvas) {
|
||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.main_window));
|
||||
@ -952,6 +789,8 @@ static void on_vblank(gpointer data) {
|
||||
else if (gui_data.gl_area) {
|
||||
gtk_gl_area_queue_render(gui_data.gl_area);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
|
||||
}
|
||||
|
||||
static void vblank(GB_gameboy_t *gb) {
|
||||
@ -987,68 +826,11 @@ static void vblank(GB_gameboy_t *gb) {
|
||||
}
|
||||
}
|
||||
|
||||
if (g_strcmp0("vram_viewer_tileset", gui_data.vram_viewer_active_tab) == 0) {
|
||||
const gchar *palette_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tileset_palette_selector"));
|
||||
|
||||
GB_palette_type_t palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
|
||||
uint8_t palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
|
||||
|
||||
GB_draw_tileset(gb, gui_data.tileset_buffer,
|
||||
palette_type,
|
||||
palette_index
|
||||
);
|
||||
}
|
||||
else if (g_strcmp0("vram_viewer_tilemap", gui_data.vram_viewer_active_tab) == 0) {
|
||||
const gchar *palette_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tilemap_palette_selector"));
|
||||
uint8_t palette_index = 0;
|
||||
GB_palette_type_t palette_type = GB_PALETTE_AUTO;
|
||||
|
||||
if (g_strcmp0("auto", palette_id) != 0) {
|
||||
palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
|
||||
palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
|
||||
}
|
||||
|
||||
GB_map_type_t map_type = GB_MAP_AUTO;
|
||||
const gchar *map_type_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tilemap_tilemap_selector"));
|
||||
if (g_strcmp0("auto", map_type_id) != 0) {
|
||||
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
|
||||
}
|
||||
|
||||
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
|
||||
const gchar *tileset_type_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tilemap_tileset_selector"));
|
||||
if (g_strcmp0("auto", tileset_type_id) != 0) {
|
||||
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
GB_draw_tilemap(gb, gui_data.tilemap_buffer,
|
||||
palette_type,
|
||||
palette_index,
|
||||
map_type,
|
||||
tileset_type
|
||||
);
|
||||
|
||||
gui_data.scroll_rect = (Rect){
|
||||
GB_read_memory(gb, 0xFF00 | GB_IO_SCX),
|
||||
GB_read_memory(gb, 0xFF00 | GB_IO_SCY),
|
||||
160, 144
|
||||
};
|
||||
}
|
||||
else if (g_strcmp0("vram_viewer_sprites", gui_data.vram_viewer_active_tab) == 0) {
|
||||
gui_data.oam_count = GB_get_oam_info(gb, gui_data.oam_info, &gui_data.oam_height);
|
||||
gui_data.vram_viewer_is_cgb = GB_is_cgb(gb);
|
||||
}
|
||||
else if (g_strcmp0("vram_viewer_palettes", gui_data.vram_viewer_active_tab) == 0) {
|
||||
size_t size;
|
||||
|
||||
for (unsigned row = 0; row < 16; ++row) {
|
||||
uint8_t *palette_data = GB_get_direct_access(gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, &size, NULL);
|
||||
memcpy(gui_data.vram_viewer_palette_data[row], palette_data, size);
|
||||
}
|
||||
}
|
||||
|
||||
gui_data.do_rewind = gui_data.rewind_down;
|
||||
|
||||
g_idle_add((GSourceFunc) on_vblank, NULL);
|
||||
vram_viewer_update(gui_data.vram_viewer, gb);
|
||||
|
||||
g_idle_add((GSourceFunc) on_vblank, gb);
|
||||
}
|
||||
|
||||
static void handle_events(GB_gameboy_t *gb) {
|
||||
@ -1402,7 +1184,6 @@ static void quit(void) {
|
||||
g_debug("quit(void);");
|
||||
|
||||
stop();
|
||||
while (gui_data.stopping);
|
||||
|
||||
GtkWindow *window = gui_data.main_window ? GTK_WINDOW(gui_data.main_window) : NULL;
|
||||
save_config(window, gui_data.config_modification_date);
|
||||
@ -1496,249 +1277,6 @@ static void on_window_state_change(GtkWidget *w, GdkEventWindowState *event, gpo
|
||||
gui_data.is_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
|
||||
}
|
||||
|
||||
// Gets called when the VRAM viewer gets realized
|
||||
static void on_vram_viewer_realize(void) {
|
||||
gui_data.vram_viewer_visible = true;
|
||||
gui_data.vram_viewer_active_tab = (gchar *)gtk_stack_get_visible_child_name(builder_get(GTK_STACK, "vram_viewer_stack"));
|
||||
}
|
||||
|
||||
// Gets called when the VRAM viewer gets unrealized
|
||||
static void on_vram_viewer_unrealize(void) {
|
||||
gui_data.vram_viewer_visible = false;
|
||||
}
|
||||
|
||||
// Gets called when the tileset viewer should be redrawn
|
||||
static gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
||||
g_mutex_lock(&gui_data.tileset_buffer_mutex);
|
||||
|
||||
guint width, height;
|
||||
GtkStyleContext *context;
|
||||
|
||||
context = gtk_widget_get_style_context(widget);
|
||||
width = gtk_widget_get_allocated_width(widget);
|
||||
height = gtk_widget_get_allocated_height(widget);
|
||||
|
||||
gtk_render_background(context, cr, 0, 0, width, height);
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *) gui_data.tileset_buffer,
|
||||
CAIRO_FORMAT_RGB24,
|
||||
256,
|
||||
192,
|
||||
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
|
||||
);
|
||||
|
||||
cairo_scale(cr, 2.0, 2.0);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (gtk_toggle_button_get_active(builder_get(GTK_TOGGLE_BUTTON, "vram_viewer_tileset_toggle_grid_button"))) {
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
|
||||
cairo_set_line_width(cr, 1);
|
||||
|
||||
const int divisions_x = 256 / 8;
|
||||
const int divisions_y = 192 / 8;
|
||||
|
||||
for (int i = 0; i < divisions_x; i++) {
|
||||
const int j = 256 * i;
|
||||
|
||||
cairo_move_to(cr, j / divisions_x, 0);
|
||||
cairo_line_to(cr, j / divisions_x, 192);
|
||||
}
|
||||
|
||||
for (int i = 0; i < divisions_y; i++) {
|
||||
const int j = 192 * i;
|
||||
|
||||
cairo_move_to(cr, 0, j / divisions_y);
|
||||
cairo_line_to(cr, 256, j / divisions_y);
|
||||
}
|
||||
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
g_mutex_unlock(&gui_data.tileset_buffer_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gets called when the tilemap viewer should be redrawn
|
||||
static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
||||
g_mutex_lock(&gui_data.tilemap_buffer_mutex);
|
||||
|
||||
guint width, height;
|
||||
GtkStyleContext *context;
|
||||
|
||||
context = gtk_widget_get_style_context(widget);
|
||||
width = gtk_widget_get_allocated_width(widget);
|
||||
height = gtk_widget_get_allocated_height(widget);
|
||||
|
||||
gtk_render_background(context, cr, 0, 0, width, height);
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *) gui_data.tilemap_buffer,
|
||||
CAIRO_FORMAT_RGB24,
|
||||
256,
|
||||
256,
|
||||
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
|
||||
);
|
||||
|
||||
cairo_scale(cr, 2.0, 2.0);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (gtk_toggle_button_get_active(builder_get(GTK_TOGGLE_BUTTON, "vram_viewer_tilemap_toggle_grid_button"))) {
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
|
||||
cairo_set_line_width(cr, 1);
|
||||
|
||||
const int divisions = 256 / 8;
|
||||
|
||||
for (int i = 0; i < divisions; i++) {
|
||||
const int j = 256 * i;
|
||||
|
||||
cairo_move_to(cr, j / divisions, 0);
|
||||
cairo_line_to(cr, j / divisions, 256);
|
||||
cairo_move_to(cr, 0, j / divisions);
|
||||
cairo_line_to(cr, 256, j / divisions);
|
||||
}
|
||||
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
if (gtk_toggle_button_get_active(builder_get(GTK_TOGGLE_BUTTON, "vram_viewer_tilemap_toggle_scrolling_button"))) {
|
||||
cairo_rectangle(cr, -2, -2, width + 2, height + 2);
|
||||
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
Rect rect = gui_data.scroll_rect;
|
||||
rect.x -= 256 * x;
|
||||
rect.y += 256 * y;
|
||||
|
||||
cairo_rectangle(cr, rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
cairo_set_line_width(cr, 2);
|
||||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
||||
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.5);
|
||||
cairo_fill_preserve(cr);
|
||||
cairo_clip_preserve(cr);
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.6);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
g_mutex_unlock(&gui_data.tilemap_buffer_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean on_motion_vram_viewer_tileset(GtkWidget *widget, GdkEventMotion *event) {
|
||||
int x, y;
|
||||
|
||||
if (event->is_hint) {
|
||||
gdk_window_get_pointer (event->window, &x, &y, NULL);
|
||||
}
|
||||
else {
|
||||
x = event->x;
|
||||
y = event->y;
|
||||
}
|
||||
|
||||
// Compensate for our canvas scale
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
|
||||
uint8_t bank = x >= 128? 1 : 0;
|
||||
x &= 127;
|
||||
uint16_t tile = x / 8 + y / 8 * 16;
|
||||
|
||||
GtkLabel *status = builder_get(GTK_LABEL, "vram_viewer_status");
|
||||
gtk_label_set_text(status, g_strdup_printf("Tile number $%02x at %d:$%04x", tile & 0xFF, bank, 0x8000 + tile * 0x10));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean on_motion_vram_viewer_tilemap(GtkWidget *widget, GdkEventMotion *event) {
|
||||
int x, y;
|
||||
|
||||
if (event->is_hint) {
|
||||
gdk_window_get_pointer (event->window, &x, &y, NULL);
|
||||
}
|
||||
else {
|
||||
x = event->x;
|
||||
y = event->y;
|
||||
}
|
||||
|
||||
// Compensate for our canvas scale
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
|
||||
GtkLabel *status = builder_get(GTK_LABEL, "vram_viewer_status");
|
||||
|
||||
uint16_t map_offset = x / 8 + y / 8 * 32;
|
||||
uint16_t map_base = 0x1800;
|
||||
|
||||
GB_map_type_t map_type = GB_MAP_AUTO;
|
||||
const gchar *map_type_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tilemap_tilemap_selector"));
|
||||
if (g_strcmp0("auto", map_type_id) != 0) {
|
||||
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
|
||||
}
|
||||
|
||||
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
|
||||
const gchar *tileset_type_id = gtk_combo_box_get_active_id(builder_get(GTK_COMBO_BOX, "vram_viewer_tilemap_tileset_selector"));
|
||||
if (g_strcmp0("auto", tileset_type_id) != 0) {
|
||||
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
uint8_t lcdc = ((uint8_t *)GB_get_direct_access(&gb, GB_DIRECT_ACCESS_IO, NULL, NULL))[GB_IO_LCDC];
|
||||
uint8_t *vram = GB_get_direct_access(&gb, GB_DIRECT_ACCESS_VRAM, NULL, NULL);
|
||||
|
||||
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && lcdc & 0x08)) {
|
||||
map_base = 0x1c00;
|
||||
}
|
||||
|
||||
if (tileset_type == GB_TILESET_AUTO) {
|
||||
tileset_type = (lcdc & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
uint8_t tile = vram[map_base + map_offset];
|
||||
uint16_t tile_address = 0;
|
||||
if (tileset_type == GB_TILESET_8000) {
|
||||
tile_address = 0x8000 + tile * 0x10;
|
||||
}
|
||||
else {
|
||||
tile_address = 0x9000 + (int8_t)tile * 0x10;
|
||||
}
|
||||
|
||||
if (GB_is_cgb(&gb)) {
|
||||
uint8_t attributes = vram[map_base + map_offset + 0x2000];
|
||||
gtk_label_set_text(status, g_strdup_printf("Tile number $%02x (%d:$%04x) at map address $%04x (Attributes: %c%c%c%d%d)",
|
||||
tile,
|
||||
attributes & 0x8? 1 : 0,
|
||||
tile_address,
|
||||
0x8000 + map_base + map_offset,
|
||||
(attributes & 0x80) ? 'P' : '-',
|
||||
(attributes & 0x40) ? 'V' : '-',
|
||||
(attributes & 0x20) ? 'H' : '-',
|
||||
attributes & 0x8? 1 : 0,
|
||||
attributes & 0x7
|
||||
));
|
||||
}
|
||||
else {
|
||||
gtk_label_set_text(status, g_strdup_printf("Tile number $%02x ($%04x) at map address $%04x",
|
||||
tile,
|
||||
tile_address,
|
||||
0x8000 + map_base + map_offset
|
||||
));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void on_vram_tab_change(GtkWidget *widget, GParamSpec *pspec, GtkStackSwitcher *self) {
|
||||
gtk_label_set_text(builder_get(GTK_LABEL, "vram_viewer_status"), "");
|
||||
gui_data.vram_viewer_active_tab = (gchar *)gtk_stack_get_visible_child_name(builder_get(GTK_STACK, "vram_viewer_stack"));
|
||||
}
|
||||
|
||||
// This functions gets called immediately after registration of the GApplication
|
||||
static void startup(GApplication *app, gpointer null_ptr) {
|
||||
signal(SIGINT, quit_interrupt);
|
||||
@ -1774,7 +1312,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
|
||||
g_signal_connect(gui_data.preferences, "realize", G_CALLBACK(on_preferences_realize), (gpointer) gui_data.builder);
|
||||
init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date, gui_data.preferences);
|
||||
|
||||
gui_data.vram_viewer = GTK_WINDOW(get_object("vram_viewer"));
|
||||
gui_data.vram_viewer = vram_viewer_new();
|
||||
gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer"));
|
||||
|
||||
gui_data.console = GTK_WINDOW(get_object("console"));
|
||||
@ -1890,26 +1428,12 @@ static void connect_signal_handlers(GApplication *app) {
|
||||
g_signal_connect(gui_data.main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
|
||||
g_signal_connect(gui_data.main_window, "window-state-event", G_CALLBACK(on_window_state_change), NULL);
|
||||
|
||||
g_signal_connect(gui_data.vram_viewer, "realize", G_CALLBACK(on_vram_viewer_realize), NULL);
|
||||
g_signal_connect(gui_data.vram_viewer, "unrealize", G_CALLBACK(on_vram_viewer_unrealize), NULL);
|
||||
|
||||
// Just hide our sub-windows when closing them
|
||||
g_signal_connect(gui_data.preferences, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||
g_signal_connect(gui_data.vram_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||
g_signal_connect(gui_data.memory_viewer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||
g_signal_connect(gui_data.console, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||
g_signal_connect(gui_data.printer, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||||
|
||||
g_signal_connect(get_object("vram_viewer_tileset_canvas"), "draw", G_CALLBACK(on_draw_vram_viewer_tileset), NULL);
|
||||
g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "draw", G_CALLBACK(on_draw_vram_viewer_tilemap), NULL);
|
||||
|
||||
gtk_widget_add_events(builder_get(GTK_WIDGET, "vram_viewer_tileset_canvas"), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
|
||||
gtk_widget_add_events(builder_get(GTK_WIDGET, "vram_viewer_tilemap_canvas"), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
|
||||
|
||||
g_signal_connect(get_object("vram_viewer_tileset_canvas"), "motion_notify_event", G_CALLBACK(on_motion_vram_viewer_tileset), NULL);
|
||||
g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "motion_notify_event", G_CALLBACK(on_motion_vram_viewer_tilemap), NULL);
|
||||
|
||||
g_signal_connect(get_object("vram_viewer_stack"), "notify::visible-child", G_CALLBACK(on_vram_tab_change), NULL);
|
||||
}
|
||||
|
||||
// TODO: Comment
|
||||
@ -1942,7 +1466,7 @@ static void gl_draw(void) {
|
||||
render_bitmap_with_shader(
|
||||
&gui_data.shader, _pixels, previous,
|
||||
GB_get_screen_width(&gb), GB_get_screen_height(&gb),
|
||||
gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.w, gui_data.viewport.h,
|
||||
gui_data.viewport.x, gui_data.viewport.y, gui_data.viewport.width, gui_data.viewport.height,
|
||||
mode
|
||||
);
|
||||
}
|
||||
@ -1965,6 +1489,7 @@ static gboolean on_draw_fallback(GtkWidget *widget, cairo_t *cr, gpointer data)
|
||||
guint screen_height = GB_get_screen_height(&gb);
|
||||
|
||||
gtk_render_background(context, cr, 0, 0, width, height);
|
||||
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *) get_current_buffer(),
|
||||
@ -1975,7 +1500,7 @@ static gboolean on_draw_fallback(GtkWidget *widget, cairo_t *cr, gpointer data)
|
||||
);
|
||||
|
||||
cairo_translate(cr, gui_data.viewport.x, gui_data.viewport.y);
|
||||
cairo_scale(cr, gui_data.viewport.w / screen_width, gui_data.viewport.h / screen_height);
|
||||
cairo_scale(cr, gui_data.viewport.width / screen_width, gui_data.viewport.height / screen_height);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_paint(cr);
|
||||
@ -2103,7 +1628,6 @@ static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar
|
||||
g_debug("SHUTDOWN");
|
||||
|
||||
stop();
|
||||
while (gui_data.stopping);
|
||||
SDL_Quit();
|
||||
|
||||
if (gui_data.image_buffers[0]) g_free(gui_data.image_buffers[0]);
|
||||
@ -2185,13 +1709,13 @@ static void close_rom(void) {
|
||||
}
|
||||
|
||||
// Clear the VRAM viewer
|
||||
g_mutex_lock(&gui_data.tileset_buffer_mutex);
|
||||
memset(gui_data.tileset_buffer, 0, sizeof gui_data.tileset_buffer);
|
||||
g_mutex_unlock(&gui_data.tileset_buffer_mutex);
|
||||
// g_mutex_lock(&gui_data.tileset_buffer_mutex);
|
||||
// memset(gui_data.tileset_buffer, 0, sizeof gui_data.tileset_buffer);
|
||||
// g_mutex_unlock(&gui_data.tileset_buffer_mutex);
|
||||
|
||||
g_mutex_lock(&gui_data.tilemap_buffer_mutex);
|
||||
memset(gui_data.tilemap_buffer, 0, sizeof gui_data.tilemap_buffer);
|
||||
g_mutex_unlock(&gui_data.tilemap_buffer_mutex);
|
||||
// g_mutex_lock(&gui_data.tilemap_buffer_mutex);
|
||||
// memset(gui_data.tilemap_buffer, 0, sizeof gui_data.tilemap_buffer);
|
||||
// g_mutex_unlock(&gui_data.tilemap_buffer_mutex);
|
||||
|
||||
gtk_stack_set_visible_child_name(builder_get(GTK_STACK, "vram_viewer_stack"), "vram_viewer_tileset");
|
||||
gtk_tree_view_set_model(builder_get(GTK_TREE_VIEW, "vram_viewer_sprites"), NULL);
|
||||
|
516
gtk3/resources/ui/vram_viewer.ui
Normal file
516
gtk3/resources/ui/vram_viewer.ui
Normal file
@ -0,0 +1,516 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.36.0
|
||||
|
||||
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>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name SameBoy -->
|
||||
<!-- interface-description SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator, written in portable C. -->
|
||||
<!-- interface-copyright 2015-2019 Lior Halphon -->
|
||||
<!-- interface-authors Maximilian Mader -->
|
||||
<template class="VramViewerWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">VRAM Viewer</property>
|
||||
<property name="resizable">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkStackSwitcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="stack">stack</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hhomogeneous">False</property>
|
||||
<property name="vhomogeneous">False</property>
|
||||
<property name="transition_type">slide-left-right</property>
|
||||
<property name="interpolate_size">True</property>
|
||||
<signal name="notify::visible-child" handler="visible_tab_changed"/>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="margin_bottom">3</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="tileset_palette_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">bg0</property>
|
||||
<items>
|
||||
<item id="bg0" translatable="yes">Background Palette 0</item>
|
||||
<item id="bg1" translatable="yes">Background Palette 1</item>
|
||||
<item id="bg2" translatable="yes">Background Palette 2</item>
|
||||
<item id="bg3" translatable="yes">Background Palette 3</item>
|
||||
<item id="bg4" translatable="yes">Background Palette 4</item>
|
||||
<item id="bg5" translatable="yes">Background Palette 5</item>
|
||||
<item id="bg6" translatable="yes">Background Palette 6</item>
|
||||
<item id="bg7" translatable="yes">Background Palette 7</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="obj0" translatable="yes">Object Palette 0</item>
|
||||
<item id="obj1" translatable="yes">Object Palette 1</item>
|
||||
<item id="obj2" translatable="yes">Object Palette 2</item>
|
||||
<item id="obj3" translatable="yes">Object Palette 3</item>
|
||||
<item id="obj4" translatable="yes">Object Palette 4</item>
|
||||
<item id="obj5" translatable="yes">Object Palette 5</item>
|
||||
<item id="obj6" translatable="yes">Object Palette 6</item>
|
||||
<item id="obj7" translatable="yes">Object Palette 7</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="tileset_toggle_grid_button">
|
||||
<property name="label" translatable="yes">Grid</property>
|
||||
<property name="width_request">70</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="vram-viewer-button-bar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="tileset_canvas">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">384</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<signal name="draw" handler="draw_tileset_canvas"/>
|
||||
<signal name="motion_notify_event" handler="tileset_canvas_motion"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">tileset</property>
|
||||
<property name="title" translatable="yes">Tileset</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="tilemap_palette_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Palettes</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="bg0" translatable="yes">Background Palette 0</item>
|
||||
<item id="bg1" translatable="yes">Background Palette 1</item>
|
||||
<item id="bg2" translatable="yes">Background Palette 2</item>
|
||||
<item id="bg3" translatable="yes">Background Palette 3</item>
|
||||
<item id="bg4" translatable="yes">Background Palette 4</item>
|
||||
<item id="bg5" translatable="yes">Background Palette 5</item>
|
||||
<item id="bg6" translatable="yes">Background Palette 6</item>
|
||||
<item id="bg7" translatable="yes">Background Palette 7</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="obj0" translatable="yes">Object Palette 0</item>
|
||||
<item id="obj1" translatable="yes">Object Palette 1</item>
|
||||
<item id="obj2" translatable="yes">Object Palette 2</item>
|
||||
<item id="obj3" translatable="yes">Object Palette 3</item>
|
||||
<item id="obj4" translatable="yes">Object Palette 4</item>
|
||||
<item id="obj5" translatable="yes">Object Palette 5</item>
|
||||
<item id="obj6" translatable="yes">Object Palette 6</item>
|
||||
<item id="obj7" translatable="yes">Object Palette 7</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="tilemap_tilemap_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Tilemap</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="9800" translatable="yes">Tilemap at $9800</item>
|
||||
<item id="9C00" translatable="yes">Tilemap at $9C00</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="tilemap_toggle_grid_button">
|
||||
<property name="label" translatable="yes">Grid</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="tilemap_toggle_scrolling_button">
|
||||
<property name="label" translatable="yes">Scrolling</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_right">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="tilemap_tileset_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Tileset</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="8800" translatable="yes">Tileset at $8800</item>
|
||||
<item id="8000" translatable="yes">Tileset at $8000</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="vram-viewer-button-bar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="tilemap_canvas">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">512</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="hexpand">False</property>
|
||||
<property name="vexpand">False</property>
|
||||
<signal name="draw" handler="draw_tilemap_canvas"/>
|
||||
<signal name="motion_notify_event" handler="tilemap_canvas_motion"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">tilemap</property>
|
||||
<property name="title" translatable="yes">Tilemap</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">512</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sprites">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="show_expanders">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="min_width">32</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf"/>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">X</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Y</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Tile</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Tile Addr.</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">OAM Addr.</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Attributes</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">6</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">sprites</property>
|
||||
<property name="title" translatable="yes">Sprites</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="palettes">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="show_expanders">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="palette_cell_renderer_0"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="palette_cell_renderer_1"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="palette_cell_renderer_2"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="palette_cell_renderer_3"/>
|
||||
<attributes>
|
||||
<attribute name="text">7</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">palettes</property>
|
||||
<property name="title" translatable="yes">Palettes</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="status">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
@ -1624,479 +1624,4 @@ Maximilian Mader https://github.com/max-m</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="vram_viewer">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">VRAM Viewer</property>
|
||||
<property name="resizable">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkStackSwitcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="stack">vram_viewer_stack</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="vram_viewer_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hhomogeneous">False</property>
|
||||
<property name="vhomogeneous">False</property>
|
||||
<property name="transition_type">slide-left-right</property>
|
||||
<property name="interpolate_size">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="margin_bottom">3</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="vram_viewer_tileset_palette_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">bg0</property>
|
||||
<items>
|
||||
<item id="bg0" translatable="yes">Background Palette 0</item>
|
||||
<item id="bg1" translatable="yes">Background Palette 1</item>
|
||||
<item id="bg2" translatable="yes">Background Palette 2</item>
|
||||
<item id="bg3" translatable="yes">Background Palette 3</item>
|
||||
<item id="bg4" translatable="yes">Background Palette 4</item>
|
||||
<item id="bg5" translatable="yes">Background Palette 5</item>
|
||||
<item id="bg6" translatable="yes">Background Palette 6</item>
|
||||
<item id="bg7" translatable="yes">Background Palette 7</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="obj0" translatable="yes">Object Palette 0</item>
|
||||
<item id="obj1" translatable="yes">Object Palette 1</item>
|
||||
<item id="obj2" translatable="yes">Object Palette 2</item>
|
||||
<item id="obj3" translatable="yes">Object Palette 3</item>
|
||||
<item id="obj4" translatable="yes">Object Palette 4</item>
|
||||
<item id="obj5" translatable="yes">Object Palette 5</item>
|
||||
<item id="obj6" translatable="yes">Object Palette 6</item>
|
||||
<item id="obj7" translatable="yes">Object Palette 7</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="vram_viewer_tileset_toggle_grid_button">
|
||||
<property name="label" translatable="yes">Grid</property>
|
||||
<property name="width_request">70</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="vram-viewer-button-bar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="vram_viewer_tileset_canvas">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">384</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">vram_viewer_tileset</property>
|
||||
<property name="title" translatable="yes">Tileset</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="vram_viewer_tilemap_palette_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Palettes</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="bg0" translatable="yes">Background Palette 0</item>
|
||||
<item id="bg1" translatable="yes">Background Palette 1</item>
|
||||
<item id="bg2" translatable="yes">Background Palette 2</item>
|
||||
<item id="bg3" translatable="yes">Background Palette 3</item>
|
||||
<item id="bg4" translatable="yes">Background Palette 4</item>
|
||||
<item id="bg5" translatable="yes">Background Palette 5</item>
|
||||
<item id="bg6" translatable="yes">Background Palette 6</item>
|
||||
<item id="bg7" translatable="yes">Background Palette 7</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="obj0" translatable="yes">Object Palette 0</item>
|
||||
<item id="obj1" translatable="yes">Object Palette 1</item>
|
||||
<item id="obj2" translatable="yes">Object Palette 2</item>
|
||||
<item id="obj3" translatable="yes">Object Palette 3</item>
|
||||
<item id="obj4" translatable="yes">Object Palette 4</item>
|
||||
<item id="obj5" translatable="yes">Object Palette 5</item>
|
||||
<item id="obj6" translatable="yes">Object Palette 6</item>
|
||||
<item id="obj7" translatable="yes">Object Palette 7</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="vram_viewer_tilemap_tilemap_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Tilemap</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="9800" translatable="yes">Tilemap at $9800</item>
|
||||
<item id="9C00" translatable="yes">Tilemap at $9C00</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="vram_viewer_tilemap_toggle_grid_button">
|
||||
<property name="label" translatable="yes">Grid</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="vram_viewer_tilemap_toggle_scrolling_button">
|
||||
<property name="label" translatable="yes">Scrolling</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_right">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="vram_viewer_tilemap_tileset_selector">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="active_id">auto</property>
|
||||
<items>
|
||||
<item id="auto" translatable="yes">Effective Tileset</item>
|
||||
<item translatable="yes"><separator></item>
|
||||
<item id="8800" translatable="yes">Tileset at $8800</item>
|
||||
<item id="8000" translatable="yes">Tileset at $8000</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="vram-viewer-button-bar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="vram_viewer_tilemap_canvas">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">512</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="hexpand">False</property>
|
||||
<property name="vexpand">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">vram_viewer_tilemap</property>
|
||||
<property name="title" translatable="yes">Tilemap</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="width_request">512</property>
|
||||
<property name="height_request">512</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="vram_viewer_sprites">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="show_expanders">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="min_width">32</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf"/>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">X</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Y</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Tile</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Tile Addr.</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">OAM Addr.</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Attributes</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">6</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">vram_viewer_sprites</property>
|
||||
<property name="title" translatable="yes">Sprites</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="vram_viewer_palettes">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="show_expanders">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="vram_viewer_palette_cell_renderer_0"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="vram_viewer_palette_cell_renderer_1"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="vram_viewer_palette_cell_renderer_2"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<property name="title" translatable="yes">Color 3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="vram_viewer_palette_cell_renderer_3"/>
|
||||
<attributes>
|
||||
<attribute name="text">7</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">vram_viewer_palettes</property>
|
||||
<property name="title" translatable="yes">Palettes</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="vram_viewer_status">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<gresources>
|
||||
<gresource prefix="/io/github/sameboy">
|
||||
<file preprocess="xml-stripblanks" compressed="true">ui/window.ui</file>
|
||||
<file preprocess="xml-stripblanks" compressed="true">ui/vram_viewer.ui</file>
|
||||
<!--
|
||||
<file preprocess="xml-stripblanks">gtk/menus.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/menus-common.ui</file>
|
||||
|
21
gtk3/types.h
21
gtk3/types.h
@ -3,13 +3,11 @@
|
||||
|
||||
#include "SDL.h"
|
||||
#include "shader.h"
|
||||
|
||||
#define tileset_buffer_length 256 * 192 * 4
|
||||
#define tilemap_buffer_length 256 * 256 * 4
|
||||
#include "vram_viewer.h"
|
||||
|
||||
typedef struct{
|
||||
int16_t x, y;
|
||||
uint16_t w, h;
|
||||
uint16_t width, height;
|
||||
} Rect;
|
||||
|
||||
typedef struct GuiData {
|
||||
@ -43,7 +41,7 @@ typedef struct GuiData {
|
||||
GtkGLArea *gl_area;
|
||||
GtkDrawingArea *fallback_canvas;
|
||||
GtkWindow *preferences;
|
||||
GtkWindow *vram_viewer;
|
||||
VramViewerWindow *vram_viewer;
|
||||
GtkWindow *memory_viewer;
|
||||
GtkWindow *console;
|
||||
GtkWindow *printer;
|
||||
@ -58,19 +56,6 @@ typedef struct GuiData {
|
||||
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;
|
||||
|
562
gtk3/vram_viewer.c
Normal file
562
gtk3/vram_viewer.c
Normal file
@ -0,0 +1,562 @@
|
||||
#include "vram_viewer.h"
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
#define tileset_buffer_length 256 * 192 * 4
|
||||
#define tilemap_buffer_length 256 * 256 * 4
|
||||
|
||||
struct _VramViewerWindow {
|
||||
GtkWindowClass parent_class;
|
||||
|
||||
GtkStack *stack;
|
||||
GtkComboBoxText *tileset_palette_selector;
|
||||
GtkToggleButton *tileset_toggle_grid_button;
|
||||
GtkDrawingArea *tileset_canvas;
|
||||
GtkComboBoxText *tilemap_palette_selector;
|
||||
GtkComboBoxText *tilemap_tilemap_selector;
|
||||
GtkToggleButton *tilemap_toggle_grid_button;
|
||||
GtkToggleButton *tilemap_toggle_scrolling_button;
|
||||
GtkComboBoxText *tilemap_tileset_selector;
|
||||
GtkDrawingArea *tilemap_canvas;
|
||||
GtkTreeView *sprites;
|
||||
GtkTreeView *palettes;
|
||||
GtkCellRendererText *palette_cell_renderer_0;
|
||||
GtkCellRendererText *palette_cell_renderer_1;
|
||||
GtkCellRendererText *palette_cell_renderer_2;
|
||||
GtkCellRendererText *palette_cell_renderer_3;
|
||||
GtkLabel *status;
|
||||
|
||||
bool is_cgb;
|
||||
uint16_t oam_count;
|
||||
uint8_t oam_height;
|
||||
|
||||
uint8_t palette_data[16][0x40];
|
||||
GB_oam_info_t oam_info[40];
|
||||
|
||||
Rect scroll_rect;
|
||||
|
||||
uint8_t gb_lcdc;
|
||||
uint8_t *gb_vram;
|
||||
|
||||
uint32_t *tileset_buffer;
|
||||
uint32_t *tilemap_buffer;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(VramViewerWindow, vram_viewer_window, GTK_TYPE_WINDOW);
|
||||
|
||||
static void visible_tab_changed(GObject *stack, GParamSpec *pspec, VramViewerWindow *window) {
|
||||
if (gtk_widget_in_destruction (GTK_WIDGET (stack))) return;
|
||||
|
||||
gtk_label_set_text(window->status, "");
|
||||
}
|
||||
|
||||
static gboolean draw_tileset_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
||||
guint width, height;
|
||||
GtkStyleContext *context;
|
||||
|
||||
context = gtk_widget_get_style_context(widget);
|
||||
width = gtk_widget_get_allocated_width(widget);
|
||||
height = gtk_widget_get_allocated_height(widget);
|
||||
|
||||
gtk_render_background(context, cr, 0, 0, width, height);
|
||||
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *) window->tileset_buffer,
|
||||
CAIRO_FORMAT_RGB24,
|
||||
256,
|
||||
192,
|
||||
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
|
||||
);
|
||||
|
||||
cairo_scale(cr, 2.0, 2.0);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (gtk_toggle_button_get_active(window->tileset_toggle_grid_button)) {
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
|
||||
cairo_set_line_width(cr, 1);
|
||||
|
||||
const int divisions_x = 256 / 8;
|
||||
const int divisions_y = 192 / 8;
|
||||
|
||||
for (int i = 0; i < divisions_x; i++) {
|
||||
const int j = 256 * i;
|
||||
|
||||
cairo_move_to(cr, j / divisions_x, 0);
|
||||
cairo_line_to(cr, j / divisions_x, 192);
|
||||
}
|
||||
|
||||
for (int i = 0; i < divisions_y; i++) {
|
||||
const int j = 192 * i;
|
||||
|
||||
cairo_move_to(cr, 0, j / divisions_y);
|
||||
cairo_line_to(cr, 256, j / divisions_y);
|
||||
}
|
||||
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean draw_tilemap_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
|
||||
guint width, height;
|
||||
GtkStyleContext *context;
|
||||
|
||||
context = gtk_widget_get_style_context(widget);
|
||||
width = gtk_widget_get_allocated_width(widget);
|
||||
height = gtk_widget_get_allocated_height(widget);
|
||||
|
||||
gtk_render_background(context, cr, 0, 0, width, height);
|
||||
gtk_render_frame(context, cr, 0, 0, width, height);
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char *) window->tilemap_buffer,
|
||||
CAIRO_FORMAT_RGB24,
|
||||
256,
|
||||
256,
|
||||
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
|
||||
);
|
||||
|
||||
cairo_scale(cr, 2.0, 2.0);
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (gtk_toggle_button_get_active(window->tilemap_toggle_grid_button)) {
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
|
||||
cairo_set_line_width(cr, 1);
|
||||
|
||||
const int divisions = 256 / 8;
|
||||
|
||||
for (int i = 0; i < divisions; i++) {
|
||||
const int j = 256 * i;
|
||||
|
||||
cairo_move_to(cr, j / divisions, 0);
|
||||
cairo_line_to(cr, j / divisions, 256);
|
||||
cairo_move_to(cr, 0, j / divisions);
|
||||
cairo_line_to(cr, 256, j / divisions);
|
||||
}
|
||||
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
if (gtk_toggle_button_get_active(window->tilemap_toggle_scrolling_button)) {
|
||||
cairo_rectangle(cr, -2, -2, width + 2, height + 2);
|
||||
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
Rect rect = window->scroll_rect;
|
||||
rect.x -= 256 * x;
|
||||
rect.y += 256 * y;
|
||||
|
||||
cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
cairo_set_line_width(cr, 2);
|
||||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
||||
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.5);
|
||||
cairo_fill_preserve(cr);
|
||||
cairo_clip_preserve(cr);
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.6);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean tileset_canvas_motion(GtkWidget *widget, GdkEventMotion *event, VramViewerWindow *window) {
|
||||
int x, y;
|
||||
|
||||
if (event->is_hint) {
|
||||
gdk_window_get_pointer(event->window, &x, &y, NULL);
|
||||
}
|
||||
else {
|
||||
x = event->x;
|
||||
y = event->y;
|
||||
}
|
||||
|
||||
// Compensate for our canvas scale
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
|
||||
uint8_t bank = x >= 128? 1 : 0;
|
||||
x &= 127;
|
||||
uint16_t tile = x / 8 + y / 8 * 16;
|
||||
|
||||
gtk_label_set_text(window->status, g_strdup_printf("Tile number $%02x at %d:$%04x", tile & 0xFF, bank, 0x8000 + tile * 0x10));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean tilemap_canvas_motion(GtkWidget *widget, GdkEventMotion *event, VramViewerWindow *window) {
|
||||
if (!window->gb_vram) {
|
||||
gtk_label_set_text(window->status, "");
|
||||
return false;
|
||||
}
|
||||
|
||||
int x, y;
|
||||
|
||||
if (event->is_hint) {
|
||||
gdk_window_get_pointer (event->window, &x, &y, NULL);
|
||||
}
|
||||
else {
|
||||
x = event->x;
|
||||
y = event->y;
|
||||
}
|
||||
|
||||
// Compensate for our canvas scale
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
|
||||
GtkLabel *status = window->status;
|
||||
|
||||
uint16_t map_offset = x / 8 + y / 8 * 32;
|
||||
uint16_t map_base = 0x1800;
|
||||
|
||||
GB_map_type_t map_type = GB_MAP_AUTO;
|
||||
const gchar *map_type_id = vram_viewer_get_tilemap_type_id(window);
|
||||
if (g_strcmp0("auto", map_type_id) != 0) {
|
||||
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
|
||||
}
|
||||
|
||||
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
|
||||
const gchar *tileset_type_id = vram_viewer_get_tileset_type_id(window);
|
||||
if (g_strcmp0("auto", tileset_type_id) != 0) {
|
||||
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && window->gb_lcdc & 0x08)) {
|
||||
map_base = 0x1c00;
|
||||
}
|
||||
|
||||
if (tileset_type == GB_TILESET_AUTO) {
|
||||
tileset_type = (window->gb_lcdc & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
uint8_t tile = window->gb_vram[map_base + map_offset];
|
||||
uint16_t tile_address = 0;
|
||||
if (tileset_type == GB_TILESET_8000) {
|
||||
tile_address = 0x8000 + tile * 0x10;
|
||||
}
|
||||
else {
|
||||
tile_address = 0x9000 + (int8_t)tile * 0x10;
|
||||
}
|
||||
|
||||
if (window->is_cgb) {
|
||||
uint8_t attributes = window->gb_vram[map_base + map_offset + 0x2000];
|
||||
gtk_label_set_text(status, g_strdup_printf("Tile number $%02x (%d:$%04x) at map address $%04x (Attributes: %c%c%c%d%d)",
|
||||
tile,
|
||||
attributes & 0x8? 1 : 0,
|
||||
tile_address,
|
||||
0x8000 + map_base + map_offset,
|
||||
(attributes & 0x80) ? 'P' : '-',
|
||||
(attributes & 0x40) ? 'V' : '-',
|
||||
(attributes & 0x20) ? 'H' : '-',
|
||||
attributes & 0x8? 1 : 0,
|
||||
attributes & 0x7
|
||||
));
|
||||
}
|
||||
else {
|
||||
gtk_label_set_text(status, g_strdup_printf("Tile number $%02x ($%04x) at map address $%04x",
|
||||
tile,
|
||||
tile_address,
|
||||
0x8000 + map_base + map_offset
|
||||
));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vram_viewer_window_init(VramViewerWindow *window) {
|
||||
gtk_widget_init_template(GTK_WIDGET(window));
|
||||
|
||||
window->tileset_buffer = g_new0(uint32_t, tileset_buffer_length);
|
||||
window->tilemap_buffer = g_new0(uint32_t, tilemap_buffer_length);
|
||||
|
||||
window->is_cgb = false;
|
||||
window->oam_count = 0;
|
||||
window->oam_height = 0;
|
||||
|
||||
gtk_widget_add_events(GTK_WIDGET(window->tileset_canvas), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
|
||||
gtk_widget_add_events(GTK_WIDGET(window->tilemap_canvas), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
|
||||
}
|
||||
|
||||
static void vram_viewer_window_class_init(VramViewerWindowClass *class) {
|
||||
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/vram_viewer.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, stack);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_palette_selector);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_toggle_grid_button);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_canvas);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_palette_selector);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_tilemap_selector);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_toggle_grid_button);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_toggle_scrolling_button);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_tileset_selector);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_canvas);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, sprites);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palettes);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_0);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_1);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_2);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_3);
|
||||
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, status);
|
||||
|
||||
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), visible_tab_changed);
|
||||
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), draw_tileset_canvas);
|
||||
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), draw_tilemap_canvas);
|
||||
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), tileset_canvas_motion);
|
||||
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), tilemap_canvas_motion);
|
||||
}
|
||||
|
||||
VramViewerWindow *vram_viewer_new(void) {
|
||||
return g_object_new (VRAM_VIEWER_WINDOW_TYPE, NULL);
|
||||
}
|
||||
|
||||
const gchar *vram_viewer_active_tab_name(VramViewerWindow *window) {
|
||||
return gtk_stack_get_visible_child_name(window->stack);
|
||||
}
|
||||
|
||||
const gchar *vram_viewer_get_tileset_palette_id(VramViewerWindow *window) {
|
||||
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tileset_palette_selector));
|
||||
}
|
||||
|
||||
const gchar *vram_viewer_get_tilemap_palette_id(VramViewerWindow *window) {
|
||||
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_palette_selector));
|
||||
}
|
||||
|
||||
const gchar *vram_viewer_get_tilemap_type_id(VramViewerWindow *window) {
|
||||
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_tilemap_selector));
|
||||
}
|
||||
|
||||
const gchar *vram_viewer_get_tileset_type_id(VramViewerWindow *window) {
|
||||
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_tileset_selector));
|
||||
}
|
||||
|
||||
static void update_sprite_list(VramViewerWindow *window) {
|
||||
GtkTreeIter iter;
|
||||
|
||||
GtkListStore *store = gtk_list_store_new(7,
|
||||
GDK_TYPE_PIXBUF, // Preview image
|
||||
G_TYPE_STRING, // X position
|
||||
G_TYPE_STRING, // Y position
|
||||
G_TYPE_STRING, // Tile
|
||||
G_TYPE_STRING, // Tile Address
|
||||
G_TYPE_STRING, // OAM Address
|
||||
G_TYPE_STRING // Attributes
|
||||
);
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
|
||||
|
||||
GB_oam_info_t *oam_info = window->oam_info;
|
||||
|
||||
for (unsigned row = 0; row < window->oam_count; ++row) {
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_bytes(
|
||||
g_bytes_new(oam_info[row].image, 128 * sizeof(uint32_t)),
|
||||
GDK_COLORSPACE_RGB, true, 8, 8, window->oam_height, 8 * sizeof(uint32_t)
|
||||
);
|
||||
|
||||
GdkPixbuf *dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, 8 * 2, window->oam_height * 2);
|
||||
|
||||
gdk_pixbuf_scale(pixbuf, dest,
|
||||
0, 0, 8 * 2, window->oam_height * 2,
|
||||
0, 0, 2.0, 2.0,
|
||||
GDK_INTERP_NEAREST
|
||||
);
|
||||
|
||||
gtk_list_store_insert_with_values(store, &iter, -1,
|
||||
0, dest,
|
||||
1, g_strdup_printf("%i", oam_info[row].x - 8),
|
||||
2, g_strdup_printf("%i", oam_info[row].y - 16),
|
||||
3, g_strdup_printf("$%02x", oam_info[row].tile),
|
||||
4, g_strdup_printf("$%04x", 0x8000 + oam_info[row].tile * 0x10),
|
||||
5, g_strdup_printf("$%04x", oam_info[row].oam_addr),
|
||||
6, window->is_cgb
|
||||
? g_strdup_printf("%c%c%c%d%d",
|
||||
oam_info[row].flags & 0x80? 'P' : '-',
|
||||
oam_info[row].flags & 0x40? 'Y' : '-',
|
||||
oam_info[row].flags & 0x20? 'X' : '-',
|
||||
oam_info[row].flags & 0x08? 1 : 0,
|
||||
oam_info[row].flags & 0x07)
|
||||
: g_strdup_printf("%c%c%c%d",
|
||||
oam_info[row].flags & 0x80? 'P' : '-',
|
||||
oam_info[row].flags & 0x40? 'Y' : '-',
|
||||
oam_info[row].flags & 0x20? 'X' : '-',
|
||||
oam_info[row].flags & 0x10? 1 : 0),
|
||||
-1
|
||||
);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
g_object_unref(dest);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(window->sprites, GTK_TREE_MODEL(store));
|
||||
g_object_unref(store);
|
||||
}
|
||||
|
||||
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);
|
||||
const uint8_t column_index = 2 + (2 * color_index);
|
||||
|
||||
GValue color_val = G_VALUE_INIT;
|
||||
gtk_tree_model_get_value(model, iter, column_index, &color_val);
|
||||
gint color = g_value_get_int(&color_val);
|
||||
gchar *color_string = g_strdup_printf("#%06x", color);
|
||||
|
||||
gint lightness = 0.299 * ((color >> 16) & 0xFF) + 0.587 * ((color >> 8) & 0xFF) + 0.114 * (color & 0xFF);
|
||||
|
||||
GValue color_str = G_VALUE_INIT;
|
||||
g_value_init(&color_str, G_TYPE_STRING);
|
||||
g_value_set_string(&color_str, color_string);
|
||||
g_object_set_property(G_OBJECT(renderer), "background", &color_str);
|
||||
|
||||
GValue fg_color_str = G_VALUE_INIT;
|
||||
g_value_init(&fg_color_str, G_TYPE_STRING);
|
||||
g_value_set_static_string(&fg_color_str, (lightness > 0x7F)? "#000000" : "#FFFFFF");
|
||||
g_object_set_property(G_OBJECT(renderer), "foreground", &fg_color_str);
|
||||
|
||||
g_value_unset(&color_val);
|
||||
g_value_unset(&color_str);
|
||||
g_value_unset(&fg_color_str);
|
||||
g_free(color_string);
|
||||
}
|
||||
|
||||
static void update_palettes(VramViewerWindow *window) {
|
||||
GtkTreeIter iter;
|
||||
|
||||
GtkListStore *store = gtk_list_store_new(9,
|
||||
G_TYPE_STRING, // Name
|
||||
|
||||
G_TYPE_STRING, // Color 0 string
|
||||
G_TYPE_INT, // Color 0 integer
|
||||
|
||||
G_TYPE_STRING, // Color 1 string
|
||||
G_TYPE_INT, // Color 1 integer
|
||||
|
||||
G_TYPE_STRING, // Color 2 string
|
||||
G_TYPE_INT, // Color 2 integer
|
||||
|
||||
G_TYPE_STRING, // Color 3 string
|
||||
G_TYPE_INT // Color 3 integer
|
||||
);
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
|
||||
|
||||
for (unsigned row = 0; row < 16; ++row) {
|
||||
uint8_t offset = (row & 7) * 4;
|
||||
|
||||
uint16_t color_0 = (window->palette_data[row][((0 + offset) << 1) + 1] << 8) | window->palette_data[row][((0 + offset) << 1)];
|
||||
uint16_t color_1 = (window->palette_data[row][((1 + offset) << 1) + 1] << 8) | window->palette_data[row][((1 + offset) << 1)];
|
||||
uint16_t color_2 = (window->palette_data[row][((2 + offset) << 1) + 1] << 8) | window->palette_data[row][((2 + offset) << 1)];
|
||||
uint16_t color_3 = (window->palette_data[row][((3 + offset) << 1) + 1] << 8) | window->palette_data[row][((3 + offset) << 1)];
|
||||
|
||||
gtk_list_store_insert_with_values(store, &iter, -1,
|
||||
0, g_strdup_printf("%s %d", row >=8 ? "Object" : "Background", row & 7),
|
||||
1, g_strdup_printf("$%04x", color_0 & 0x7FFF),
|
||||
2, convert_color(color_0),
|
||||
3, g_strdup_printf("$%04x", color_1 & 0x7FFF),
|
||||
4, convert_color(color_1),
|
||||
5, g_strdup_printf("$%04x", color_2 & 0x7FFF),
|
||||
6, convert_color(color_2),
|
||||
7, g_strdup_printf("$%04x", color_3 & 0x7FFF),
|
||||
8, convert_color(color_3),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
GtkTreeViewColumn *column_0 = gtk_tree_view_get_column(window->palettes, 1);
|
||||
GtkTreeViewColumn *column_1 = gtk_tree_view_get_column(window->palettes, 2);
|
||||
GtkTreeViewColumn *column_2 = gtk_tree_view_get_column(window->palettes, 3);
|
||||
GtkTreeViewColumn *column_3 = gtk_tree_view_get_column(window->palettes, 4);
|
||||
|
||||
GtkCellRendererText *cell_renderer_0 = window->palette_cell_renderer_0;
|
||||
GtkCellRendererText *cell_renderer_1 = window->palette_cell_renderer_1;
|
||||
GtkCellRendererText *cell_renderer_2 = window->palette_cell_renderer_2;
|
||||
GtkCellRendererText *cell_renderer_3 = window->palette_cell_renderer_3;
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func(column_0, GTK_CELL_RENDERER(cell_renderer_0), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_1, GTK_CELL_RENDERER(cell_renderer_1), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_2, GTK_CELL_RENDERER(cell_renderer_2), palette_color_data_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(column_3, GTK_CELL_RENDERER(cell_renderer_3), palette_color_data_func, NULL, NULL);
|
||||
|
||||
gtk_tree_view_set_model(window->palettes, GTK_TREE_MODEL(store));
|
||||
g_object_unref(store);
|
||||
}
|
||||
|
||||
void vram_viewer_update(VramViewerWindow *window, GB_gameboy_t *gb) {
|
||||
if (!gtk_widget_is_visible(GTK_WIDGET(window))) return;
|
||||
|
||||
window->is_cgb = GB_is_cgb(gb);
|
||||
window->gb_lcdc = ((uint8_t *)GB_get_direct_access(gb, GB_DIRECT_ACCESS_IO, NULL, NULL))[GB_IO_LCDC];
|
||||
window->gb_vram = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, NULL, NULL);
|
||||
|
||||
const gchar *active_tab_name = vram_viewer_active_tab_name(window);
|
||||
|
||||
if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_TILESET) == 0) {
|
||||
const gchar *palette_id = vram_viewer_get_tileset_palette_id(window);
|
||||
|
||||
GB_palette_type_t palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
|
||||
uint8_t palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
|
||||
|
||||
GB_draw_tileset(gb, window->tileset_buffer,
|
||||
palette_type,
|
||||
palette_index
|
||||
);
|
||||
}
|
||||
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_TILEMAP) == 0) {
|
||||
const gchar *palette_id = vram_viewer_get_tilemap_palette_id(window);
|
||||
uint8_t palette_index = 0;
|
||||
GB_palette_type_t palette_type = GB_PALETTE_AUTO;
|
||||
|
||||
if (g_strcmp0("auto", palette_id) != 0) {
|
||||
palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
|
||||
palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
|
||||
}
|
||||
|
||||
GB_map_type_t map_type = GB_MAP_AUTO;
|
||||
const gchar *map_type_id = vram_viewer_get_tilemap_type_id(window);
|
||||
if (g_strcmp0("auto", map_type_id) != 0) {
|
||||
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
|
||||
}
|
||||
|
||||
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
|
||||
const gchar *tileset_type_id = vram_viewer_get_tileset_type_id(window);
|
||||
if (g_strcmp0("auto", tileset_type_id) != 0) {
|
||||
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
|
||||
}
|
||||
|
||||
GB_draw_tilemap(gb, window->tilemap_buffer,
|
||||
palette_type,
|
||||
palette_index,
|
||||
map_type,
|
||||
tileset_type
|
||||
);
|
||||
|
||||
window->scroll_rect = (Rect){
|
||||
GB_read_memory(gb, 0xFF00 | GB_IO_SCX),
|
||||
GB_read_memory(gb, 0xFF00 | GB_IO_SCY),
|
||||
160, 144
|
||||
};
|
||||
}
|
||||
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_SPRITES) == 0) {
|
||||
window->oam_count = GB_get_oam_info(gb, (GB_oam_info_t *)window->oam_info, &window->oam_height);
|
||||
|
||||
g_idle_add((GSourceFunc) update_sprite_list, window);
|
||||
}
|
||||
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_PALETTES) == 0) {
|
||||
size_t size;
|
||||
|
||||
for (unsigned row = 0; row < 16; ++row) {
|
||||
uint8_t *palette_data = GB_get_direct_access(gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, &size, NULL);
|
||||
memcpy(window->palette_data[row], palette_data, size);
|
||||
}
|
||||
|
||||
g_idle_add((GSourceFunc) update_palettes, window);
|
||||
}
|
||||
}
|
30
gtk3/vram_viewer.h
Normal file
30
gtk3/vram_viewer.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef vram_viewer_h
|
||||
#define vram_viewer_h
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <Core/gb.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define VRAM_VIEWER_WINDOW_TYPE (vram_viewer_window_get_type())
|
||||
G_DECLARE_FINAL_TYPE(VramViewerWindow, vram_viewer_window, SAMEBOY, WINDOW, GtkWindow)
|
||||
|
||||
#define VRAM_VIEWER_TAB_TILESET "tileset"
|
||||
#define VRAM_VIEWER_TAB_TILEMAP "tilemap"
|
||||
#define VRAM_VIEWER_TAB_SPRITES "sprites"
|
||||
#define VRAM_VIEWER_TAB_PALETTES "palettes"
|
||||
|
||||
VramViewerWindow *vram_viewer_new(void);
|
||||
|
||||
const gchar *vram_viewer_active_tab_name(VramViewerWindow *window);
|
||||
|
||||
const gchar *vram_viewer_get_tileset_palette_id(VramViewerWindow *window);
|
||||
const gchar *vram_viewer_get_tilemap_palette_id(VramViewerWindow *window);
|
||||
const gchar *vram_viewer_get_tilemap_type_id(VramViewerWindow *window);
|
||||
const gchar *vram_viewer_get_tileset_type_id(VramViewerWindow *window);
|
||||
|
||||
uint32_t *vram_viewer_get_tileset_buffer(VramViewerWindow *window);
|
||||
uint32_t *vram_viewer_get_tilemap_buffer(VramViewerWindow *window);
|
||||
|
||||
void vram_viewer_update(VramViewerWindow *window, GB_gameboy_t *gb);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user