mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
We still don't want to fully rely on the CSS machinery because that would require widgets, which is far too heavy for the number of render nodes we need to regularly draw.
387 lines
12 KiB
C
387 lines
12 KiB
C
/* sysprof-mark-chart-row.c
|
|
*
|
|
* Copyright 2023 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 "config.h"
|
|
|
|
#include <sysprof-analyze.h>
|
|
|
|
#include "sysprof-mark-chart-row-private.h"
|
|
|
|
struct _SysprofMarkChartRow
|
|
{
|
|
GtkWidget parent_instance;
|
|
|
|
GCancellable *cancellable;
|
|
SysprofTimeSeries *series;
|
|
SysprofMarkChartItem *item;
|
|
|
|
GdkRGBA accent_fg_color;
|
|
GdkRGBA accent_bg_color;
|
|
GdkRGBA success_bg_color;
|
|
|
|
guint update_source;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_ITEM,
|
|
N_PROPS
|
|
};
|
|
|
|
G_DEFINE_FINAL_TYPE (SysprofMarkChartRow, sysprof_mark_chart_row, GTK_TYPE_WIDGET)
|
|
|
|
static GParamSpec *properties [N_PROPS];
|
|
|
|
static void
|
|
cancel_and_clear (GCancellable **cancellable)
|
|
{
|
|
g_cancellable_cancel (*cancellable);
|
|
g_clear_object (cancellable);
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_set_series (SysprofMarkChartRow *self,
|
|
SysprofTimeSeries *series)
|
|
{
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
|
|
g_clear_pointer (&self->series, sysprof_time_series_unref);
|
|
self->series = series ? sysprof_time_series_ref (series) : NULL;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_css_changed (GtkWidget *widget,
|
|
GtkCssStyleChange *change)
|
|
{
|
|
SysprofMarkChartRow *self = (SysprofMarkChartRow *)widget;
|
|
|
|
g_assert (SYSPROF_MARK_CHART_ROW (self));
|
|
|
|
GTK_WIDGET_CLASS (sysprof_mark_chart_row_parent_class)->css_changed (widget, change);
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
{
|
|
GtkStyleContext *style_context;
|
|
|
|
style_context = gtk_widget_get_style_context (widget);
|
|
gtk_style_context_lookup_color (style_context, "accent_bg_color", &self->accent_bg_color);
|
|
gtk_style_context_lookup_color (style_context, "accent_fg_color", &self->accent_fg_color);
|
|
gtk_style_context_lookup_color (style_context, "success_bg_color", &self->success_bg_color);
|
|
}
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_snapshot (GtkWidget *widget,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
SysprofMarkChartRow *self = (SysprofMarkChartRow *)widget;
|
|
const SysprofTimeSeriesValue *values;
|
|
PangoLayout *layout;
|
|
GListModel *model;
|
|
guint n_values;
|
|
float last_end_x;
|
|
int width;
|
|
int height;
|
|
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
g_assert (GTK_IS_SNAPSHOT (snapshot));
|
|
|
|
if (self->series == NULL)
|
|
return;
|
|
|
|
model = sysprof_time_series_get_model (self->series);
|
|
values = sysprof_time_series_get_values (self->series, &n_values);
|
|
if (model == NULL || n_values == 0)
|
|
return;
|
|
|
|
width = gtk_widget_get_width (widget);
|
|
height = gtk_widget_get_height (widget);
|
|
|
|
layout = gtk_widget_create_pango_layout (widget, NULL);
|
|
pango_layout_set_single_paragraph_mode (layout, TRUE);
|
|
pango_layout_set_height (layout, height * PANGO_SCALE);
|
|
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
|
|
|
|
last_end_x = 0;
|
|
|
|
/* First pass, draw our rectangles for duration which we
|
|
* always want in the background compared to "points" which
|
|
* are marks w/o a duration.
|
|
*/
|
|
for (guint i = 0; i < n_values; i++)
|
|
{
|
|
const SysprofTimeSeriesValue *v = &values[i];
|
|
|
|
if (v->begin != v->end)
|
|
{
|
|
graphene_rect_t rect;
|
|
float end_x;
|
|
|
|
rect = GRAPHENE_RECT_INIT (floorf (v->begin * width),
|
|
0,
|
|
ceilf ((v->end - v->begin) * width),
|
|
height);
|
|
|
|
/* Ignore empty sized draws */
|
|
if (rect.size.width == 0)
|
|
continue;
|
|
|
|
/* Cull draw unless it will extend past last rect */
|
|
end_x = rect.origin.x + rect.size.width;
|
|
if (end_x <= last_end_x)
|
|
continue;
|
|
else
|
|
last_end_x = end_x;
|
|
|
|
gtk_snapshot_append_color (snapshot, &self->accent_bg_color, &rect);
|
|
|
|
/* Only show the message text if the next item does
|
|
* not overlap and there are at least 20 pixels
|
|
* available to render into.
|
|
*/
|
|
if (i + 1 < n_values &&
|
|
values[i+1].begin > v->end &&
|
|
rect.size.width > 20)
|
|
{
|
|
g_autoptr(SysprofDocumentMark) mark = g_list_model_get_item (model, v->index);
|
|
const char *message = sysprof_document_mark_get_message (mark);
|
|
|
|
if (message && message[0])
|
|
{
|
|
pango_layout_set_width (layout, rect.size.width * PANGO_SCALE);
|
|
pango_layout_set_text (layout, message, -1);
|
|
|
|
gtk_snapshot_save (snapshot);
|
|
gtk_snapshot_push_clip (snapshot,
|
|
&GRAPHENE_RECT_INIT (v->begin * width,
|
|
0,
|
|
v->end * width,
|
|
height));
|
|
gtk_snapshot_translate (snapshot,
|
|
&GRAPHENE_POINT_INIT (v->begin * width, 0));
|
|
gtk_snapshot_append_layout (snapshot, layout, &self->accent_fg_color);
|
|
gtk_snapshot_pop (snapshot);
|
|
gtk_snapshot_restore (snapshot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (guint i = 0; i < n_values; i++)
|
|
{
|
|
const SysprofTimeSeriesValue *v = &values[i];
|
|
|
|
if (v->begin == v->end)
|
|
{
|
|
gtk_snapshot_save (snapshot);
|
|
gtk_snapshot_translate (snapshot,
|
|
&GRAPHENE_POINT_INIT (v->begin * width, height / 2));
|
|
gtk_snapshot_rotate (snapshot, 45.f);
|
|
gtk_snapshot_append_color (snapshot,
|
|
&self->success_bg_color,
|
|
&GRAPHENE_RECT_INIT (-4, -4, 8, 8));
|
|
gtk_snapshot_restore (snapshot);
|
|
}
|
|
}
|
|
|
|
g_clear_object (&layout);
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_load_time_series_cb (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
SysprofMarkChartItem *item = (SysprofMarkChartItem *)object;
|
|
g_autoptr(SysprofMarkChartRow) self = user_data;
|
|
g_autoptr(SysprofTimeSeries) series = NULL;
|
|
|
|
g_assert (SYSPROF_IS_MARK_CHART_ITEM (item));
|
|
g_assert (G_IS_ASYNC_RESULT (result));
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
|
|
series = sysprof_mark_chart_item_load_time_series_finish (item, result, NULL);
|
|
|
|
sysprof_mark_chart_row_set_series (self, series);
|
|
}
|
|
|
|
static gboolean
|
|
sysprof_mark_chart_row_dispatch_update (gpointer user_data)
|
|
{
|
|
SysprofMarkChartRow *self = user_data;
|
|
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
|
|
g_clear_handle_id (&self->update_source, g_source_remove);
|
|
cancel_and_clear (&self->cancellable);
|
|
|
|
if (self->item != NULL)
|
|
{
|
|
self->cancellable = g_cancellable_new ();
|
|
sysprof_mark_chart_item_load_time_series (self->item,
|
|
self->cancellable,
|
|
sysprof_mark_chart_row_load_time_series_cb,
|
|
g_object_ref (self));
|
|
}
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_queue_update (SysprofMarkChartRow *self)
|
|
{
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
|
|
cancel_and_clear (&self->cancellable);
|
|
g_clear_handle_id (&self->update_source, g_source_remove);
|
|
self->update_source = g_idle_add (sysprof_mark_chart_row_dispatch_update, self);
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_item_changed_cb (SysprofMarkChartRow *self,
|
|
SysprofMarkChartItem *item)
|
|
{
|
|
g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
g_assert (SYSPROF_IS_MARK_CHART_ITEM (item));
|
|
|
|
sysprof_mark_chart_row_queue_update (self);
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_dispose (GObject *object)
|
|
{
|
|
SysprofMarkChartRow *self = (SysprofMarkChartRow *)object;
|
|
|
|
g_clear_handle_id (&self->update_source, g_source_remove);
|
|
|
|
sysprof_mark_chart_row_set_item (self, NULL);
|
|
|
|
G_OBJECT_CLASS (sysprof_mark_chart_row_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
SysprofMarkChartRow *self = SYSPROF_MARK_CHART_ROW (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ITEM:
|
|
g_value_set_object (value, sysprof_mark_chart_row_get_item (self));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
SysprofMarkChartRow *self = SYSPROF_MARK_CHART_ROW (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ITEM:
|
|
sysprof_mark_chart_row_set_item (self, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_class_init (SysprofMarkChartRowClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
object_class->dispose = sysprof_mark_chart_row_dispose;
|
|
object_class->get_property = sysprof_mark_chart_row_get_property;
|
|
object_class->set_property = sysprof_mark_chart_row_set_property;
|
|
|
|
widget_class->css_changed = sysprof_mark_chart_row_css_changed;
|
|
widget_class->snapshot = sysprof_mark_chart_row_snapshot;
|
|
|
|
properties [PROP_ITEM] =
|
|
g_param_spec_object ("item", NULL, NULL,
|
|
SYSPROF_TYPE_MARK_CHART_ITEM,
|
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, properties);
|
|
|
|
gtk_widget_class_set_css_name (widget_class, "markchartrow");
|
|
}
|
|
|
|
static void
|
|
sysprof_mark_chart_row_init (SysprofMarkChartRow *self)
|
|
{
|
|
}
|
|
|
|
SysprofMarkChartItem *
|
|
sysprof_mark_chart_row_get_item (SysprofMarkChartRow *self)
|
|
{
|
|
g_return_val_if_fail (SYSPROF_IS_MARK_CHART_ROW (self), NULL);
|
|
|
|
return self->item;
|
|
}
|
|
|
|
void
|
|
sysprof_mark_chart_row_set_item (SysprofMarkChartRow *self,
|
|
SysprofMarkChartItem *item)
|
|
{
|
|
g_return_if_fail (SYSPROF_IS_MARK_CHART_ROW (self));
|
|
g_return_if_fail (!item || SYSPROF_IS_MARK_CHART_ITEM (item));
|
|
|
|
if (self->item == item)
|
|
return;
|
|
|
|
cancel_and_clear (&self->cancellable);
|
|
g_clear_handle_id (&self->update_source, g_source_remove);
|
|
|
|
if (self->item)
|
|
g_signal_handlers_disconnect_by_func (self->item,
|
|
G_CALLBACK (sysprof_mark_chart_row_item_changed_cb),
|
|
self);
|
|
|
|
g_set_object (&self->item, item);
|
|
|
|
if (item)
|
|
g_signal_connect_object (self->item,
|
|
"changed",
|
|
G_CALLBACK (sysprof_mark_chart_row_item_changed_cb),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
|
|
|
sysprof_mark_chart_row_queue_update (self);
|
|
}
|