Files
sysprof/lib/util/rectangles.c
Christian Hergert d523fcc024 rectangles: cycle colors
We will still probably want something smarter here, but this gets things
to be more distinguished than black.
2018-05-17 09:05:26 +01:00

249 lines
6.4 KiB
C

/* rectangles.c
*
* Copyright 2018 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "util/rectangles.h"
#include "util/sp-color-cycle.h"
#include "visualizers/sp-visualizer-row.h"
typedef struct
{
const gchar *name;
const gchar *message;
gint64 begin;
gint64 end;
GdkRectangle area;
} Rectangle;
struct _Rectangles
{
GStringChunk *strings;
GArray *rectangles;
GHashTable *y_indexes;
GHashTable *colors;
SpColorCycle *cycle;
gint64 begin_time;
gint64 end_time;
guint sorted : 1;
};
Rectangles *
rectangles_new (gint64 begin_time,
gint64 end_time)
{
Rectangles *self;
self = g_slice_new0 (Rectangles);
self->strings = g_string_chunk_new (4096);
self->rectangles = g_array_new (FALSE, FALSE, sizeof (Rectangle));
self->y_indexes = g_hash_table_new (g_str_hash, g_str_equal);
self->colors = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
self->cycle = sp_color_cycle_new ();
self->begin_time = begin_time;
self->end_time = end_time;
self->sorted = FALSE;
return self;
}
void
rectangles_add (Rectangles *self,
gint64 begin_time,
gint64 end_time,
const gchar *name,
const gchar *message)
{
Rectangle rect = {0};
g_assert (self != NULL);
if (message != NULL)
rect.message = g_string_chunk_insert_const (self->strings, message);
if (name != NULL)
rect.name = g_string_chunk_insert_const (self->strings, name);
rect.begin = begin_time;
rect.end = end_time;
if (rect.end == rect.begin)
rect.area.width = 1;
g_array_append_val (self->rectangles, rect);
self->sorted = FALSE;
}
void
rectangles_free (Rectangles *self)
{
g_string_chunk_free (self->strings);
g_array_unref (self->rectangles);
g_hash_table_unref (self->colors);
g_hash_table_unref (self->y_indexes);
sp_color_cycle_unref (self->cycle);
g_slice_free (Rectangles, self);
}
static gint
sort_rectangles (gconstpointer a,
gconstpointer b)
{
const Rectangle *r1 = a;
const Rectangle *r2 = b;
gint64 r = r1->begin - r2->begin;
if (r == 0)
r = r1->end - r2->end;
if (r > 0) return 1;
else if (r < 0) return -1;
else return 0;
}
static void
rectangles_sort (Rectangles *self)
{
guint sequence = 0;
g_assert (self != NULL);
if (self->sorted)
return;
g_array_sort (self->rectangles, sort_rectangles);
g_hash_table_remove_all (self->y_indexes);
for (guint i = 0; i < self->rectangles->len; i++)
{
const Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i);
if (!g_hash_table_contains (self->y_indexes, rect->name))
{
GdkRGBA rgba;
sp_color_cycle_next (self->cycle, &rgba);
g_hash_table_insert (self->y_indexes, (gchar *)rect->name, GUINT_TO_POINTER (++sequence));
g_hash_table_insert (self->colors, (gchar *)rect->name, g_memdup (&rgba, sizeof rgba));
}
}
self->sorted = TRUE;
}
void
rectangles_draw (Rectangles *self,
GtkWidget *row,
cairo_t *cr)
{
GtkAllocation alloc;
gdouble range;
guint n_rows;
g_assert (self != NULL);
g_assert (SP_IS_VISUALIZER_ROW (row));
g_assert (cr != NULL);
if (!self->sorted)
rectangles_sort (self);
gtk_widget_get_allocation (row, &alloc);
n_rows = g_hash_table_size (self->y_indexes);
if (n_rows == 0 || alloc.height == 0)
return;
range = self->end_time - self->begin_time;
for (guint i = 0; i < self->rectangles->len; i++)
{
Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i);
guint y_index = GPOINTER_TO_UINT (g_hash_table_lookup (self->y_indexes, rect->name));
SpVisualizerRowRelativePoint in_points[2];
SpVisualizerRowAbsolutePoint out_points[2];
GdkRectangle r;
GdkRGBA *rgba;
g_assert (y_index > 0);
g_assert (y_index <= n_rows);
in_points[0].x = (rect->begin - self->begin_time) / range;
in_points[0].y = (y_index - 1) / (gdouble)n_rows;
in_points[1].x = (rect->end - self->begin_time) / range;
in_points[1].y = 0;
sp_visualizer_row_translate_points (SP_VISUALIZER_ROW (row),
in_points, G_N_ELEMENTS (in_points),
out_points, G_N_ELEMENTS (out_points));
r.height = alloc.height / (gdouble)n_rows;
r.x = out_points[0].x;
r.y = out_points[0].y - r.height;
if (rect->end <= rect->begin)
r.width = 1;
else
r.width = MAX (1, out_points[1].x - out_points[0].x);
rect->area = r;
rgba = g_hash_table_lookup (self->colors, rect->name);
gdk_cairo_rectangle (cr, &r);
gdk_cairo_set_source_rgba (cr, rgba);
cairo_fill (cr);
}
}
void
rectangles_set_end_time (Rectangles *self,
gint64 end_time)
{
/* We might not know the real end time until we've exhausted the stream */
self->end_time = end_time;
}
gboolean
rectangles_query_tooltip (Rectangles *self,
GtkTooltip *tooltip,
const gchar *group,
gint x,
gint y)
{
g_assert (self != NULL);
g_assert (GTK_IS_TOOLTIP (tooltip));
/* TODO: Sort, binary search, etc. */
for (guint i = 0; i < self->rectangles->len; i++)
{
const Rectangle *r = &g_array_index (self->rectangles, Rectangle, i);
if (r->area.x <= x &&
r->area.y <= y &&
r->area.x + r->area.width >= x &&
r->area.y + r->area.height >= y)
{
g_autofree gchar *text = g_strdup_printf ("%s:%s: %s", group, r->name, r->message);
gtk_tooltip_set_text (tooltip, text);
return TRUE;
}
}
return FALSE;
}