From 25ee19ce91dcb204d93e6d8cceb7cd64d5f057f9 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 13 Jul 2026 21:05:24 +0300 Subject: fullscreen: info/EXIF, fullscreen, slideshow, Esc back (ot0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M4 (task ot0): fullscreen + slideshow + info overlay. - src/info.{c,h}: plain-C EXIF gather via libexif (camera, lens, focal, aperture, shutter, ISO, datetime, orientation) + file info (dims, format, size). info_format() produces the overlay text. Unit-tested. - src/window.c: fullscreen toggle (f), slideshow (S, 3s auto-advance), info overlay (i, GtkLabel in GtkOverlay, auto-hide 5s), Esc contextual back (fullscreen→unfullscreen→large→grid→quit). Stack wrapped in GtkOverlay. ggaze_window_get_stack() accessor for tests. - src/shortcuts.c: f, S, i, Escape bindings. - meson.build: libexif dep added. - tests: unit test_info (EXIF extraction + orientation tag). 14/14 green, ASan clean. --- tests/meson.build | 10 ++++++ tests/test_grid_cull.c | 6 ++-- tests/test_info.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_open_and_show.c | 4 +-- tests/test_responsive_nav.c | 2 +- tests/test_walk_folder.c | 4 +-- tests/test_window.c | 2 +- 7 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 tests/test_info.c (limited to 'tests') diff --git a/tests/meson.build b/tests/meson.build index d1cb8af..86eee1d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -97,6 +97,16 @@ test_thumbnail = executable( ) test('thumbnail', test_thumbnail, suite : 'unit', env : fixtures_env) +test_info = executable( + 'test_info', + ['test_info.c', ggaze_conf_h], + include_directories : [inc, src_inc], + dependencies : ggaze_deps, + link_with : ggaze_lib, + install : false, +) +test('info', test_info, suite : 'unit', env : fixtures_env) + test_texturecache = executable( 'test_texturecache', ['test_texturecache.c', ggaze_conf_h], diff --git a/tests/test_grid_cull.c b/tests/test_grid_cull.c index 37a2a68..db5a512 100644 --- a/tests/test_grid_cull.c +++ b/tests/test_grid_cull.c @@ -89,7 +89,7 @@ new_window(void) { static GdkTexture * viewer_texture(GgazeWindow *p_win) { - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); return (ggaze_viewer_get_texture(GGAZE_VIEWER(p_large))); @@ -120,7 +120,7 @@ test_grid_cull_temp(void) { /* The grid exists in the stack with 3 cells (count without toggling to * avoid realizing cells and firing async thumbnail requests that can't * complete before exit under ASan). */ - GtkWidget *p_stack = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_stack = GTK_WIDGET(ggaze_window_get_stack(p_win)); GgazeGrid *p_grid = GGAZE_GRID(gtk_stack_get_child_by_name(GTK_STACK(p_stack), "grid")); g_assert_nonnull(p_grid); @@ -188,7 +188,7 @@ test_grid_sample(void) { /* The grid exists in the stack (created on open) with hundreds of cells, * but we don't toggle to it (that would realize cells and fire thumbnail * requests that can't all complete before exit under ASan). */ - GtkWidget *p_stack = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_stack = GTK_WIDGET(ggaze_window_get_stack(p_win)); GgazeGrid *p_grid = GGAZE_GRID(gtk_stack_get_child_by_name(GTK_STACK(p_stack), "grid")); g_assert_nonnull(p_grid); diff --git a/tests/test_info.c b/tests/test_info.c new file mode 100644 index 0000000..9beceb9 --- /dev/null +++ b/tests/test_info.c @@ -0,0 +1,74 @@ +/*:* + * ggaze — info / EXIF unit test + * + * Gathers EXIF from the fixtures: plain.jpg (orientation 1) and rot6.jpg + * (orientation 6, 8x4 stored). Asserts dimensions, orientation, and that + * the format string is non-empty. Uses $GGAZE_FIXTURES_DIR. + * + * Copyright (c) 2026 ggaze contributors + * SPDX-License-Identifier: GPL-3.0-or-later + *:*/ + +#include "info.h" + +#include + +static GgazeInfo * +info_from_fixture(const char *c_name) { + const char *c_dir = g_getenv("GGAZE_FIXTURES_DIR"); + g_assert_nonnull(c_dir); + char *c_path = g_build_filename(c_dir, c_name, NULL); + GFile *p_file = g_file_new_for_path(c_path); + g_free(c_path); + GgazeInfo *p_info = info_new(p_file); + g_assert_nonnull(p_info); + g_object_unref(p_file); + return (p_info); +} + +static void +test_plain_jpeg(void) { + GgazeInfo *p_info = info_from_fixture("plain.jpg"); + g_assert_cmpint(p_info->i_width, ==, 6); + g_assert_cmpint(p_info->i_height, ==, 3); + g_assert_cmpint(p_info->i_orientation, ==, 1); /* Horizontal (normal) */ + g_assert_nonnull(p_info->c_format); + g_assert(p_info->i_size > 0); + char *c_fmt = info_format(p_info); + g_assert_nonnull(c_fmt); + g_free(c_fmt); + info_delete(p_info); +} + +static void +test_rotated_jpeg(void) { + GgazeInfo *p_info = info_from_fixture("rot6.jpg"); + /* Stored dimensions are 8x4 (before orientation is applied). */ + g_assert_cmpint(p_info->i_width, ==, 8); + g_assert_cmpint(p_info->i_height, ==, 4); + g_assert_cmpint(p_info->i_orientation, ==, 6); /* Rotate 90 CW */ + info_delete(p_info); +} + +static void +test_png_no_exif(void) { + GgazeInfo *p_info = info_from_fixture("small.png"); + g_assert_cmpint(p_info->i_width, ==, 5); + g_assert_cmpint(p_info->i_height, ==, 2); + g_assert_cmpint(p_info->i_orientation, ==, 0); /* no EXIF */ + g_assert_null(p_info->c_camera); /* no EXIF → no camera */ + info_delete(p_info); +} + +int +main(int i_argc, char **c_argv) { + g_test_init(&i_argc, &c_argv, NULL); + if (g_getenv("GGAZE_FIXTURES_DIR") == NULL) { + g_test_skip("GGAZE_FIXTURES_DIR unset"); + return (g_test_run()); + } + g_test_add_func("/info/plain_jpeg", test_plain_jpeg); + g_test_add_func("/info/rotated_jpeg", test_rotated_jpeg); + g_test_add_func("/info/png_no_exif", test_png_no_exif); + return (g_test_run()); +} \ No newline at end of file diff --git a/tests/test_open_and_show.c b/tests/test_open_and_show.c index 3c16996..83c04c7 100644 --- a/tests/test_open_and_show.c +++ b/tests/test_open_and_show.c @@ -48,7 +48,7 @@ drain_main(guint u_ms) { static void assert_shown_large_with_dims(GgazeWindow *p_win, int i_w, int i_h) { - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); g_assert_true(GTK_IS_STACK(p_child)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); @@ -127,7 +127,7 @@ test_open_sample_image(void) { GgazeWindow *p_win = new_window(); GFile *p_file = g_file_new_for_path(c_path); ggaze_window_open(p_win, p_file); - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); /* async load: pump until a texture lands */ diff --git a/tests/test_responsive_nav.c b/tests/test_responsive_nav.c index 0a13e1a..61a90ab 100644 --- a/tests/test_responsive_nav.c +++ b/tests/test_responsive_nav.c @@ -74,7 +74,7 @@ drain_main(guint u_ms) { static GdkTexture * viewer_texture(GgazeWindow *p_win) { - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); return (ggaze_viewer_get_texture(GGAZE_VIEWER(p_large))); diff --git a/tests/test_walk_folder.c b/tests/test_walk_folder.c index f2f4f15..a175d3e 100644 --- a/tests/test_walk_folder.c +++ b/tests/test_walk_folder.c @@ -85,7 +85,7 @@ drain_main(guint u_ms) { static GdkTexture * viewer_texture(GgazeWindow *p_win) { - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); return (ggaze_viewer_get_texture(GGAZE_VIEWER(p_large))); @@ -94,7 +94,7 @@ viewer_texture(GgazeWindow *p_win) { static void assert_dims(GgazeWindow *p_win, int i_w, int i_h) { /* Loads are async; pump the main loop until the viewer shows i_w x i_h. */ - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); GtkWidget *p_large = gtk_stack_get_child_by_name(GTK_STACK(p_child), "large"); GdkTexture *p_tex = NULL; diff --git a/tests/test_window.c b/tests/test_window.c index 63e54bb..76b9363 100644 --- a/tests/test_window.c +++ b/tests/test_window.c @@ -33,7 +33,7 @@ test_stack_has_two_views(void) { GgazeWindow *p_win = new_window(); g_assert_nonnull(p_win); - GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win)); + GtkWidget *p_child = GTK_WIDGET(ggaze_window_get_stack(p_win)); g_assert_nonnull(p_child); g_assert_true(GTK_IS_STACK(p_child)); -- cgit v1.2.3