forked from AuroraMiddleware/gtk
a5e7e72dd8
Get the native transform only once, for all overlays. Unfortunately we have to undo this for the updates overlay since that one gets values in surface coordinates.
289 lines
8.6 KiB
C
289 lines
8.6 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_expose (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;
|
|
double native_x, native_y;
|
|
|
|
if (!GTK_IS_NATIVE (widget))
|
|
return;
|
|
|
|
/* The coordinates we're getting from GdkSurface API are in GdkSurface coordinate spaces,
|
|
* but we're snapshotting in widget space, so we need to transform */
|
|
gtk_native_get_surface_transform (GTK_NATIVE (widget), &native_x, &native_y);
|
|
|
|
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 - native_x, rect.y - native_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_expose (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);
|
|
}
|
|
|