From 3cddf32eca273d62024c52cf5118c1cf4910639b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 23 Jun 2023 17:32:14 -0700 Subject: [PATCH] libsysprof-gtk: start on new axis/series design This will mean we move series work from libsysprof-analyze into libsysprof-gtk so we get access to things like GtkExpression. --- src/libsysprof-gtk/meson.build | 8 + src/libsysprof-gtk/sysprof-axis-private.h | 61 +++ src/libsysprof-gtk/sysprof-axis.c | 160 ++++++ src/libsysprof-gtk/sysprof-axis.h | 47 ++ .../sysprof-normalized-series-item.c | 148 ++++++ .../sysprof-normalized-series-item.h | 39 ++ .../sysprof-normalized-series.c | 477 ++++++++++++++++++ .../sysprof-normalized-series.h | 63 +++ src/libsysprof-gtk/sysprof-series-private.h | 53 ++ src/libsysprof-gtk/sysprof-series.c | 255 ++++++++++ src/libsysprof-gtk/sysprof-series.h | 52 ++ src/libsysprof-gtk/sysprof-value-axis.c | 212 ++++++++ src/libsysprof-gtk/sysprof-value-axis.h | 51 ++ src/sysprof-series.h | 37 ++ 14 files changed, 1663 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-axis-private.h create mode 100644 src/libsysprof-gtk/sysprof-axis.c create mode 100644 src/libsysprof-gtk/sysprof-axis.h create mode 100644 src/libsysprof-gtk/sysprof-normalized-series-item.c create mode 100644 src/libsysprof-gtk/sysprof-normalized-series-item.h create mode 100644 src/libsysprof-gtk/sysprof-normalized-series.c create mode 100644 src/libsysprof-gtk/sysprof-normalized-series.h create mode 100644 src/libsysprof-gtk/sysprof-series-private.h create mode 100644 src/libsysprof-gtk/sysprof-series.c create mode 100644 src/libsysprof-gtk/sysprof-series.h create mode 100644 src/libsysprof-gtk/sysprof-value-axis.c create mode 100644 src/libsysprof-gtk/sysprof-value-axis.h create mode 100644 src/sysprof-series.h diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 71849b47..f0d3ce57 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -1,4 +1,5 @@ libsysprof_gtk_public_sources = [ + 'sysprof-axis.c', 'sysprof-callgraph-view.c', 'sysprof-chart.c', 'sysprof-chart-layer.c', @@ -6,6 +7,9 @@ libsysprof_gtk_public_sources = [ 'sysprof-line-layer.c', 'sysprof-mark-chart.c', 'sysprof-mark-table.c', + 'sysprof-normalized-series.c', + 'sysprof-normalized-series-item.c', + 'sysprof-series.c', 'sysprof-session.c', 'sysprof-split-layer.c', 'sysprof-time-span-layer.c', @@ -15,6 +19,7 @@ libsysprof_gtk_public_sources = [ libsysprof_gtk_public_headers = [ 'sysprof-gtk.h', + 'sysprof-axis.h', 'sysprof-callgraph-view.h', 'sysprof-chart.h', 'sysprof-chart-layer.h', @@ -22,6 +27,9 @@ libsysprof_gtk_public_headers = [ 'sysprof-line-layer.h', 'sysprof-mark-chart.h', 'sysprof-mark-table.h', + 'sysprof-normalized-series.h', + 'sysprof-normalized-series-item.h', + 'sysprof-series.h', 'sysprof-session.h', 'sysprof-split-layer.h', 'sysprof-time-span-layer.h', diff --git a/src/libsysprof-gtk/sysprof-axis-private.h b/src/libsysprof-gtk/sysprof-axis-private.h new file mode 100644 index 00000000..853fcda0 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-axis-private.h @@ -0,0 +1,61 @@ +/* sysprof-axis-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-axis.h" + +G_BEGIN_DECLS + +struct _SysprofAxis +{ + GObject parent_instance; + char *title; +}; + +struct _SysprofAxisClass +{ + GObjectClass parent_class; + + void (*get_min_value) (SysprofAxis *axis, + GValue *min_value); + double (*normalize) (SysprofAxis *axis, + const GValue *value); +}; + +#define SYSPROF_AXIS_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_AXIS, SysprofAxisClass) + +static inline void +_sysprof_axis_get_min_value (SysprofAxis *axis, + GValue *min_value) +{ + SYSPROF_AXIS_GET_CLASS (axis)->get_min_value (axis, min_value); +} + +static inline double +_sysprof_axis_normalize (SysprofAxis *axis, + const GValue *value) +{ + return SYSPROF_AXIS_GET_CLASS (axis)->normalize (axis, value); +} + +void _sysprof_axis_emit_range_changed (SysprofAxis *self); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-axis.c b/src/libsysprof-gtk/sysprof-axis.c new file mode 100644 index 00000000..ff7f7069 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-axis.c @@ -0,0 +1,160 @@ +/* sysprof-axis.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-axis-private.h" + +enum { + PROP_0, + PROP_TITLE, + N_PROPS +}; + +enum { + RANGE_CHANGED, + N_SIGNALS +}; + +G_DEFINE_ABSTRACT_TYPE (SysprofAxis, sysprof_axis, G_TYPE_OBJECT) + +static GParamSpec *properties[N_PROPS]; +static guint signals[N_SIGNALS]; + +static double +sysprof_axis_real_normalize (SysprofAxis *axis, + const GValue *value) +{ + return -INFINITY; +} + +static void +sysprof_axis_finalize (GObject *object) +{ + SysprofAxis *self = (SysprofAxis *)object; + + g_clear_pointer (&self->title, g_free); + + G_OBJECT_CLASS (sysprof_axis_parent_class)->finalize (object); +} + +static void +sysprof_axis_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofAxis *self = SYSPROF_AXIS (object); + + switch (prop_id) + { + case PROP_TITLE: + g_value_set_string (value, sysprof_axis_get_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_axis_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofAxis *self = SYSPROF_AXIS (object); + + switch (prop_id) + { + case PROP_TITLE: + sysprof_axis_set_title (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_axis_class_init (SysprofAxisClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_axis_finalize; + object_class->get_property = sysprof_axis_get_property; + object_class->set_property = sysprof_axis_set_property; + + klass->normalize = sysprof_axis_real_normalize; + + properties [PROP_TITLE] = + g_param_spec_string ("title", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + /** + * SysprofAxis::range-changed: + * + * This signal is emitted when the range of the axis has changed. + */ + signals[RANGE_CHANGED] = + g_signal_new ("range-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +static void +sysprof_axis_init (SysprofAxis *self) +{ +} + +const char * +sysprof_axis_get_title (SysprofAxis *self) +{ + g_return_val_if_fail (SYSPROF_IS_AXIS (self), NULL); + + return self->title; +} + +void +sysprof_axis_set_title (SysprofAxis *self, + const char *title) +{ + g_return_if_fail (SYSPROF_IS_AXIS (self)); + + if (g_set_str (&self->title, title)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); +} + +void +_charts_axis_emit_range_changed (SysprofAxis *self) +{ + g_return_if_fail (SYSPROF_IS_AXIS (self)); + + g_signal_emit (self, signals[RANGE_CHANGED], 0); +} diff --git a/src/libsysprof-gtk/sysprof-axis.h b/src/libsysprof-gtk/sysprof-axis.h new file mode 100644 index 00000000..ec0eb2bc --- /dev/null +++ b/src/libsysprof-gtk/sysprof-axis.h @@ -0,0 +1,47 @@ +/* sysprof-axis.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_AXIS (sysprof_axis_get_type()) +#define SYSPROF_IS_AXIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_AXIS)) +#define SYSPROF_AXIS(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_AXIS, SysprofAxis)) +#define SYSPROF_AXIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_AXIS, SysprofAxisClass)) + +typedef struct _SysprofAxis SysprofAxis; +typedef struct _SysprofAxisClass SysprofAxisClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_axis_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_axis_get_title (SysprofAxis *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_axis_set_title (SysprofAxis *self, + const char *title); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofAxis, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-normalized-series-item.c b/src/libsysprof-gtk/sysprof-normalized-series-item.c new file mode 100644 index 00000000..829d28a0 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-normalized-series-item.c @@ -0,0 +1,148 @@ +/* sysprof-normalized-series-item.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-normalized-series-item.h" + +struct _SysprofNormalizedSeriesItem +{ + GObject parent_instance; + GObject *item; + float value; +}; + +enum { + PROP_0, + PROP_ITEM, + PROP_VALUE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofNormalizedSeriesItem, sysprof_normalized_series_item, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_normalized_series_item_finalize (GObject *object) +{ + SysprofNormalizedSeriesItem *self = (SysprofNormalizedSeriesItem *)object; + + g_clear_object (&self->item); + + G_OBJECT_CLASS (sysprof_normalized_series_item_parent_class)->finalize (object); +} + +static void +sysprof_normalized_series_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofNormalizedSeriesItem *self = SYSPROF_NORMALIZED_SERIES_ITEM (object); + + switch (prop_id) + { + case PROP_ITEM: + g_value_set_object (value, self->item); + break; + + case PROP_VALUE: + g_value_set_float (value, self->value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_normalized_series_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofNormalizedSeriesItem *self = SYSPROF_NORMALIZED_SERIES_ITEM (object); + + switch (prop_id) + { + case PROP_ITEM: + self->item = g_value_dup_object (value); + break; + + case PROP_VALUE: + self->value = g_value_get_float (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_normalized_series_item_class_init (SysprofNormalizedSeriesItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_normalized_series_item_finalize; + object_class->get_property = sysprof_normalized_series_item_get_property; + object_class->set_property = sysprof_normalized_series_item_set_property; + + properties [PROP_ITEM] = + g_param_spec_object ("item", NULL, NULL, + G_TYPE_OBJECT, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_VALUE] = + g_param_spec_float ("value", NULL, NULL, + 0, 1, 0, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_normalized_series_item_init (SysprofNormalizedSeriesItem *self) +{ +} + +float +sysprof_normalized_series_item_get_value (SysprofNormalizedSeriesItem *self) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES_ITEM (self), 0); + + return self->value; +} + +/** + * sysprof_normalized_series_item_get_item: + * @self: a #SysprofNormalizedSeriesItem + * + * Gets the underlying item the normalized value was calculated for. + * + * Returns: (transfer none) (type GObject): a #GObject + */ +gpointer +sysprof_normalized_series_item_get_item (SysprofNormalizedSeriesItem *self) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES_ITEM (self), NULL); + + return self->item; +} diff --git a/src/libsysprof-gtk/sysprof-normalized-series-item.h b/src/libsysprof-gtk/sysprof-normalized-series-item.h new file mode 100644 index 00000000..e20c8474 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-normalized-series-item.h @@ -0,0 +1,39 @@ +/* sysprof-normalized-series-item.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_NORMALIZED_SERIES_ITEM (sysprof_normalized_series_item_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofNormalizedSeriesItem, sysprof_normalized_series_item, SYSPROF, NORMALIZED_SERIES_ITEM, GObject) + +SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_normalized_series_item_get_item (SysprofNormalizedSeriesItem *self); +SYSPROF_AVAILABLE_IN_ALL +float sysprof_normalized_series_item_get_value (SysprofNormalizedSeriesItem *self); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-normalized-series.c b/src/libsysprof-gtk/sysprof-normalized-series.c new file mode 100644 index 00000000..41689757 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-normalized-series.c @@ -0,0 +1,477 @@ +/* sysprof-normalized-series.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "eggbitset.h" + +#include "sysprof-axis-private.h" +#include "sysprof-normalized-series.h" +#include "sysprof-normalized-series-item.h" +#include "sysprof-series-private.h" + +#define SYSPROF_NORMALIZED_SERIES_STEP_TIME_USEC (1000) /* 1 msec */ + +struct _SysprofNormalizedSeries +{ + SysprofSeries parent_instance; + + SysprofSeries *series; + SysprofAxis *axis; + GtkExpression *expression; + GArray *values; + EggBitset *missing; + + gulong range_changed_handler; + guint update_source; +}; + +struct _SysprofNormalizedSeriesClass +{ + SysprofSeriesClass parent_class; +}; + +enum { + PROP_0, + PROP_AXIS, + PROP_EXPRESSION, + PROP_SERIES, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofNormalizedSeries, sysprof_normalized_series, SYSPROF_TYPE_SERIES) + +static GParamSpec *properties [N_PROPS]; + +static gboolean +sysprof_normalized_series_update_missing (gpointer user_data) +{ + SysprofNormalizedSeries *self = user_data; + GListModel *model; + EggBitsetIter iter; + guint position; + + g_assert (SYSPROF_IS_NORMALIZED_SERIES (self)); + + model = G_LIST_MODEL (self); + + if (egg_bitset_iter_init_first (&iter, self->missing, &position)) + { + gint64 deadline = g_get_monotonic_time () + SYSPROF_NORMALIZED_SERIES_STEP_TIME_USEC; + guint first = position; + + for (;;) + { + GObject *item = g_list_model_get_item (model, position); + GValue value = G_VALUE_INIT; + guint next; + + gtk_expression_evaluate (self->expression, item, &value); + + g_array_index (self->values, float, position) = _sysprof_axis_normalize (self->axis, &value); + + egg_bitset_remove (self->missing, position); + + g_value_unset (&value); + g_clear_object (&item); + + if (!egg_bitset_iter_next (&iter, &next) || next != position + 1) + { + g_list_model_items_changed (G_LIST_MODEL (self), + first, + position-first+1, + position-first+1); + first = next; + } + + if (g_get_monotonic_time () >= deadline) + break; + + position = next; + } + } + + if (egg_bitset_is_empty (self->missing)) + { + self->update_source = 0; + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +static void +sysprof_normalized_series_maybe_update (SysprofNormalizedSeries *self) +{ + GSource *source; + + g_assert (SYSPROF_IS_NORMALIZED_SERIES (self)); + + if (self->update_source) + return; + + if (egg_bitset_is_empty (self->missing)) + return; + + source = g_idle_source_new (); + g_source_set_callback (source, sysprof_normalized_series_update_missing, self, NULL); + g_source_set_static_name (source, "[SysprofNormalizedSeries]"); + g_source_set_priority (source, G_PRIORITY_LOW); + self->update_source = g_source_attach (source, NULL); + g_source_unref (source); +} + +static void +sysprof_normalized_series_items_changed (SysprofSeries *series, + GListModel *model, + guint position, + guint removed, + guint added) +{ + SysprofNormalizedSeries *self = (SysprofNormalizedSeries *)series; + + g_assert (SYSPROF_IS_NORMALIZED_SERIES (self)); + g_assert (G_IS_LIST_MODEL (model)); + + egg_bitset_splice (self->missing, position, removed, added); + egg_bitset_add_range (self->missing, position, added); + + if (removed > 0) + g_array_remove_range (self->values, position, removed); + + if (added > 0) + { + if (position == self->values->len) + { + g_array_set_size (self->values, self->values->len + added); + } + else + { + static const float empty[32] = {0}; + const float *vals = empty; + float *alloc = NULL; + + if (added > G_N_ELEMENTS (empty)) + vals = alloc = g_new0 (float, added); + + g_array_insert_vals (self->values, position, vals, added); + + g_free (alloc); + } + } + + SYSPROF_SERIES_CLASS (sysprof_normalized_series_parent_class)->items_changed (series, model, position, removed, added); + + sysprof_normalized_series_maybe_update (self); +} + +static void +sysprof_normalized_series_invalidate (SysprofNormalizedSeries *self) +{ + guint n_items; + + g_assert (SYSPROF_IS_NORMALIZED_SERIES (self)); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + + if (n_items > 0) + { + egg_bitset_remove_all (self->missing); + egg_bitset_add_range (self->missing, 0, n_items); + + g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items); + } +} + +static gpointer +sysprof_normalized_series_get_series_item (SysprofSeries *series, + guint position, + gpointer item) +{ + SysprofNormalizedSeries *self = SYSPROF_NORMALIZED_SERIES (series); + SysprofNormalizedSeriesItem *ret; + + ret = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES_ITEM, + "item", item, + "value", g_array_index (self->values, float, position), + NULL); + + g_object_unref (item); + + return ret; +} + +static void +sysprof_normalized_series_dispose (GObject *object) +{ + SysprofNormalizedSeries *self = (SysprofNormalizedSeries *)object; + + g_clear_handle_id (&self->update_source, g_source_remove); + + g_clear_signal_handler (&self->range_changed_handler, self->axis); + g_clear_object (&self->axis); + + g_clear_object (&self->series); + + g_clear_pointer (&self->expression, gtk_expression_unref); + g_clear_pointer (&self->missing, egg_bitset_unref); + g_clear_pointer (&self->values, g_array_unref); + + G_OBJECT_CLASS (sysprof_normalized_series_parent_class)->dispose (object); +} + +static void +sysprof_normalized_series_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofNormalizedSeries *self = SYSPROF_NORMALIZED_SERIES (object); + + switch (prop_id) + { + case PROP_AXIS: + g_value_set_object (value, sysprof_normalized_series_get_axis (self)); + break; + + case PROP_EXPRESSION: + gtk_value_set_expression (value, sysprof_normalized_series_get_expression (self)); + break; + + case PROP_SERIES: + g_value_set_object (value, sysprof_normalized_series_get_series (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_normalized_series_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofNormalizedSeries *self = SYSPROF_NORMALIZED_SERIES (object); + + switch (prop_id) + { + case PROP_AXIS: + sysprof_normalized_series_set_axis (self, g_value_get_object (value)); + break; + + case PROP_EXPRESSION: + sysprof_normalized_series_set_expression (self, gtk_value_get_expression (value)); + break; + + case PROP_SERIES: + sysprof_normalized_series_set_series (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_normalized_series_class_init (SysprofNormalizedSeriesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSeriesClass *series_class = SYSPROF_SERIES_CLASS (klass); + + object_class->dispose = sysprof_normalized_series_dispose; + object_class->get_property = sysprof_normalized_series_get_property; + object_class->set_property = sysprof_normalized_series_set_property; + + series_class->series_item_type = SYSPROF_TYPE_NORMALIZED_SERIES_ITEM; + series_class->get_series_item = sysprof_normalized_series_get_series_item; + series_class->items_changed = sysprof_normalized_series_items_changed; + + properties [PROP_AXIS] = + g_param_spec_object ("axis", NULL, NULL, + SYSPROF_TYPE_AXIS, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_EXPRESSION] = + gtk_param_spec_expression ("expression", NULL, NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SERIES] = + g_param_spec_object ("series", NULL, NULL, + SYSPROF_TYPE_SERIES, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_normalized_series_init (SysprofNormalizedSeries *self) +{ + self->values = g_array_new (FALSE, TRUE, sizeof (float)); + self->missing = egg_bitset_new_empty (); +} + +/** + * sysprof_normalized_series_new: + * @series: (transfer-full) (nullable): a #SysprofSeries + * @axis: (transfer-full) (nullable): a #SysprofAxis + * @expression: (transfer-full) (nullable): a #GtkExpression + * + * Creates a new series that will normalize values from @series + * by extracting values using @expression and determining where + * they fall between the axis's range. + * + * Returns: (transfer full) (type SysprofNormalizedSeries): a #SysprofNormalizedSeries + */ +SysprofSeries * +sysprof_normalized_series_new (SysprofSeries *series, + SysprofAxis *axis, + GtkExpression *expression) +{ + SysprofNormalizedSeries *normalized; + + g_return_val_if_fail (!series || SYSPROF_IS_SERIES (series), NULL); + g_return_val_if_fail (!axis || SYSPROF_IS_AXIS (axis), NULL); + g_return_val_if_fail (!expression || GTK_IS_EXPRESSION (expression), NULL); + + normalized = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, + "axis", axis, + "series", series, + NULL); + + g_clear_object (&series); + g_clear_object (&axis); + g_clear_pointer (&expression, gtk_expression_unref); + + return SYSPROF_SERIES (normalized); +} + +float +sysprof_normalized_series_get_value_at (SysprofNormalizedSeries *self, + guint position) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self), .0f); + + if (egg_bitset_contains (self->missing, position)) + return -INFINITY; + + if (position >= self->values->len) + return -INFINITY; + + return g_array_index (self->values, float, position); +} + +/** + * sysprof_normalized_series_get_axis: + * @self: a #SysprofNormalizedSeries + * + * Gets the axis used to normalize values. + * + * Returns: (transfer none) (nullable): a #SysprofAxis or %NULL + */ +SysprofAxis * +sysprof_normalized_series_get_axis (SysprofNormalizedSeries *self) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self), NULL); + + return self->axis; +} + +void +sysprof_normalized_series_set_axis (SysprofNormalizedSeries *self, + SysprofAxis *axis) +{ + g_return_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self)); + g_return_if_fail (!axis || SYSPROF_IS_AXIS (axis)); + + if (g_set_object (&self->axis, axis)) + { + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_AXIS]); + sysprof_normalized_series_invalidate (self); + } +} + +/** + * sysprof_normalized_series_get_expression: + * @self: a #SysprofNormalizedSeries + * + * Gets the expression to used to extract values from items within + * #SysprofNormalizedSeries:series. + * + * Returns: (transfer none) (nullable): a #GtkExpression or %NULL + */ +GtkExpression * +sysprof_normalized_series_get_expression (SysprofNormalizedSeries *self) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self), NULL); + + return self->expression; +} + +void +sysprof_normalized_series_set_expression (SysprofNormalizedSeries *self, + GtkExpression *expression) +{ + g_return_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self)); + g_return_if_fail (!expression || GTK_IS_EXPRESSION (expression)); + + if (expression == self->expression) + return; + + if (expression) + gtk_expression_ref (expression); + + g_clear_pointer (&self->expression, gtk_expression_unref); + + self->expression = expression; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPRESSION]); +} + +/** + * sysprof_normalized_series_get_series: + * @self: a #SysprofNormalizedSeries + * + * Gets the series containing values to normalize. + * + * Returns: (transfer none) (nullable): a #SysprofSeries or %NULL + */ +SysprofSeries * +sysprof_normalized_series_get_series (SysprofNormalizedSeries *self) +{ + g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self), NULL); + + return self->series; +} + +void +sysprof_normalized_series_set_series (SysprofNormalizedSeries *self, + SysprofSeries *series) +{ + g_return_if_fail (SYSPROF_IS_NORMALIZED_SERIES (self)); + g_return_if_fail (!series || SYSPROF_IS_SERIES (series)); + + if (g_set_object (&self->series, series)) + { + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SERIES]); + sysprof_series_set_model (SYSPROF_SERIES (self), G_LIST_MODEL (series)); + } +} diff --git a/src/libsysprof-gtk/sysprof-normalized-series.h b/src/libsysprof-gtk/sysprof-normalized-series.h new file mode 100644 index 00000000..e6c3fc37 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-normalized-series.h @@ -0,0 +1,63 @@ +/* sysprof-normalized-series.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-axis.h" +#include "sysprof-series.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_NORMALIZED_SERIES (sysprof_normalized_series_get_type()) +#define SYSPROF_IS_NORMALIZED_SERIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_NORMALIZED_SERIES)) +#define SYSPROF_NORMALIZED_SERIES(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_NORMALIZED_SERIES, SysprofNormalizedSeries)) +#define SYSPROF_NORMALIZED_SERIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_NORMALIZED_SERIES, SysprofNormalizedSeriesClass)) + +typedef struct _SysprofNormalizedSeries SysprofNormalizedSeries; +typedef struct _SysprofNormalizedSeriesClass SysprofNormalizedSeriesClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_normalized_series_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofSeries *sysprof_normalized_series_new (SysprofSeries *series, + SysprofAxis *axis, + GtkExpression *expression); +SYSPROF_AVAILABLE_IN_ALL +GtkExpression *sysprof_normalized_series_get_expression (SysprofNormalizedSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_normalized_series_set_expression (SysprofNormalizedSeries *self, + GtkExpression *expression); +SYSPROF_AVAILABLE_IN_ALL +SysprofAxis *sysprof_normalized_series_get_axis (SysprofNormalizedSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_normalized_series_set_axis (SysprofNormalizedSeries *self, + SysprofAxis *axis); +SYSPROF_AVAILABLE_IN_ALL +SysprofSeries *sysprof_normalized_series_get_series (SysprofNormalizedSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_normalized_series_set_series (SysprofNormalizedSeries *self, + SysprofSeries *series); +SYSPROF_AVAILABLE_IN_ALL +float sysprof_normalized_series_value_at (SysprofNormalizedSeries *self, + guint position); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-series-private.h b/src/libsysprof-gtk/sysprof-series-private.h new file mode 100644 index 00000000..212b484a --- /dev/null +++ b/src/libsysprof-gtk/sysprof-series-private.h @@ -0,0 +1,53 @@ +/* sysprof-series-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-series.h" + +G_BEGIN_DECLS + +#define SYSPROF_SERIES_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_SERIES, SysprofSeriesClass) + +struct _SysprofSeries +{ + GObject parent_instance; + char *title; + GListModel *model; + gulong items_changed_handler; +}; + +struct _SysprofSeriesClass +{ + GObjectClass parent_class; + + GType series_item_type; + + gpointer (*get_series_item) (SysprofSeries *self, + guint position, + gpointer item); + void (*items_changed) (SysprofSeries *self, + GListModel *model, + guint position, + guint removed, + guint added); +}; + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-series.c b/src/libsysprof-gtk/sysprof-series.c new file mode 100644 index 00000000..b71323ee --- /dev/null +++ b/src/libsysprof-gtk/sysprof-series.c @@ -0,0 +1,255 @@ +/* sysprof-series.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-series-private.h" + +enum { + PROP_0, + PROP_MODEL, + PROP_TITLE, + N_PROPS +}; + +static GType +sysprof_series_get_item_type (GListModel *model) +{ + return SYSPROF_SERIES_GET_CLASS (model)->series_item_type; +} + +static guint +sysprof_series_get_n_items (GListModel *model) +{ + SysprofSeries *self = SYSPROF_SERIES (model); + + if (self->model != NULL) + return g_list_model_get_n_items (self->model); + + return 0; +} + +static gpointer +sysprof_series_get_item (GListModel *model, + guint position) +{ + SysprofSeries *self = SYSPROF_SERIES (model); + gpointer item; + + if (self->model == NULL) + return NULL; + + item = g_list_model_get_item (self->model, position); + + if (SYSPROF_SERIES_GET_CLASS (self)->get_series_item == NULL) + return item; + + return SYSPROF_SERIES_GET_CLASS (self)->get_series_item (self, position, item); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_series_get_item_type; + iface->get_n_items = sysprof_series_get_n_items; + iface->get_item = sysprof_series_get_item; +} + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SysprofSeries, sysprof_series, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_series_items_changed_cb (SysprofSeries *self, + guint position, + guint removed, + guint added, + GListModel *model) +{ + g_assert (SYSPROF_IS_SERIES (self)); + g_assert (G_IS_LIST_MODEL (model)); + + if (SYSPROF_SERIES_GET_CLASS (self)->items_changed) + SYSPROF_SERIES_GET_CLASS (self)->items_changed (self, model, position, removed, added); + + g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); +} + +static void +sysprof_series_dispose (GObject *object) +{ + SysprofSeries *self = (SysprofSeries *)object; + + sysprof_series_set_model (self, NULL); + + g_clear_pointer (&self->title, g_free); + + G_OBJECT_CLASS (sysprof_series_parent_class)->dispose (object); +} + +static void +sysprof_series_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofSeries *self = SYSPROF_SERIES (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, sysprof_series_get_model (self)); + break; + + case PROP_TITLE: + g_value_set_string (value, sysprof_series_get_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_series_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofSeries *self = SYSPROF_SERIES (object); + + switch (prop_id) + { + case PROP_MODEL: + sysprof_series_set_model (self, g_value_get_object (value)); + break; + + case PROP_TITLE: + sysprof_series_set_title (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_series_class_init (SysprofSeriesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = sysprof_series_dispose; + object_class->get_property = sysprof_series_get_property; + object_class->set_property = sysprof_series_set_property; + + klass->series_item_type = G_TYPE_OBJECT; + + properties[PROP_MODEL] = + g_param_spec_object ("model", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TITLE] = + g_param_spec_string ("title", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_series_init (SysprofSeries *self) +{ +} + +const char * +sysprof_series_get_title (SysprofSeries *self) +{ + g_return_val_if_fail (SYSPROF_IS_SERIES (self), NULL); + + return self->title; +} + +void +sysprof_series_set_title (SysprofSeries *self, + const char *title) +{ + g_return_if_fail (SYSPROF_IS_SERIES (self)); + + if (g_set_str (&self->title, title)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); +} + +/** + * sysprof_series_get_model: + * @self: a #SysprofSeries + * + * Returns: (transfer none) (nullable): a #GListModel + */ +GListModel * +sysprof_series_get_model (SysprofSeries *self) +{ + g_return_val_if_fail (SYSPROF_IS_SERIES (self), NULL); + + return self->model; +} + +void +sysprof_series_set_model (SysprofSeries *self, + GListModel *model) +{ + guint old_len = 0; + guint new_len = 0; + + g_return_if_fail (SYSPROF_IS_SERIES (self)); + g_return_if_fail (!model || G_IS_LIST_MODEL (model)); + + if (model == self->model) + return; + + if (model != NULL) + { + new_len = g_list_model_get_n_items (model); + g_object_ref (model); + } + + if (self->model != NULL) + { + old_len = g_list_model_get_n_items (self->model); + g_clear_signal_handler (&self->items_changed_handler, self->model); + SYSPROF_SERIES_GET_CLASS (self)->items_changed (self, self->model, 0, old_len, 0); + } + + g_set_object (&self->model, model); + + if (model != NULL) + { + self->items_changed_handler = + g_signal_connect_object (model, + "items-changed", + G_CALLBACK (sysprof_series_items_changed_cb), + self, + G_CONNECT_SWAPPED); + SYSPROF_SERIES_GET_CLASS (self)->items_changed (self, self->model, 0, 0, new_len); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); +} diff --git a/src/libsysprof-gtk/sysprof-series.h b/src/libsysprof-gtk/sysprof-series.h new file mode 100644 index 00000000..5a30aa75 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-series.h @@ -0,0 +1,52 @@ +/* sysprof-series.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SERIES (sysprof_series_get_type()) +#define SYSPROF_IS_SERIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_SERIES)) +#define SYSPROF_SERIES(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_SERIES, SysprofSeries)) +#define SYSPROF_SERIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_SERIES, SysprofSeriesClass)) + +typedef struct _SysprofSeries SysprofSeries; +typedef struct _SysprofSeriesClass SysprofSeriesClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_series_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_series_get_model (SysprofSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_series_set_model (SysprofSeries *self, + GListModel *model); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_series_get_title (SysprofSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_series_set_title (SysprofSeries *self, + const char *title); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofSeries, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-value-axis.c b/src/libsysprof-gtk/sysprof-value-axis.c new file mode 100644 index 00000000..5cec74f7 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-value-axis.c @@ -0,0 +1,212 @@ +/* sysprof-value-axis.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-value-axis.h" + +enum { + PROP_0, + PROP_MIN_VALUE, + PROP_MAX_VALUE, + N_PROPS +}; + +G_DEFINE_TYPE (SysprofValueAxis, sysprof_value_axis, SYSPROF_TYPE_AXIS) + +static GParamSpec *properties [N_PROPS]; + +static inline double +get_as_double (const GValue *value) +{ + if (G_VALUE_HOLDS_DOUBLE (value)) + return g_value_get_double (value); + + if (G_VALUE_HOLDS_FLOAT (value)) + return g_value_get_float (value); + + if (G_VALUE_HOLDS_INT64 (value)) + return g_value_get_int64 (value); + + if (G_VALUE_HOLDS_UINT64 (value)) + return g_value_get_uint64 (value); + + if (G_VALUE_HOLDS_INT (value)) + return g_value_get_int (value); + + if (G_VALUE_HOLDS_UINT (value)) + return g_value_get_uint (value); + + if (g_value_type_transformable (G_VALUE_TYPE (value), G_TYPE_DOUBLE)) + { + GValue dst = G_VALUE_INIT; + + g_value_init (&dst, G_TYPE_DOUBLE); + g_value_transform (value, &dst); + g_value_unset (&dst); + + return g_value_get_double (&dst); + } + + return -INFINITY; +} + +static void +sysprof_value_axis_real_get_min_value (SysprofAxis *axis, + GValue *value) +{ + SysprofValueAxis *self = SYSPROF_VALUE_AXIS (axis); + + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, MIN (self->min_value, self->max_value)); +} + +static double +sysprof_value_axis_normalize (SysprofAxis *axis, + const GValue *value) +{ + SysprofValueAxis *self = (SysprofValueAxis *)axis; + double v = get_as_double (value); + double r = (v - self->min_value) / self->distance; + + return r; +} + +static void +sysprof_value_axis_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofValueAxis *self = SYSPROF_VALUE_AXIS (object); + + switch (prop_id) + { + case PROP_MIN_VALUE: + g_value_set_double (value, sysprof_value_axis_get_min_value (self)); + break; + + case PROP_MAX_VALUE: + g_value_set_double (value, sysprof_value_axis_get_max_value (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_value_axis_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofValueAxis *self = SYSPROF_VALUE_AXIS (object); + + switch (prop_id) + { + case PROP_MIN_VALUE: + sysprof_value_axis_set_min_value (self, g_value_get_double (value)); + break; + + case PROP_MAX_VALUE: + sysprof_value_axis_set_max_value (self, g_value_get_double (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_value_axis_class_init (SysprofValueAxisClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofAxisClass *axis_class = SYSPROF_AXIS_CLASS (klass); + + object_class->get_property = sysprof_value_axis_get_property; + object_class->set_property = sysprof_value_axis_set_property; + + axis_class->get_min_value = sysprof_value_axis_real_get_min_value; + axis_class->normalize = sysprof_value_axis_normalize; + + properties[PROP_MIN_VALUE] = + g_param_spec_double ("min-value", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MAX_VALUE] = + g_param_spec_double ("max-value", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_value_axis_init (SysprofValueAxis *self) +{ +} + +double +sysprof_value_axis_get_min_value (SysprofValueAxis *self) +{ + g_return_val_if_fail (SYSPROF_IS_VALUE_AXIS (self), .0); + + return self->min_value; +} + +void +sysprof_value_axis_set_min_value (SysprofValueAxis *self, + double min_value) +{ + g_return_if_fail (SYSPROF_IS_VALUE_AXIS (self)); + + if (min_value != self->min_value) + { + self->min_value = min_value; + self->distance = self->max_value - self->min_value; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_VALUE]); + _sysprof_axis_emit_range_changed (SYSPROF_AXIS (self)); + } +} + +double +sysprof_value_axis_get_max_value (SysprofValueAxis *self) +{ + g_return_val_if_fail (SYSPROF_IS_VALUE_AXIS (self), .0); + + return self->max_value; +} + +void +sysprof_value_axis_set_max_value (SysprofValueAxis *self, + double max_value) +{ + g_return_if_fail (SYSPROF_IS_VALUE_AXIS (self)); + + if (max_value != self->max_value) + { + self->max_value = max_value; + self->distance = self->max_value - self->min_value; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_VALUE]); + _sysprof_axis_emit_range_changed (SYSPROF_AXIS (self)); + } +} diff --git a/src/libsysprof-gtk/sysprof-value-axis.h b/src/libsysprof-gtk/sysprof-value-axis.h new file mode 100644 index 00000000..8eb21c6e --- /dev/null +++ b/src/libsysprof-gtk/sysprof-value-axis.h @@ -0,0 +1,51 @@ +/* sysprof-value-axis.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-axis.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_VALUE_AXIS (sysprof_value_axis_get_type()) +#define SYSPROF_IS_AXIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_AXIS)) +#define SYSPROF_AXIS(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_AXIS, SysprofValueAxis)) +#define SYSPROF_AXIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_AXIS, SysprofValueAxisClass)) + +typedef struct _SysprofValueAxis SysprofValueAxis; +typedef struct _SysprofValueAxisClass SysprofValueAxisClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_value_axis_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofAxis *sysprof_value_axis_new (double min_value, + double max_value); +SYSPROF_AVAILABLE_IN_ALL +double sysprof_value_axis_get_min_value (SysprofValueAxis *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_value_axis_set_min_value (SysprofValueAxis *self, + double min_value); +SYSPROF_AVAILABLE_IN_ALL +double sysprof_value_axis_get_max_value (SysprofValueAxis *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_value_axis_set_max_value (SysprofValueAxis *self, + double max_value); + +G_END_DECLS diff --git a/src/sysprof-series.h b/src/sysprof-series.h new file mode 100644 index 00000000..69632d71 --- /dev/null +++ b/src/sysprof-series.h @@ -0,0 +1,37 @@ +/* sysprof-series.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SERIES (sysprof_series_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE (SysprofSeries, sysprof_series, SYSPROF, SERIES, GObject) + +struct _SysprofSeriesClass +{ + GObjectClass parent_class; +}; + +G_END_DECLS