diff --git a/gtk3/main.c b/gtk3/main.c
index 7984917..9f20fed 100644
--- a/gtk3/main.c
+++ b/gtk3/main.c
@@ -468,6 +468,14 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
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);
+
// create our renderer area
if (supports_gl) {
gl_area = GTK_GL_AREA(gtk_gl_area_new());
@@ -826,6 +834,112 @@ static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpoi
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"), "");
+}
+
G_MODULE_EXPORT void on_boot_rom_location_changed(GtkWidget *w, gpointer user_data_gptr) {
GtkComboBox *box = GTK_COMBO_BOX(w);
const gchar *id = gtk_combo_box_get_active_id(box);
diff --git a/gtk3/main.h b/gtk3/main.h
index b597863..08292e5 100644
--- a/gtk3/main.h
+++ b/gtk3/main.h
@@ -88,6 +88,9 @@ static void on_vram_viewer_realize();
static void on_vram_viewer_unrealize();
static gboolean on_draw_vram_viewer_tilemap(GtkWidget *widget, cairo_t *cr, gpointer data);
static gboolean on_draw_vram_viewer_tileset(GtkWidget *widget, cairo_t *cr, gpointer data);
+static gboolean on_motion_vram_viewer_tileset(GtkWidget *widget, GdkEventMotion *event);
+static gboolean on_motion_vram_viewer_tilemap(GtkWidget *widget, GdkEventMotion *event);
+static void on_vram_tab_change(GtkWidget *widget, GParamSpec *pspec, GtkStackSwitcher *self);
// Option bindings
G_MODULE_EXPORT void on_boot_rom_location_changed(GtkWidget *w, gpointer user_data_gptr);
diff --git a/gtk3/resources/ui/window.ui b/gtk3/resources/ui/window.ui
index de839b1..cddc669 100644
--- a/gtk3/resources/ui/window.ui
+++ b/gtk3/resources/ui/window.ui
@@ -595,23 +595,6 @@ Maximilian Mader https://github.com/max-m
3
-
-
-
- False
- True
- 4
-
-
+
+
+
+ False
+ True
+ 4
+
+
-