gtk2/gtk/inspector/updatesoverlay.c
Matthias Clasen 5556907194 inspector: Fix surface transform regression
Ever since we added surface-to-widget transforms,
the autofading of the fps overlay did not work anymore,
since it was given the transient transform node, most
of the time.

Fix this by passing both the root of the node tree and
the node for the toplevel widget to prepare_render, and
hande the widget node to the overlays. Update the
affected overlays that need to have their positioning
adjusted.
2021-03-09 18:27:04 -05:00

284 lines
8.3 KiB
C

/*
* Copyright © 2018 Benjamin Otte
*
* 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.1 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "updatesoverlay.h"
#include "gtkintl.h"
#include "gtkwidget.h"
#include "gtknative.h"
#include "gsk/gskrendernodeprivate.h"
/* duration before we start fading in us */
#define GDK_DRAW_REGION_MIN_DURATION 50 * 1000
/* duration when fade is finished in us */
#define GDK_DRAW_REGION_MAX_DURATION 200 * 1000
typedef struct {
gint64 timestamp;
cairo_region_t *region;
} GtkUpdate;
typedef struct {
GQueue *updates;
GskRenderNode *last;
GtkWidget *widget;
guint tick_callback;
gulong unmap_callback;
} GtkWidgetUpdates;
struct _GtkUpdatesOverlay
{
GtkInspectorOverlay parent_instance;
GHashTable *toplevels; /* widget => GtkWidgetUpdates */
};
struct _GtkUpdatesOverlayClass
{
GtkInspectorOverlayClass parent_class;
};
G_DEFINE_TYPE (GtkUpdatesOverlay, gtk_updates_overlay, GTK_TYPE_INSPECTOR_OVERLAY)
static void
gtk_update_free (gpointer data)
{
GtkUpdate *region = data;
cairo_region_destroy (region->region);
g_slice_free (GtkUpdate, region);
}
static void
gtk_widget_updates_free (gpointer data)
{
GtkWidgetUpdates *updates = data;
g_queue_free_full (updates->updates, gtk_update_free);
g_clear_pointer (&updates->last, gsk_render_node_unref);
g_signal_handler_disconnect (updates->widget, updates->unmap_callback);
if (updates->tick_callback)
gtk_widget_remove_tick_callback (updates->widget, updates->tick_callback);
updates->tick_callback = 0;
g_slice_free (GtkWidgetUpdates, updates);
}
static void
gtk_widget_updates_unmap_widget (GtkWidget *widget,
GtkUpdatesOverlay *self)
{
g_hash_table_remove (self->toplevels, widget);
}
static gboolean
gtk_widget_updates_tick (GtkWidget *widget,
GdkFrameClock *clock,
gpointer data)
{
GtkWidgetUpdates *updates = data;
GtkUpdate *draw;
gint64 now;
now = gdk_frame_clock_get_frame_time (clock);
for (draw = g_queue_pop_tail (updates->updates);
draw != NULL && (now - draw->timestamp >= GDK_DRAW_REGION_MAX_DURATION);
draw = g_queue_pop_tail (updates->updates))
{
gtk_update_free (draw);
}
gdk_surface_queue_render (gtk_native_get_surface (gtk_widget_get_native (widget)));
if (draw)
{
g_queue_push_tail (updates->updates, draw);
return G_SOURCE_CONTINUE;
}
else
{
updates->tick_callback = 0;
return G_SOURCE_REMOVE;
}
}
static GtkWidgetUpdates *
gtk_update_overlay_lookup_for_widget (GtkUpdatesOverlay *self,
GtkWidget *widget,
gboolean create)
{
GtkWidgetUpdates *updates = g_hash_table_lookup (self->toplevels, widget);
if (updates || !create)
return updates;
updates = g_slice_new0 (GtkWidgetUpdates);
updates->updates = g_queue_new ();
updates->widget = widget;
updates->unmap_callback = g_signal_connect (widget, "unmap", G_CALLBACK (gtk_widget_updates_unmap_widget), self);
g_hash_table_insert (self->toplevels, g_object_ref (widget), updates);
return updates;
}
static void
gtk_widget_updates_add (GtkWidgetUpdates *updates,
gint64 timestamp,
cairo_region_t *region)
{
GtkUpdate *update;
GList *l;
update = g_slice_new0 (GtkUpdate);
update->timestamp = timestamp;
update->region = region;
for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next)
{
GtkUpdate *u = l->data;
cairo_region_subtract (u->region, region);
}
g_queue_push_head (updates->updates, update);
if (updates->tick_callback == 0)
updates->tick_callback = gtk_widget_add_tick_callback (updates->widget, gtk_widget_updates_tick, updates, NULL);
}
static void
gtk_updates_overlay_snapshot (GtkInspectorOverlay *overlay,
GtkSnapshot *snapshot,
GskRenderNode *node,
GtkWidget *widget)
{
GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay);
GtkWidgetUpdates *updates;
GtkUpdate *draw;
gint64 now;
GList *l;
if (!GTK_IS_NATIVE (widget))
return;
updates = gtk_update_overlay_lookup_for_widget (self, widget, TRUE);
now = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
if (updates->last)
{
cairo_region_t *diff;
diff = cairo_region_create ();
gsk_render_node_diff (updates->last, node, diff);
if (cairo_region_is_empty (diff))
cairo_region_destroy (diff);
else
gtk_widget_updates_add (updates, now, diff);
}
else
{
cairo_region_t *region;
graphene_rect_t bounds;
gsk_render_node_get_bounds (node, &bounds);
region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
floor (bounds.origin.x),
floor (bounds.origin.y),
ceil (bounds.origin.x + bounds.size.width) - floor (bounds.origin.x),
ceil (bounds.origin.y + bounds.size.height) - floor (bounds.origin.y)
});
gtk_widget_updates_add (updates, now, region);
}
g_clear_pointer (&updates->last, gsk_render_node_unref);
updates->last = gsk_render_node_ref (node);
for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next)
{
double progress;
guint i;
draw = l->data;
if (now - draw->timestamp < GDK_DRAW_REGION_MIN_DURATION)
progress = 0.0;
else if (now - draw->timestamp < GDK_DRAW_REGION_MAX_DURATION)
progress = (double) (now - draw->timestamp - GDK_DRAW_REGION_MIN_DURATION)
/ (GDK_DRAW_REGION_MAX_DURATION - GDK_DRAW_REGION_MIN_DURATION);
else
break;
for (i = 0; i < cairo_region_num_rectangles (draw->region); i++)
{
GdkRectangle rect;
cairo_region_get_rectangle (draw->region, i, &rect);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA) { 1, 0, 0, 0.4 * (1 - progress) },
&GRAPHENE_RECT_INIT(rect.x, rect.y,
rect.width, rect.height));
}
}
}
static void
gtk_updates_overlay_queue_draw (GtkInspectorOverlay *overlay)
{
GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay);
GHashTableIter iter;
gpointer widget;
g_hash_table_iter_init (&iter, self->toplevels);
while (g_hash_table_iter_next (&iter, &widget, NULL))
gdk_surface_queue_render (gtk_native_get_surface (gtk_widget_get_native (widget)));
}
static void
gtk_updates_overlay_dispose (GObject *object)
{
GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (object);
g_hash_table_unref (self->toplevels);
G_OBJECT_CLASS (gtk_updates_overlay_parent_class)->dispose (object);
}
static void
gtk_updates_overlay_class_init (GtkUpdatesOverlayClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkInspectorOverlayClass *overlay_class = GTK_INSPECTOR_OVERLAY_CLASS (klass);
overlay_class->snapshot = gtk_updates_overlay_snapshot;
overlay_class->queue_draw = gtk_updates_overlay_queue_draw;
gobject_class->dispose = gtk_updates_overlay_dispose;
}
static void
gtk_updates_overlay_init (GtkUpdatesOverlay *self)
{
self->toplevels = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, gtk_widget_updates_free);
}
GtkInspectorOverlay *
gtk_updates_overlay_new (void)
{
return g_object_new (GTK_TYPE_UPDATES_OVERLAY, NULL);
}