forked from AuroraMiddleware/gtk
319 lines
8.9 KiB
C
319 lines
8.9 KiB
C
|
/* GTK - The GIMP Toolkit
|
||
|
* Copyright (C) 2016 Benjamin Otte <otte@gnome.org>
|
||
|
*
|
||
|
* 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include "gtkdebugupdatesprivate.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;
|
||
|
} GtkDebugUpdate;
|
||
|
|
||
|
static gboolean _gtk_debug_updates_enabled = FALSE;
|
||
|
|
||
|
static GQuark _gtk_debug_updates_quark = 0;
|
||
|
|
||
|
static void
|
||
|
gtk_debug_updates_init (void)
|
||
|
{
|
||
|
static gboolean inited = FALSE;
|
||
|
|
||
|
if (G_LIKELY (inited))
|
||
|
return;
|
||
|
|
||
|
_gtk_debug_updates_quark = g_quark_from_static_string ("-gtk-debug-updates");
|
||
|
|
||
|
inited = TRUE;
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
gtk_debug_updates_get_enabled (void)
|
||
|
{
|
||
|
return _gtk_debug_updates_enabled;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gtk_debug_updates_set_enabled (gboolean enabled)
|
||
|
{
|
||
|
if (enabled)
|
||
|
gtk_debug_updates_init ();
|
||
|
|
||
|
_gtk_debug_updates_enabled = enabled;
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
gtk_debug_updates_get_enabled_for_display (GdkDisplay *display)
|
||
|
{
|
||
|
if (gtk_debug_updates_get_enabled ())
|
||
|
return TRUE;
|
||
|
|
||
|
if (_gtk_debug_updates_quark == 0)
|
||
|
return FALSE;
|
||
|
|
||
|
return g_object_get_qdata (G_OBJECT (display), _gtk_debug_updates_quark) != NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gtk_debug_updates_set_enabled_for_display (GdkDisplay *display,
|
||
|
gboolean enabled)
|
||
|
{
|
||
|
if (enabled)
|
||
|
{
|
||
|
gtk_debug_updates_init ();
|
||
|
|
||
|
g_object_set_qdata (G_OBJECT (display), _gtk_debug_updates_quark, GINT_TO_POINTER (TRUE));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (_gtk_debug_updates_quark != 0)
|
||
|
g_object_steal_qdata (G_OBJECT (display), _gtk_debug_updates_quark);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gtk_debug_update_free (gpointer data)
|
||
|
{
|
||
|
GtkDebugUpdate *region = data;
|
||
|
|
||
|
cairo_region_destroy (region->region);
|
||
|
g_slice_free (GtkDebugUpdate, region);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gtk_debug_update_free_queue (gpointer data)
|
||
|
{
|
||
|
GQueue *queue = data;
|
||
|
|
||
|
g_queue_free_full (queue, gtk_debug_update_free);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gtk_debug_updates_print (GQueue *queue,
|
||
|
const cairo_region_t *region,
|
||
|
const char *format,
|
||
|
...) G_GNUC_PRINTF(3,4);
|
||
|
static void
|
||
|
gtk_debug_updates_print (GQueue *queue,
|
||
|
const cairo_region_t *region,
|
||
|
const char *format,
|
||
|
...)
|
||
|
{
|
||
|
#if 0
|
||
|
GString *string = g_string_new (NULL);
|
||
|
va_list args;
|
||
|
|
||
|
g_string_append_printf (string, "%3u: ", g_queue_get_length (queue));
|
||
|
|
||
|
if (region)
|
||
|
{
|
||
|
cairo_rectangle_int_t extents;
|
||
|
|
||
|
cairo_region_get_extents (region, &extents);
|
||
|
g_string_append_printf (string, "{%u,%u,%u,%u}(%u) ",
|
||
|
extents.x, extents.y,
|
||
|
extents.width, extents.height,
|
||
|
cairo_region_num_rectangles (region));
|
||
|
}
|
||
|
|
||
|
va_start (args, format);
|
||
|
g_string_append_vprintf (string, format, args);
|
||
|
va_end (args);
|
||
|
|
||
|
g_print ("%s\n", string->str);
|
||
|
|
||
|
g_string_free (string, TRUE);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
gtk_window_manage_updates (GtkWidget *widget,
|
||
|
GdkFrameClock *frame_clock,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GQueue *updates;
|
||
|
GtkDebugUpdate *draw;
|
||
|
cairo_region_t *region;
|
||
|
gint64 timestamp;
|
||
|
GList *l;
|
||
|
|
||
|
updates = g_object_get_qdata (G_OBJECT (widget), _gtk_debug_updates_quark);
|
||
|
if (updates == NULL)
|
||
|
return FALSE;
|
||
|
timestamp = gdk_frame_clock_get_frame_time (frame_clock);
|
||
|
|
||
|
gtk_debug_updates_print (updates, NULL, "Managing updates");
|
||
|
/* First queue an update for all regions.
|
||
|
* While doing so, set the correct timestamp on all untimed regions */
|
||
|
region = cairo_region_create ();
|
||
|
for (l = g_queue_peek_head_link (updates); l != NULL; l = l->next)
|
||
|
{
|
||
|
draw = l->data;
|
||
|
|
||
|
if (draw->timestamp == 0)
|
||
|
{
|
||
|
draw->timestamp = timestamp;
|
||
|
gtk_debug_updates_print (updates, draw->region, "Setting timestamp to %lli\n", (long long) timestamp);
|
||
|
}
|
||
|
|
||
|
cairo_region_union (region, draw->region);
|
||
|
}
|
||
|
gtk_debug_updates_print (updates, region, "Queued update");
|
||
|
gdk_window_invalidate_region (gtk_widget_get_window (widget), region, TRUE);
|
||
|
cairo_region_destroy (region);
|
||
|
|
||
|
/* Then remove all outdated regions */
|
||
|
do {
|
||
|
draw = g_queue_peek_tail (updates);
|
||
|
|
||
|
if (draw == NULL)
|
||
|
{
|
||
|
gtk_debug_updates_print (updates, NULL, "Empty, no more updates");
|
||
|
g_object_set_qdata (G_OBJECT (widget), _gtk_debug_updates_quark, NULL);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (draw->timestamp + GDK_DRAW_REGION_MAX_DURATION >= timestamp)
|
||
|
break;
|
||
|
|
||
|
gtk_debug_updates_print (updates, draw->region, "Popped region");
|
||
|
draw = g_queue_pop_tail (updates);
|
||
|
gtk_debug_update_free (draw);
|
||
|
} while (TRUE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gtk_debug_updates_add (GtkWidget *widget,
|
||
|
const cairo_region_t *region)
|
||
|
{
|
||
|
GQueue *updates;
|
||
|
GtkDebugUpdate *draw;
|
||
|
|
||
|
if (!gtk_debug_updates_get_enabled_for_display (gtk_widget_get_display (widget)))
|
||
|
return;
|
||
|
|
||
|
updates = g_object_get_qdata (G_OBJECT (widget), _gtk_debug_updates_quark);
|
||
|
if (updates == NULL)
|
||
|
{
|
||
|
updates = g_queue_new ();
|
||
|
gtk_widget_add_tick_callback (widget,
|
||
|
gtk_window_manage_updates,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
g_object_set_qdata_full (G_OBJECT (widget),
|
||
|
_gtk_debug_updates_quark,
|
||
|
updates,
|
||
|
gtk_debug_update_free_queue);
|
||
|
gtk_debug_updates_print (updates, NULL, "Newly created");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GtkDebugUpdate *first = g_queue_peek_head (updates);
|
||
|
|
||
|
if (first->timestamp == 0)
|
||
|
{
|
||
|
cairo_region_union (first->region, region);
|
||
|
gtk_debug_updates_print (updates, first->region, "Added to existing region");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
draw = g_slice_new0 (GtkDebugUpdate);
|
||
|
draw->region = cairo_region_copy (region);
|
||
|
g_queue_push_head (updates, draw);
|
||
|
gtk_debug_updates_print (updates, draw->region, "Added new region");
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gtk_debug_updates_queue_get_extents (GQueue *updates,
|
||
|
GdkRectangle *extents)
|
||
|
{
|
||
|
GtkDebugUpdate *draw;
|
||
|
GdkRectangle rect;
|
||
|
GList *l;
|
||
|
|
||
|
*extents = (GdkRectangle) { 0, 0, 0, 0 };
|
||
|
|
||
|
for (l = g_queue_peek_head_link (updates); l != NULL; l = l->next)
|
||
|
{
|
||
|
draw = l->data;
|
||
|
|
||
|
cairo_region_get_extents (draw->region, &rect);
|
||
|
gdk_rectangle_union (extents, &rect, extents);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GskRenderNode *
|
||
|
gtk_debug_updates_get_render_node (GtkWidget *widget,
|
||
|
GskRenderer *renderer)
|
||
|
{
|
||
|
GQueue *updates;
|
||
|
GskRenderNode *node;
|
||
|
GtkDebugUpdate *draw;
|
||
|
GdkRectangle rect;
|
||
|
gint64 timestamp;
|
||
|
double progress;
|
||
|
cairo_t *cr;
|
||
|
GList *l;
|
||
|
|
||
|
if (!gtk_debug_updates_get_enabled_for_display (gtk_widget_get_display (widget)))
|
||
|
return NULL;
|
||
|
|
||
|
updates = g_object_get_qdata (G_OBJECT (widget), _gtk_debug_updates_quark);
|
||
|
if (updates == NULL)
|
||
|
return NULL;
|
||
|
timestamp = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
|
||
|
|
||
|
gtk_debug_updates_print (updates, NULL, "Painting at %lli", (long long) timestamp);
|
||
|
|
||
|
node = gsk_renderer_create_render_node (renderer);
|
||
|
gsk_render_node_set_name (node, "Debug Updates");
|
||
|
gtk_debug_updates_queue_get_extents (updates, &rect);
|
||
|
gsk_render_node_set_bounds (node, &(graphene_rect_t) GRAPHENE_RECT_INIT(rect.x, rect.y, rect.width, rect.height));
|
||
|
|
||
|
cr = gsk_render_node_get_draw_context (node);
|
||
|
|
||
|
for (l = g_queue_peek_head_link (updates); l != NULL; l = l->next)
|
||
|
{
|
||
|
draw = l->data;
|
||
|
if (timestamp - draw->timestamp < GDK_DRAW_REGION_MIN_DURATION)
|
||
|
progress = 0.0;
|
||
|
else if (timestamp - draw->timestamp < GDK_DRAW_REGION_MAX_DURATION)
|
||
|
progress = (double) (timestamp - draw->timestamp - GDK_DRAW_REGION_MIN_DURATION)
|
||
|
/ (GDK_DRAW_REGION_MAX_DURATION - GDK_DRAW_REGION_MIN_DURATION);
|
||
|
else
|
||
|
continue;
|
||
|
|
||
|
cairo_set_source_rgba (cr, 1, 0, 0, 0.4 * (1 - progress));
|
||
|
gtk_debug_updates_print (updates, draw->region, "Painting with progress %g", progress);
|
||
|
gdk_cairo_region (cr, draw->region);
|
||
|
cairo_fill (cr);
|
||
|
}
|
||
|
|
||
|
cairo_destroy (cr);
|
||
|
|
||
|
return node;
|
||
|
}
|
||
|
|