2014-09-21 16:33:42 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 Red Hat Inc.
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Benjamin Otte <otte@gnome.org>
|
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "reftest-snapshot.h"
|
|
|
|
|
|
|
|
#include "reftest-module.h"
|
|
|
|
|
2016-11-20 10:20:34 +00:00
|
|
|
#ifdef GDK_WINDOWING_X11
|
2019-03-26 16:35:26 +00:00
|
|
|
#include <gdk/x11/gdkx.h>
|
2016-11-20 10:20:34 +00:00
|
|
|
#include <cairo-xlib.h>
|
|
|
|
#endif
|
|
|
|
|
2014-09-21 16:33:42 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2019-11-30 00:17:10 +00:00
|
|
|
#define REFTEST_TYPE_SCOPE (reftest_scope_get_type ())
|
|
|
|
|
|
|
|
G_DECLARE_FINAL_TYPE (ReftestScope, reftest_scope, REFTEST, SCOPE, GtkBuilderCScope)
|
|
|
|
|
|
|
|
static GtkBuilderScopeInterface *parent_scope_iface;
|
|
|
|
|
|
|
|
struct _ReftestScope
|
|
|
|
{
|
|
|
|
GtkBuilderCScope parent_instance;
|
|
|
|
|
|
|
|
char *directory;
|
|
|
|
};
|
|
|
|
|
|
|
|
static GClosure *
|
|
|
|
reftest_scope_create_closure (GtkBuilderScope *scope,
|
|
|
|
GtkBuilder *builder,
|
|
|
|
const char *function_name,
|
|
|
|
GtkBuilderClosureFlags flags,
|
|
|
|
GObject *object,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
ReftestScope *self = REFTEST_SCOPE (scope);
|
|
|
|
ReftestModule *module;
|
|
|
|
GCallback func;
|
|
|
|
GClosure *closure;
|
|
|
|
char **split;
|
|
|
|
|
|
|
|
split = g_strsplit (function_name, ":", -1);
|
|
|
|
|
|
|
|
switch (g_strv_length (split))
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
closure = parent_scope_iface->create_closure (scope, builder, split[0], flags, object, error);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
module = reftest_module_new (self->directory, split[0]);
|
|
|
|
if (module == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error,
|
|
|
|
GTK_BUILDER_ERROR,
|
|
|
|
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
|
|
|
"Could not load module '%s' from '%s' when looking up '%s': %s", split[0], self->directory, function_name, g_module_error ());
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
func = reftest_module_lookup (module, split[1]);
|
|
|
|
if (!func)
|
|
|
|
{
|
|
|
|
g_set_error (error,
|
|
|
|
GTK_BUILDER_ERROR,
|
|
|
|
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
|
|
|
"failed to lookup function for name '%s' in module '%s'", split[1], split[0]);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object)
|
|
|
|
{
|
|
|
|
if (flags & GTK_BUILDER_CLOSURE_SWAPPED)
|
|
|
|
closure = g_cclosure_new_object_swap (func, object);
|
|
|
|
else
|
|
|
|
closure = g_cclosure_new_object (func, object);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (flags & GTK_BUILDER_CLOSURE_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);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_set_error (error,
|
|
|
|
GTK_BUILDER_ERROR,
|
|
|
|
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
|
|
|
"Could not find function named '%s'", function_name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev (split);
|
|
|
|
|
|
|
|
return closure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
reftest_scope_scope_init (GtkBuilderScopeInterface *iface)
|
|
|
|
{
|
|
|
|
iface->create_closure = reftest_scope_create_closure;
|
|
|
|
|
|
|
|
parent_scope_iface = g_type_interface_peek_parent (iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ReftestScope, reftest_scope, GTK_TYPE_BUILDER_CSCOPE,
|
|
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
|
|
|
|
reftest_scope_scope_init))
|
|
|
|
|
|
|
|
static void
|
|
|
|
reftest_scope_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
ReftestScope *self = REFTEST_SCOPE (object);
|
|
|
|
|
|
|
|
g_free (self->directory);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (reftest_scope_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
reftest_scope_class_init (ReftestScopeClass *scope_class)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (scope_class);
|
|
|
|
|
|
|
|
object_class->finalize = reftest_scope_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
reftest_scope_init (ReftestScope *self)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static GtkBuilderScope *
|
|
|
|
reftest_scope_new (const char *directory)
|
|
|
|
{
|
|
|
|
ReftestScope *result;
|
|
|
|
|
|
|
|
g_return_val_if_fail (directory != NULL, NULL);
|
|
|
|
|
|
|
|
result = g_object_new (REFTEST_TYPE_SCOPE, NULL);
|
|
|
|
|
|
|
|
result->directory = g_strdup (directory);
|
|
|
|
|
|
|
|
return GTK_BUILDER_SCOPE (result);
|
|
|
|
}
|
|
|
|
|
2014-09-21 16:33:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-24 13:54:49 +00:00
|
|
|
static int inhibit_count;
|
2014-09-21 16:33:42 +00:00
|
|
|
static GMainLoop *loop;
|
|
|
|
|
2019-04-01 04:06:25 +00:00
|
|
|
G_MODULE_EXPORT void
|
2014-09-21 16:33:42 +00:00
|
|
|
reftest_inhibit_snapshot (void)
|
|
|
|
{
|
|
|
|
inhibit_count++;
|
|
|
|
}
|
|
|
|
|
2020-06-04 13:45:29 +00:00
|
|
|
G_MODULE_EXPORT void
|
2014-09-21 16:33:42 +00:00
|
|
|
reftest_uninhibit_snapshot (void)
|
|
|
|
{
|
2021-04-12 12:30:56 +00:00
|
|
|
g_assert_true (inhibit_count > 0);
|
2014-09-21 16:33:42 +00:00
|
|
|
inhibit_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-03-27 02:19:05 +00:00
|
|
|
draw_paintable (GdkPaintable *paintable,
|
2021-09-15 04:55:25 +00:00
|
|
|
gpointer out_texture)
|
2014-09-21 16:33:42 +00:00
|
|
|
{
|
2019-03-27 02:19:05 +00:00
|
|
|
GtkSnapshot *snapshot;
|
|
|
|
GskRenderNode *node;
|
2021-09-15 04:55:25 +00:00
|
|
|
GdkTexture *texture;
|
|
|
|
GskRenderer *renderer;
|
2019-03-27 02:19:05 +00:00
|
|
|
|
2020-06-04 13:45:29 +00:00
|
|
|
if (inhibit_count > 0)
|
|
|
|
return;
|
2019-03-27 02:19:05 +00:00
|
|
|
|
|
|
|
snapshot = gtk_snapshot_new ();
|
|
|
|
gdk_paintable_snapshot (paintable,
|
|
|
|
snapshot,
|
|
|
|
gdk_paintable_get_intrinsic_width (paintable),
|
|
|
|
gdk_paintable_get_intrinsic_height (paintable));
|
|
|
|
node = gtk_snapshot_free_to_node (snapshot);
|
|
|
|
|
|
|
|
/* If the window literally draws nothing, we assume it hasn't been mapped yet and as such
|
|
|
|
* the invalidations were only side effects of resizes.
|
|
|
|
*/
|
|
|
|
if (node == NULL)
|
2020-06-04 13:45:29 +00:00
|
|
|
return;
|
2019-03-27 02:19:05 +00:00
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
renderer = gtk_native_get_renderer (
|
|
|
|
gtk_widget_get_native (
|
|
|
|
gtk_widget_paintable_get_widget (GTK_WIDGET_PAINTABLE (paintable))));
|
|
|
|
texture = gsk_renderer_render_texture (renderer,
|
|
|
|
node,
|
|
|
|
&GRAPHENE_RECT_INIT (
|
|
|
|
0, 0,
|
|
|
|
gdk_paintable_get_intrinsic_width (paintable),
|
|
|
|
gdk_paintable_get_intrinsic_height (paintable)
|
|
|
|
));
|
2019-03-27 02:19:05 +00:00
|
|
|
gsk_render_node_unref (node);
|
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
g_signal_handlers_disconnect_by_func (paintable, draw_paintable, out_texture);
|
2019-03-27 02:19:05 +00:00
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
*(GdkTexture **) out_texture = texture;
|
2020-06-04 13:45:29 +00:00
|
|
|
|
|
|
|
g_idle_add (quit_when_idle, loop);
|
2016-11-20 10:20:34 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
static GdkTexture *
|
2019-03-26 16:35:26 +00:00
|
|
|
snapshot_widget (GtkWidget *widget)
|
2014-09-21 16:33:42 +00:00
|
|
|
{
|
2019-03-26 16:35:26 +00:00
|
|
|
GdkPaintable *paintable;
|
2021-09-15 04:55:25 +00:00
|
|
|
GdkTexture *texture = NULL;
|
2014-09-21 16:33:42 +00:00
|
|
|
|
2021-04-12 12:30:56 +00:00
|
|
|
g_assert_true (gtk_widget_get_realized (widget));
|
2014-09-21 16:33:42 +00:00
|
|
|
|
|
|
|
loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
|
|
|
|
/* We wait until the widget is drawn for the first time.
|
|
|
|
*
|
|
|
|
* We also use an inhibit mechanism, to give module functions a chance
|
|
|
|
* to delay the snapshot.
|
|
|
|
*/
|
2019-03-26 16:35:26 +00:00
|
|
|
paintable = gtk_widget_paintable_new (widget);
|
2021-09-15 04:55:25 +00:00
|
|
|
g_signal_connect (paintable, "invalidate-contents", G_CALLBACK (draw_paintable), &texture);
|
2014-09-21 16:33:42 +00:00
|
|
|
g_main_loop_run (loop);
|
|
|
|
|
|
|
|
g_main_loop_unref (loop);
|
2019-03-27 02:19:05 +00:00
|
|
|
g_object_unref (paintable);
|
2020-05-09 14:26:22 +00:00
|
|
|
gtk_window_destroy (GTK_WINDOW (widget));
|
2014-09-21 16:33:42 +00:00
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
return texture;
|
2014-09-21 16:33:42 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 04:55:25 +00:00
|
|
|
GdkTexture *
|
2014-09-21 16:33:42 +00:00
|
|
|
reftest_snapshot_ui_file (const char *ui_file)
|
|
|
|
{
|
|
|
|
GtkWidget *window;
|
|
|
|
GtkBuilder *builder;
|
2019-11-30 00:17:10 +00:00
|
|
|
GtkBuilderScope *scope;
|
2014-09-21 16:33:42 +00:00
|
|
|
GError *error = NULL;
|
|
|
|
char *directory;
|
|
|
|
|
2019-11-30 00:17:10 +00:00
|
|
|
if (g_getenv ("REFTEST_MODULE_DIR"))
|
|
|
|
directory = g_strdup (g_getenv ("REFTEST_MODULE_DIR"));
|
|
|
|
else
|
|
|
|
directory = g_path_get_dirname (ui_file);
|
|
|
|
scope = reftest_scope_new (directory);
|
|
|
|
g_free (directory);
|
2014-09-21 16:33:42 +00:00
|
|
|
|
|
|
|
builder = gtk_builder_new ();
|
2019-11-30 00:17:10 +00:00
|
|
|
gtk_builder_set_scope (builder, scope);
|
|
|
|
g_object_unref (scope);
|
|
|
|
|
2014-09-21 16:33:42 +00:00
|
|
|
gtk_builder_add_from_file (builder, ui_file, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
window = builder_get_toplevel (builder);
|
|
|
|
g_object_unref (builder);
|
2021-04-12 12:30:56 +00:00
|
|
|
g_assert_true (window);
|
2014-09-21 16:33:42 +00:00
|
|
|
|
|
|
|
gtk_widget_show (window);
|
|
|
|
|
2019-03-26 16:35:26 +00:00
|
|
|
return snapshot_widget (window);
|
2014-09-21 16:33:42 +00:00
|
|
|
}
|