forked from AuroraMiddleware/gtk
5556907194
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.
284 lines
8.3 KiB
C
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);
|
|
}
|
|
|