diff --git a/contrib/meson.build b/contrib/meson.build
index 574a013a..63c38a76 100644
--- a/contrib/meson.build
+++ b/contrib/meson.build
@@ -1,3 +1,4 @@
subdir('eggbitset')
subdir('elfparser')
subdir('linereader')
+subdir('tree')
diff --git a/contrib/tree/meson.build b/contrib/tree/meson.build
new file mode 100644
index 00000000..05ef08f5
--- /dev/null
+++ b/contrib/tree/meson.build
@@ -0,0 +1,7 @@
+libtree_deps = [
+ gio_dep,
+]
+
+libtree_static_dep = declare_dependency(
+ include_directories: include_directories('.'),
+)
diff --git a/src/libsysprof/tree.h b/contrib/tree/tree.h
similarity index 100%
rename from src/libsysprof/tree.h
rename to contrib/tree/tree.h
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0cb3fa09..fdae6bda 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -46,6 +46,7 @@ src/sysprof/sysprof-processes-section.ui
src/sysprof/sysprof-recording-pad.c
src/sysprof/sysprof-recording-pad.ui
src/sysprof/sysprof-samples-section.ui
+src/sysprof/sysprof-session-filters-widget.ui
src/sysprof/sysprof-sidebar.ui
src/sysprof/sysprof-storage-section.ui
src/sysprof/sysprof-traceables-utility.ui
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index ca77a2c0..7c823021 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -217,6 +217,7 @@ libsysprof_deps = [
libeggbitset_static_dep,
libelfparser_static_dep,
liblinereader_static_dep,
+ libtree_static_dep,
libsysprof_capture_dep,
]
diff --git a/src/sysprof/icons/scalable/actions/funnel-outline-symbolic.svg b/src/sysprof/icons/scalable/actions/funnel-outline-symbolic.svg
new file mode 100644
index 00000000..10b32288
--- /dev/null
+++ b/src/sysprof/icons/scalable/actions/funnel-outline-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build
index 62f3441f..038f58f9 100644
--- a/src/sysprof/meson.build
+++ b/src/sysprof/meson.build
@@ -31,6 +31,7 @@ sysprof_sources = [
'sysprof-mark-chart-item.c',
'sysprof-mark-chart-row.c',
'sysprof-mark-chart.c',
+ 'sysprof-mark-filter.c',
'sysprof-mark-table.c',
'sysprof-marks-section.c',
'sysprof-memory-callgraph-view.c',
@@ -51,6 +52,7 @@ sysprof_sources = [
'sysprof-scheduler.c',
'sysprof-section.c',
'sysprof-series.c',
+ 'sysprof-session-filters-widget.c',
'sysprof-session-model-item.c',
'sysprof-session-model.c',
'sysprof-session.c',
@@ -90,6 +92,7 @@ sysprof_deps = [
dependency('libadwaita-1', version: '>= 1.6.0'),
dependency('libpanel-1', version: '>= 1.4'),
+ libtree_static_dep,
libsysprof_static_dep,
]
diff --git a/src/sysprof/style.css b/src/sysprof/style.css
index d5d7b5e2..750ffdf0 100644
--- a/src/sysprof/style.css
+++ b/src/sysprof/style.css
@@ -96,3 +96,17 @@ popover.tasks listview row {
popover.tasks listview row label.heading {
padding-bottom: 3px;
}
+
+sessionfilters row {
+ padding: 3px 0;
+}
+
+sessionfilters row:hover {
+ background: none;
+}
+
+sessionfilters row button {
+ min-width: 0;
+ min-height: 0;
+ padding: 3px;
+}
diff --git a/src/sysprof/sysprof-mark-chart-row.c b/src/sysprof/sysprof-mark-chart-row.c
index 5d41ad81..76b38aa5 100644
--- a/src/sysprof/sysprof-mark-chart-row.c
+++ b/src/sysprof/sysprof-mark-chart-row.c
@@ -31,6 +31,9 @@ struct _SysprofMarkChartRow
{
GtkWidget parent_instance;
+ GMenuModel *context_menu;
+ GtkWidget *context_menu_popover;
+
SysprofMarkChartItem *item;
SysprofChart *chart;
@@ -47,6 +50,27 @@ G_DEFINE_FINAL_TYPE (SysprofMarkChartRow, sysprof_mark_chart_row, GTK_TYPE_WIDGE
static GParamSpec *properties [N_PROPS];
+static void
+filter_by_mark_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ SysprofMarkChartRow *self = (SysprofMarkChartRow *)widget;
+ SysprofMarkCatalog *catalog;
+ SysprofSession *session;
+
+ g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
+
+ if (self->item == NULL ||
+ !(session = sysprof_mark_chart_item_get_session (self->item)))
+ return;
+
+ catalog = sysprof_mark_chart_item_get_catalog (self->item);
+ g_assert (SYSPROF_IS_MARK_CATALOG (catalog));
+
+ sysprof_session_filter_by_mark (session, catalog);
+}
+
static gboolean
sysprof_mark_chart_row_activate_layer_item_cb (SysprofMarkChartRow *self,
SysprofChartLayer *layer,
@@ -79,6 +103,49 @@ sysprof_mark_chart_row_activate_layer_item_cb (SysprofMarkChartRow *self,
return FALSE;
}
+static void
+sysprof_mark_chart_row_button_pressed_cb (SysprofMarkChartRow *self,
+ int n_press,
+ double x,
+ double y,
+ GtkGestureClick *gesture)
+{
+ GdkEventSequence *current;
+ GdkEvent *event;
+
+ g_assert (SYSPROF_IS_MARK_CHART_ROW (self));
+ g_assert (GTK_IS_GESTURE_CLICK (gesture));
+
+ current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), current);
+
+ if (!gdk_event_triggers_context_menu (event))
+ return;
+
+ if (!self->context_menu_popover)
+ {
+ self->context_menu_popover = gtk_popover_menu_new_from_model (self->context_menu);
+ gtk_popover_set_position (GTK_POPOVER (self->context_menu_popover), GTK_POS_BOTTOM);
+ gtk_popover_set_has_arrow (GTK_POPOVER (self->context_menu_popover), FALSE);
+ gtk_widget_set_halign (self->context_menu_popover, GTK_ALIGN_START);
+ gtk_widget_set_parent (GTK_WIDGET (self->context_menu_popover), GTK_WIDGET (self));
+ }
+
+ if (x != -1 && y != -1)
+ {
+ GdkRectangle rect = { x, y, 1, 1 };
+ gtk_popover_set_pointing_to (GTK_POPOVER (self->context_menu_popover), &rect);
+ }
+ else
+ {
+ gtk_popover_set_pointing_to (GTK_POPOVER (self->context_menu_popover), NULL);
+ }
+
+ gtk_popover_popup (GTK_POPOVER (self->context_menu_popover));
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
static void
sysprof_mark_chart_row_dispose (GObject *object)
{
@@ -154,8 +221,12 @@ sysprof_mark_chart_row_class_init (SysprofMarkChartRowClass *klass)
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-mark-chart-row.ui");
gtk_widget_class_bind_template_child (widget_class, SysprofMarkChartRow, chart);
+ gtk_widget_class_bind_template_child (widget_class, SysprofMarkChartRow, context_menu);
gtk_widget_class_bind_template_child (widget_class, SysprofMarkChartRow, layer);
gtk_widget_class_bind_template_callback (widget_class, sysprof_mark_chart_row_activate_layer_item_cb);
+ gtk_widget_class_bind_template_callback (widget_class, sysprof_mark_chart_row_button_pressed_cb);
+
+ gtk_widget_class_install_action (widget_class, "markchartrow.filter-by-mark", NULL, filter_by_mark_action);
g_type_ensure (SYSPROF_TYPE_CHART);
g_type_ensure (SYSPROF_TYPE_TIME_SERIES_ITEM);
diff --git a/src/sysprof/sysprof-mark-chart-row.ui b/src/sysprof/sysprof-mark-chart-row.ui
index f11bd532..3bd54d45 100644
--- a/src/sysprof/sysprof-mark-chart-row.ui
+++ b/src/sysprof/sysprof-mark-chart-row.ui
@@ -1,28 +1,70 @@
+
+
+
-
+
+
+
+
-
-
-
-
+
+ 1
+ 30
+ 30
+ 8
+ ellipsize-end
+
+
+
SysprofMarkChartRow
-
-
- SysprofMarkChartRow
-
-
+
+
+
+
+ true
+ false
+
+
+
+
+
+ SysprofMarkChartRow
+
+
+
+
+
+ SysprofMarkChartRow
+
+
+
+
+
+
+
+
diff --git a/src/sysprof/sysprof-mark-chart.ui b/src/sysprof/sysprof-mark-chart.ui
index 02e3c200..15f6e22e 100644
--- a/src/sysprof/sysprof-mark-chart.ui
+++ b/src/sysprof/sysprof-mark-chart.ui
@@ -45,31 +45,11 @@
-
-
-
- 1
- 30
- 30
- 8
- ellipsize-end
-
-
-
- GtkListItem
-
-
-
-
-
-
-
- true
-
- GtkListItem
-
-
-
+
+ true
+
+ GtkListItem
+
diff --git a/src/sysprof/sysprof-mark-filter.c b/src/sysprof/sysprof-mark-filter.c
new file mode 100644
index 00000000..00f99da5
--- /dev/null
+++ b/src/sysprof/sysprof-mark-filter.c
@@ -0,0 +1,331 @@
+/*
+ * sysprof-mark-filter.c
+ *
+ * Copyright 2025 Georges Basile Stavracas Neto
+ *
+ * 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 "sysprof-mark-filter.h"
+
+typedef struct _SysprofMarkFilterNode SysprofMarkFilterNode;
+static void sysprof_mark_filter_node_augment (SysprofMarkFilterNode *node);
+#define RB_AUGMENT(elem) (sysprof_mark_filter_node_augment (elem))
+
+#include "tree.h"
+
+struct _SysprofMarkFilterNode
+{
+ RB_ENTRY (_SysprofMarkFilterNode) link;
+ SysprofTimeSpan time_span;
+ gint64 max;
+};
+
+struct _SysprofMarkFilter
+{
+ GtkFilter parent_instance;
+
+ SysprofDocument *document;
+ SysprofMarkCatalog *catalog;
+
+ RB_HEAD (sysprof_mark_filter, _SysprofMarkFilterNode) head;
+};
+
+G_DEFINE_FINAL_TYPE (SysprofMarkFilter, sysprof_mark_filter, GTK_TYPE_FILTER)
+
+enum {
+ PROP_0,
+ PROP_DOCUMENT,
+ PROP_CATALOG,
+ N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static inline int
+sysprof_mark_filter_node_compare (SysprofMarkFilterNode *a,
+ SysprofMarkFilterNode *b)
+{
+ if (a->time_span.begin_nsec < b->time_span.begin_nsec)
+ return -1;
+ else if (a->time_span.begin_nsec > b->time_span.begin_nsec)
+ return 1;
+ else if (a->time_span.end_nsec < b->time_span.end_nsec)
+ return -1;
+ else if (a->time_span.end_nsec > b->time_span.end_nsec)
+ return -1;
+ else
+ return 0;
+}
+
+RB_GENERATE_STATIC (sysprof_mark_filter, _SysprofMarkFilterNode, link, sysprof_mark_filter_node_compare);
+
+static void
+sysprof_mark_filter_node_augment (SysprofMarkFilterNode *node)
+{
+ node->max = node->time_span.end_nsec;
+
+ if (RB_LEFT (node, link) && RB_LEFT (node, link)->max > node->max)
+ node->max = RB_LEFT (node, link)->max;
+
+ if (RB_RIGHT (node, link) && RB_RIGHT (node, link)->max > node->max)
+ node->max = RB_RIGHT (node, link)->max;
+}
+
+static void
+sysprof_mark_filter_node_finalize (SysprofMarkFilterNode *node)
+{
+ g_free (node);
+}
+
+static void
+sysprof_mark_filter_node_free (SysprofMarkFilterNode *node)
+{
+ SysprofMarkFilterNode *right = RB_RIGHT (node, link);
+ SysprofMarkFilterNode *left = RB_LEFT (node, link);
+
+ if (left != NULL)
+ sysprof_mark_filter_node_free (left);
+
+ sysprof_mark_filter_node_finalize (node);
+
+ if (right != NULL)
+ sysprof_mark_filter_node_free (right);
+}
+
+static void
+populate_intervals (SysprofMarkFilter *self)
+{
+ unsigned int n_marks;
+ GListModel *model;
+ int64_t begin_nsec;
+
+ g_assert (SYSPROF_IS_MARK_FILTER (self));
+ g_assert (SYSPROF_IS_MARK_CATALOG (self->catalog));
+ g_assert (SYSPROF_IS_DOCUMENT (self->document));
+
+ begin_nsec = sysprof_document_get_time_span (self->document)->begin_nsec;
+
+ model = G_LIST_MODEL (self->catalog);
+ n_marks = g_list_model_get_n_items (model);
+
+ for (unsigned int i = 0; i < n_marks; i++)
+ {
+ g_autoptr(SysprofDocumentMark) mark = g_list_model_get_item (model, i);
+ SysprofMarkFilterNode *parent;
+ SysprofMarkFilterNode *node;
+ SysprofMarkFilterNode *ret;
+ SysprofTimeSpan time_span;
+
+ g_assert (SYSPROF_IS_DOCUMENT_MARK (mark));
+
+ time_span.begin_nsec = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (mark));
+ time_span.end_nsec = sysprof_document_mark_get_end_time (mark);
+
+ if (sysprof_time_span_duration (time_span) == 0)
+ continue;
+
+ time_span = sysprof_time_span_relative_to (time_span, begin_nsec);
+
+ node = g_new0 (SysprofMarkFilterNode, 1);
+ node->time_span = time_span;
+ node->max = time_span.end_nsec;
+
+ /* If there is a collision, then the node is returned. Otherwise
+ * if the node was inserted, NULL is returned.
+ */
+ if ((ret = RB_INSERT (sysprof_mark_filter, &self->head, node)))
+ {
+ sysprof_mark_filter_node_free (node);
+ return;
+ }
+
+ parent = RB_PARENT (node, link);
+ while (parent != NULL)
+ {
+ if (node->max > parent->max)
+ parent->max = node->max;
+ node = parent;
+ parent = RB_PARENT (parent, link);
+ }
+ }
+}
+
+static gboolean
+sysprof_mark_filter_match (GtkFilter *filter,
+ gpointer item)
+{
+ SysprofDocumentFrame *frame = (SysprofDocumentFrame *) item;
+ SysprofMarkFilter *self = (SysprofMarkFilter *)filter;
+ SysprofMarkFilterNode *node;
+ int64_t value;
+
+ g_assert (SYSPROF_IS_MARK_FILTER (self));
+ g_assert (SYSPROF_IS_DOCUMENT_FRAME (item));
+
+ value = sysprof_document_frame_get_time_offset (frame);
+ node = RB_ROOT (&self->head);
+
+ /* The root node contains our calculated max as augmented in RBTree.
+ * Therefore, we can know if value falls beyond the upper bound
+ * in O(1) without having to add a branch to the while loop below.
+ */
+ if (node == NULL || node->max < value)
+ return FALSE;
+
+ while (node != NULL)
+ {
+ g_assert (RB_LEFT (node, link) == NULL ||
+ node->max >= RB_LEFT (node, link)->max);
+ g_assert (RB_RIGHT (node, link) == NULL ||
+ node->max >= RB_RIGHT (node, link)->max);
+
+ if (value >= node->time_span.begin_nsec && value <= node->time_span.end_nsec)
+ return TRUE;
+
+ if (RB_LEFT (node, link) && RB_LEFT (node, link)->max >= value)
+ node = RB_LEFT (node, link);
+ else
+ node = RB_RIGHT (node, link);
+ }
+
+ return FALSE;
+}
+
+static void
+sysprof_mark_filter_finalize (GObject *object)
+{
+ SysprofMarkFilter *self = (SysprofMarkFilter *)object;
+ SysprofMarkFilterNode *node = RB_ROOT(&self->head);
+
+ g_clear_object (&self->catalog);
+
+ if (node != NULL)
+ sysprof_mark_filter_node_free (node);
+
+ G_OBJECT_CLASS (sysprof_mark_filter_parent_class)->finalize (object);
+}
+
+static void
+sysprof_mark_filter_constructed (GObject *object)
+{
+ SysprofMarkFilter *self = (SysprofMarkFilter *)object;
+
+ G_OBJECT_CLASS (sysprof_mark_filter_parent_class)->constructed (object);
+
+ populate_intervals (self);
+}
+
+static void
+sysprof_mark_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofMarkFilter *self = SYSPROF_MARK_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_value_set_object (value, self->document);
+ break;
+
+ case PROP_CATALOG:
+ g_value_set_object (value, self->catalog);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_mark_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofMarkFilter *self = SYSPROF_MARK_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CATALOG:
+ g_assert (self->catalog == NULL);
+ self->catalog = g_value_dup_object (value);
+ break;
+
+ case PROP_DOCUMENT:
+ g_assert (self->document == NULL);
+ self->document = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_mark_filter_class_init (SysprofMarkFilterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkFilterClass *filter_class = GTK_FILTER_CLASS (klass);
+
+ object_class->finalize = sysprof_mark_filter_finalize;
+ object_class->constructed = sysprof_mark_filter_constructed;
+ object_class->get_property = sysprof_mark_filter_get_property;
+ object_class->set_property = sysprof_mark_filter_set_property;
+
+ filter_class->match = sysprof_mark_filter_match;
+
+ properties[PROP_CATALOG] =
+ g_param_spec_object ("catalog", NULL, NULL,
+ SYSPROF_TYPE_MARK_CATALOG,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties[PROP_DOCUMENT] =
+ g_param_spec_object ("document", NULL, NULL,
+ SYSPROF_TYPE_DOCUMENT,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+sysprof_mark_filter_init (SysprofMarkFilter *self)
+{
+ RB_INIT (&self->head);
+}
+
+SysprofMarkFilter *
+sysprof_mark_filter_new (SysprofDocument *document,
+ SysprofMarkCatalog *catalog)
+{
+ g_return_val_if_fail (SYSPROF_IS_DOCUMENT (document), NULL);
+ g_return_val_if_fail (SYSPROF_IS_MARK_CATALOG (catalog), NULL);
+
+ return g_object_new (SYSPROF_TYPE_MARK_FILTER,
+ "catalog", catalog,
+ "document", document,
+ NULL);
+}
+
+SysprofMarkCatalog *
+sysprof_mark_filter_get_catalog (SysprofMarkFilter *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_MARK_FILTER (self), NULL);
+
+ return self->catalog;
+}
diff --git a/src/sysprof/sysprof-mark-filter.h b/src/sysprof/sysprof-mark-filter.h
new file mode 100644
index 00000000..c64364bd
--- /dev/null
+++ b/src/sysprof/sysprof-mark-filter.h
@@ -0,0 +1,38 @@
+/*
+ * sysprof-mark-filter.h
+ *
+ * Copyright 2025 Georges Basile Stavracas Neto
+ *
+ * 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-session.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_MARK_FILTER (sysprof_mark_filter_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofMarkFilter, sysprof_mark_filter, SYSPROF, MARK_FILTER, GtkFilter)
+
+SysprofMarkFilter *sysprof_mark_filter_new (SysprofDocument *document,
+ SysprofMarkCatalog *catalog);
+SysprofMarkCatalog *sysprof_mark_filter_get_catalog (SysprofMarkFilter *self);
+
+G_END_DECLS
diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui
index 7ef6325f..3f3719be 100644
--- a/src/sysprof/sysprof-samples-section.ui
+++ b/src/sysprof/sysprof-samples-section.ui
@@ -44,19 +44,28 @@
5000
-
-
-
+
+
+
SysprofSamplesSection
-
-
-
- SysprofSamplesSection
-
-
-
+
+
+
+
+ SysprofSamplesSection
+
+
+
+
+
+ SysprofSamplesSection
+
+
+
+
+
diff --git a/src/sysprof/sysprof-session-filters-widget.c b/src/sysprof/sysprof-session-filters-widget.c
new file mode 100644
index 00000000..96b2cb2e
--- /dev/null
+++ b/src/sysprof/sysprof-session-filters-widget.c
@@ -0,0 +1,225 @@
+/*
+ * sysprof-session-filters-widget.c
+ *
+ * Copyright 2025 Georges Basile Stavracas Neto
+ *
+ * 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 "sysprof-mark-filter.h"
+#include "sysprof-session-filters-widget.h"
+#include "sysprof-session.h"
+
+struct _SysprofSessionFiltersWidget
+{
+ GtkWidget parent_instance;
+
+ SysprofSession *session;
+};
+
+G_DEFINE_FINAL_TYPE (SysprofSessionFiltersWidget, sysprof_session_filters_widget, GTK_TYPE_WIDGET)
+
+enum {
+ PROP_0,
+ PROP_SESSION,
+ N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+sysprof_session_filters_widget_remove_filter (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ SysprofSessionFiltersWidget *self = (SysprofSessionFiltersWidget *) widget;
+ GtkFilter *filter;
+ unsigned int position;
+
+ g_assert (SYSPROF_IS_SESSION_FILTERS_WIDGET (widget));
+ g_assert (SYSPROF_IS_SESSION (self->session));
+
+ filter = sysprof_session_get_filter (self->session);
+ g_assert (GTK_IS_MULTI_FILTER (filter));
+
+ position = g_variant_get_uint32 (param);
+
+ gtk_multi_filter_remove (GTK_MULTI_FILTER (filter), position);
+}
+
+static GVariant *
+item_to_action_target (gpointer unused,
+ GtkFilter *filter,
+ unsigned int position)
+{
+ if (filter)
+ return g_variant_new_uint32 (position);
+
+ return NULL;
+}
+
+static char *
+filter_to_icon_name (gpointer unused,
+ GtkFilter *filter)
+{
+ if (!filter)
+ return g_strdup ("");
+
+ if (SYSPROF_IS_MARK_FILTER (filter))
+ return g_strdup ("mark-chart-symbolic");
+
+ return g_strdup ("funnel-outline-symbolic");
+}
+
+static char *
+filter_to_string (gpointer unused,
+ GtkFilter *filter)
+{
+ if (!filter)
+ return g_strdup ("");
+
+ if (SYSPROF_IS_MARK_FILTER (filter))
+ {
+ SysprofMarkFilter *mark_filter = (SysprofMarkFilter *) filter;
+ SysprofMarkCatalog *catalog = sysprof_mark_filter_get_catalog (mark_filter);
+
+ return g_strdup_printf ("%s / %s",
+ sysprof_mark_catalog_get_group (catalog),
+ sysprof_mark_catalog_get_name (catalog));
+ }
+
+ return g_strdup (G_OBJECT_TYPE_NAME (filter));
+}
+
+static void
+clear_all_filters (SysprofSessionFiltersWidget *self,
+ GtkButton *button)
+{
+ GtkFilter *filter;
+
+ g_assert (SYSPROF_IS_SESSION_FILTERS_WIDGET (self));
+ g_assert (SYSPROF_IS_SESSION (self->session));
+ g_assert (GTK_IS_BUTTON (button));
+
+ filter = sysprof_session_get_filter (self->session);
+ g_assert (GTK_IS_MULTI_FILTER (filter));
+
+ while (g_list_model_get_n_items (G_LIST_MODEL (filter)) > 0)
+ gtk_multi_filter_remove (GTK_MULTI_FILTER (filter), 0);
+}
+
+static void
+sysprof_session_filters_widget_set_session (SysprofSessionFiltersWidget *self,
+ SysprofSession *session)
+{
+ if (g_set_object (&self->session, session))
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]);
+}
+
+static void
+sysprof_session_filters_widget_finalize (GObject *object)
+{
+ SysprofSessionFiltersWidget *self = (SysprofSessionFiltersWidget *)object;
+
+ g_clear_object (&self->session);
+
+ G_OBJECT_CLASS (sysprof_session_filters_widget_parent_class)->finalize (object);
+}
+
+static void
+sysprof_session_filters_widget_dispose (GObject *object)
+{
+ SysprofSessionFiltersWidget *self = (SysprofSessionFiltersWidget *)object;
+
+ gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_SESSION_FILTERS_WIDGET);
+
+ G_OBJECT_CLASS (sysprof_session_filters_widget_parent_class)->dispose (object);
+}
+
+static void
+sysprof_session_filters_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofSessionFiltersWidget *self = SYSPROF_SESSION_FILTERS_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_SESSION:
+ g_value_set_object (value, self->session);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_session_filters_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofSessionFiltersWidget *self = SYSPROF_SESSION_FILTERS_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_SESSION:
+ sysprof_session_filters_widget_set_session (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_session_filters_widget_class_init (SysprofSessionFiltersWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = sysprof_session_filters_widget_finalize;
+ object_class->dispose = sysprof_session_filters_widget_dispose;
+ object_class->get_property = sysprof_session_filters_widget_get_property;
+ object_class->set_property = sysprof_session_filters_widget_set_property;
+
+ properties[PROP_SESSION] =
+ g_param_spec_object ("session", NULL, NULL,
+ SYSPROF_TYPE_SESSION,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-session-filters-widget.ui");
+
+ gtk_widget_class_set_css_name (widget_class, "sessionfilters");
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+
+ gtk_widget_class_bind_template_callback (widget_class, clear_all_filters);
+ gtk_widget_class_bind_template_callback (widget_class, filter_to_icon_name);
+ gtk_widget_class_bind_template_callback (widget_class, filter_to_string);
+ gtk_widget_class_bind_template_callback (widget_class, item_to_action_target);
+
+ gtk_widget_class_install_action (widget_class, "sessionfilters.remove-filter", "u", sysprof_session_filters_widget_remove_filter);
+}
+
+static void
+sysprof_session_filters_widget_init (SysprofSessionFiltersWidget *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/sysprof/sysprof-session-filters-widget.h b/src/sysprof/sysprof-session-filters-widget.h
new file mode 100644
index 00000000..22a2c892
--- /dev/null
+++ b/src/sysprof/sysprof-session-filters-widget.h
@@ -0,0 +1,32 @@
+/*
+ * sysprof-session-filters-widget.c
+ *
+ * Copyright 2025 Georges Basile Stavracas Neto
+ *
+ * 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_SESSION_FILTERS_WIDGET (sysprof_session_filters_widget_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofSessionFiltersWidget, sysprof_session_filters_widget, SYSPROF, SESSION_FILTERS_WIDGET, GtkWidget)
+
+G_END_DECLS
diff --git a/src/sysprof/sysprof-session-filters-widget.ui b/src/sysprof/sysprof-session-filters-widget.ui
new file mode 100644
index 00000000..ebad1025
--- /dev/null
+++ b/src/sysprof/sysprof-session-filters-widget.ui
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+ vertical
+
+
+
+ never
+ 400
+ true
+
+
+
+ 6
+ 6
+ 6
+
+
+
+
+ SysprofSessionFiltersWidget
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 12
+
+
+ GtkListItem
+
+
+
+
+
+
+
+ 0
+ true
+
+
+ GtkListItem
+
+
+
+
+
+
+
+ window-close-symbolic
+ sessionfilters.remove-filter
+
+
+ GtkListItem
+ GtkListItem
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 6
+ 6
+ 6
+ Clear All Filters
+
+
+
+
+
+
+
+
+
diff --git a/src/sysprof/sysprof-session.c b/src/sysprof/sysprof-session.c
index 4a571a12..ee0fa7a9 100644
--- a/src/sysprof/sysprof-session.c
+++ b/src/sysprof/sysprof-session.c
@@ -22,6 +22,7 @@
#include
+#include "sysprof-mark-filter.h"
#include "sysprof-session-private.h"
#include "sysprof-value-axis.h"
@@ -488,6 +489,24 @@ sysprof_session_zoom_to_selection (SysprofSession *self)
sysprof_session_update_axis (self);
}
+void
+sysprof_session_filter_by_mark (SysprofSession *self,
+ SysprofMarkCatalog *catalog)
+{
+ g_return_if_fail (SYSPROF_IS_SESSION (self));
+ g_return_if_fail (catalog == NULL || SYSPROF_IS_MARK_CATALOG (catalog));
+
+ while (g_list_model_get_n_items (G_LIST_MODEL (self->filter)) > 0)
+ gtk_multi_filter_remove (GTK_MULTI_FILTER (self->filter), 0);
+
+ if (catalog)
+ {
+ g_autoptr(SysprofMarkFilter) mark_filter = sysprof_mark_filter_new (self->document, catalog);
+
+ gtk_multi_filter_append (GTK_MULTI_FILTER (self->filter), GTK_FILTER (g_steal_pointer (&mark_filter)));
+ }
+}
+
static char *
get_time_str (gint64 o)
{
diff --git a/src/sysprof/sysprof-session.h b/src/sysprof/sysprof-session.h
index 0e7417dd..c1a4f541 100644
--- a/src/sysprof/sysprof-session.h
+++ b/src/sysprof/sysprof-session.h
@@ -44,5 +44,7 @@ SysprofAxis *sysprof_session_get_selected_time_axis (SysprofSession
void sysprof_session_select_time (SysprofSession *self,
const SysprofTimeSpan *time_span);
void sysprof_session_zoom_to_selection (SysprofSession *self);
+void sysprof_session_filter_by_mark (SysprofSession *self,
+ SysprofMarkCatalog *catalog);
G_END_DECLS
diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c
index e81e264b..cc351836 100644
--- a/src/sysprof/sysprof-window.c
+++ b/src/sysprof/sysprof-window.c
@@ -41,6 +41,7 @@
#include "sysprof-pair.h"
#include "sysprof-processes-section.h"
#include "sysprof-samples-section.h"
+#include "sysprof-session-filters-widget.h"
#include "sysprof-sidebar.h"
#include "sysprof-storage-section.h"
#include "sysprof-task-row.h"
@@ -328,6 +329,13 @@ main_view_notify_sidebar (SysprofWindow *self,
gtk_widget_set_sensitive (GTK_WIDGET (self->show_right_sidebar), sidebar != NULL);
}
+static gboolean
+n_filters_to_button_visibility (SysprofWindow *self,
+ unsigned int n_filters)
+{
+ return n_filters > 0;
+}
+
static void
sysprof_window_session_seek_backward (GtkWidget *widget,
const char *action_name,
@@ -617,6 +625,7 @@ sysprof_window_class_init (SysprofWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, SysprofWindow, stack_title);
gtk_widget_class_bind_template_callback (widget_class, main_view_notify_sidebar);
+ gtk_widget_class_bind_template_callback (widget_class, n_filters_to_button_visibility);
gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action);
gtk_widget_class_install_action (widget_class, "win.record-capture", NULL, sysprof_window_record_capture_action);
@@ -649,6 +658,7 @@ sysprof_window_class_init (SysprofWindowClass *klass)
g_type_ensure (SYSPROF_TYPE_NETWORK_SECTION);
g_type_ensure (SYSPROF_TYPE_PROCESSES_SECTION);
g_type_ensure (SYSPROF_TYPE_SAMPLES_SECTION);
+ g_type_ensure (SYSPROF_TYPE_SESSION_FILTERS_WIDGET);
g_type_ensure (SYSPROF_TYPE_STORAGE_SECTION);
g_type_ensure (SYSPROF_TYPE_SESSION);
g_type_ensure (SYSPROF_TYPE_SYMBOL);
diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui
index f4e1e7cb..c18b4a47 100644
--- a/src/sysprof/sysprof-window.ui
+++ b/src/sysprof/sysprof-window.ui
@@ -126,6 +126,36 @@
Toggle Left Panel
+
+
+
6
diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml
index 3903b5a5..eb5d9307 100644
--- a/src/sysprof/sysprof.gresource.xml
+++ b/src/sysprof/sysprof.gresource.xml
@@ -8,6 +8,7 @@
icons/scalable/actions/empty-symbolic.svg
icons/scalable/actions/energy-symbolic.svg
icons/scalable/actions/flamegraph-symbolic.svg
+ icons/scalable/actions/funnel-outline-symbolic.svg
icons/scalable/actions/graphics-symbolic.svg
icons/scalable/actions/mark-chart-symbolic.svg
icons/scalable/actions/mark-table-symbolic.svg
@@ -50,6 +51,7 @@
sysprof-processes-section.ui
sysprof-recording-pad.ui
sysprof-samples-section.ui
+ sysprof-session-filters-widget.ui
sysprof-sidebar.ui
sysprof-storage-section.ui
sysprof-task-row.ui