diff --git a/testsuite/reftests/Makefile.am b/testsuite/reftests/Makefile.am index 61135b2747..50c532de67 100644 --- a/testsuite/reftests/Makefile.am +++ b/testsuite/reftests/Makefile.am @@ -34,6 +34,8 @@ gtk_reftest_SOURCES = \ reftest-compare.h \ reftest-module.c \ reftest-module.h \ + reftest-snapshot.c \ + reftest-snapshot.h \ gtk-reftest.c \ gtk-reftest.h diff --git a/testsuite/reftests/gtk-reftest.c b/testsuite/reftests/gtk-reftest.c index 89cd2adb41..4f5b6f4007 100644 --- a/testsuite/reftests/gtk-reftest.c +++ b/testsuite/reftests/gtk-reftest.c @@ -208,28 +208,6 @@ remove_extra_css (GtkStyleProvider *provider) provider); } -static GtkWidget * -builder_get_toplevel (GtkBuilder *builder) -{ - GSList *list, *walk; - GtkWidget *window = NULL; - - list = gtk_builder_get_objects (builder); - for (walk = list; walk; walk = walk->next) - { - if (GTK_IS_WINDOW (walk->data) && - gtk_widget_get_parent (walk->data) == NULL) - { - window = walk->data; - break; - } - } - - g_slist_free (list); - - return window; -} - static gboolean quit_when_idle (gpointer loop) { @@ -238,219 +216,6 @@ quit_when_idle (gpointer loop) return G_SOURCE_REMOVE; } -static gint inhibit_count; -static GMainLoop *loop; - -void -reftest_inhibit_snapshot (void) -{ - inhibit_count++; -} - -void -reftest_uninhibit_snapshot (void) -{ - g_assert (inhibit_count > 0); - inhibit_count--; - - if (inhibit_count == 0) - g_idle_add (quit_when_idle, loop); -} - -static void -check_for_draw (GdkEvent *event, gpointer data) -{ - if (event->type == GDK_EXPOSE) - { - reftest_uninhibit_snapshot (); - gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL); - } - - gtk_main_do_event (event); -} - -static cairo_surface_t * -snapshot_widget (GtkWidget *widget, SnapshotMode mode) -{ - cairo_surface_t *surface; - cairo_pattern_t *bg; - cairo_t *cr; - - g_assert (gtk_widget_get_realized (widget)); - - loop = g_main_loop_new (NULL, FALSE); - - /* We wait until the widget is drawn for the first time. - * We can not wait for a GtkWidget::draw event, because that might not - * happen if the window is fully obscured by windowed child widgets. - * Alternatively, we could wait for an expose event on widget's window. - * Both of these are rather hairy, not sure what's best. - * - * We also use an inhibit mechanism, to give module functions a chance - * to delay the snapshot. - */ - reftest_inhibit_snapshot (); - gdk_event_handler_set (check_for_draw, NULL, NULL); - g_main_loop_run (loop); - - surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget), - CAIRO_CONTENT_COLOR, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); - - cr = cairo_create (surface); - - switch (mode) - { - case SNAPSHOT_WINDOW: - { - GdkWindow *window = gtk_widget_get_window (widget); - if (gdk_window_get_window_type (window) == GDK_WINDOW_TOPLEVEL || - gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN) - { - /* give the WM/server some time to sync. They need it. - * Also, do use popups instead of toplevls in your tests - * whenever you can. */ - gdk_display_sync (gdk_window_get_display (window)); - g_timeout_add (500, quit_when_idle, loop); - g_main_loop_run (loop); - } - gdk_cairo_set_source_window (cr, window, 0, 0); - cairo_paint (cr); - } - break; - case SNAPSHOT_DRAW: - bg = gdk_window_get_background_pattern (gtk_widget_get_window (widget)); - if (bg) - { - cairo_set_source (cr, bg); - cairo_paint (cr); - } - gtk_widget_draw (widget, cr); - break; - default: - g_assert_not_reached(); - break; - } - - cairo_destroy (cr); - g_main_loop_unref (loop); - gtk_widget_destroy (widget); - - return surface; -} - -static void -connect_signals (GtkBuilder *builder, - GObject *object, - const gchar *signal_name, - const gchar *handler_name, - GObject *connect_object, - GConnectFlags flags, - gpointer user_data) -{ - ReftestModule *module; - const char *directory; - GCallback func; - GClosure *closure; - char **split; - - directory = user_data; - split = g_strsplit (handler_name, ":", -1); - - switch (g_strv_length (split)) - { - case 1: - func = gtk_builder_lookup_callback_symbol (builder, split[0]); - - if (func) - { - module = NULL; - } - else - { - module = reftest_module_new_self (); - if (module == NULL) - { - g_error ("glib compiled without module support."); - return; - } - func = reftest_module_lookup (module, split[0]); - if (!func) - { - g_error ("failed to lookup handler for name '%s' when connecting signals", split[0]); - return; - } - } - break; - case 2: - if (g_getenv ("REFTEST_MODULE_DIR")) - directory = g_getenv ("REFTEST_MODULE_DIR"); - module = reftest_module_new (directory, split[0]); - if (module == NULL) - { - g_error ("Could not load module '%s' from '%s' when looking up '%s'", split[0], directory, handler_name); - return; - } - func = reftest_module_lookup (module, split[1]); - if (!func) - { - g_error ("failed to lookup handler for name '%s' in module '%s'", split[1], split[0]); - return; - } - break; - default: - g_error ("Could not connect signal handler named '%s'", handler_name); - return; - } - - g_strfreev (split); - - if (connect_object) - { - if (flags & G_CONNECT_SWAPPED) - closure = g_cclosure_new_object_swap (func, connect_object); - else - closure = g_cclosure_new_object (func, connect_object); - } - else - { - if (flags & G_CONNECT_SWAPPED) - closure = g_cclosure_new_swap (func, NULL, NULL); - else - closure = g_cclosure_new (func, NULL, NULL); - } - - if (module) - g_closure_add_finalize_notifier (closure, module, (GClosureNotify) reftest_module_unref); - - g_signal_connect_closure (object, signal_name, closure, flags & G_CONNECT_AFTER ? TRUE : FALSE); -} - -static cairo_surface_t * -snapshot_ui_file (const char *ui_file) -{ - GtkWidget *window; - GtkBuilder *builder; - GError *error = NULL; - char *directory; - - get_components_of_test_file (ui_file, &directory, NULL); - - builder = gtk_builder_new (); - gtk_builder_add_from_file (builder, ui_file, &error); - g_assert_no_error (error); - gtk_builder_connect_signals_full (builder, connect_signals, directory); - window = builder_get_toplevel (builder); - g_object_unref (builder); - g_free (directory); - g_assert (window); - - gtk_widget_show (window); - - return snapshot_widget (window, SNAPSHOT_WINDOW); -} - static void save_image (cairo_surface_t *surface, const char *test_name, @@ -475,11 +240,11 @@ test_ui_file (GFile *file) provider = add_extra_css (ui_file, ".css"); - ui_image = snapshot_ui_file (ui_file); + ui_image = reftest_snapshot_ui_file (ui_file); reference_file = get_test_file (ui_file, ".ref.ui", TRUE); if (reference_file) - reference_image = snapshot_ui_file (reference_file); + reference_image = reftest_snapshot_ui_file (reference_file); else { reference_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); diff --git a/testsuite/reftests/gtk-reftest.h b/testsuite/reftests/gtk-reftest.h index aa527b617a..e30c78f0ea 100644 --- a/testsuite/reftests/gtk-reftest.h +++ b/testsuite/reftests/gtk-reftest.h @@ -20,6 +20,7 @@ G_BEGIN_DECLS +/* reftest-snapshot.c */ void reftest_inhibit_snapshot (void); void reftest_uninhibit_snapshot (void); diff --git a/testsuite/reftests/reftest-snapshot.c b/testsuite/reftests/reftest-snapshot.c new file mode 100644 index 0000000000..478db4a460 --- /dev/null +++ b/testsuite/reftests/reftest-snapshot.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2011 Red Hat Inc. + * + * Author: + * Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "reftest-snapshot.h" + +#include "reftest-module.h" + +#include + +typedef enum { + SNAPSHOT_WINDOW, + SNAPSHOT_DRAW +} SnapshotMode; + +static GtkWidget * +builder_get_toplevel (GtkBuilder *builder) +{ + GSList *list, *walk; + GtkWidget *window = NULL; + + list = gtk_builder_get_objects (builder); + for (walk = list; walk; walk = walk->next) + { + if (GTK_IS_WINDOW (walk->data) && + gtk_widget_get_parent (walk->data) == NULL) + { + window = walk->data; + break; + } + } + + g_slist_free (list); + + return window; +} + +static gboolean +quit_when_idle (gpointer loop) +{ + g_main_loop_quit (loop); + + return G_SOURCE_REMOVE; +} + +static gint inhibit_count; +static GMainLoop *loop; + +void +reftest_inhibit_snapshot (void) +{ + inhibit_count++; +} + +void +reftest_uninhibit_snapshot (void) +{ + g_assert (inhibit_count > 0); + inhibit_count--; + + if (inhibit_count == 0) + g_idle_add (quit_when_idle, loop); +} + +static void +check_for_draw (GdkEvent *event, gpointer data) +{ + if (event->type == GDK_EXPOSE) + { + reftest_uninhibit_snapshot (); + gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL); + } + + gtk_main_do_event (event); +} + +static cairo_surface_t * +snapshot_widget (GtkWidget *widget, SnapshotMode mode) +{ + cairo_surface_t *surface; + cairo_pattern_t *bg; + cairo_t *cr; + + g_assert (gtk_widget_get_realized (widget)); + + loop = g_main_loop_new (NULL, FALSE); + + /* We wait until the widget is drawn for the first time. + * We can not wait for a GtkWidget::draw event, because that might not + * happen if the window is fully obscured by windowed child widgets. + * Alternatively, we could wait for an expose event on widget's window. + * Both of these are rather hairy, not sure what's best. + * + * We also use an inhibit mechanism, to give module functions a chance + * to delay the snapshot. + */ + reftest_inhibit_snapshot (); + gdk_event_handler_set (check_for_draw, NULL, NULL); + g_main_loop_run (loop); + + surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget), + CAIRO_CONTENT_COLOR, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + + cr = cairo_create (surface); + + switch (mode) + { + case SNAPSHOT_WINDOW: + { + GdkWindow *window = gtk_widget_get_window (widget); + if (gdk_window_get_window_type (window) == GDK_WINDOW_TOPLEVEL || + gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN) + { + /* give the WM/server some time to sync. They need it. + * Also, do use popups instead of toplevls in your tests + * whenever you can. */ + gdk_display_sync (gdk_window_get_display (window)); + g_timeout_add (500, quit_when_idle, loop); + g_main_loop_run (loop); + } + gdk_cairo_set_source_window (cr, window, 0, 0); + cairo_paint (cr); + } + break; + case SNAPSHOT_DRAW: + bg = gdk_window_get_background_pattern (gtk_widget_get_window (widget)); + if (bg) + { + cairo_set_source (cr, bg); + cairo_paint (cr); + } + gtk_widget_draw (widget, cr); + break; + default: + g_assert_not_reached(); + break; + } + + cairo_destroy (cr); + g_main_loop_unref (loop); + gtk_widget_destroy (widget); + + return surface; +} + +static void +connect_signals (GtkBuilder *builder, + GObject *object, + const gchar *signal_name, + const gchar *handler_name, + GObject *connect_object, + GConnectFlags flags, + gpointer user_data) +{ + ReftestModule *module; + const char *directory; + GCallback func; + GClosure *closure; + char **split; + + directory = user_data; + split = g_strsplit (handler_name, ":", -1); + + switch (g_strv_length (split)) + { + case 1: + func = gtk_builder_lookup_callback_symbol (builder, split[0]); + + if (func) + { + module = NULL; + } + else + { + module = reftest_module_new_self (); + if (module == NULL) + { + g_error ("glib compiled without module support."); + return; + } + func = reftest_module_lookup (module, split[0]); + if (!func) + { + g_error ("failed to lookup handler for name '%s' when connecting signals", split[0]); + return; + } + } + break; + case 2: + if (g_getenv ("REFTEST_MODULE_DIR")) + directory = g_getenv ("REFTEST_MODULE_DIR"); + module = reftest_module_new (directory, split[0]); + if (module == NULL) + { + g_error ("Could not load module '%s' from '%s' when looking up '%s'", split[0], directory, handler_name); + return; + } + func = reftest_module_lookup (module, split[1]); + if (!func) + { + g_error ("failed to lookup handler for name '%s' in module '%s'", split[1], split[0]); + return; + } + break; + default: + g_error ("Could not connect signal handler named '%s'", handler_name); + return; + } + + g_strfreev (split); + + if (connect_object) + { + if (flags & G_CONNECT_SWAPPED) + closure = g_cclosure_new_object_swap (func, connect_object); + else + closure = g_cclosure_new_object (func, connect_object); + } + else + { + if (flags & G_CONNECT_SWAPPED) + closure = g_cclosure_new_swap (func, NULL, NULL); + else + closure = g_cclosure_new (func, NULL, NULL); + } + + if (module) + g_closure_add_finalize_notifier (closure, module, (GClosureNotify) reftest_module_unref); + + g_signal_connect_closure (object, signal_name, closure, flags & G_CONNECT_AFTER ? TRUE : FALSE); +} + +cairo_surface_t * +reftest_snapshot_ui_file (const char *ui_file) +{ + GtkWidget *window; + GtkBuilder *builder; + GError *error = NULL; + char *directory; + + directory = g_path_get_dirname (ui_file); + + builder = gtk_builder_new (); + gtk_builder_add_from_file (builder, ui_file, &error); + g_assert_no_error (error); + gtk_builder_connect_signals_full (builder, connect_signals, directory); + window = builder_get_toplevel (builder); + g_object_unref (builder); + g_free (directory); + g_assert (window); + + gtk_widget_show (window); + + return snapshot_widget (window, SNAPSHOT_WINDOW); +} diff --git a/testsuite/reftests/reftest-snapshot.h b/testsuite/reftests/reftest-snapshot.h new file mode 100644 index 0000000000..178d2e8469 --- /dev/null +++ b/testsuite/reftests/reftest-snapshot.h @@ -0,0 +1,29 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __REFTEST_SNAPSHOT_H__ +#define __REFTEST_SNAPSHOT_H__ + +#include + +G_BEGIN_DECLS + +cairo_surface_t * reftest_snapshot_ui_file (const char *ui_file); + +G_END_DECLS + +#endif /* __REFTEST_SNAPSHOT_H__ */