[sysprof] libsysprof-ui: land new UI design
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof] libsysprof-ui: land new UI design
- Date: Tue, 25 Jun 2019 03:53:07 +0000 (UTC)
commit e8528609ece584e5d3ce3274119044a5110939ee
Author: Christian Hergert <chergert redhat com>
Date: Mon Jun 24 20:28:18 2019 -0700
libsysprof-ui: land new UI design
This comprises a massive rewrite of the UI for browsing captures. We use
the SysprofAid class to scan capture files for content and then auto-
matically add visualizers and details pages.
To avoid breaking things incrementally, we just land this as a very large
commit. Not necessarily ideal, but given the amount of stuff that could
break, this is easier.
As part of this process, we're removing a lot of the surface API so that
we can limit how much we need to maintain in terms of ABI.
src/libsysprof-ui/css/SysprofDisplay-shared.css | 89 ++
.../css/SysprofVisualizerView-Adwaita-dark.css | 6 -
.../css/SysprofVisualizerView-Adwaita.css | 7 -
.../css/SysprofVisualizerView-shared.css | 13 -
src/libsysprof-ui/libsysprof-ui.gresource.xml | 16 +-
src/libsysprof-ui/meson.build | 85 +-
src/libsysprof-ui/rectangles.c | 22 +-
src/libsysprof-ui/sysprof-aid.c | 63 +-
src/libsysprof-ui/sysprof-aid.h | 62 +-
src/libsysprof-ui/sysprof-battery-aid.c | 239 ++++
...epth-visualizer-row.h => sysprof-battery-aid.h} | 10 +-
src/libsysprof-ui/sysprof-callgraph-aid.c | 177 +++
src/libsysprof-ui/sysprof-callgraph-aid.h | 2 -
src/libsysprof-ui/sysprof-callgraph-page.c | 1293 ++++++++++++++++++++
src/libsysprof-ui/sysprof-callgraph-page.h | 51 +
src/libsysprof-ui/sysprof-callgraph-page.ui | 235 ++++
src/libsysprof-ui/sysprof-callgraph-view.h | 12 -
src/libsysprof-ui/sysprof-capture-view.c | 1092 -----------------
src/libsysprof-ui/sysprof-capture-view.h | 75 --
src/libsysprof-ui/sysprof-capture-view.ui | 187 ---
src/libsysprof-ui/sysprof-cell-renderer-duration.c | 2 +-
src/libsysprof-ui/sysprof-cell-renderer-percent.c | 2 +
src/libsysprof-ui/sysprof-cell-renderer-percent.h | 13 +-
src/libsysprof-ui/sysprof-color-cycle.c | 65 +-
src/libsysprof-ui/sysprof-color-cycle.h | 14 +-
src/libsysprof-ui/sysprof-counters-aid.c | 248 ++++
src/libsysprof-ui/sysprof-counters-aid.h | 33 +
src/libsysprof-ui/sysprof-cpu-aid.c | 249 ++++
src/libsysprof-ui/sysprof-cpu-aid.h | 2 -
src/libsysprof-ui/sysprof-cpu-visualizer-row.c | 239 ----
src/libsysprof-ui/sysprof-depth-visualizer-row.c | 357 ------
src/libsysprof-ui/sysprof-depth-visualizer.c | 430 +++++++
src/libsysprof-ui/sysprof-depth-visualizer.h | 40 +
src/libsysprof-ui/sysprof-details-page.c | 324 +++++
src/libsysprof-ui/sysprof-details-page.h | 60 +
src/libsysprof-ui/sysprof-details-page.ui | 372 ++++++
src/libsysprof-ui/sysprof-display.c | 942 ++++++++++----
src/libsysprof-ui/sysprof-display.h | 55 +-
src/libsysprof-ui/sysprof-display.ui | 40 +-
src/libsysprof-ui/sysprof-empty-state-view.h | 6 -
src/libsysprof-ui/sysprof-failed-state-view.h | 7 -
src/libsysprof-ui/sysprof-line-visualizer-row.h | 70 --
...-visualizer-row.c => sysprof-line-visualizer.c} | 313 +++--
src/libsysprof-ui/sysprof-line-visualizer.h | 57 +
src/libsysprof-ui/sysprof-logs-aid.c | 237 ++++
src/libsysprof-ui/sysprof-logs-aid.h | 33 +
src/libsysprof-ui/sysprof-logs-page.c | 116 ++
...isualizer-row-private.h => sysprof-logs-page.h} | 10 +-
src/libsysprof-ui/sysprof-logs-page.ui | 75 ++
src/libsysprof-ui/sysprof-mark-visualizer-row.c | 492 --------
src/libsysprof-ui/sysprof-mark-visualizer-row.h | 53 -
src/libsysprof-ui/sysprof-mark-visualizer.c | 287 +++++
src/libsysprof-ui/sysprof-mark-visualizer.h | 47 +
src/libsysprof-ui/sysprof-marks-aid.c | 319 +++++
src/libsysprof-ui/sysprof-marks-aid.h | 33 +
.../{sysprof-marks-view.c => sysprof-marks-page.c} | 343 ++++--
...f-cpu-visualizer-row.h => sysprof-marks-page.h} | 29 +-
src/libsysprof-ui/sysprof-marks-page.ui | 231 ++++
src/libsysprof-ui/sysprof-marks-view.h | 55 -
src/libsysprof-ui/sysprof-marks-view.ui | 57 -
src/libsysprof-ui/sysprof-memory-aid.h | 2 -
src/libsysprof-ui/sysprof-model-filter.h | 2 +-
src/libsysprof-ui/sysprof-notebook.h | 4 +
src/libsysprof-ui/sysprof-page.c | 235 ++++
src/libsysprof-ui/sysprof-page.h | 86 ++
src/libsysprof-ui/sysprof-profiler-assistant.c | 11 +-
src/libsysprof-ui/sysprof-profiler-assistant.h | 2 -
src/libsysprof-ui/sysprof-profiler-assistant.ui | 8 +-
src/libsysprof-ui/sysprof-proxy-aid.h | 3 -
src/libsysprof-ui/sysprof-recording-state-view.h | 7 -
src/libsysprof-ui/sysprof-scrollmap.c | 306 +++++
src/libsysprof-ui/sysprof-scrollmap.h | 37 +
src/libsysprof-ui/sysprof-theme-manager.c | 6 +-
src/libsysprof-ui/sysprof-time-visualizer.c | 543 ++++++++
src/libsysprof-ui/sysprof-time-visualizer.h | 54 +
src/libsysprof-ui/sysprof-ui-private.h | 38 +-
src/libsysprof-ui/sysprof-ui.h | 23 +-
.../sysprof-visualizer-group-header.c | 159 +++
.../sysprof-visualizer-group-header.h | 31 +
.../sysprof-visualizer-group-private.h | 48 +
src/libsysprof-ui/sysprof-visualizer-group.c | 511 ++++++++
src/libsysprof-ui/sysprof-visualizer-group.h | 76 ++
src/libsysprof-ui/sysprof-visualizer-list.c | 582 ---------
src/libsysprof-ui/sysprof-visualizer-list.h | 60 -
src/libsysprof-ui/sysprof-visualizer-row.c | 300 -----
src/libsysprof-ui/sysprof-visualizer-row.h | 83 --
src/libsysprof-ui/sysprof-visualizer-ticks.c | 81 +-
src/libsysprof-ui/sysprof-visualizer-ticks.h | 5 +-
src/libsysprof-ui/sysprof-visualizer-view.c | 762 ------------
src/libsysprof-ui/sysprof-visualizer-view.h | 70 --
src/libsysprof-ui/sysprof-visualizer-view.ui | 32 -
src/libsysprof-ui/sysprof-visualizer.c | 346 ++++++
src/libsysprof-ui/sysprof-visualizer.h | 82 ++
src/libsysprof-ui/sysprof-visualizers-frame.c | 767 ++++++++++++
src/libsysprof-ui/sysprof-visualizers-frame.h | 49 +
src/libsysprof-ui/sysprof-visualizers-frame.ui | 419 +++++++
src/libsysprof-ui/sysprof-zoom-manager.h | 27 +-
src/tests/meson.build | 3 +-
src/tests/test-capture-view.c | 8 +-
99 files changed, 10497 insertions(+), 5464 deletions(-)
---
diff --git a/src/libsysprof-ui/css/SysprofDisplay-shared.css b/src/libsysprof-ui/css/SysprofDisplay-shared.css
new file mode 100644
index 0000000..45eddf0
--- /dev/null
+++ b/src/libsysprof-ui/css/SysprofDisplay-shared.css
@@ -0,0 +1,89 @@
+SysprofDisplay dzlmultipaned {
+ -DzlMultiPaned-handle-size: 0;
+ }
+
+SysprofVisualizer {
+ background: @content_view_bg;
+ }
+SysprofVisualizer:not(:last-child) {
+ border-bottom: 1px solid alpha(@borders, 0.3);
+ }
+
+SysprofVisualizerGroup {
+ border-bottom: 1px solid @borders;
+ }
+SysprofVisualizerGroup:last-child {
+ box-shadow: 0 20px 15px 15px alpha(@borders, 0.3);
+ }
+
+SysprofVisualizersFrame box.horizontal.inline-toolbar {
+ padding: 0;
+ margin: 0;
+ border: none;
+ border-radius: 0;
+ border-width: 0;
+ }
+
+SysprofVisualizersFrame scrollbar.horizontal {
+ color: mix(@theme_fg_color, @theme_selected_bg_color, 0.5);
+ background: transparent;
+ }
+
+SysprofVisualizersFrame scrollbar.horizontal contents trough {
+ background: transparent;
+ }
+
+SysprofVisualizersFrame scrollbar.horizontal contents trough slider {
+ margin-left: 1px;
+ margin-right: 1px;
+ padding: 6px;
+ min-height: 14px;
+ background: alpha(@content_view_bg, 0.2);
+ border-radius: 3px;
+ border: 2px solid @theme_selected_bg_color;
+ box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3),
+ inset 0 -15px 5px alpha(@content_view_bg,.1),
+ 0px 2px 4px @borders;
+ }
+
+SysprofVisualizerTicks {
+ color: mix(@theme_fg_color, @borders, 0.5);
+ background-color: @theme_bg_color;
+ }
+
+SysprofVisualizersFrame list {
+ background-color: @theme_bg_color;
+ }
+
+SysprofVisualizersFrame list.visualizer-groups row {
+ padding: 0;
+ border-bottom: 1px solid @borders;
+ }
+SysprofVisualizersFrame list.visualizer-groups row:not(:selected) {
+ background-color: @theme_bg_color;
+ }
+SysprofVisualizersFrame list.visualizer-groups row:last-child {
+ box-shadow: 0 20px 15px 15px alpha(@borders, 0.3);
+ }
+
+SysprofVisualizersFrame .left-column .small-button.flat {
+ border-color: transparent;
+ min-height: 8px;
+ min-width: 8px;
+ }
+SysprofVisualizersFrame .left-column .small-button.flat:checked,
+SysprofVisualizersFrame .left-column .small-button.flat:hover {
+ border-color: @borders;
+ }
+
+SysprofDisplay > dzlmultipaned > :nth-child(2) {
+ border-top: 1px solid @borders;
+ }
+
+SysprofVisualizersFrame .selection {
+ border-radius: 3px;
+ background-color: alpha(@theme_selected_bg_color, 0.35);
+ box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3),
+ inset 0 -15px 5px alpha(@content_view_bg,.1),
+ inset 0 0 1px 1px @theme_selected_bg_color;
+ }
diff --git a/src/libsysprof-ui/libsysprof-ui.gresource.xml b/src/libsysprof-ui/libsysprof-ui.gresource.xml
index a4bca35..fd6a597 100644
--- a/src/libsysprof-ui/libsysprof-ui.gresource.xml
+++ b/src/libsysprof-ui/libsysprof-ui.gresource.xml
@@ -2,9 +2,7 @@
<gresources>
<gresource prefix="/org/gnome/sysprof">
<file compressed="true">css/SysprofEnvironEditor-shared.css</file>
- <file compressed="true">css/SysprofVisualizerView-shared.css</file>
- <file compressed="true">css/SysprofVisualizerView-Adwaita.css</file>
- <file compressed="true">css/SysprofVisualizerView-Adwaita-dark.css</file>
+ <file compressed="true">css/SysprofDisplay-shared.css</file>
<!-- Application icons -->
<file
alias="icons/scalable/apps/org.gnome.Sysprof.svg">../../data/icons/scalable/apps/org.gnome.Sysprof.svg</file>
@@ -14,20 +12,18 @@
<gresource prefix="/org/gnome/sysprof/ui">
<file preprocess="xml-stripblanks">sysprof-aid-icon.ui</file>
- <file preprocess="xml-stripblanks">sysprof-callgraph-view.ui</file>
- <file preprocess="xml-stripblanks">sysprof-capture-view.ui</file>
+ <file preprocess="xml-stripblanks">sysprof-callgraph-page.ui</file>
+ <file preprocess="xml-stripblanks">sysprof-details-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-display.ui</file>
- <file preprocess="xml-stripblanks">sysprof-details-view.ui</file>
- <file preprocess="xml-stripblanks">sysprof-empty-state-view.ui</file>
<file preprocess="xml-stripblanks">sysprof-environ-editor-row.ui</file>
<file preprocess="xml-stripblanks">sysprof-failed-state-view.ui</file>
- <file preprocess="xml-stripblanks">sysprof-logs-view.ui</file>
- <file preprocess="xml-stripblanks">sysprof-marks-view.ui</file>
+ <file preprocess="xml-stripblanks">sysprof-logs-page.ui</file>
+ <file preprocess="xml-stripblanks">sysprof-marks-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-process-model-row.ui</file>
<file preprocess="xml-stripblanks">sysprof-profiler-assistant.ui</file>
<file preprocess="xml-stripblanks">sysprof-recording-state-view.ui</file>
<file preprocess="xml-stripblanks">sysprof-tab.ui</file>
<file preprocess="xml-stripblanks">sysprof-time-label.ui</file>
- <file preprocess="xml-stripblanks">sysprof-visualizer-view.ui</file>
+ <file preprocess="xml-stripblanks">sysprof-visualizers-frame.ui</file>
</gresource>
</gresources>
diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build
index 5a919ac..69ec579 100644
--- a/src/libsysprof-ui/meson.build
+++ b/src/libsysprof-ui/meson.build
@@ -1,84 +1,71 @@
if get_option('enable_gtk') and get_option('libsysprof')
libsysprof_ui_public_sources = [
- 'sysprof-aid.c',
- 'sysprof-capture-view.c',
- 'sysprof-callgraph-aid.c',
- 'sysprof-callgraph-view.c',
'sysprof-check.c',
- 'sysprof-color-cycle.c',
- 'sysprof-cpu-aid.c',
- 'sysprof-cpu-visualizer-row.c',
'sysprof-display.c',
- 'sysprof-empty-state-view.c',
- 'sysprof-failed-state-view.c',
- 'sysprof-line-visualizer-row.c',
- 'sysprof-marks-model.c',
- 'sysprof-marks-view.c',
- 'sysprof-mark-visualizer-row.c',
- 'sysprof-memory-aid.c',
'sysprof-model-filter.c',
'sysprof-notebook.c',
+ 'sysprof-page.c',
'sysprof-process-model-row.c',
- 'sysprof-proxy-aid.c',
- 'sysprof-profiler-assistant.c',
- 'sysprof-recording-state-view.c',
- 'sysprof-visualizer-list.c',
- 'sysprof-visualizer-row.c',
- 'sysprof-visualizer-ticks.c',
- 'sysprof-visualizer-view.c',
+ 'sysprof-visualizer.c',
+ 'sysprof-visualizer-group.c',
'sysprof-zoom-manager.c',
]
libsysprof_ui_private_sources = [
'pointcache.c',
'rectangles.c',
+ 'sysprof-aid.c',
'sysprof-aid-icon.c',
- 'sysprof-details-view.c',
+ 'sysprof-battery-aid.c',
'sysprof-cairo.c',
+ 'sysprof-callgraph-aid.c',
+ 'sysprof-callgraph-page.c',
'sysprof-cell-renderer-duration.c',
'sysprof-cell-renderer-percent.c',
- 'sysprof-depth-visualizer-row.c',
- 'sysprof-environ-editor-row.c',
+ 'sysprof-color-cycle.c',
+ 'sysprof-counters-aid.c',
+ 'sysprof-cpu-aid.c',
+ 'sysprof-depth-visualizer.c',
+ 'sysprof-details-page.c',
+ 'sysprof-display.c',
+ 'sysprof-environ.c',
'sysprof-environ-editor.c',
+ 'sysprof-environ-editor-row.c',
'sysprof-environ-variable.c',
- 'sysprof-environ.c',
+ 'sysprof-failed-state-view.c',
+ 'sysprof-line-visualizer.c',
'sysprof-log-model.c',
- 'sysprof-logs-view.c',
+ 'sysprof-logs-aid.c',
+ 'sysprof-logs-page.c',
+ 'sysprof-marks-aid.c',
+ 'sysprof-marks-model.c',
+ 'sysprof-marks-page.c',
+ 'sysprof-mark-visualizer.c',
+ 'sysprof-memory-aid.c',
+ 'sysprof-profiler-assistant.c',
+ 'sysprof-proxy-aid.c',
+ 'sysprof-recording-state-view.c',
+ 'sysprof-scrollmap.c',
'sysprof-tab.c',
- 'sysprof-time-label.c',
'sysprof-theme-manager.c',
+ 'sysprof-time-label.c',
+ 'sysprof-time-visualizer.c',
+ 'sysprof-visualizer-group-header.c',
+ 'sysprof-visualizers-frame.c',
+ 'sysprof-visualizer-ticks.c',
'../stackstash.c',
]
libsysprof_ui_public_headers = [
- 'sysprof-aid.h',
- 'sysprof-capture-view.h',
- 'sysprof-callgraph-aid.h',
- 'sysprof-callgraph-view.h',
- 'sysprof-cell-renderer-percent.h',
'sysprof-check.h',
- 'sysprof-cpu-aid.h',
- 'sysprof-cpu-visualizer-row.h',
'sysprof-display.h',
- 'sysprof-empty-state-view.h',
- 'sysprof-failed-state-view.h',
- 'sysprof-line-visualizer-row.h',
- 'sysprof-marks-model.h',
- 'sysprof-marks-view.h',
- 'sysprof-mark-visualizer-row.h',
- 'sysprof-memory-aid.h',
- 'sysprof-model-filter.c',
'sysprof-model-filter.h',
'sysprof-notebook.h',
+ 'sysprof-page.h',
'sysprof-process-model-row.h',
- 'sysprof-proxy-aid.h',
- 'sysprof-profiler-assistant.h',
- 'sysprof-recording-state-view.h',
- 'sysprof-visualizer-list.h',
- 'sysprof-visualizer-row.h',
- 'sysprof-visualizer-ticks.h',
- 'sysprof-visualizer-view.h',
+ 'sysprof-visualizer.h',
+ 'sysprof-visualizer-group.h',
'sysprof-zoom-manager.h',
'sysprof-ui.h',
]
diff --git a/src/libsysprof-ui/rectangles.c b/src/libsysprof-ui/rectangles.c
index 1623502..2ac8ca0 100644
--- a/src/libsysprof-ui/rectangles.c
+++ b/src/libsysprof-ui/rectangles.c
@@ -20,7 +20,7 @@
#include "rectangles.h"
#include "sysprof-color-cycle.h"
-#include "sysprof-visualizer-row.h"
+#include "sysprof-visualizer.h"
typedef struct
{
@@ -153,18 +153,18 @@ rectangles_draw (Rectangles *self,
{
GtkAllocation alloc;
gdouble range;
- guint n_rows;
+ guint ns;
g_assert (self != NULL);
- g_assert (SYSPROF_IS_VISUALIZER_ROW (row));
+ g_assert (SYSPROF_IS_VISUALIZER (row));
g_assert (cr != NULL);
if (!self->sorted)
rectangles_sort (self);
gtk_widget_get_allocation (row, &alloc);
- n_rows = g_hash_table_size (self->y_indexes);
- if (n_rows == 0 || alloc.height == 0)
+ ns = g_hash_table_size (self->y_indexes);
+ if (ns == 0 || alloc.height == 0)
return;
range = self->end_time - self->begin_time;
@@ -173,24 +173,24 @@ rectangles_draw (Rectangles *self,
{
Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i);
guint y_index = GPOINTER_TO_UINT (g_hash_table_lookup (self->y_indexes, rect->name));
- SysprofVisualizerRowRelativePoint in_points[2];
- SysprofVisualizerRowAbsolutePoint out_points[2];
+ SysprofVisualizerRelativePoint in_points[2];
+ SysprofVisualizerAbsolutePoint out_points[2];
GdkRectangle r;
GdkRGBA *rgba;
g_assert (y_index > 0);
- g_assert (y_index <= n_rows);
+ g_assert (y_index <= ns);
in_points[0].x = (rect->begin - self->begin_time) / range;
- in_points[0].y = (y_index - 1) / (gdouble)n_rows;
+ in_points[0].y = (y_index - 1) / (gdouble)ns;
in_points[1].x = (rect->end - self->begin_time) / range;
in_points[1].y = 0;
- sysprof_visualizer_row_translate_points (SYSPROF_VISUALIZER_ROW (row),
+ sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (row),
in_points, G_N_ELEMENTS (in_points),
out_points, G_N_ELEMENTS (out_points));
- r.height = alloc.height / (gdouble)n_rows;
+ r.height = alloc.height / (gdouble)ns;
r.x = out_points[0].x;
r.y = out_points[0].y - r.height;
diff --git a/src/libsysprof-ui/sysprof-aid.c b/src/libsysprof-ui/sysprof-aid.c
index c1461f2..1bf3a48 100644
--- a/src/libsysprof-ui/sysprof-aid.c
+++ b/src/libsysprof-ui/sysprof-aid.c
@@ -49,6 +49,29 @@ enum {
static GParamSpec *properties [N_PROPS];
+static void
+sysprof_aid_real_present_async (SysprofAid *self,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data,
+ sysprof_aid_real_present_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Not supported");
+}
+
+static gboolean
+sysprof_aid_real_present_finish (SysprofAid *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
sysprof_aid_finalize (GObject *object)
{
@@ -121,6 +144,9 @@ sysprof_aid_class_init (SysprofAidClass *klass)
object_class->get_property = sysprof_aid_get_property;
object_class->set_property = sysprof_aid_set_property;
+ klass->present_async = sysprof_aid_real_present_async;
+ klass->present_finish = sysprof_aid_real_present_finish;
+
properties [PROP_DISPLAY_NAME] =
g_param_spec_string ("display-name",
"Display Name",
@@ -146,7 +172,7 @@ sysprof_aid_class_init (SysprofAidClass *klass)
}
static void
-sysprof_aid_init (SysprofAid *self)
+sysprof_aid_init (SysprofAid *self G_GNUC_UNUSED)
{
}
@@ -258,10 +284,10 @@ sysprof_aid_prepare (SysprofAid *self,
}
static void
-sysprof_aid_add_child (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *object,
- const gchar *type)
+sysprof_aid_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *object,
+ const gchar *type G_GNUC_UNUSED)
{
SysprofAid *self = (SysprofAid *)buildable;
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
@@ -298,3 +324,30 @@ sysprof_aid_new (const gchar *display_name,
"icon-name", icon_name,
NULL);
}
+
+void
+sysprof_aid_present_async (SysprofAid *self,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (SYSPROF_IS_AID (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (SYSPROF_IS_DISPLAY (display));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ SYSPROF_AID_GET_CLASS (self)->present_async (self, reader, display, cancellable, callback, user_data);
+}
+
+gboolean
+sysprof_aid_present_finish (SysprofAid *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_AID (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return SYSPROF_AID_GET_CLASS (self)->present_finish (self, result, error);
+}
diff --git a/src/libsysprof-ui/sysprof-aid.h b/src/libsysprof-ui/sysprof-aid.h
index ba8dfe3..70a1666 100644
--- a/src/libsysprof-ui/sysprof-aid.h
+++ b/src/libsysprof-ui/sysprof-aid.h
@@ -20,49 +20,57 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gio/gio.h>
#include <sysprof.h>
+#include "sysprof-display.h"
+
G_BEGIN_DECLS
#define SYSPROF_TYPE_AID (sysprof_aid_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofAid, sysprof_aid, SYSPROF, AID, GObject)
struct _SysprofAidClass
{
GObjectClass parent_class;
- void (*prepare) (SysprofAid *self,
- SysprofProfiler *profiler);
+ void (*prepare) (SysprofAid *self,
+ SysprofProfiler *profiler);
+ void (*present_async) (SysprofAid *self,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*present_finish) (SysprofAid *self,
+ GAsyncResult *result,
+ GError **error);
- /*< gpointer >*/
+ /*< private >*/
gpointer _reserved[16];
};
-SYSPROF_AVAILABLE_IN_ALL
-SysprofAid *sysprof_aid_new (const gchar *display_name,
- const gchar *icon_name);
-SYSPROF_AVAILABLE_IN_ALL
-const gchar *sysprof_aid_get_display_name (SysprofAid *self);
-SYSPROF_AVAILABLE_IN_ALL
-void sysprof_aid_set_display_name (SysprofAid *self,
- const gchar *display_name);
-SYSPROF_AVAILABLE_IN_ALL
-GIcon *sysprof_aid_get_icon (SysprofAid *self);
-SYSPROF_AVAILABLE_IN_ALL
-void sysprof_aid_set_icon (SysprofAid *self,
- GIcon *icon);
-SYSPROF_AVAILABLE_IN_ALL
-void sysprof_aid_set_icon_name (SysprofAid *self,
- const gchar *icon_name);
-SYSPROF_AVAILABLE_IN_ALL
-void sysprof_aid_prepare (SysprofAid *self,
- SysprofProfiler *profiler);
+SysprofAid *sysprof_aid_new (const gchar *display_name,
+ const gchar *icon_name);
+const gchar *sysprof_aid_get_display_name (SysprofAid *self);
+void sysprof_aid_set_display_name (SysprofAid *self,
+ const gchar *display_name);
+GIcon *sysprof_aid_get_icon (SysprofAid *self);
+void sysprof_aid_set_icon (SysprofAid *self,
+ GIcon *icon);
+void sysprof_aid_set_icon_name (SysprofAid *self,
+ const gchar *icon_name);
+void sysprof_aid_prepare (SysprofAid *self,
+ SysprofProfiler *profiler);
+void sysprof_aid_present_async (SysprofAid *self,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean sysprof_aid_present_finish (SysprofAid *self,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-battery-aid.c b/src/libsysprof-ui/sysprof-battery-aid.c
new file mode 100644
index 0000000..5675030
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-battery-aid.c
@@ -0,0 +1,239 @@
+/* sysprof-battery-aid.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-battery-aid"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-color-cycle.h"
+#include "sysprof-battery-aid.h"
+#include "sysprof-line-visualizer.h"
+
+struct _SysprofBatteryAid
+{
+ SysprofAid parent_instance;
+};
+
+typedef struct
+{
+ SysprofCaptureCursor *cursor;
+ SysprofDisplay *display;
+} Present;
+
+G_DEFINE_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF_TYPE_AID)
+
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
+/**
+ * sysprof_battery_aid_new:
+ *
+ * Create a new #SysprofBatteryAid.
+ *
+ * Returns: (transfer full): a newly created #SysprofBatteryAid
+ *
+ * Since: 3.34
+ */
+SysprofAid *
+sysprof_battery_aid_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_BATTERY_AID, NULL);
+}
+
+static void
+sysprof_battery_aid_prepare (SysprofAid *self,
+ SysprofProfiler *profiler)
+{
+#ifdef __linux__
+ g_autoptr(SysprofSource) source = NULL;
+
+ g_assert (SYSPROF_IS_BATTERY_AID (self));
+ g_assert (SYSPROF_IS_PROFILER (profiler));
+
+ source = sysprof_battery_source_new ();
+ sysprof_profiler_add_source (profiler, source);
+#endif
+}
+
+static gboolean
+collect_battery_counters (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
+ GArray *counters = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
+ g_assert (counters != NULL);
+
+ for (guint i = 0; i < def->n_counters; i++)
+ {
+ const SysprofCaptureCounter *counter = &def->counters[i];
+
+ if (g_strcmp0 (counter->category, "Battery Charge") == 0)
+ g_array_append_vals (counters, counter, 1);
+ }
+
+ return TRUE;
+}
+
+static void
+sysprof_battery_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *present = task_data;
+ g_autoptr(GArray) counters = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_BATTERY_AID (source_object));
+ g_assert (present != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
+ sysprof_capture_cursor_foreach (present->cursor, collect_battery_counters, counters);
+ g_task_return_pointer (task,
+ g_steal_pointer (&counters),
+ (GDestroyNotify) g_array_unref);
+}
+
+static void
+sysprof_battery_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
+ g_autoptr(SysprofCaptureCondition) condition = NULL;
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ g_autoptr(GTask) task = NULL;
+ Present present;
+
+ g_assert (SYSPROF_IS_BATTERY_AID (aid));
+ g_assert (reader != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (display));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ condition = sysprof_capture_condition_new_where_type_in (1, types);
+ cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
+
+ present.cursor = g_steal_pointer (&cursor);
+ present.display = g_object_ref (display);
+
+ task = g_task_new (aid, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_battery_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &present),
+ present_free);
+ g_task_run_in_thread (task, sysprof_battery_aid_present_worker);
+}
+
+static gboolean
+sysprof_battery_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_autoptr(GArray) counters = NULL;
+ Present *present;
+
+ g_assert (SYSPROF_IS_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ present = g_task_get_task_data (G_TASK (result));
+
+ if ((counters = g_task_propagate_pointer (G_TASK (result), error)))
+ {
+ g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
+ SysprofVisualizerGroup *group;
+
+ group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "title", _("Battery Charge"),
+ "visible", TRUE,
+ NULL);
+
+ for (guint i = 0; i < counters->len; i++)
+ {
+ const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
+
+ if (g_strcmp0 (ctr->category, "Battery Charge") == 0)
+ {
+ g_autofree gchar *title = NULL;
+ gboolean is_combined = g_str_equal (ctr->name, "Combined");
+ GtkWidget *row;
+ GdkRGBA rgba;
+
+ if (is_combined)
+ title = g_strdup (_("Battery Charge (All)"));
+ else
+ title = g_strdup_printf ("Battery Charge (%s)", ctr->name);
+
+ row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", title,
+ "height-request", 35,
+ "visible", is_combined,
+ NULL);
+ sysprof_color_cycle_next (cycle, &rgba);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_visualizer_group_insert (group,
+ SYSPROF_VISUALIZER (row),
+ is_combined ? 0 : -1,
+ !is_combined);
+ }
+ }
+
+ if (counters->len > 0)
+ sysprof_display_add_group (present->display, group);
+ else
+ gtk_widget_destroy (GTK_WIDGET (group));
+ }
+
+ return counters != NULL;
+}
+
+static void
+sysprof_battery_aid_class_init (SysprofBatteryAidClass *klass)
+{
+ SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
+
+ aid_class->prepare = sysprof_battery_aid_prepare;
+ aid_class->present_async = sysprof_battery_aid_present_async;
+ aid_class->present_finish = sysprof_battery_aid_present_finish;
+}
+
+static void
+sysprof_battery_aid_init (SysprofBatteryAid *self)
+{
+ sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery"));
+ sysprof_aid_set_icon_name (SYSPROF_AID (self), "battery-low-charging-symbolic");
+}
diff --git a/src/libsysprof-ui/sysprof-depth-visualizer-row.h b/src/libsysprof-ui/sysprof-battery-aid.h
similarity index 73%
rename from src/libsysprof-ui/sysprof-depth-visualizer-row.h
rename to src/libsysprof-ui/sysprof-battery-aid.h
index f413f8d..563793f 100644
--- a/src/libsysprof-ui/sysprof-depth-visualizer-row.h
+++ b/src/libsysprof-ui/sysprof-battery-aid.h
@@ -1,4 +1,4 @@
-/* sysprof-depth-visualizer-row.h
+/* sysprof-battery-aid.h
*
* Copyright 2019 Christian Hergert <chergert redhat com>
*
@@ -20,12 +20,14 @@
#pragma once
-#include "sysprof-visualizer-row.h"
+#include "sysprof-aid.h"
G_BEGIN_DECLS
-#define SYSPROF_TYPE_DEPTH_VISUALIZER_ROW (sysprof_depth_visualizer_row_get_type())
+#define SYSPROF_TYPE_BATTERY_AID (sysprof_battery_aid_get_type())
-G_DECLARE_FINAL_TYPE (SysprofDepthVisualizerRow, sysprof_depth_visualizer_row, SYSPROF,
DEPTH_VISUALIZER_ROW, SysprofVisualizerRow)
+G_DECLARE_FINAL_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF, BATTERY_AID, SysprofAid)
+
+SysprofAid *sysprof_battery_aid_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.c b/src/libsysprof-ui/sysprof-callgraph-aid.c
index 9e3c45a..5b0ebb0 100644
--- a/src/libsysprof-ui/sysprof-callgraph-aid.c
+++ b/src/libsysprof-ui/sysprof-callgraph-aid.c
@@ -25,14 +25,46 @@
#include <glib/gi18n.h>
#include "sysprof-callgraph-aid.h"
+#include "sysprof-callgraph-page.h"
+#include "sysprof-depth-visualizer.h"
struct _SysprofCallgraphAid
{
SysprofAid parent_instance;
};
+typedef struct
+{
+ SysprofCaptureCursor *cursor;
+ SysprofDisplay *display;
+ guint has_samples : 1;
+} Present;
+
G_DEFINE_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF_TYPE_AID)
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
+static void
+on_group_activated_cb (SysprofVisualizerGroup *group,
+ SysprofPage *page)
+{
+ SysprofDisplay *display;
+
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
+ g_assert (SYSPROF_IS_PAGE (page));
+
+ display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
+ sysprof_display_set_visible_page (display, page);
+}
+
/**
* sysprof_callgraph_aid_new:
*
@@ -82,12 +114,157 @@ sysprof_callgraph_aid_prepare (SysprofAid *self,
#endif
}
+static gboolean
+discover_samples_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ Present *p = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (p != NULL);
+
+ if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE)
+ {
+ p->has_samples = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+sysprof_callgraph_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *p = task_data;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_CALLGRAPH_AID (source_object));
+ g_assert (p != NULL);
+ g_assert (p->cursor != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /* If we find a sample frame, then we should enable the callgraph
+ * and stack visualizers.
+ */
+ sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p);
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_callgraph_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE };
+ g_autoptr(SysprofCaptureCondition) condition = NULL;
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ g_autoptr(GTask) task = NULL;
+ Present present;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_AID (aid));
+ g_assert (reader != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (display));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ condition = sysprof_capture_condition_new_where_type_in (1, types);
+ cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
+
+ present.cursor = g_steal_pointer (&cursor);
+ present.display = g_object_ref (display);
+
+ task = g_task_new (aid, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_callgraph_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &present),
+ present_free);
+ g_task_run_in_thread (task, sysprof_callgraph_aid_present_worker);
+}
+
+static gboolean
+sysprof_callgraph_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ Present *p;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ p = g_task_get_task_data (G_TASK (result));
+
+ if (p->has_samples)
+ {
+ SysprofVisualizerGroup *group;
+ SysprofVisualizer *depth;
+ SysprofPage *page;
+
+ group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "has-page", TRUE,
+ "priority", -500,
+ "title", _("Stack Traces"),
+ "visible", TRUE,
+ NULL);
+
+ depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_COMBINED);
+ g_object_set (depth,
+ "title", _("Stack Traces"),
+ "height-request", 35,
+ "visible", TRUE,
+ NULL);
+ sysprof_visualizer_group_insert (group, depth, 0, FALSE);
+
+ depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY);
+ g_object_set (depth,
+ "title", _("Stack Traces (In Kernel)"),
+ "height-request", 35,
+ "visible", FALSE,
+ NULL);
+ sysprof_visualizer_group_insert (group, depth, 1, TRUE);
+
+ depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_USER_ONLY);
+ g_object_set (depth,
+ "title", _("Stack Traces (In User)"),
+ "height-request", 35,
+ "visible", FALSE,
+ NULL);
+ sysprof_visualizer_group_insert (group, depth, 2, TRUE);
+
+ sysprof_display_add_group (p->display, group);
+
+ page = g_object_new (SYSPROF_TYPE_CALLGRAPH_PAGE,
+ "title", _("Callgraph"),
+ "vexpand", TRUE,
+ "visible", TRUE,
+ NULL);
+ sysprof_display_add_page (p->display, page);
+ sysprof_display_set_visible_page (p->display, page);
+
+ g_signal_connect_object (group,
+ "group-activated",
+ G_CALLBACK (on_group_activated_cb),
+ page,
+ 0);
+ }
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
sysprof_callgraph_aid_class_init (SysprofCallgraphAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_callgraph_aid_prepare;
+ aid_class->present_async = sysprof_callgraph_aid_present_async;
+ aid_class->present_finish = sysprof_callgraph_aid_present_finish;
}
static void
diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.h b/src/libsysprof-ui/sysprof-callgraph-aid.h
index ecfe053..3fdde80 100644
--- a/src/libsysprof-ui/sysprof-callgraph-aid.h
+++ b/src/libsysprof-ui/sysprof-callgraph-aid.h
@@ -26,10 +26,8 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_AID (sysprof_callgraph_aid_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF, CALLGRAPH_AID, SysprofAid)
-SYSPROF_AVAILABLE_IN_ALL
SysprofAid *sysprof_callgraph_aid_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-callgraph-page.c b/src/libsysprof-ui/sysprof-callgraph-page.c
new file mode 100644
index 0000000..354cdf1
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-callgraph-page.c
@@ -0,0 +1,1293 @@
+/* sysprof-callgraph-page.c
+ *
+ * Copyright 2016-2019 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
+ */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, 2006, Soeren Sandmann
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#include "../stackstash.h"
+
+#include "sysprof-callgraph-page.h"
+#include "sysprof-cell-renderer-percent.h"
+
+typedef struct
+{
+ SysprofCallgraphProfile *profile;
+
+ GtkTreeView *callers_view;
+ GtkTreeView *functions_view;
+ GtkTreeView *descendants_view;
+ GtkTreeViewColumn *descendants_name_column;
+ GtkStack *stack;
+
+ GQueue *history;
+
+ guint profile_size;
+ guint loading;
+} SysprofCallgraphPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF_TYPE_PAGE)
+
+enum {
+ PROP_0,
+ PROP_PROFILE,
+ N_PROPS
+};
+
+enum {
+ GO_PREVIOUS,
+ N_SIGNALS
+};
+
+enum {
+ COLUMN_NAME,
+ COLUMN_SELF,
+ COLUMN_TOTAL,
+ COLUMN_POINTER,
+ COLUMN_HITS,
+};
+
+static void sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self,
+ StackNode *node);
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static guint
+sysprof_callgraph_page_get_profile_size (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ StackStash *stash;
+ StackNode *node;
+ guint size = 0;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ if (priv->profile_size != 0)
+ return priv->profile_size;
+
+ if (priv->profile == NULL)
+ return 0;
+
+ if (NULL == (stash = sysprof_callgraph_profile_get_stash (priv->profile)))
+ return 0;
+
+ for (node = stack_stash_get_root (stash); node != NULL; node = node->siblings)
+ size += node->total;
+
+ priv->profile_size = size;
+
+ return size;
+}
+
+static void
+build_functions_store (StackNode *node,
+ gpointer user_data)
+{
+ struct {
+ GtkListStore *store;
+ gdouble profile_size;
+ } *state = user_data;
+ GtkTreeIter iter;
+ const StackNode *n;
+ guint size = 0;
+ guint total = 0;
+
+ g_assert (state != NULL);
+ g_assert (GTK_IS_LIST_STORE (state->store));
+
+ for (n = node; n != NULL; n = n->next)
+ {
+ size += n->size;
+ if (n->toplevel)
+ total += n->total;
+ }
+
+ gtk_list_store_append (state->store, &iter);
+ gtk_list_store_set (state->store, &iter,
+ COLUMN_NAME, U64_TO_POINTER(node->data),
+ COLUMN_SELF, 100.0 * size / state->profile_size,
+ COLUMN_TOTAL, 100.0 * total / state->profile_size,
+ COLUMN_POINTER, node,
+ -1);
+
+}
+
+static void
+sysprof_callgraph_page_load (SysprofCallgraphPage *self,
+ SysprofCallgraphProfile *profile)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkListStore *functions;
+ StackStash *stash;
+ StackNode *n;
+ GtkTreeIter iter;
+ struct {
+ GtkListStore *store;
+ gdouble profile_size;
+ } state = { 0 };
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (profile));
+
+ /*
+ * TODO: This is probably the type of thing we want to do off the main
+ * thread. We should be able to build the tree models off thread
+ * and then simply apply them on the main thread.
+ *
+ * In the mean time, we should set the state of the widget to
+ * insensitive and give some indication of loading progress.
+ */
+
+ if (!g_set_object (&priv->profile, profile))
+ return;
+
+ if (sysprof_callgraph_profile_is_empty (profile))
+ return;
+
+ stash = sysprof_callgraph_profile_get_stash (profile);
+
+ for (n = stack_stash_get_root (stash); n; n = n->siblings)
+ state.profile_size += n->total;
+
+ functions = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER);
+
+ state.store = functions;
+ stack_stash_foreach_by_address (stash, build_functions_store, &state);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (functions),
+ COLUMN_TOTAL,
+ GTK_SORT_DESCENDING);
+
+ gtk_tree_view_set_model (priv->functions_view, GTK_TREE_MODEL (functions));
+ gtk_tree_view_set_model (priv->callers_view, NULL);
+ gtk_tree_view_set_model (priv->descendants_view, NULL);
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (functions), &iter))
+ {
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (priv->functions_view);
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ gtk_stack_set_visible_child_name (priv->stack, "callgraph");
+
+ g_clear_object (&functions);
+}
+
+void
+_sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ gtk_stack_set_visible_child_name (priv->stack, "empty-state");
+}
+
+static void
+sysprof_callgraph_page_unload (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (priv->profile));
+
+ g_queue_clear (priv->history);
+ g_clear_object (&priv->profile);
+ priv->profile_size = 0;
+
+ gtk_tree_view_set_model (priv->callers_view, NULL);
+ gtk_tree_view_set_model (priv->functions_view, NULL);
+ gtk_tree_view_set_model (priv->descendants_view, NULL);
+
+ gtk_stack_set_visible_child_name (priv->stack, "empty-state");
+}
+
+/**
+ * sysprof_callgraph_page_get_profile:
+ *
+ * Returns: (transfer none): An #SysprofCallgraphProfile.
+ */
+SysprofCallgraphProfile *
+sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL);
+
+ return priv->profile;
+}
+
+void
+sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self,
+ SysprofCallgraphProfile *profile)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_return_if_fail (!profile || SYSPROF_IS_CALLGRAPH_PROFILE (profile));
+
+ if (profile != priv->profile)
+ {
+ if (priv->profile)
+ sysprof_callgraph_page_unload (self);
+
+ if (profile)
+ sysprof_callgraph_page_load (self, profile);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILE]);
+ }
+}
+
+static void
+sysprof_callgraph_page_expand_descendants (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeModel *model;
+ GList *all_paths = NULL;
+ GtkTreePath *first_path;
+ GtkTreeIter iter;
+ gdouble top_value = 0;
+ gint max_rows = 40; /* FIXME */
+ gint n_rows;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ model = gtk_tree_view_get_model (priv->descendants_view);
+ first_path = gtk_tree_path_new_first ();
+ all_paths = g_list_prepend (all_paths, first_path);
+ n_rows = 1;
+
+ gtk_tree_model_get_iter (model, &iter, first_path);
+ gtk_tree_model_get (model, &iter,
+ COLUMN_TOTAL, &top_value,
+ -1);
+
+ while ((all_paths != NULL) && (n_rows < max_rows))
+ {
+ GtkTreeIter best_iter;
+ GtkTreePath *best_path = NULL;
+ GList *list;
+ gdouble best_value = 0.0;
+ gint n_children;
+ gint i;
+
+ for (list = all_paths; list != NULL; list = list->next)
+ {
+ GtkTreePath *path = list->data;
+
+ g_assert (path != NULL);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ {
+ gdouble value;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_TOTAL, &value,
+ -1);
+
+ if (value >= best_value)
+ {
+ best_value = value;
+ best_path = path;
+ best_iter = iter;
+ }
+ }
+ }
+
+ n_children = gtk_tree_model_iter_n_children (model, &best_iter);
+
+ if ((n_children > 0) &&
+ ((best_value / top_value) > 0.04) &&
+ ((n_children + gtk_tree_path_get_depth (best_path)) / (gdouble)max_rows) < (best_value /
top_value))
+ {
+ gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE);
+ n_rows += n_children;
+
+ if (gtk_tree_path_get_depth (best_path) < 4)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_path_copy (best_path);
+ gtk_tree_path_down (path);
+
+ for (i = 0; i < n_children; i++)
+ {
+ all_paths = g_list_prepend (all_paths, path);
+
+ path = gtk_tree_path_copy (path);
+ gtk_tree_path_next (path);
+ }
+
+ gtk_tree_path_free (path);
+ }
+ }
+
+ all_paths = g_list_remove (all_paths, best_path);
+
+ /* Always expand at least once */
+ if ((all_paths == NULL) && (n_rows == 1))
+ gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE);
+
+ gtk_tree_path_free (best_path);
+ }
+
+ g_list_free_full (all_paths, (GDestroyNotify)gtk_tree_path_free);
+}
+
+typedef struct
+{
+ StackNode *node;
+ const gchar *name;
+ guint self;
+ guint total;
+} Caller;
+
+static Caller *
+caller_new (StackNode *node)
+{
+ Caller *c;
+
+ c = g_slice_new (Caller);
+ c->name = U64_TO_POINTER (node->data);
+ c->self = 0;
+ c->total = 0;
+ c->node = node;
+
+ return c;
+}
+
+static void
+caller_free (gpointer data)
+{
+ Caller *c = data;
+ g_slice_free (Caller, c);
+}
+
+static void
+sysprof_callgraph_page_function_selection_changed (SysprofCallgraphPage *self,
+ GtkTreeSelection *selection)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter;
+ GtkListStore *callers_store;
+ g_autoptr(GHashTable) callers = NULL;
+ g_autoptr(GHashTable) processed = NULL;
+ StackNode *callees = NULL;
+ StackNode *node;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (GTK_IS_TREE_SELECTION (selection));
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_view_set_model (priv->callers_view, NULL);
+ gtk_tree_view_set_model (priv->descendants_view, NULL);
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_POINTER, &callees,
+ -1);
+
+ sysprof_callgraph_page_update_descendants (self, callees);
+
+ callers_store = gtk_list_store_new (4,
+ G_TYPE_STRING,
+ G_TYPE_DOUBLE,
+ G_TYPE_DOUBLE,
+ G_TYPE_POINTER);
+
+ callers = g_hash_table_new_full (NULL, NULL, NULL, caller_free);
+ processed = g_hash_table_new (NULL, NULL);
+
+ for (node = callees; node != NULL; node = node->next)
+ {
+ Caller *c;
+
+ if (!node->parent)
+ continue;
+
+ c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data));
+
+ if (c == NULL)
+ {
+ c = caller_new (node->parent);
+ g_hash_table_insert (callers, (gpointer)c->name, c);
+ }
+ }
+
+ for (node = callees; node != NULL; node = node->next)
+ {
+ StackNode *top_caller = node->parent;
+ StackNode *top_callee = node;
+ StackNode *n;
+ Caller *c;
+
+ if (!node->parent)
+ continue;
+
+ /*
+ * We could have a situation where the function was called in a
+ * reentrant fashion, so we want to take the top-most match in the
+ * stack.
+ */
+ for (n = node; n && n->parent; n = n->parent)
+ {
+ if (n->data == node->data && n->parent->data == node->parent->data)
+ {
+ top_caller = n->parent;
+ top_callee = n;
+ }
+ }
+
+ c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data));
+
+ g_assert (c != NULL);
+
+ if (!g_hash_table_lookup (processed, top_caller))
+ {
+ c->total += top_callee->total;
+ g_hash_table_insert (processed, top_caller, top_caller);
+ }
+
+ c->self += node->size;
+ }
+
+ {
+ GHashTableIter hiter;
+ gpointer key, value;
+ guint size = 0;
+
+ size = MAX (1, sysprof_callgraph_page_get_profile_size (self));
+
+ g_hash_table_iter_init (&hiter, callers);
+
+ while (g_hash_table_iter_next (&hiter, &key, &value))
+ {
+ Caller *c = value;
+
+ gtk_list_store_append (callers_store, &iter);
+ gtk_list_store_set (callers_store, &iter,
+ COLUMN_NAME, c->name,
+ COLUMN_SELF, c->self * 100.0 / size,
+ COLUMN_TOTAL, c->total * 100.0 / size,
+ COLUMN_POINTER, c->node,
+ -1);
+ }
+ }
+
+ gtk_tree_view_set_model (priv->callers_view, GTK_TREE_MODEL (callers_store));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (callers_store),
+ COLUMN_TOTAL,
+ GTK_SORT_DESCENDING);
+
+ g_clear_object (&callers_store);
+}
+
+static void
+sysprof_callgraph_page_set_node (SysprofCallgraphPage *self,
+ StackNode *node)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (node != NULL);
+
+ if (priv->profile == NULL)
+ return;
+
+ model = gtk_tree_view_get_model (priv->functions_view);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ StackNode *item = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_POINTER, &item,
+ -1);
+
+ if (item != NULL && item->data == node->data)
+ {
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (priv->functions_view);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ break;
+ }
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+}
+
+static void
+sysprof_callgraph_page_descendant_activated (SysprofCallgraphPage *self,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GtkTreeView *tree_view)
+{
+ GtkTreeModel *model;
+ StackNode *node = NULL;
+ GtkTreeIter iter;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (GTK_IS_TREE_VIEW (tree_view));
+ g_assert (path != NULL);
+ g_assert (GTK_IS_TREE_VIEW_COLUMN (column));
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_POINTER, &node,
+ -1);
+
+ if (node != NULL)
+ sysprof_callgraph_page_set_node (self, node);
+}
+
+static void
+sysprof_callgraph_page_caller_activated (SysprofCallgraphPage *self,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GtkTreeView *tree_view)
+{
+ GtkTreeModel *model;
+ StackNode *node = NULL;
+ GtkTreeIter iter;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (GTK_IS_TREE_VIEW (tree_view));
+ g_assert (path != NULL);
+ g_assert (GTK_IS_TREE_VIEW_COLUMN (column));
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_POINTER, &node,
+ -1);
+
+ if (node != NULL)
+ sysprof_callgraph_page_set_node (self, node);
+}
+
+static void
+sysprof_callgraph_page_tag_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ SysprofCallgraphPage *self = data;
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ StackNode *node = NULL;
+ const gchar *str = NULL;
+
+ if (priv->profile == NULL)
+ return;
+
+ gtk_tree_model_get (model, iter, COLUMN_POINTER, &node, -1);
+
+ if (node && node->data)
+ {
+ GQuark tag;
+
+ tag = sysprof_callgraph_profile_get_tag (priv->profile, GSIZE_TO_POINTER (node->data));
+ if (tag != 0)
+ str = g_quark_to_string (tag);
+ }
+
+ g_object_set (cell, "text", str, NULL);
+}
+
+static void
+sysprof_callgraph_page_real_go_previous (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ StackNode *node;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ node = g_queue_pop_head (priv->history);
+
+ if (NULL != (node = g_queue_peek_head (priv->history)))
+ sysprof_callgraph_page_set_node (self, node);
+}
+
+static void
+descendants_view_move_cursor_cb (GtkTreeView *descendants_view,
+ GtkMovementStep step,
+ int direction,
+ gpointer user_data)
+{
+ if (step == GTK_MOVEMENT_VISUAL_POSITIONS)
+ {
+ GtkTreePath *path;
+
+ gtk_tree_view_get_cursor (descendants_view, &path, NULL);
+
+ if (direction == 1)
+ {
+ gtk_tree_view_expand_row (descendants_view, path, FALSE);
+ g_signal_stop_emission_by_name (descendants_view, "move-cursor");
+ }
+ else if (direction == -1)
+ {
+ gtk_tree_view_collapse_row (descendants_view, path);
+ g_signal_stop_emission_by_name (descendants_view, "move-cursor");
+ }
+
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+copy_tree_view_selection_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ g_autofree gchar *name = NULL;
+ gchar sstr[16];
+ gchar tstr[16];
+ GString *str = data;
+ gdouble self;
+ gdouble total;
+ gint depth;
+
+ g_assert (GTK_IS_TREE_MODEL (model));
+ g_assert (path != NULL);
+ g_assert (iter != NULL);
+ g_assert (str != NULL);
+
+ depth = gtk_tree_path_get_depth (path);
+ gtk_tree_model_get (model, iter,
+ COLUMN_NAME, &name,
+ COLUMN_SELF, &self,
+ COLUMN_TOTAL, &total,
+ -1);
+
+ g_snprintf (sstr, sizeof sstr, "%.2lf%%", self);
+ g_snprintf (tstr, sizeof tstr, "%.2lf%%", total);
+
+ g_string_append_printf (str, "[%8s] [%8s] ", sstr, tstr);
+
+ for (gint i = 1; i < depth; i++)
+ g_string_append (str, " ");
+ g_string_append (str, name);
+ g_string_append_c (str, '\n');
+}
+
+static void
+copy_tree_view_selection (GtkTreeView *tree_view)
+{
+ g_autoptr(GString) str = NULL;
+ GtkClipboard *clipboard;
+
+ g_assert (GTK_IS_TREE_VIEW (tree_view));
+
+ str = g_string_new (" SELF TOTAL FUNCTION\n");
+ gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (tree_view),
+ copy_tree_view_selection_cb,
+ str);
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (tree_view), GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, str->str, str->len);
+}
+
+static void
+sysprof_callgraph_page_copy_cb (GtkWidget *widget,
+ SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkWidget *toplevel;
+ GtkWidget *focus;
+
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ if (!(toplevel = gtk_widget_get_toplevel (widget)) ||
+ !GTK_IS_WINDOW (toplevel) ||
+ !(focus = gtk_window_get_focus (GTK_WINDOW (toplevel))))
+ return;
+
+ if (focus == GTK_WIDGET (priv->descendants_view))
+ copy_tree_view_selection (priv->descendants_view);
+ else if (focus == GTK_WIDGET (priv->callers_view))
+ copy_tree_view_selection (priv->callers_view);
+ else if (focus == GTK_WIDGET (priv->functions_view))
+ copy_tree_view_selection (priv->functions_view);
+}
+
+static void
+sysprof_callgraph_page_generate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofProfile *profile = (SysprofProfile *)object;
+ SysprofCallgraphPage *self;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (SYSPROF_IS_PROFILE (profile));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+
+ if (!sysprof_profile_generate_finish (profile, result, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ sysprof_callgraph_page_set_profile (self, SYSPROF_CALLGRAPH_PROFILE (profile));
+}
+
+static void
+sysprof_callgraph_page_load_async (SysprofPage *page,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *filter,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SysprofCallgraphPage *self = (SysprofCallgraphPage *)page;
+ g_autoptr(SysprofCaptureReader) copy = NULL;
+ g_autoptr(SysprofProfile) profile = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+ g_assert (reader != NULL);
+ g_assert (SYSPROF_IS_SELECTION (selection));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_callgraph_page_load_async);
+
+ copy = sysprof_capture_reader_copy (reader);
+
+ profile = sysprof_callgraph_profile_new_with_selection (selection);
+ sysprof_profile_set_reader (profile, reader);
+ sysprof_profile_generate (profile,
+ cancellable,
+ sysprof_callgraph_page_generate_cb,
+ g_steal_pointer (&task));
+}
+
+static gboolean
+sysprof_callgraph_page_load_finish (SysprofPage *page,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (page), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_callgraph_page_finalize (GObject *object)
+{
+ SysprofCallgraphPage *self = (SysprofCallgraphPage *)object;
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_clear_pointer (&priv->history, g_queue_free);
+ g_clear_object (&priv->profile);
+
+ G_OBJECT_CLASS (sysprof_callgraph_page_parent_class)->finalize (object);
+}
+
+static void
+sysprof_callgraph_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object);
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_PROFILE:
+ g_value_set_object (value, priv->profile);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_callgraph_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROFILE:
+ sysprof_callgraph_page_set_profile (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_callgraph_page_class_init (SysprofCallgraphPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass);
+ GtkBindingSet *bindings;
+
+ object_class->finalize = sysprof_callgraph_page_finalize;
+ object_class->get_property = sysprof_callgraph_page_get_property;
+ object_class->set_property = sysprof_callgraph_page_set_property;
+
+ page_class->load_async = sysprof_callgraph_page_load_async;
+ page_class->load_finish = sysprof_callgraph_page_load_finish;
+
+ klass->go_previous = sysprof_callgraph_page_real_go_previous;
+
+ properties [PROP_PROFILE] =
+ g_param_spec_object ("profile",
+ "Profile",
+ "The callgraph profile to view",
+ SYSPROF_TYPE_CALLGRAPH_PROFILE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [GO_PREVIOUS] =
+ g_signal_new ("go-previous",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (SysprofCallgraphPageClass, go_previous),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/sysprof/ui/sysprof-callgraph-page.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, callers_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, functions_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_name_column);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, stack);
+
+ bindings = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (bindings, GDK_KEY_Left, GDK_MOD1_MASK, "go-previous", 0);
+
+ g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_PERCENT);
+}
+
+static void
+sysprof_callgraph_page_init (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ DzlShortcutController *controller;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *cell;
+
+ priv->history = g_queue_new ();
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_stack_set_visible_child_name (priv->stack, "empty-state");
+
+ selection = gtk_tree_view_get_selection (priv->functions_view);
+
+ g_signal_connect_object (selection,
+ "changed",
+ G_CALLBACK (sysprof_callgraph_page_function_selection_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (priv->descendants_view,
+ "row-activated",
+ G_CALLBACK (sysprof_callgraph_page_descendant_activated),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (priv->callers_view,
+ "row-activated",
+ G_CALLBACK (sysprof_callgraph_page_caller_activated),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect (priv->descendants_view,
+ "move-cursor",
+ G_CALLBACK (descendants_view_move_cursor_cb),
+ NULL);
+
+ cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+ "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+ "xalign", 0.0f,
+ NULL);
+ gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (priv->descendants_name_column, cell, "text", 0);
+
+ cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+ "foreground", "#666666",
+ "scale", PANGO_SCALE_SMALL,
+ "xalign", 1.0f,
+ NULL);
+ gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (priv->descendants_name_column, cell,
+ sysprof_callgraph_page_tag_data_func,
+ self, NULL);
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->descendants_view),
+ GTK_SELECTION_MULTIPLE);
+
+ controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
+
+ dzl_shortcut_controller_add_command_callback (controller,
+ "org.gnome.sysprof3.capture.copy",
+ "<Control>c",
+ DZL_SHORTCUT_PHASE_BUBBLE,
+ (GtkCallback) sysprof_callgraph_page_copy_cb,
+ self,
+ NULL);
+}
+
+typedef struct _Descendant Descendant;
+
+struct _Descendant
+{
+ const gchar *name;
+ guint self;
+ guint cumulative;
+ Descendant *parent;
+ Descendant *siblings;
+ Descendant *children;
+};
+
+static void
+build_tree_cb (StackLink *trace,
+ gint size,
+ gpointer user_data)
+{
+ Descendant **tree = user_data;
+ Descendant *parent = NULL;
+ StackLink *link;
+
+ g_assert (trace != NULL);
+ g_assert (tree != NULL);
+
+ /* Get last item */
+ link = trace;
+ while (link->next)
+ link = link->next;
+
+ for (; link != NULL; link = link->prev)
+ {
+ const gchar *address = U64_TO_POINTER (link->data);
+ Descendant *prev = NULL;
+ Descendant *match = NULL;
+
+ for (match = *tree; match != NULL; match = match->siblings)
+ {
+ if (match->name == address)
+ {
+ if (prev != NULL)
+ {
+ /* Move to front */
+ prev->siblings = match->siblings;
+ match->siblings = *tree;
+ *tree = match;
+ }
+ break;
+ }
+ }
+
+ if (match == NULL)
+ {
+ /* Have we seen this object further up the tree? */
+ for (match = parent; match != NULL; match = match->parent)
+ {
+ if (match->name == address)
+ break;
+ }
+ }
+
+ if (match == NULL)
+ {
+ match = g_slice_new (Descendant);
+ match->name = address;
+ match->cumulative = 0;
+ match->self = 0;
+ match->children = NULL;
+ match->parent = parent;
+ match->siblings = *tree;
+ *tree = match;
+ }
+
+ tree = &match->children;
+ parent = match;
+ }
+
+ parent->self += size;
+
+ for (; parent != NULL; parent = parent->parent)
+ parent->cumulative += size;
+}
+
+static Descendant *
+build_tree (StackNode *node)
+{
+ Descendant *tree = NULL;
+
+ for (; node != NULL; node = node->next)
+ {
+ if (node->toplevel)
+ stack_node_foreach_trace (node, build_tree_cb, &tree);
+ }
+
+ return tree;
+}
+
+static void
+append_to_tree_and_free (SysprofCallgraphPage *self,
+ StackStash *stash,
+ GtkTreeStore *store,
+ Descendant *item,
+ GtkTreeIter *parent)
+{
+ StackNode *node = NULL;
+ GtkTreeIter iter;
+ guint profile_size;
+
+ g_assert (GTK_IS_TREE_STORE (store));
+ g_assert (item != NULL);
+
+ profile_size = MAX (1, sysprof_callgraph_page_get_profile_size (self));
+
+ gtk_tree_store_append (store, &iter, parent);
+
+ node = stack_stash_find_node (stash, (gpointer)item->name);
+
+ gtk_tree_store_set (store, &iter,
+ COLUMN_NAME, item->name,
+ COLUMN_SELF, item->self * 100.0 / (gdouble)profile_size,
+ COLUMN_TOTAL, item->cumulative * 100.0 / (gdouble)profile_size,
+ COLUMN_POINTER, node,
+ COLUMN_HITS, (guint)item->cumulative,
+ -1);
+
+ if (item->siblings != NULL)
+ append_to_tree_and_free (self, stash, store, item->siblings, parent);
+
+ if (item->children != NULL)
+ append_to_tree_and_free (self, stash, store, item->children, &iter);
+
+ g_slice_free (Descendant, item);
+}
+
+static void
+sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self,
+ StackNode *node)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeStore *store;
+
+ g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ if (g_queue_peek_head (priv->history) != node)
+ g_queue_push_head (priv->history, node);
+
+ store = gtk_tree_store_new (5,
+ G_TYPE_STRING,
+ G_TYPE_DOUBLE,
+ G_TYPE_DOUBLE,
+ G_TYPE_POINTER,
+ G_TYPE_UINT);
+
+ if (priv->profile != NULL)
+ {
+ StackStash *stash;
+
+ stash = sysprof_callgraph_profile_get_stash (priv->profile);
+ if (stash != NULL)
+ {
+ Descendant *tree;
+
+ tree = build_tree (node);
+ if (tree != NULL)
+ append_to_tree_and_free (self, stash, store, tree, NULL);
+ }
+ }
+
+ gtk_tree_view_set_model (priv->descendants_view, GTK_TREE_MODEL (store));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ COLUMN_TOTAL, GTK_SORT_DESCENDING);
+ sysprof_callgraph_page_expand_descendants (self);
+
+ g_clear_object (&store);
+}
+
+/**
+ * sysprof_callgraph_page_screenshot:
+ * @self: A #SysprofCallgraphPage.
+ *
+ * This function will generate a text representation of the descendants tree.
+ * This is useful if you want to include various profiling information in a
+ * commit message or email.
+ *
+ * The text generated will match the current row expansion in the tree view.
+ *
+ * Returns: (nullable) (transfer full): A newly allocated string that should be freed
+ * with g_free().
+ */
+gchar *
+sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreePath *tree_path;
+ GString *str;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL);
+
+ tree_view = priv->descendants_view;
+
+ if (NULL == (model = gtk_tree_view_get_model (tree_view)))
+ return NULL;
+
+ /*
+ * To avoid having to precalculate the deepest visible row, we
+ * put the timing information at the beginning of the line.
+ */
+
+ str = g_string_new (" SELF CUMULATIVE FUNCTION\n");
+ tree_path = gtk_tree_path_new_first ();
+
+ for (;;)
+ {
+ if (gtk_tree_model_get_iter (model, &iter, tree_path))
+ {
+ guint depth = gtk_tree_path_get_depth (tree_path);
+ StackNode *node;
+ gdouble in_self;
+ gdouble total;
+ guint i;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_SELF, &in_self,
+ COLUMN_TOTAL, &total,
+ COLUMN_POINTER, &node,
+ -1);
+
+ g_string_append_printf (str, "[% 7.2lf%%] [% 7.2lf%%] ", in_self, total);
+
+ for (i = 0; i < depth; i++)
+ g_string_append (str, " ");
+ g_string_append (str, GSIZE_TO_POINTER (node->data));
+ g_string_append_c (str, '\n');
+
+ if (gtk_tree_view_row_expanded (tree_view, tree_path))
+ gtk_tree_path_down (tree_path);
+ else
+ gtk_tree_path_next (tree_path);
+
+ continue;
+ }
+
+ if (!gtk_tree_path_up (tree_path) || !gtk_tree_path_get_depth (tree_path))
+ break;
+
+ gtk_tree_path_next (tree_path);
+ }
+
+ gtk_tree_path_free (tree_path);
+
+ return g_string_free (str, FALSE);
+}
+
+guint
+sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+ GtkTreeModel *model;
+ guint ret = 0;
+
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), 0);
+
+ if (NULL != (model = gtk_tree_view_get_model (priv->functions_view)))
+ ret = gtk_tree_model_iter_n_children (model, NULL);
+
+ return ret;
+}
+
+void
+_sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self,
+ gboolean loading)
+{
+ SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self));
+
+ if (loading)
+ priv->loading++;
+ else
+ priv->loading--;
+
+ if (priv->loading)
+ gtk_stack_set_visible_child_name (priv->stack, "loading");
+ else
+ gtk_stack_set_visible_child_name (priv->stack, "callgraph");
+}
diff --git a/src/libsysprof-ui/sysprof-callgraph-page.h b/src/libsysprof-ui/sysprof-callgraph-page.h
new file mode 100644
index 0000000..7624c58
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-callgraph-page.h
@@ -0,0 +1,51 @@
+/* sysprof-callgraph-page.h
+ *
+ * Copyright 2016-2019 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
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <sysprof.h>
+
+#include "sysprof-page.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_CALLGRAPH_PAGE (sysprof_callgraph_page_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF, CALLGRAPH_PAGE, SysprofPage)
+
+struct _SysprofCallgraphPageClass
+{
+ SysprofPageClass parent_class;
+
+ void (*go_previous) (SysprofCallgraphPage *self);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+GtkWidget *sysprof_callgraph_page_new (void);
+SysprofCallgraphProfile *sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self);
+void sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self,
+ SysprofCallgraphProfile *profile);
+gchar *sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self);
+guint sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-callgraph-page.ui b/src/libsysprof-ui/sysprof-callgraph-page.ui
new file mode 100644
index 0000000..2642e01
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-callgraph-page.ui
@@ -0,0 +1,235 @@
+<interface>
+ <template class="SysprofCallgraphPage" parent="SysprofPage">
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="orientation">horizontal</property>
+ <property name="position">450</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="functions_view">
+ <property name="fixed-height-mode">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="function_name_column">
+ <property name="expand">true</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">0</property>
+ <property name="title" translatable="yes">Functions</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="ellipsize">middle</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="function_self_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">1</property>
+ <property name="title" translatable="yes">Self</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="function_total_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">2</property>
+ <property name="title" translatable="yes">Total</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">true</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="callers_view">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="callers_name_column">
+ <property name="expand">true</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">0</property>
+ <property name="title" translatable="yes">Callers</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="ellipsize">middle</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="callers_self_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">1</property>
+ <property name="title" translatable="yes">Self</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="callers_total_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">2</property>
+ <property name="title" translatable="yes">Total</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">true</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="descendants_view">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="descendants_name_column">
+ <property name="expand">true</property>
+ <property name="sizing">autosize</property>
+ <property name="sort-column-id">0</property>
+ <property name="title" translatable="yes">Descendants</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="descendants_self_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">1</property>
+ <property name="title" translatable="yes">Self</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="descendants_total_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="sort-column-id">2</property>
+ <property name="title" translatable="yes">Total</property>
+ <child>
+ <object class="SysprofCellRendererPercent">
+ <property name="width">65</property>
+ </object>
+ <attributes>
+ <attribute name="percent">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="function_hits_column">
+ <property name="expand">false</property>
+ <property name="sizing">fixed</property>
+ <property name="title" translatable="yes">Hits</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">1.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">callgraph</property>
+ </packing>
+ </child>
+ <child>
+ <object class="DzlEmptyState">
+ <property name="icon-name">content-loading-symbolic</property>
+ <property name="title" translatable="yes">Generating Callgraph</property>
+ <property name="subtitle" translatable="yes">Sysprof is busy creating the selected
callgraph.</property>
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="name">loading</property>
+ </packing>
+ </child>
+ <child>
+ <object class="DzlEmptyState">
+ <property name="icon-name">computer-fail-symbolic</property>
+ <property name="title" translatable="yes">Not Enough Samples</property>
+ <property name="subtitle" translatable="yes">More samples are necessary to display a
callgraph.</property>
+ <property name="visible">false</property>
+ </object>
+ <packing>
+ <property name="name">empty-state</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libsysprof-ui/sysprof-callgraph-view.h b/src/libsysprof-ui/sysprof-callgraph-view.h
index 6cd68c5..7b0c2ec 100644
--- a/src/libsysprof-ui/sysprof-callgraph-view.h
+++ b/src/libsysprof-ui/sysprof-callgraph-view.h
@@ -20,10 +20,6 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
#include <sysprof.h>
@@ -31,7 +27,6 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_VIEW (sysprof_callgraph_view_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphView, sysprof_callgraph_view, SYSPROF, CALLGRAPH_VIEW, GtkBin)
struct _SysprofCallgraphViewClass
@@ -39,20 +34,13 @@ struct _SysprofCallgraphViewClass
GtkBinClass parent_class;
void (*go_previous) (SysprofCallgraphView *self);
-
- gpointer padding[8];
};
-SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_callgraph_view_new (void);
-SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraphProfile *sysprof_callgraph_view_get_profile (SysprofCallgraphView *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_callgraph_view_set_profile (SysprofCallgraphView *self,
SysprofCallgraphProfile *profile);
-SYSPROF_AVAILABLE_IN_ALL
gchar *sysprof_callgraph_view_screenshot (SysprofCallgraphView *self);
-SYSPROF_AVAILABLE_IN_ALL
guint sysprof_callgraph_view_get_n_functions (SysprofCallgraphView *self);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-cell-renderer-duration.c
b/src/libsysprof-ui/sysprof-cell-renderer-duration.c
index 49c4017..462b83e 100644
--- a/src/libsysprof-ui/sysprof-cell-renderer-duration.c
+++ b/src/libsysprof-ui/sysprof-cell-renderer-duration.c
@@ -110,7 +110,7 @@ sysprof_cell_renderer_duration_render (GtkCellRenderer *renderer,
if (r.width > 3)
{
- _sysprof_rounded_rectangle (cr, &r, 2, 2);
+ dzl_cairo_rounded_rectangle (cr, &r, 2, 2);
cairo_fill (cr);
}
else if (r.width > 1)
diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.c
b/src/libsysprof-ui/sysprof-cell-renderer-percent.c
index 97aa0a0..d0d25d6 100644
--- a/src/libsysprof-ui/sysprof-cell-renderer-percent.c
+++ b/src/libsysprof-ui/sysprof-cell-renderer-percent.c
@@ -18,6 +18,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+#define G_LOG_DOMAIN "sysprof-cell-renderer-percent"
+
#include "config.h"
#include <glib/gi18n.h>
diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.h
b/src/libsysprof-ui/sysprof-cell-renderer-percent.h
index 9c43720..b917ea7 100644
--- a/src/libsysprof-ui/sysprof-cell-renderer-percent.h
+++ b/src/libsysprof-ui/sysprof-cell-renderer-percent.h
@@ -20,14 +20,8 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
-#include "sysprof-version-macros.h"
-
G_BEGIN_DECLS
#define SYSPROF_TYPE_CELL_RENDERER_PERCENT (sysprof_cell_renderer_percent_get_type())
@@ -50,16 +44,13 @@ struct _SysprofCellRendererPercentClass
{
GtkCellRendererProgressClass parent_class;
- gpointer padding[4];
+ /*< private >*/
+ gpointer _reserved[4];
};
-SYSPROF_AVAILABLE_IN_ALL
GType sysprof_cell_renderer_percent_get_type (void);
-SYSPROF_AVAILABLE_IN_ALL
GtkCellRenderer *sysprof_cell_renderer_percent_new (void);
-SYSPROF_AVAILABLE_IN_ALL
gdouble sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self,
gdouble percent);
diff --git a/src/libsysprof-ui/sysprof-color-cycle.c b/src/libsysprof-ui/sysprof-color-cycle.c
index d1c63c5..582878e 100644
--- a/src/libsysprof-ui/sysprof-color-cycle.c
+++ b/src/libsysprof-ui/sysprof-color-cycle.c
@@ -27,30 +27,47 @@
G_DEFINE_BOXED_TYPE (SysprofColorCycle, sysprof_color_cycle, sysprof_color_cycle_ref,
sysprof_color_cycle_unref)
static const gchar *default_colors[] = {
- "#73d216",
- "#f57900",
- "#3465a4",
- "#ef2929",
- "#75507b",
- "#ce5c00",
- "#c17d11",
- "#cc0000",
- "#edd400",
- "#555753",
- "#4e9a06",
- "#204a87",
- "#5c3566",
- "#a40000",
- "#c4a000",
- "#8f5902",
- "#2e3436",
- "#8ae234",
- "#729fcf",
- "#ad7fa8",
- "#fce94f",
- "#fcaf3e",
- "#e9b96e",
- "#888a85",
+
+ "#1a5fb4", /* Blue 5 */
+ "#26a269", /* Green 5 */
+ "#e5a50a", /* Yellow 5 */
+ "#c64600", /* Orange 5 */
+ "#a51d2d", /* Red 5 */
+ "#613583", /* Purple 5 */
+ "#63452c", /* Brown 5 */
+
+ "#1c71d8", /* Blue 4 */
+ "#2ec27e", /* Green 4 */
+ "#f5c211", /* Yellow 4 */
+ "#e66100", /* Orange 4 */
+ "#c01c28", /* Red 4 */
+ "#813d9c", /* Purple 4 */
+ "#865e3c", /* Brown 4 */
+
+ "#3584e4", /* Blue 3 */
+ "#33d17a", /* Green 3 */
+ "#f6d32d", /* Yellow 3 */
+ "#ff7800", /* Orange 3 */
+ "#e01b24", /* Red 3 */
+ "#9141ac", /* Purple 3 */
+ "#986a44", /* Brown 3 */
+
+ "#62a0ea", /* Blue 2 */
+ "#57e389", /* Green 2 */
+ "#f8e45c", /* Yellow 2 */
+ "#ffa348", /* Orange 2 */
+ "#ed333b", /* Red 2 */
+ "#c061cb", /* Purple 2 */
+ "#b5835a", /* Brown 2 */
+
+ "#99c1f1", /* Blue 1 */
+ "#8ff0a4", /* Green 1 */
+ "#f9f06b", /* Yellow 1 */
+ "#ffbe6f", /* Orange 1 */
+ "#f66151", /* Red 1 */
+ "#dc8add", /* Purple 1 */
+ "#cdab8f", /* Brown 1 */
+
NULL
};
diff --git a/src/libsysprof-ui/sysprof-color-cycle.h b/src/libsysprof-ui/sysprof-color-cycle.h
index b512eed..650acb7 100644
--- a/src/libsysprof-ui/sysprof-color-cycle.h
+++ b/src/libsysprof-ui/sysprof-color-cycle.h
@@ -20,32 +20,22 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
-#include "sysprof-version-macros.h"
-
G_BEGIN_DECLS
#define SYSPROF_TYPE_COLOR_CYCLE (sysprof_color_cycle_get_type())
typedef struct _SysprofColorCycle SysprofColorCycle;
-SYSPROF_AVAILABLE_IN_ALL
GType sysprof_color_cycle_get_type (void);
-SYSPROF_AVAILABLE_IN_ALL
SysprofColorCycle *sysprof_color_cycle_ref (SysprofColorCycle *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_color_cycle_unref (SysprofColorCycle *self);
-SYSPROF_AVAILABLE_IN_ALL
SysprofColorCycle *sysprof_color_cycle_new (void);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_color_cycle_reset (SysprofColorCycle *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_color_cycle_next (SysprofColorCycle *self,
GdkRGBA *rgba);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofColorCycle, sysprof_color_cycle_unref)
+
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-counters-aid.c b/src/libsysprof-ui/sysprof-counters-aid.c
new file mode 100644
index 0000000..86a9f27
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-counters-aid.c
@@ -0,0 +1,248 @@
+/* sysprof-counters-aid.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-counters-aid"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-color-cycle.h"
+#include "sysprof-counters-aid.h"
+#include "sysprof-line-visualizer.h"
+#include "sysprof-marks-page.h"
+#include "sysprof-time-visualizer.h"
+
+struct _SysprofCountersAid
+{
+ SysprofAid parent_instance;
+};
+
+typedef struct
+{
+ SysprofCaptureCursor *cursor;
+ SysprofDisplay *display;
+} Present;
+
+G_DEFINE_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF_TYPE_AID)
+
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
+static void
+on_group_activated_cb (SysprofVisualizerGroup *group,
+ SysprofPage *page)
+{
+ SysprofDisplay *display;
+
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
+ g_assert (SYSPROF_IS_PAGE (page));
+
+ display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
+ sysprof_display_set_visible_page (display, page);
+}
+
+/**
+ * sysprof_counters_aid_new:
+ *
+ * Create a new #SysprofCountersAid.
+ *
+ * Returns: (transfer full): a newly created #SysprofCountersAid
+ *
+ * Since: 3.34
+ */
+SysprofAid *
+sysprof_counters_aid_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_COUNTERS_AID, NULL);
+}
+
+static void
+sysprof_counters_aid_prepare (SysprofAid *self,
+ SysprofProfiler *profiler)
+{
+}
+
+static gboolean
+collect_counters (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
+ GArray *counters = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
+ g_assert (counters != NULL);
+
+ if (def->n_counters > 0)
+ g_array_append_vals (counters, def->counters, def->n_counters);
+
+ return TRUE;
+}
+
+static void
+sysprof_counters_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *present = task_data;
+ g_autoptr(GArray) counters = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_COUNTERS_AID (source_object));
+ g_assert (present != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
+ sysprof_capture_cursor_foreach (present->cursor, collect_counters, counters);
+ g_task_return_pointer (task,
+ g_steal_pointer (&counters),
+ (GDestroyNotify) g_array_unref);
+}
+
+static void
+sysprof_counters_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
+ g_autoptr(SysprofCaptureCondition) condition = NULL;
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ g_autoptr(GTask) task = NULL;
+ Present present;
+
+ g_assert (SYSPROF_IS_COUNTERS_AID (aid));
+ g_assert (reader != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (display));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ condition = sysprof_capture_condition_new_where_type_in (1, types);
+ cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
+
+ present.cursor = g_steal_pointer (&cursor);
+ present.display = g_object_ref (display);
+
+ task = g_task_new (aid, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_counters_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &present),
+ present_free);
+ g_task_run_in_thread (task, sysprof_counters_aid_present_worker);
+}
+
+static gboolean
+sysprof_counters_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_autoptr(GArray) counters = NULL;
+ Present *present;
+
+ g_assert (SYSPROF_IS_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ present = g_task_get_task_data (G_TASK (result));
+
+ if ((counters = g_task_propagate_pointer (G_TASK (result), error)) && counters->len > 0)
+ {
+ g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
+ SysprofVisualizerGroup *group;
+ SysprofVisualizer *combined;
+ GtkWidget *page;
+
+ group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "has-page", TRUE,
+ "title", _("Counters"),
+ "visible", TRUE,
+ NULL);
+
+ combined = g_object_new (SYSPROF_TYPE_TIME_VISUALIZER,
+ "title", _("Counters"),
+ "height-request", 35,
+ "visible", TRUE,
+ NULL);
+ sysprof_visualizer_group_insert (group, combined, -1, TRUE);
+
+ for (guint i = 0; i < counters->len; i++)
+ {
+ const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
+ g_autofree gchar *title = g_strdup_printf ("%s — %s", ctr->category, ctr->name);
+ GtkWidget *row;
+ GdkRGBA rgba;
+
+ row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", title,
+ "height-request", 35,
+ "visible", FALSE,
+ NULL);
+ sysprof_color_cycle_next (cycle, &rgba);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ rgba.alpha = .5;
+ sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_time_visualizer_add_counter (SYSPROF_TIME_VISUALIZER (combined), ctr->id, &rgba);
+ sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), -1, TRUE);
+ }
+
+ sysprof_display_add_group (present->display, group);
+
+ page = sysprof_marks_page_new (sysprof_display_get_zoom_manager (present->display),
+ SYSPROF_MARKS_MODEL_COUNTERS);
+ gtk_widget_show (page);
+
+ g_signal_connect_object (group,
+ "group-activated",
+ G_CALLBACK (on_group_activated_cb),
+ page,
+ 0);
+ sysprof_display_add_page (present->display, SYSPROF_PAGE (page));
+ }
+
+ return counters != NULL;
+}
+
+static void
+sysprof_counters_aid_class_init (SysprofCountersAidClass *klass)
+{
+ SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
+
+ aid_class->prepare = sysprof_counters_aid_prepare;
+ aid_class->present_async = sysprof_counters_aid_present_async;
+ aid_class->present_finish = sysprof_counters_aid_present_finish;
+}
+
+static void
+sysprof_counters_aid_init (SysprofCountersAid *self)
+{
+ sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery"));
+ sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof3-symbolic");
+}
diff --git a/src/libsysprof-ui/sysprof-counters-aid.h b/src/libsysprof-ui/sysprof-counters-aid.h
new file mode 100644
index 0000000..5541b60
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-counters-aid.h
@@ -0,0 +1,33 @@
+/* sysprof-counters-aid.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include "sysprof-aid.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_COUNTERS_AID (sysprof_counters_aid_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF, COUNTERS_AID, SysprofAid)
+
+SysprofAid *sysprof_counters_aid_new (void);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-cpu-aid.c b/src/libsysprof-ui/sysprof-cpu-aid.c
index 22cd05e..31d913f 100644
--- a/src/libsysprof-ui/sysprof-cpu-aid.c
+++ b/src/libsysprof-ui/sysprof-cpu-aid.c
@@ -24,15 +24,33 @@
#include <glib/gi18n.h>
+#include "sysprof-color-cycle.h"
#include "sysprof-cpu-aid.h"
+#include "sysprof-line-visualizer.h"
struct _SysprofCpuAid
{
SysprofAid parent_instance;
};
+typedef struct
+{
+ SysprofCaptureCursor *cursor;
+ SysprofDisplay *display;
+} Present;
+
G_DEFINE_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF_TYPE_AID)
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
/**
* sysprof_cpu_aid_new:
*
@@ -63,12 +81,243 @@ sysprof_cpu_aid_prepare (SysprofAid *self,
#endif
}
+static gboolean
+collect_cpu_counters (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
+ GArray *counters = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
+ g_assert (counters != NULL);
+
+ for (guint i = 0; i < def->n_counters; i++)
+ {
+ const SysprofCaptureCounter *counter = &def->counters[i];
+
+ if (g_strcmp0 (counter->category, "CPU Percent") == 0 ||
+ g_strcmp0 (counter->category, "CPU Frequency") == 0)
+ g_array_append_vals (counters, counter, 1);
+ }
+
+ return TRUE;
+}
+
+static void
+sysprof_cpu_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *present = task_data;
+ g_autoptr(GArray) counters = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_CPU_AID (source_object));
+ g_assert (present != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
+ sysprof_capture_cursor_foreach (present->cursor, collect_cpu_counters, counters);
+ g_task_return_pointer (task,
+ g_steal_pointer (&counters),
+ (GDestroyNotify) g_array_unref);
+}
+
+static void
+sysprof_cpu_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
+ g_autoptr(SysprofCaptureCondition) condition = NULL;
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ g_autoptr(GTask) task = NULL;
+ Present present;
+
+ g_assert (SYSPROF_IS_CPU_AID (aid));
+ g_assert (reader != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (display));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ condition = sysprof_capture_condition_new_where_type_in (1, types);
+ cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
+
+ present.cursor = g_steal_pointer (&cursor);
+ present.display = g_object_ref (display);
+
+ task = g_task_new (aid, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_cpu_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &present),
+ present_free);
+ g_task_run_in_thread (task, sysprof_cpu_aid_present_worker);
+}
+
+static gboolean
+sysprof_cpu_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_autoptr(GArray) counters = NULL;
+ Present *present;
+
+ g_assert (SYSPROF_IS_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ present = g_task_get_task_data (G_TASK (result));
+
+ if ((counters = g_task_propagate_pointer (G_TASK (result), error)))
+ {
+ g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
+ g_autoptr(SysprofColorCycle) freq_cycle = sysprof_color_cycle_new ();
+ SysprofVisualizerGroup *usage;
+ SysprofVisualizerGroup *freq;
+ SysprofVisualizer *freq_row = NULL;
+ SysprofVisualizer *over_row = NULL;
+ gboolean found_combined = FALSE;
+ gboolean has_usage = FALSE;
+ gboolean has_freq = FALSE;
+
+ usage = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "priority", -1000,
+ "title", _("CPU Usage"),
+ "visible", TRUE,
+ NULL);
+
+ freq = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "priority", -999,
+ "title", _("CPU Frequency"),
+ "visible", TRUE,
+ NULL);
+ freq_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", _("CPU Frequency (All)"),
+ "height-request", 35,
+ "visible", TRUE,
+ "y-lower", 0.0,
+ "y-upper", 100.0,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (freq), GTK_WIDGET (freq_row));
+
+ over_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", _("CPU Usage (All)"),
+ "height-request", 35,
+ "visible", TRUE,
+ "y-lower", 0.0,
+ "y-upper", 100.0,
+ NULL);
+
+ for (guint i = 0; i < counters->len; i++)
+ {
+ const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
+
+ if (g_strcmp0 (ctr->category, "CPU Percent") == 0)
+ {
+ if (strstr (ctr->name, "Combined") != NULL)
+ {
+ GtkWidget *row;
+ GdkRGBA rgba;
+
+ found_combined = TRUE;
+
+ gdk_rgba_parse (&rgba, "#1a5fb4");
+ row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ /* Translators: CPU is the processor. */
+ "title", _("CPU Usage (All)"),
+ "height-request", 35,
+ "visible", TRUE,
+ "y-lower", 0.0,
+ "y-upper", 100.0,
+ NULL);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ rgba.alpha = 0.5;
+ sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), 0, FALSE);
+ has_usage = TRUE;
+ }
+ else if (g_str_has_prefix (ctr->name, "Total CPU "))
+ {
+ GtkWidget *row;
+ GdkRGBA rgba;
+
+ sysprof_color_cycle_next (cycle, &rgba);
+ row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", ctr->name,
+ "height-request", 35,
+ "visible", FALSE,
+ "y-lower", 0.0,
+ "y-upper", 100.0,
+ NULL);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (over_row), ctr->id, &rgba);
+ rgba.alpha = 0.5;
+ sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE);
+ has_usage = TRUE;
+ }
+ }
+ else if (g_strcmp0 (ctr->category, "CPU Frequency") == 0)
+ {
+ if (g_str_has_prefix (ctr->name, "CPU "))
+ {
+ g_autofree gchar *title = g_strdup_printf ("%s Frequency", ctr->name);
+ GtkWidget *row;
+ GdkRGBA rgba;
+
+ sysprof_color_cycle_next (freq_cycle, &rgba);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, &rgba);
+ sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, TRUE);
+
+ row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
+ "title", title,
+ "height-request", 35,
+ "visible", FALSE,
+ "y-lower", 0.0,
+ "y-upper", 100.0,
+ NULL);
+ sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
+ sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (row), ctr->id, TRUE);
+ sysprof_visualizer_group_insert (freq, SYSPROF_VISUALIZER (row), -1, TRUE);
+
+ has_freq = TRUE;
+ }
+ }
+ }
+
+ if (has_usage && !found_combined)
+ sysprof_visualizer_group_insert (usage, over_row, 0, FALSE);
+ else
+ gtk_widget_destroy (GTK_WIDGET (over_row));
+
+ if (has_usage)
+ sysprof_display_add_group (present->display, usage);
+ else
+ gtk_widget_destroy (GTK_WIDGET (usage));
+
+ if (has_freq)
+ sysprof_display_add_group (present->display, freq);
+ else
+ gtk_widget_destroy (GTK_WIDGET (freq));
+ }
+
+ return counters != NULL;
+}
+
static void
sysprof_cpu_aid_class_init (SysprofCpuAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_cpu_aid_prepare;
+ aid_class->present_async = sysprof_cpu_aid_present_async;
+ aid_class->present_finish = sysprof_cpu_aid_present_finish;
}
static void
diff --git a/src/libsysprof-ui/sysprof-cpu-aid.h b/src/libsysprof-ui/sysprof-cpu-aid.h
index ca5899b..4769885 100644
--- a/src/libsysprof-ui/sysprof-cpu-aid.h
+++ b/src/libsysprof-ui/sysprof-cpu-aid.h
@@ -26,10 +26,8 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_CPU_AID (sysprof_cpu_aid_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF, CPU_AID, SysprofAid)
-SYSPROF_AVAILABLE_IN_ALL
SysprofAid *sysprof_cpu_aid_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.c b/src/libsysprof-ui/sysprof-depth-visualizer.c
new file mode 100644
index 0000000..08183eb
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-depth-visualizer.c
@@ -0,0 +1,430 @@
+/* sysprof-depth-visualizer.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-depth-visualizer"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "pointcache.h"
+#include "sysprof-depth-visualizer.h"
+
+struct _SysprofDepthVisualizer
+{
+ SysprofVisualizer parent_instance;
+ SysprofCaptureReader *reader;
+ PointCache *points;
+ guint reload_source;
+ guint mode;
+ GtkAllocation last_alloc;
+};
+
+typedef struct
+{
+ SysprofCaptureReader *reader;
+ PointCache *pc;
+ gint64 begin_time;
+ gint64 end_time;
+ gint64 duration;
+ guint max_n_addrs;
+ guint mode;
+} State;
+
+G_DEFINE_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF_TYPE_VISUALIZER)
+
+static void
+state_free (State *st)
+{
+ g_clear_pointer (&st->reader, sysprof_capture_reader_unref);
+ g_clear_pointer (&st->pc, point_cache_unref);
+ g_slice_free (State, st);
+}
+
+static gboolean
+discover_max_n_addr (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
+ State *st = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
+ g_assert (st != NULL);
+
+ st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs);
+
+ return TRUE;
+}
+
+static gboolean
+build_point_cache_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
+ State *st = user_data;
+ gdouble x, y;
+ gboolean has_kernel = FALSE;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
+ g_assert (st != NULL);
+
+ x = (frame->time - st->begin_time) / (gdouble)st->duration;
+ y = sample->n_addrs / (gdouble)st->max_n_addrs;
+
+ /* If this contains a context-switch (meaning we're going into the kernel
+ * to do some work, use a negative value for Y so that we know later on
+ * that we should draw it with a different color (after removing the negation
+ * on the value.
+ *
+ * We skip past the first index, which is always a context switch as it is
+ * our perf handler.
+ */
+ for (guint i = 1; i < sample->n_addrs; i++)
+ {
+ SysprofAddressContext kind;
+
+ if (sysprof_address_is_context_switch (sample->addrs[i], &kind))
+ {
+ has_kernel = TRUE;
+ y = -y;
+ break;
+ }
+ }
+
+ if (!has_kernel)
+ point_cache_add_point_to_set (st->pc, 1, x, y);
+ else
+ point_cache_add_point_to_set (st->pc, 2, x, y);
+
+ return TRUE;
+}
+
+static void
+sysprof_depth_visualizer_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, };
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ SysprofCaptureCondition *condition;
+ State *st = task_data;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (source_object));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (st->duration != 0)
+ {
+ cursor = sysprof_capture_cursor_new (st->reader);
+ condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
+ sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
+
+ sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st);
+ sysprof_capture_cursor_reset (cursor);
+ sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st);
+ }
+
+ g_task_return_pointer (task,
+ g_steal_pointer (&st->pc),
+ (GDestroyNotify) point_cache_unref);
+}
+
+static void
+apply_point_cache_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
+ PointCache *pc;
+
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
+ g_assert (G_IS_TASK (result));
+
+ if ((pc = g_task_propagate_pointer (G_TASK (result), NULL)))
+ {
+ g_clear_pointer (&self->points, point_cache_unref);
+ self->points = g_steal_pointer (&pc);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self)
+{
+ g_autoptr(GTask) task = NULL;
+ GtkAllocation alloc;
+ State *st;
+
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ st = g_slice_new0 (State);
+ st->reader = sysprof_capture_reader_ref (self->reader);
+ st->pc = point_cache_new ();
+ st->max_n_addrs = 0;
+ st->begin_time = sysprof_capture_reader_get_start_time (self->reader);
+ st->end_time = sysprof_capture_reader_get_end_time (self->reader);
+ st->duration = st->end_time - st->begin_time;
+ st->mode = self->mode;
+
+ point_cache_add_set (st->pc, 1);
+ point_cache_add_set (st->pc, 2);
+
+ task = g_task_new (self, NULL, apply_point_cache_cb, NULL);
+ g_task_set_source_tag (task, sysprof_depth_visualizer_reload);
+ g_task_set_task_data (task, st, (GDestroyNotify) state_free);
+ g_task_run_in_thread (task, sysprof_depth_visualizer_worker);
+}
+
+static void
+sysprof_depth_visualizer_set_reader (SysprofVisualizer *row,
+ SysprofCaptureReader *reader)
+{
+ SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)row;
+
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
+
+ if (self->reader != reader)
+ {
+ if (self->reader != NULL)
+ {
+ sysprof_capture_reader_unref (self->reader);
+ self->reader = NULL;
+ }
+
+ if (reader != NULL)
+ {
+ self->reader = sysprof_capture_reader_ref (reader);
+ sysprof_depth_visualizer_reload (self);
+ }
+ }
+}
+
+static gboolean
+sysprof_depth_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
+ GtkAllocation alloc;
+ GdkRectangle clip;
+ const Point *points;
+ gboolean ret;
+ guint n_points = 0;
+ GdkRGBA user;
+ GdkRGBA system;
+
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
+ g_assert (cr != NULL);
+
+ ret = GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->draw (widget, cr);
+
+ if (self->points == NULL)
+ return ret;
+
+ gdk_rgba_parse (&user, "#1a5fb4");
+ gdk_rgba_parse (&system, "#3584e4");
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ if (!gdk_cairo_get_clip_rectangle (cr, &clip))
+ return ret;
+
+ /* Draw user-space stacks */
+ if (self->mode != SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY &&
+ (points = point_cache_get_points (self->points, 1, &n_points)))
+ {
+ g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
+
+ out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
+ sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
+ (const SysprofVisualizerRelativePoint *)points,
+ n_points, out_points, n_points);
+
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_rgba (cr, &user);
+
+ for (guint i = 0; i < n_points; i++)
+ {
+ gdouble x, y;
+
+ x = out_points[i].x;
+ y = out_points[i].y;
+
+ if (x < clip.x)
+ continue;
+
+ if (x > clip.x + clip.width)
+ break;
+
+ for (guint j = i + 1; j < n_points; j++)
+ {
+ if (out_points[j].x != x)
+ break;
+
+ y = MIN (y, out_points[j].y);
+ }
+
+ x += alloc.x;
+
+ cairo_move_to (cr, (guint)x + .5, alloc.height);
+ cairo_line_to (cr, (guint)x + .5, y);
+ }
+
+ cairo_stroke (cr);
+ }
+
+ /* Draw kernel-space stacks */
+ if (self->mode != SYSPROF_DEPTH_VISUALIZER_USER_ONLY &&
+ (points = point_cache_get_points (self->points, 2, &n_points)))
+ {
+ g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
+
+ out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
+ sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
+ (const SysprofVisualizerRelativePoint *)points,
+ n_points, out_points, n_points);
+
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_rgba (cr, &system);
+
+ for (guint i = 0; i < n_points; i++)
+ {
+ gdouble x, y;
+
+ x = out_points[i].x;
+ y = out_points[i].y;
+
+ if (x < clip.x)
+ continue;
+
+ if (x > clip.x + clip.width)
+ break;
+
+ for (guint j = i + 1; j < n_points; j++)
+ {
+ if (out_points[j].x != x)
+ break;
+
+ y = MIN (y, out_points[j].y);
+ }
+
+ x += alloc.x;
+
+ cairo_move_to (cr, (guint)x + .5, alloc.height);
+ cairo_line_to (cr, (guint)x + .5, y);
+ }
+
+ cairo_stroke (cr);
+ }
+
+ return ret;
+}
+
+static gboolean
+sysprof_depth_visualizer_do_reload (gpointer data)
+{
+ SysprofDepthVisualizer *self = data;
+ self->reload_source = 0;
+ sysprof_depth_visualizer_reload (self);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+sysprof_depth_visualizer_queue_reload (SysprofDepthVisualizer *self)
+{
+ g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
+
+ if (self->reload_source)
+ g_source_remove (self->reload_source);
+
+ self->reload_source = gdk_threads_add_idle (sysprof_depth_visualizer_do_reload, self);
+}
+
+static void
+sysprof_depth_visualizer_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
+{
+ SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
+
+ GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->size_allocate (widget, alloc);
+
+ if (alloc->width != self->last_alloc.x ||
+ alloc->height != self->last_alloc.height)
+ {
+ sysprof_depth_visualizer_queue_reload (SYSPROF_DEPTH_VISUALIZER (widget));
+ self->last_alloc = *alloc;
+ }
+}
+
+static void
+sysprof_depth_visualizer_finalize (GObject *object)
+{
+ SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
+
+ g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
+
+ if (self->reload_source)
+ {
+ g_source_remove (self->reload_source);
+ self->reload_source = 0;
+ }
+
+ G_OBJECT_CLASS (sysprof_depth_visualizer_parent_class)->finalize (object);
+}
+
+static void
+sysprof_depth_visualizer_class_init (SysprofDepthVisualizerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ SysprofVisualizerClass *row_class = SYSPROF_VISUALIZER_CLASS (klass);
+
+ object_class->finalize = sysprof_depth_visualizer_finalize;
+
+ widget_class->draw = sysprof_depth_visualizer_draw;
+ widget_class->size_allocate = sysprof_depth_visualizer_size_allocate;
+
+ row_class->set_reader = sysprof_depth_visualizer_set_reader;
+}
+
+static void
+sysprof_depth_visualizer_init (SysprofDepthVisualizer *self)
+{
+}
+
+SysprofVisualizer *
+sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode)
+{
+ SysprofDepthVisualizer *self;
+
+ g_return_val_if_fail (mode == SYSPROF_DEPTH_VISUALIZER_COMBINED ||
+ mode == SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY ||
+ mode == SYSPROF_DEPTH_VISUALIZER_USER_ONLY,
+ NULL);
+
+ self = g_object_new (SYSPROF_TYPE_DEPTH_VISUALIZER, NULL);
+ self->mode = mode;
+
+ return SYSPROF_VISUALIZER (g_steal_pointer (&self));
+}
diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.h b/src/libsysprof-ui/sysprof-depth-visualizer.h
new file mode 100644
index 0000000..425c4d4
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-depth-visualizer.h
@@ -0,0 +1,40 @@
+/* sysprof-depth-visualizer.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include "sysprof-visualizer.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ SYSPROF_DEPTH_VISUALIZER_COMBINED,
+ SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY,
+ SYSPROF_DEPTH_VISUALIZER_USER_ONLY,
+} SysprofDepthVisualizerMode;
+
+#define SYSPROF_TYPE_DEPTH_VISUALIZER (sysprof_depth_visualizer_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF, DEPTH_VISUALIZER,
SysprofVisualizer)
+
+SysprofVisualizer *sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-details-page.c b/src/libsysprof-ui/sysprof-details-page.c
new file mode 100644
index 0000000..13170f4
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-details-page.c
@@ -0,0 +1,324 @@
+/* sysprof-details-page.c
+ *
+ * Copyright 2019 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
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#define G_LOG_DOMAIN "sysprof-details-page"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "sysprof-details-page.h"
+#include "sysprof-ui-private.h"
+
+#define NSEC_PER_SEC (G_USEC_PER_SEC * 1000L)
+
+struct _SysprofDetailsPage
+{
+ SysprofPage parent_instance;
+
+ /* Template Objects */
+ DzlThreeGrid *three_grid;
+ GtkListStore *marks_store;
+ GtkTreeView *marks_view;
+ GtkLabel *counters;
+ GtkLabel *duration;
+ GtkLabel *filename;
+ GtkLabel *forks;
+ GtkLabel *marks;
+ GtkLabel *processes;
+ GtkLabel *samples;
+ GtkLabel *start_time;
+ GtkLabel *cpu_label;
+
+ guint next_row;
+};
+
+G_DEFINE_TYPE (SysprofDetailsPage, sysprof_details_page, GTK_TYPE_BIN)
+
+#if GLIB_CHECK_VERSION(2, 56, 0)
+# define _g_date_time_new_from_iso8601 g_date_time_new_from_iso8601
+#else
+static GDateTime *
+_g_date_time_new_from_iso8601 (const gchar *str,
+ GTimeZone *default_tz)
+{
+ GTimeVal tv;
+
+ if (g_time_val_from_iso8601 (str, &tv))
+ {
+ g_autoptr(GDateTime) dt = g_date_time_new_from_timeval_utc (&tv);
+
+ if (default_tz)
+ return g_date_time_to_timezone (dt, default_tz);
+ else
+ return g_steal_pointer (&dt);
+ }
+
+ return NULL;
+}
+#endif
+
+static void
+sysprof_details_page_class_init (SysprofDetailsPageClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/sysprof/ui/sysprof-details-page.ui");
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, counters);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, cpu_label);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, duration);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, filename);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, forks);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_store);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_view);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, processes);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, samples);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, start_time);
+ gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, three_grid);
+
+ g_type_ensure (DZL_TYPE_THREE_GRID);
+}
+
+static void
+sysprof_details_page_init (SysprofDetailsPage *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (self->marks_view),
+ GTK_SELECTION_MULTIPLE);
+
+ self->next_row = 8;
+}
+
+GtkWidget *
+sysprof_details_page_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_DETAILS_PAGE, NULL);
+}
+
+static void
+update_cpu_info_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(SysprofDetailsPage) self = user_data;
+ g_autofree gchar *str = NULL;
+
+ g_assert (SYSPROF_IS_DETAILS_PAGE (self));
+ g_assert (G_IS_TASK (result));
+
+ if ((str = g_task_propagate_pointer (G_TASK (result), NULL)))
+ gtk_label_set_label (self->cpu_label, str);
+}
+
+static gboolean
+cpu_info_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ const SysprofCaptureFileChunk *fc = (gpointer)frame;
+ const gchar *endptr;
+ const gchar *line;
+ gchar **str = user_data;
+
+ endptr = (gchar *)fc->data + fc->len;
+ line = memmem ((gchar *)fc->data, fc->len, "model name", 10);
+ endptr = memchr (line, '\n', endptr - line);
+
+ if (endptr)
+ {
+ gchar *tmp = *str = g_strndup (line, endptr - line);
+ for (; *tmp && *tmp != ':'; tmp++)
+ *tmp = ' ';
+ if (*tmp == ':')
+ *tmp = ' ';
+ g_strstrip (*str);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+sysprof_details_page_update_cpu_info_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ SysprofCaptureCursor *cursor = task_data;
+ gchar *str = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (cursor != NULL);
+
+ sysprof_capture_cursor_foreach (cursor, cpu_info_cb, &str);
+ g_task_return_pointer (task, g_steal_pointer (&str), g_free);
+}
+
+static void
+sysprof_details_page_update_cpu_info (SysprofDetailsPage *self,
+ SysprofCaptureReader *reader)
+{
+ g_autoptr(SysprofCaptureCursor) cursor = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (SYSPROF_IS_DETAILS_PAGE (self));
+ g_assert (reader != NULL);
+
+ cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (cursor,
+ sysprof_capture_condition_new_where_file ("/proc/cpuinfo"));
+
+ task = g_task_new (NULL, NULL, update_cpu_info_cb, g_object_ref (self));
+ g_task_set_task_data (task,
+ g_steal_pointer (&cursor),
+ (GDestroyNotify) sysprof_capture_cursor_unref);
+ g_task_run_in_thread (task, sysprof_details_page_update_cpu_info_worker);
+}
+
+void
+sysprof_details_page_set_reader (SysprofDetailsPage *self,
+ SysprofCaptureReader *reader)
+{
+ g_autoptr(GDateTime) dt = NULL;
+ g_autoptr(GDateTime) local = NULL;
+ g_autofree gchar *duration_str = NULL;
+ const gchar *filename;
+ const gchar *capture_at;
+ SysprofCaptureStat st_buf;
+ gint64 duration;
+
+ g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
+ g_return_if_fail (reader != NULL);
+
+ sysprof_details_page_update_cpu_info (self, reader);
+
+ if (!(filename = sysprof_capture_reader_get_filename (reader)))
+ filename = _("Memory Capture");
+
+ gtk_label_set_label (self->filename, filename);
+
+ if ((capture_at = sysprof_capture_reader_get_time (reader)) &&
+ (dt = _g_date_time_new_from_iso8601 (capture_at, NULL)) &&
+ (local = g_date_time_to_local (dt)))
+ {
+ g_autofree gchar *str = g_date_time_format (local, "%x %X");
+ gtk_label_set_label (self->start_time, str);
+ }
+
+ duration = sysprof_capture_reader_get_end_time (reader) -
+ sysprof_capture_reader_get_start_time (reader);
+ duration_str = g_strdup_printf (_("%0.4lf seconds"), duration / (gdouble)NSEC_PER_SEC);
+ gtk_label_set_label (self->duration, duration_str);
+
+ if (sysprof_capture_reader_get_stat (reader, &st_buf))
+ {
+#define SET_FRAME_COUNT(field, TYPE) \
+ G_STMT_START { \
+ g_autofree gchar *str = NULL; \
+ str = g_strdup_printf ("%"G_GSIZE_FORMAT, st_buf.frame_count[TYPE]); \
+ gtk_label_set_label (self->field, str); \
+ } G_STMT_END
+
+ SET_FRAME_COUNT (samples, SYSPROF_CAPTURE_FRAME_SAMPLE);
+ SET_FRAME_COUNT (marks, SYSPROF_CAPTURE_FRAME_MARK);
+ SET_FRAME_COUNT (processes, SYSPROF_CAPTURE_FRAME_PROCESS);
+ SET_FRAME_COUNT (forks, SYSPROF_CAPTURE_FRAME_FORK);
+ SET_FRAME_COUNT (counters, SYSPROF_CAPTURE_FRAME_CTRSET);
+
+#undef SET_FRAME_COUNT
+ }
+}
+
+void
+sysprof_details_page_add_item (SysprofDetailsPage *self,
+ GtkWidget *left,
+ GtkWidget *center)
+{
+ g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
+ g_return_if_fail (!left || GTK_IS_WIDGET (left));
+ g_return_if_fail (!center || GTK_IS_WIDGET (center));
+
+ if (left)
+ gtk_container_add_with_properties (GTK_CONTAINER (self->three_grid), left,
+ "row", self->next_row,
+ "column", DZL_THREE_GRID_COLUMN_LEFT,
+ NULL);
+
+ if (center)
+ gtk_container_add_with_properties (GTK_CONTAINER (self->three_grid), center,
+ "row", self->next_row,
+ "column", DZL_THREE_GRID_COLUMN_CENTER,
+ NULL);
+
+ self->next_row++;
+}
+
+void
+sysprof_details_page_add_mark (SysprofDetailsPage *self,
+ const gchar *mark,
+ gint64 min,
+ gint64 max,
+ gint64 avg,
+ gint64 hits)
+{
+ GtkTreeIter iter;
+
+ g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
+
+ gtk_list_store_append (self->marks_store, &iter);
+ gtk_list_store_set (self->marks_store, &iter,
+ 0, mark,
+ 1, min ? _sysprof_format_duration (min) : "—",
+ 2, max ? _sysprof_format_duration (max) : "—",
+ 3, avg ? _sysprof_format_duration (avg) : "—",
+ 4, hits,
+ -1);
+}
+
+void
+sysprof_details_page_add_marks (SysprofDetailsPage *self,
+ const SysprofMarkStat *marks,
+ guint n_marks)
+{
+ g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
+ g_return_if_fail (marks != NULL || n_marks == 0);
+
+ if (marks == NULL || n_marks == 0)
+ return;
+
+ /* Be reasonable */
+ if (n_marks > 100)
+ n_marks = 100;
+
+ for (guint i = 0; i < n_marks; i++)
+ sysprof_details_page_add_mark (self,
+ marks[i].name,
+ marks[i].min,
+ marks[i].max,
+ marks[i].avg,
+ marks[i].count);
+}
diff --git a/src/libsysprof-ui/sysprof-details-page.h b/src/libsysprof-ui/sysprof-details-page.h
new file mode 100644
index 0000000..251bbe5
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-details-page.h
@@ -0,0 +1,60 @@
+/* sysprof-details-page.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <sysprof-capture.h>
+
+G_BEGIN_DECLS
+
+SYSPROF_ALIGNED_BEGIN (8)
+typedef struct
+{
+ gchar name[152];
+ guint64 count;
+ gint64 max;
+ gint64 min;
+ gint64 avg;
+ guint64 avg_count;
+} SysprofMarkStat
+SYSPROF_ALIGNED_END (8);
+
+#define SYSPROF_TYPE_DETAILS_PAGE (sysprof_details_page_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofDetailsPage, sysprof_details_page, SYSPROF, DETAILS_PAGE, GtkBin)
+
+GtkWidget *sysprof_details_page_new (void);
+void sysprof_details_page_set_reader (SysprofDetailsPage *self,
+ SysprofCaptureReader *reader);
+void sysprof_details_page_add_marks (SysprofDetailsPage *self,
+ const SysprofMarkStat *marks,
+ guint n_marks);
+void sysprof_details_page_add_mark (SysprofDetailsPage *self,
+ const gchar *mark,
+ gint64 min,
+ gint64 max,
+ gint64 avg,
+ gint64 hits);
+void sysprof_details_page_add_item (SysprofDetailsPage *self,
+ GtkWidget *left,
+ GtkWidget *center);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-details-page.ui b/src/libsysprof-ui/sysprof-details-page.ui
new file mode 100644
index 0000000..c7373a3
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-details-page.ui
@@ -0,0 +1,372 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+ <requires lib="gtk+" version="3.22"/>
+ <template class="SysprofDetailsPage" parent="GtkBin">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">never</property>
+ <property name="propagate-natural-height">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="DzlThreeGrid" id="three_grid">
+ <property name="margin">36</property>
+ <property name="column-spacing">12</property>
+ <property name="row-spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Filename</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Captured at</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Duration</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">CPU Model</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="margin-top">12</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Samples Captured</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Marks Captured</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Processes Captured</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Forks Captured</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="margin-bottom">12</property>
+ <property name="label" translatable="yes">Counters Captured</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="filename">
+ <property name="width-chars">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="start_time">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="duration">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="cpu_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="samples">
+ <property name="margin-top">12</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="marks">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="processes">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="forks">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="counters">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="ellipsize">start</property>
+ <property name="margin-bottom">12</property>
+ <property name="xalign">0</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="shadow-type">in</property>
+ <property name="margin-bottom">12</property>
+ <child>
+ <object class="GtkTreeView" id="marks_view">
+ <property name="model">marks_store</property>
+ <property name="width-request">500</property>
+ <property name="height-request">100</property>
+ <property name="enable-grid-lines">both</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">true</property>
+ <property name="title" translatable="yes">Mark</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Hits</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Min</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Max</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Avg</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">9</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkListStore" id="marks_store">
+ <columns>
+ <!-- column-name Mark -->
+ <column type="gchararray"/>
+ <!-- column-name Min -->
+ <column type="gchararray"/>
+ <!-- column-name Max -->
+ <column type="gchararray"/>
+ <!-- column-name Avg -->
+ <column type="gchararray"/>
+ <!-- column-name Hits -->
+ <column type="gint64"/>
+ </columns>
+ </object>
+</interface>
diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c
index 7f31a54..d9dbf24 100644
--- a/src/libsysprof-ui/sysprof-display.c
+++ b/src/libsysprof-ui/sysprof-display.c
@@ -22,31 +22,49 @@
#include "config.h"
+#include <dazzle.h>
#include <glib/gi18n.h>
-#include <sysprof-capture.h>
-#include <sysprof-ui.h>
-#include <sysprof.h>
-
-#include "sysprof-profiler-assistant.h"
-#include "sysprof-capture-view.h"
+#include "sysprof-details-page.h"
#include "sysprof-display.h"
-#include "sysprof-empty-state-view.h"
+#include "sysprof-profiler-assistant.h"
+#include "sysprof-failed-state-view.h"
#include "sysprof-recording-state-view.h"
+#include "sysprof-theme-manager.h"
#include "sysprof-ui-private.h"
+#include "sysprof-visualizers-frame.h"
+#include "sysprof-visualizer-group-private.h"
+
+#include "sysprof-battery-aid.h"
+#include "sysprof-callgraph-aid.h"
+#include "sysprof-counters-aid.h"
+#include "sysprof-cpu-aid.h"
+#include "sysprof-logs-aid.h"
+#include "sysprof-marks-aid.h"
+
+typedef enum
+{
+ SYSPROF_CAPTURE_FLAGS_CAN_REPLAY = 1 << 1,
+} SysprofCaptureFlags;
typedef struct
{
+ SysprofCaptureReader *reader;
+ SysprofCaptureCondition *filter;
GFile *file;
SysprofProfiler *profiler;
GError *error;
- /* Template Objects */
+ /* Template Widgets */
+ SysprofVisualizersFrame *visualizers;
+ GtkStack *pages;
+ SysprofDetailsPage *details;
+ GtkStack *stack;
SysprofProfilerAssistant *assistant;
- SysprofCaptureView *capture_view;
- SysprofEmptyStateView *failed_view;
SysprofRecordingStateView *recording_view;
- GtkStack *stack;
+ SysprofFailedStateView *failed_view;
+
+ SysprofCaptureFlags flags;
} SysprofDisplayPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (SysprofDisplay, sysprof_display, GTK_TYPE_BIN)
@@ -57,43 +75,29 @@ enum {
PROP_CAN_SAVE,
PROP_RECORDING,
PROP_TITLE,
+ PROP_VISIBLE_PAGE,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
-/**
- * sysprof_display_new:
- *
- * Create a new #SysprofDisplay.
- *
- * Returns: (transfer full): a newly created #SysprofDisplay
- *
- * Since: 3.34
- */
-GtkWidget *
-sysprof_display_new (void)
-{
- return g_object_new (SYSPROF_TYPE_DISPLAY, NULL);
-}
-
static void
-sysprof_display_load_cb (SysprofCaptureView *view,
- GAsyncResult *result,
- gpointer user_data)
+update_title_child_property (SysprofDisplay *self)
{
- g_autoptr(SysprofDisplay) self = user_data;
- g_autoptr(GError) error = NULL;
+ GtkWidget *parent;
- g_assert (SYSPROF_IS_CAPTURE_VIEW (view));
- g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_DISPLAY (self));
- if (!sysprof_capture_view_load_finish (view, result, &error))
- g_warning ("Failed to load capture: %s", error->message);
+ if ((parent = gtk_widget_get_parent (GTK_WIDGET (self))) && GTK_IS_NOTEBOOK (parent))
+ {
+ g_autofree gchar *title = sysprof_display_dup_title (self);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]);
+ gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self),
+ "menu-label", title,
+ NULL);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
}
static void
@@ -141,12 +145,10 @@ sysprof_display_profiler_stopped_cb (SysprofDisplay *self,
goto notify;
}
- sysprof_capture_view_load_async (priv->capture_view,
- reader,
- NULL,
- (GAsyncReadyCallback) sysprof_display_load_cb,
- g_object_ref (self));
- gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->capture_view));
+ sysprof_display_load_async (self,
+ reader,
+ NULL, NULL, NULL);
+ gtk_stack_set_visible_child_name (priv->stack, "view");
}
notify:
@@ -201,11 +203,20 @@ sysprof_display_start_recording_cb (SysprofDisplay *self,
sysprof_profiler_start (profiler);
}
+static gboolean
+sysprof_display_get_is_recording (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_DISPLAY (self));
+
+ return GTK_WIDGET (priv->recording_view) == gtk_stack_get_visible_child (priv->stack);
+}
+
gchar *
sysprof_display_dup_title (SysprofDisplay *self)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- SysprofCaptureReader *reader;
g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL);
@@ -221,44 +232,100 @@ sysprof_display_dup_title (SysprofDisplay *self)
if (priv->file != NULL)
return g_file_get_basename (priv->file);
- if ((reader = sysprof_capture_view_get_reader (priv->capture_view)))
+ if (priv->reader != NULL)
{
const gchar *filename;
- if ((filename = sysprof_capture_reader_get_filename (reader)))
+ if ((filename = sysprof_capture_reader_get_filename (priv->reader)))
return g_path_get_basename (filename);
}
return g_strdup (_("New Recording"));
}
+/**
+ * sysprof_display_new:
+ *
+ * Create a new #SysprofDisplay.
+ *
+ * Returns: (transfer full): a newly created #SysprofDisplay
+ */
+GtkWidget *
+sysprof_display_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_DISPLAY, NULL);
+}
+
static void
-update_title_child_property (SysprofDisplay *self)
+sysprof_display_notify_selection_cb (SysprofDisplay *self,
+ GParamSpec *pspec,
+ SysprofVisualizersFrame *visualizers)
{
- GtkWidget *parent;
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ SysprofSelection *selection;
g_assert (SYSPROF_IS_DISPLAY (self));
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (visualizers));
- if ((parent = gtk_widget_get_parent (GTK_WIDGET (self))) && GTK_IS_NOTEBOOK (parent))
+ g_clear_pointer (&priv->filter, sysprof_capture_condition_unref);
+
+ if ((selection = sysprof_visualizers_frame_get_selection (visualizers)))
{
- g_autofree gchar *title = sysprof_display_dup_title (self);
+ SysprofCaptureCondition *cond = NULL;
+ guint n_ranges = sysprof_selection_get_n_ranges (selection);
- gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self),
- "menu-label", title,
- NULL);
- }
+ for (guint i = 0; i < n_ranges; i++)
+ {
+ SysprofCaptureCondition *c;
+ gint64 begin, end;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ sysprof_selection_get_nth_range (selection, i, &begin, &end);
+ c = sysprof_capture_condition_new_where_time_between (begin, end);
+
+ if (cond == NULL)
+ cond = c;
+ else
+ cond = sysprof_capture_condition_new_or (cond, c);
+ }
+
+ priv->filter = cond;
+
+ /* Opportunistically load pages */
+ if (priv->reader != NULL)
+ {
+ GList *pages = gtk_container_get_children (GTK_CONTAINER (priv->pages));
+
+ for (const GList *iter = pages; iter; iter = iter->next)
+ {
+ if (SYSPROF_IS_PAGE (iter->data))
+ sysprof_page_load_async (iter->data,
+ priv->reader,
+ selection,
+ priv->filter,
+ NULL, NULL, NULL);
+ }
+
+ g_list_free (pages);
+ }
+ }
}
-static gboolean
-sysprof_display_get_is_recording (SysprofDisplay *self)
+static void
+change_page_cb (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
{
+ SysprofDisplay *self = user_data;
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_assert (SYSPROF_IS_DISPLAY (self));
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (param != NULL);
- return GTK_WIDGET (priv->recording_view) == gtk_stack_get_visible_child (priv->stack);
+ if (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *str = g_variant_get_string (param, NULL);
+ gtk_stack_set_visible_child_name (priv->pages, str);
+ }
}
static void
@@ -274,27 +341,14 @@ stop_recording_cb (GSimpleAction *action,
sysprof_display_stop_recording (self);
}
-static void
-sysprof_display_parent_set (GtkWidget *widget,
- GtkWidget *old_parent)
-{
- g_assert (SYSPROF_IS_DISPLAY (widget));
-
- if (GTK_WIDGET_CLASS (sysprof_display_parent_class)->parent_set)
- GTK_WIDGET_CLASS (sysprof_display_parent_class)->parent_set (widget, old_parent);
-
- update_title_child_property (SYSPROF_DISPLAY (widget));
-}
-
static void
sysprof_display_finalize (GObject *object)
{
SysprofDisplay *self = (SysprofDisplay *)object;
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_clear_error (&priv->error);
- g_clear_object (&priv->profiler);
- g_clear_object (&priv->file);
+ g_clear_pointer (&priv->reader, sysprof_capture_reader_unref);
+ g_clear_pointer (&priv->filter, sysprof_capture_condition_unref);
G_OBJECT_CLASS (sysprof_display_parent_class)->finalize (object);
}
@@ -305,7 +359,7 @@ sysprof_display_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
- SysprofDisplay *self = (SysprofDisplay *)object;
+ SysprofDisplay *self = SYSPROF_DISPLAY (object);
switch (prop_id)
{
@@ -325,6 +379,29 @@ sysprof_display_get_property (GObject *object,
g_value_take_string (value, sysprof_display_dup_title (self));
break;
+ case PROP_VISIBLE_PAGE:
+ g_value_set_object (value, sysprof_display_get_visible_page (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_display_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofDisplay *self = SYSPROF_DISPLAY (object);
+
+ switch (prop_id)
+ {
+ case PROP_VISIBLE_PAGE:
+ sysprof_display_set_visible_page (self, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -338,8 +415,22 @@ sysprof_display_class_init (SysprofDisplayClass *klass)
object_class->finalize = sysprof_display_finalize;
object_class->get_property = sysprof_display_get_property;
+ object_class->set_property = sysprof_display_set_property;
+
+ sysprof_theme_manager_register_resource (sysprof_theme_manager_get_default (),
+ NULL,
+ NULL,
+ "/org/gnome/sysprof/css/SysprofDisplay-shared.css");
- widget_class->parent_set = sysprof_display_parent_set;
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui");
+ gtk_widget_class_set_css_name (widget_class, "SysprofDisplay");
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, assistant);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, details);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, failed_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, pages);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, recording_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, stack);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, visualizers);
properties [PROP_CAN_REPLAY] =
g_param_spec_boolean ("can-replay",
@@ -367,20 +458,23 @@ sysprof_display_class_init (SysprofDisplayClass *klass)
"Title",
"The title of the display",
NULL,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
- g_object_class_install_properties (object_class, N_PROPS, properties);
+ properties [PROP_VISIBLE_PAGE] =
+ g_param_spec_object ("visible-page",
+ "Visible Page",
+ "Visible Page",
+ SYSPROF_TYPE_PAGE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui");
- gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, capture_view);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, failed_view);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, recording_view);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, assistant);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, stack);
+ g_object_class_install_properties (object_class, N_PROPS, properties);
- g_type_ensure (SYSPROF_TYPE_CAPTURE_VIEW);
- g_type_ensure (SYSPROF_TYPE_EMPTY_STATE_VIEW);
+ g_type_ensure (DZL_TYPE_MULTI_PANED);
+ g_type_ensure (SYSPROF_TYPE_DETAILS_PAGE);
+ g_type_ensure (SYSPROF_TYPE_FAILED_STATE_VIEW);
+ g_type_ensure (SYSPROF_TYPE_PROFILER_ASSISTANT);
g_type_ensure (SYSPROF_TYPE_RECORDING_STATE_VIEW);
+ g_type_ensure (SYSPROF_TYPE_VISUALIZERS_FRAME);
}
static void
@@ -388,202 +482,561 @@ sysprof_display_init (SysprofDisplay *self)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new ();
- static const GActionEntry actions[] = {
+ static GActionEntry entries[] = {
+ { "page", change_page_cb, "s" },
{ "stop-recording", stop_recording_cb },
};
+ g_autoptr(GPropertyAction) page = NULL;
gtk_widget_init_template (GTK_WIDGET (self));
- g_action_map_add_action_entries (G_ACTION_MAP (group),
- actions,
- G_N_ELEMENTS (actions),
- self);
- gtk_widget_insert_action_group (GTK_WIDGET (self), "display", G_ACTION_GROUP (group));
-
g_signal_connect_object (priv->assistant,
"start-recording",
G_CALLBACK (sysprof_display_start_recording_cb),
self,
G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (priv->visualizers,
+ "notify::selection",
+ G_CALLBACK (sysprof_display_notify_selection_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ page = g_property_action_new ("page", priv->pages, "visible-child-name");
+ g_action_map_add_action_entries (G_ACTION_MAP (group),
+ entries,
+ G_N_ELEMENTS (entries),
+ self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "display", G_ACTION_GROUP (group));
}
-/**
- * sysprof_display_get_profiler:
- *
- * Gets the proflier for the display.
- *
- * Returns: (transfer none) (nullable): a #SysprofProfiler or %NULL
- *
- * Since: 3.34
- */
-SysprofProfiler *
-sysprof_display_get_profiler (SysprofDisplay *self)
+void
+sysprof_display_add_group (SysprofDisplay *self,
+ SysprofVisualizerGroup *group)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (group));
+
+ if (priv->reader != NULL)
+ _sysprof_visualizer_group_set_reader (group, priv->reader);
+
+ gtk_container_add (GTK_CONTAINER (priv->visualizers), GTK_WIDGET (group));
+}
+
+void
+sysprof_display_add_page (SysprofDisplay *self,
+ SysprofPage *page)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ SysprofSelection *selection;
+ const gchar *title;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (SYSPROF_IS_PAGE (page));
+
+ title = sysprof_page_get_title (page);
+
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->pages), GTK_WIDGET (page),
+ "title", title,
+ NULL);
+
+ selection = sysprof_visualizers_frame_get_selection (priv->visualizers);
+
+ sysprof_page_set_size_group (page,
+ sysprof_visualizers_frame_get_size_group (priv->visualizers));
+
+ sysprof_page_set_hadjustment (page,
+ sysprof_visualizers_frame_get_hadjustment (priv->visualizers));
+
+ if (priv->reader != NULL)
+ sysprof_page_load_async (page,
+ priv->reader,
+ selection,
+ priv->filter,
+ NULL, NULL, NULL);
+}
+
+SysprofPage *
+sysprof_display_get_visible_page (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ GtkWidget *visible_page;
+
g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL);
- return priv->profiler;
+ visible_page = gtk_stack_get_visible_child (priv->pages);
+
+ if (SYSPROF_IS_PAGE (visible_page))
+ return SYSPROF_PAGE (visible_page);
+
+ return NULL;
}
-/**
- * sysprof_display_is_empty:
- *
- * Checks if any content is or will be loaded into @self.
- *
- * Returns: %TRUE if the tab is unperterbed.
- *
- * Since: 3.34
- */
-gboolean
-sysprof_display_is_empty (SysprofDisplay *self)
+void
+sysprof_display_set_visible_page (SysprofDisplay *self,
+ SysprofPage *page)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (SYSPROF_IS_PAGE (page));
- return priv->file == NULL &&
- priv->profiler == NULL &&
- gtk_stack_get_visible_child (priv->stack) == GTK_WIDGET (priv->assistant) &&
- NULL == sysprof_capture_view_get_reader (priv->capture_view);
+ gtk_stack_set_visible_child (priv->pages, GTK_WIDGET (page));
}
static void
-sysprof_display_open_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+sysprof_display_present_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- g_autoptr(SysprofDisplay) self = user_data;
- SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_autoptr(SysprofCaptureReader) reader = NULL;
+ SysprofAid *aid = (SysprofAid *)object;
+ g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
+ guint *n_active;
- g_assert (SYSPROF_IS_DISPLAY (self));
- g_assert (G_IS_TASK (result));
+ g_assert (SYSPROF_IS_AID (aid));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!sysprof_aid_present_finish (aid, result, &error))
+ g_warning ("Failed to present aid %s: %s", G_OBJECT_TYPE_NAME (aid), error->message);
+
+ n_active = g_task_get_task_data (task);
+
+ (*n_active)--;
+
+ if (n_active == 0)
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_display_present_async (SysprofDisplay *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GPtrArray) aids = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (!(reader = g_task_propagate_pointer (G_TASK (result), &error)))
+ aids = g_ptr_array_new_with_free_func (g_object_unref);
+ g_ptr_array_add (aids, sysprof_battery_aid_new ());
+ g_ptr_array_add (aids, sysprof_counters_aid_new ());
+ g_ptr_array_add (aids, sysprof_cpu_aid_new ());
+ g_ptr_array_add (aids, sysprof_callgraph_aid_new ());
+ g_ptr_array_add (aids, sysprof_logs_aid_new ());
+ g_ptr_array_add (aids, sysprof_marks_aid_new ());
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_display_present_async);
+
+ if (aids->len == 0)
{
- gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->failed_view));
+ g_task_return_boolean (task, TRUE);
return;
}
- sysprof_capture_view_load_async (priv->capture_view,
- reader,
- NULL,
- (GAsyncReadyCallback) sysprof_display_load_cb,
- g_object_ref (self));
- gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->capture_view));
+ g_task_set_task_data (task, g_memdup (&aids->len, sizeof aids->len), g_free);
+
+ for (guint i = 0; i < aids->len; i++)
+ {
+ SysprofAid *aid = g_ptr_array_index (aids, i);
+
+ sysprof_aid_present_async (aid,
+ reader,
+ self,
+ cancellable,
+ sysprof_display_present_cb,
+ g_object_ref (task));
+ }
+}
+
+static gboolean
+sysprof_display_present_finish (SysprofDisplay *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
-sysprof_display_open_worker (GTask *task,
+sysprof_display_scan_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
- g_autofree gchar *path = NULL;
+ SysprofDisplay *self = source_object;
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ SysprofCaptureReader *reader = task_data;
+ g_autoptr(GHashTable) mark_stats = NULL;
+ g_autoptr(GArray) marks = NULL;
+ SysprofCaptureFrame frame;
+ SysprofCaptureStat st = {{0}};
+ SysprofCaptureFlags flags = 0;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_DISPLAY (self));
+ g_assert (reader != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /* Scan the reader until the end so that we know we have gotten
+ * all of the timing data loaded into the underlying reader.
+ */
+
+ mark_stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkStat));
+
+ while (sysprof_capture_reader_peek_frame (reader, &frame))
+ {
+ st.frame_count[frame.type]++;
+
+ if (frame.type == SYSPROF_CAPTURE_FRAME_METADATA)
+ {
+ const SysprofCaptureMetadata *meta;
+
+ if ((meta = sysprof_capture_reader_read_metadata (reader)))
+ {
+ if (g_strcmp0 (meta->id, "local-profiler") == 0)
+ flags |= SYSPROF_CAPTURE_FLAGS_CAN_REPLAY;
+ }
+ }
+ else if (frame.type == SYSPROF_CAPTURE_FRAME_MARK)
+ {
+ const SysprofCaptureMark *mark;
+
+ if ((mark = sysprof_capture_reader_read_mark (reader)))
+ {
+ SysprofMarkStat *mstat;
+ gchar name[152];
+ gpointer idx;
+
+ g_snprintf (name, sizeof name, "%s:%s", mark->group, mark->name);
+
+ if (!(idx = g_hash_table_lookup (mark_stats, name)))
+ {
+ SysprofMarkStat empty = {{0}};
+
+ g_strlcpy (empty.name, name, sizeof empty.name);
+ g_array_append_val (marks, empty);
+ idx = GUINT_TO_POINTER (marks->len);
+ g_hash_table_insert (mark_stats, g_strdup (name), idx);
+ }
+
+ mstat = &g_array_index (marks, SysprofMarkStat, GPOINTER_TO_UINT (idx) - 1);
+
+ if (mark->duration > 0)
+ {
+ if (mstat->min == 0 || mark->duration < mstat->min)
+ mstat->min = mark->duration;
+ }
+
+ if (mark->duration > mstat->max)
+ mstat->max = mark->duration;
+
+ if (mark->duration > 0)
+ {
+ mstat->avg += mark->duration;
+ mstat->avg_count++;
+ }
+
+ mstat->count++;
+ }
+ }
+ else
+ {
+ sysprof_capture_reader_skip (reader);
+ }
+ }
+
+ {
+ GHashTableIter iter;
+ gpointer k,v;
+
+ g_hash_table_iter_init (&iter, mark_stats);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ guint idx = GPOINTER_TO_UINT (v) - 1;
+ SysprofMarkStat *mstat = &g_array_index (marks, SysprofMarkStat, idx);
+
+ if (mstat->avg_count > 0 && mstat->avg > 0)
+ mstat->avg /= mstat->avg_count;
+
+#if 0
+ g_print ("%s: count=%ld avg=%ld min=%ld max=%ld\n",
+ (gchar*)k,
+ ((SysprofMarkStat *)v)->count,
+ ((SysprofMarkStat *)v)->avg,
+ ((SysprofMarkStat *)v)->min,
+ ((SysprofMarkStat *)v)->max);
+#endif
+ }
+ }
+
+ g_object_set_data_full (G_OBJECT (task),
+ "MARK_STAT",
+ g_steal_pointer (&marks),
+ (GDestroyNotify) g_array_unref);
+
+ g_atomic_int_set (&priv->flags, flags);
+ sysprof_capture_reader_reset (reader);
+ sysprof_capture_reader_set_stat (reader, &st);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_display_scan_async (SysprofDisplay *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_display_scan_async);
+ g_task_set_task_data (task,
+ sysprof_capture_reader_ref (reader),
+ (GDestroyNotify) sysprof_capture_reader_unref);
+ g_task_run_in_thread (task, sysprof_display_scan_worker);
+}
+
+static gboolean
+sysprof_display_scan_finish (SysprofDisplay *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ GArray *marks;
+
+ g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ if ((marks = g_object_get_data (G_OBJECT (result), "MARK_STAT")))
+ sysprof_details_page_add_marks (priv->details,
+ (const SysprofMarkStat *)(gpointer)marks->data,
+ marks->len);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_display_load_present_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofDisplay *self = (SysprofDisplay *)object;
g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+
+ g_assert (SYSPROF_IS_DISPLAY (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!sysprof_display_present_finish (self, result, &error))
+ g_warning ("Error presenting: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_display_load_frame_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofVisualizersFrame *frame = (SysprofVisualizersFrame *)object;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
SysprofCaptureReader *reader;
- GFile *file = task_data;
+ SysprofDisplay *self;
+ GCancellable *cancellable;
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (frame));
+ g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
- g_assert (source_object == NULL);
- g_assert (G_IS_FILE (file));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- path = g_file_get_path (file);
+ self = g_task_get_source_object (task);
+ reader = g_task_get_task_data (task);
+ cancellable = g_task_get_cancellable (task);
- if (!(reader = sysprof_capture_reader_new (path, &error)))
- g_task_return_error (task, g_steal_pointer ((&error)));
+ if (!sysprof_visualizers_frame_load_finish (frame, result, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
else
- g_task_return_pointer (task,
- g_steal_pointer (&reader),
- (GDestroyNotify) sysprof_capture_reader_unref);
+ sysprof_display_present_async (self,
+ reader,
+ cancellable,
+ sysprof_display_load_present_cb,
+ g_steal_pointer (&task));
}
-void
-sysprof_display_open (SysprofDisplay *self,
- GFile *file)
+static void
+sysprof_display_load_scan_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ SysprofDisplay *self = (SysprofDisplay *)object;
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_autoptr(GTask) task = NULL;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ SysprofCaptureReader *reader;
+ SysprofSelection *selection;
+ GCancellable *cancellable;
+ GList *pages;
- g_return_if_fail (SYSPROF_IS_DISPLAY (self));
- g_return_if_fail (G_IS_FILE (file));
- g_return_if_fail (g_file_is_native (file));
- g_return_if_fail (sysprof_display_is_empty (self));
+ g_assert (SYSPROF_IS_DISPLAY (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
- g_set_object (&priv->file, file);
+ reader = g_task_get_task_data (task);
+ cancellable = g_task_get_cancellable (task);
- task = g_task_new (NULL, NULL, sysprof_display_open_cb, g_object_ref (self));
- g_task_set_task_data (task, g_file_dup (file), g_object_unref);
- g_task_run_in_thread (task, sysprof_display_open_worker);
+ if (!sysprof_display_scan_finish (self, result, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ sysprof_visualizers_frame_load_async (priv->visualizers,
+ reader,
+ cancellable,
+ sysprof_display_load_frame_cb,
+ g_steal_pointer (&task));
- update_title_child_property (self);
+ selection = sysprof_visualizers_frame_get_selection (priv->visualizers);
+
+ sysprof_details_page_set_reader (priv->details, reader);
+
+ /* Opportunistically load pages */
+ pages = gtk_container_get_children (GTK_CONTAINER (priv->pages));
+ for (const GList *iter = pages; iter; iter = iter->next)
+ {
+ if (SYSPROF_IS_PAGE (iter->data))
+ sysprof_page_load_async (iter->data,
+ reader,
+ selection,
+ priv->filter,
+ NULL, NULL, NULL);
+ }
+ g_list_free (pages);
+
+ gtk_stack_set_visible_child_name (priv->stack, "view");
}
void
-sysprof_display_save (SysprofDisplay *self)
+sysprof_display_load_async (SysprofDisplay *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- g_autoptr(GFile) file = NULL;
- GtkFileChooserNative *native;
- SysprofCaptureReader *reader;
- GtkWindow *parent;
- gint res;
+ g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (!(reader = sysprof_capture_view_get_reader (priv->capture_view)))
- return;
+ if (priv->reader != reader)
+ {
+ g_clear_pointer (&priv->reader, sysprof_capture_reader_unref);
+ priv->reader = sysprof_capture_reader_ref (reader);
+ }
- parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_display_load_async);
+ g_task_set_task_data (task,
+ sysprof_capture_reader_ref (reader),
+ (GDestroyNotify) sysprof_capture_reader_unref);
+
+ /* First scan the reader for any sort of data we care about before
+ * we notify aids to load content. That allows us to ensure we have
+ * proper timing data for the consumers.
+ */
+ sysprof_display_scan_async (self,
+ reader,
+ cancellable,
+ sysprof_display_load_scan_cb,
+ g_steal_pointer (&task));
+}
- native = gtk_file_chooser_native_new (_("Save Recording"),
- parent,
- GTK_FILE_CHOOSER_ACTION_SAVE,
- _("Save"),
- _("Cancel"));
- gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (native), TRUE);
- gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (native), TRUE);
- gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE);
- gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.syscap");
+gboolean
+sysprof_display_load_finish (SysprofDisplay *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
- res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native));
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
- switch (res)
- {
- case GTK_RESPONSE_ACCEPT:
- file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+SysprofZoomManager *
+sysprof_display_get_zoom_manager (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- if (g_file_is_native (file))
- {
- g_autofree gchar *path = g_file_get_path (file);
- g_autoptr(GError) error = NULL;
+ g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL);
- if (!sysprof_capture_reader_save_as (reader, path, &error))
- {
- GtkWidget *msg;
+ return sysprof_visualizers_frame_get_zoom_manager (priv->visualizers);
+}
- msg = gtk_message_dialog_new (parent,
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_USE_HEADER_BAR,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Failed to save recording: %s"),
- error->message);
- gtk_window_present (GTK_WINDOW (msg));
- g_signal_connect (msg, "response", G_CALLBACK (gtk_widget_destroy), NULL);
- }
- }
+/**
+ * sysprof_display_is_empty:
+ *
+ * Checks if any content is or will be loaded into @self.
+ *
+ * Returns: %TRUE if the tab is unperterbed.
+ *
+ * Since: 3.34
+ */
+gboolean
+sysprof_display_is_empty (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
- break;
+ g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
- default:
- break;
- }
+ return priv->file == NULL &&
+ priv->profiler == NULL &&
+ gtk_stack_get_visible_child (priv->stack) == GTK_WIDGET (priv->assistant) &&
+ NULL == priv->reader;
+}
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
- gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+void
+sysprof_display_open (SysprofDisplay *self,
+ GFile *file)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ g_autoptr(SysprofCaptureReader) reader = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autofree gchar *path = NULL;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (g_file_is_native (file));
+ g_return_if_fail (sysprof_display_is_empty (self));
+
+ g_set_object (&priv->file, file);
+
+ path = g_file_get_path (file);
+
+ if (!(reader = sysprof_capture_reader_new (path, &error)))
+ g_warning ("Failed to open capture: %s", error->message);
+ else
+ sysprof_display_load_async (self, reader, NULL, NULL, NULL);
+
+ update_title_child_property (self);
}
gboolean
@@ -593,7 +1046,7 @@ sysprof_display_get_can_save (SysprofDisplay *self)
g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
- return sysprof_capture_view_get_reader (priv->capture_view) != NULL;
+ return priv->reader != NULL;
}
void
@@ -625,7 +1078,8 @@ sysprof_display_get_can_replay (SysprofDisplay *self)
g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE);
return !sysprof_display_is_empty (self) &&
- sysprof_capture_view_get_can_replay (priv->capture_view);
+ priv->reader != NULL &&
+ !!(priv->flags & SYSPROF_CAPTURE_FLAGS_CAN_REPLAY);
}
/**
@@ -645,15 +1099,12 @@ sysprof_display_replay (SysprofDisplay *self)
{
SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
g_autoptr(SysprofProfiler) profiler = NULL;
- SysprofCaptureReader *reader;
SysprofDisplay *copy;
g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL);
+ g_return_val_if_fail (priv->reader != NULL, NULL);
- reader = sysprof_capture_view_get_reader (priv->capture_view);
- g_return_val_if_fail (reader != NULL, NULL);
-
- profiler = sysprof_local_profiler_new_replay (reader);
+ profiler = sysprof_local_profiler_new_replay (priv->reader);
g_return_val_if_fail (profiler != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (profiler), NULL);
@@ -676,3 +1127,64 @@ sysprof_display_new_for_profiler (SysprofProfiler *profiler)
return GTK_WIDGET (g_steal_pointer (&self));
}
+
+void
+sysprof_display_save (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ g_autoptr(GFile) file = NULL;
+ GtkFileChooserNative *native;
+ GtkWindow *parent;
+ gint res;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+ g_return_if_fail (priv->reader != NULL);
+
+ parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+
+ native = gtk_file_chooser_native_new (_("Save Recording"),
+ parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("Save"),
+ _("Cancel"));
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.syscap");
+
+ res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native));
+
+ switch (res)
+ {
+ case GTK_RESPONSE_ACCEPT:
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+
+ if (g_file_is_native (file))
+ {
+ g_autofree gchar *path = g_file_get_path (file);
+ g_autoptr(GError) error = NULL;
+
+ if (!sysprof_capture_reader_save_as (priv->reader, path, &error))
+ {
+ GtkWidget *msg;
+
+ msg = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_USE_HEADER_BAR,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to save recording: %s"),
+ error->message);
+ gtk_window_present (GTK_WINDOW (msg));
+ g_signal_connect (msg, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+}
diff --git a/src/libsysprof-ui/sysprof-display.h b/src/libsysprof-ui/sysprof-display.h
index 96d780b..7f0ea9c 100644
--- a/src/libsysprof-ui/sysprof-display.h
+++ b/src/libsysprof-ui/sysprof-display.h
@@ -23,7 +23,9 @@
#include <gtk/gtk.h>
#include <sysprof.h>
-#include "sysprof-version-macros.h"
+#include "sysprof-page.h"
+#include "sysprof-visualizer-group.h"
+#include "sysprof-zoom-manager.h"
G_BEGIN_DECLS
@@ -32,38 +34,59 @@ G_BEGIN_DECLS
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofDisplay, sysprof_display, SYSPROF, DISPLAY, GtkBin)
-SYSPROF_ALIGNED_BEGIN(8)
struct _SysprofDisplayClass
{
GtkBinClass parent_class;
/*< private >*/
gpointer _reserved[16];
-}
-SYSPROF_ALIGNED_END(8);
+};
SYSPROF_AVAILABLE_IN_ALL
-GtkWidget *sysprof_display_new (void);
+GtkWidget *sysprof_display_new (void);
SYSPROF_AVAILABLE_IN_ALL
-GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler);
+GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler);
SYSPROF_AVAILABLE_IN_ALL
-gchar *sysprof_display_dup_title (SysprofDisplay *self);
+char *sysprof_display_dup_title (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
-SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self);
+SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
-gboolean sysprof_display_is_empty (SysprofDisplay *self);
+void sysprof_display_add_group (SysprofDisplay *self,
+ SysprofVisualizerGroup *group);
SYSPROF_AVAILABLE_IN_ALL
-void sysprof_display_open (SysprofDisplay *self,
- GFile *file);
+void sysprof_display_add_page (SysprofDisplay *self,
+ SysprofPage *page);
SYSPROF_AVAILABLE_IN_ALL
-void sysprof_display_save (SysprofDisplay *self);
+SysprofPage *sysprof_display_get_visible_page (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
-gboolean sysprof_display_get_can_save (SysprofDisplay *self);
+void sysprof_display_set_visible_page (SysprofDisplay *self,
+ SysprofPage *page);
SYSPROF_AVAILABLE_IN_ALL
-void sysprof_display_stop_recording (SysprofDisplay *self);
+SysprofZoomManager *sysprof_display_get_zoom_manager (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
-gboolean sysprof_display_get_can_replay (SysprofDisplay *self);
+void sysprof_display_load_async (SysprofDisplay *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
-SysprofDisplay *sysprof_display_replay (SysprofDisplay *self);
+gboolean sysprof_display_load_finish (SysprofDisplay *self,
+ GAsyncResult *result,
+ GError **error);
+SYSPROF_AVAILABLE_IN_ALL
+gboolean sysprof_display_is_empty (SysprofDisplay *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_display_open (SysprofDisplay *self,
+ GFile *file);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_display_save (SysprofDisplay *self);
+SYSPROF_AVAILABLE_IN_ALL
+gboolean sysprof_display_get_can_save (SysprofDisplay *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_display_stop_recording (SysprofDisplay *self);
+SYSPROF_AVAILABLE_IN_ALL
+gboolean sysprof_display_get_can_replay (SysprofDisplay *self);
+SYSPROF_AVAILABLE_IN_ALL
+SysprofDisplay *sysprof_display_replay (SysprofDisplay *self);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-display.ui b/src/libsysprof-ui/sysprof-display.ui
index a9af096..43661e6 100644
--- a/src/libsysprof-ui/sysprof-display.ui
+++ b/src/libsysprof-ui/sysprof-display.ui
@@ -14,11 +14,40 @@
</packing>
</child>
<child>
- <object class="SysprofCaptureView" id="capture_view">
+ <object class="DzlMultiPaned">
+ <property name="orientation">vertical</property>
<property name="visible">true</property>
+ <child>
+ <object class="SysprofVisualizersFrame" id="visualizers">
+ <property name="vexpand">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="pages">
+ <property name="homogeneous">false</property>
+ <property name="vexpand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="SysprofDetailsPage" id="details">
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="title" translatable="yes">Details</property>
+ <property name="name">details</property>
+ </packing>
+ </child>
+ </object>
+ </child>
</object>
<packing>
- <property name="name">capture</property>
+ <property name="name">view</property>
</packing>
</child>
<child>
@@ -26,14 +55,11 @@
<property name="visible">true</property>
</object>
<packing>
- <property name="name">recording</property>
+ <property name="name">record</property>
</packing>
</child>
<child>
- <object class="SysprofEmptyStateView" id="failed_view">
- <property name="icon-name">computer-fail-symbolic</property>
- <property name="title" translatable="yes">Something went wrong</property>
- <property name="subtitle" translatable="yes">Sysprof failed to access the requested performance
data.</property>
+ <object class="SysprofFailedStateView" id="failed_view">
<property name="visible">true</property>
</object>
<packing>
diff --git a/src/libsysprof-ui/sysprof-empty-state-view.h b/src/libsysprof-ui/sysprof-empty-state-view.h
index 9e7aa72..4c15aec 100644
--- a/src/libsysprof-ui/sysprof-empty-state-view.h
+++ b/src/libsysprof-ui/sysprof-empty-state-view.h
@@ -20,10 +20,6 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
#include <sysprof.h>
@@ -31,7 +27,6 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_EMPTY_STATE_VIEW (sysprof_empty_state_view_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofEmptyStateView, sysprof_empty_state_view, SYSPROF, EMPTY_STATE_VIEW, GtkBin)
struct _SysprofEmptyStateViewClass
@@ -41,7 +36,6 @@ struct _SysprofEmptyStateViewClass
gpointer padding[4];
};
-SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_empty_state_view_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-failed-state-view.h b/src/libsysprof-ui/sysprof-failed-state-view.h
index ad14efc..91ed603 100644
--- a/src/libsysprof-ui/sysprof-failed-state-view.h
+++ b/src/libsysprof-ui/sysprof-failed-state-view.h
@@ -20,10 +20,6 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
#include <sysprof.h>
@@ -31,7 +27,6 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_FAILED_STATE_VIEW (sysprof_failed_state_view_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofFailedStateView, sysprof_failed_state_view, SYSPROF, FAILED_STATE_VIEW,
GtkBin)
struct _SysprofFailedStateViewClass
@@ -41,9 +36,7 @@ struct _SysprofFailedStateViewClass
gpointer padding[4];
};
-SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_failed_state_view_new (void);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_failed_state_view_set_profiler (SysprofFailedStateView *self,
SysprofProfiler *profiler);
diff --git a/src/libsysprof-ui/sysprof-line-visualizer-row.c b/src/libsysprof-ui/sysprof-line-visualizer.c
similarity index 62%
rename from src/libsysprof-ui/sysprof-line-visualizer-row.c
rename to src/libsysprof-ui/sysprof-line-visualizer.c
index 670f84f..1b0295e 100644
--- a/src/libsysprof-ui/sysprof-line-visualizer-row.c
+++ b/src/libsysprof-ui/sysprof-line-visualizer.c
@@ -1,4 +1,4 @@
-/* sysprof-line-visualizer-row.c
+/* sysprof-line-visualizer.c
*
* Copyright 2016-2019 Christian Hergert <christian hergert me>
*
@@ -18,7 +18,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "sysprof-line-visualizer-row"
+#define G_LOG_DOMAIN "sysprof-line-visualizer"
#include "config.h"
@@ -27,7 +27,7 @@
#include <sysprof.h>
#include "pointcache.h"
-#include "sysprof-line-visualizer-row.h"
+#include "sysprof-line-visualizer.h"
typedef struct
{
@@ -49,11 +49,6 @@ typedef struct
*/
PointCache *cache;
- /*
- * Child widget to display the label in the upper corner.
- */
- GtkLabel *label;
-
/*
* Range of the scale for lower and upper.
*/
@@ -69,7 +64,7 @@ typedef struct
guint y_lower_set : 1;
guint y_upper_set : 1;
-} SysprofLineVisualizerRowPrivate;
+} SysprofLineVisualizerPrivate;
typedef struct
{
@@ -96,19 +91,18 @@ typedef struct
guint y_upper_set : 1;
} LoadData;
-G_DEFINE_TYPE_WITH_PRIVATE (SysprofLineVisualizerRow, sysprof_line_visualizer_row,
SYSPROF_TYPE_VISUALIZER_ROW)
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF_TYPE_VISUALIZER)
-static void sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-static PointCache *sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self,
- GAsyncResult *result,
- GError **error);
+static void sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static PointCache *sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self,
+ GAsyncResult *result,
+ GError **error);
enum {
PROP_0,
- PROP_TITLE,
PROP_Y_LOWER,
PROP_Y_UPPER,
N_PROPS
@@ -144,34 +138,38 @@ copy_array (GArray *ar)
}
static gboolean
-sysprof_line_visualizer_row_draw (GtkWidget *widget,
- cairo_t *cr)
+sysprof_line_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
{
- SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)widget;
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = (SysprofLineVisualizer *)widget;
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
GtkStyleContext *style_context;
GtkStateFlags flags;
GtkAllocation alloc;
+ GdkRectangle clip;
GdkRGBA foreground;
gboolean ret;
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (widget));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (widget));
g_assert (cr != NULL);
gtk_widget_get_allocation (widget, &alloc);
- ret = GTK_WIDGET_CLASS (sysprof_line_visualizer_row_parent_class)->draw (widget, cr);
+ ret = GTK_WIDGET_CLASS (sysprof_line_visualizer_parent_class)->draw (widget, cr);
if (priv->cache == NULL)
return ret;
+ if (!gdk_cairo_get_clip_rectangle (cr, &clip))
+ return ret;
+
style_context = gtk_widget_get_style_context (widget);
flags = gtk_widget_get_state_flags (widget);
gtk_style_context_get_color (style_context, flags, &foreground);
for (guint line = 0; line < priv->lines->len; line++)
{
- g_autofree SysprofVisualizerRowAbsolutePoint *points = NULL;
+ g_autofree SysprofVisualizerAbsolutePoint *points = NULL;
const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line);
const Point *fpoints;
guint n_fpoints = 0;
@@ -181,19 +179,32 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget,
if (n_fpoints > 0)
{
- gdouble last_x;
- gdouble last_y;
+ gdouble last_x = 0;
+ gdouble last_y = 0;
+ guint p;
- points = g_new0 (SysprofVisualizerRowAbsolutePoint, n_fpoints);
+ points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints);
- sysprof_visualizer_row_translate_points (SYSPROF_VISUALIZER_ROW (self),
- (const SysprofVisualizerRowRelativePoint *)fpoints,
- n_fpoints,
- points,
- n_fpoints);
+ sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self),
+ (const SysprofVisualizerRelativePoint *)fpoints,
+ n_fpoints,
+ points,
+ n_fpoints);
- last_x = points[0].x;
- last_y = points[0].y;
+ for (p = 0; p < n_fpoints; p++)
+ {
+ if (points[p].x >= clip.x)
+ break;
+ }
+
+ if (p >= n_fpoints)
+ return ret;
+
+ if (p > 0)
+ p--;
+
+ last_x = points[p].x;
+ last_y = points[p].y;
if (line_info->fill)
{
@@ -205,7 +216,7 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget,
cairo_move_to (cr, last_x, last_y);
}
- for (guint i = 1; i < n_fpoints; i++)
+ for (guint i = p + 1; i < n_fpoints; i++)
{
cairo_curve_to (cr,
last_x + ((points[i].x - last_x) / 2),
@@ -214,8 +225,12 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget,
points[i].y,
points[i].x,
points[i].y);
+
last_x = points[i].x;
last_y = points[i].y;
+
+ if (points[i].x > clip.x + clip.width)
+ break;
}
if (line_info->fill)
@@ -249,18 +264,18 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget,
}
static void
-sysprof_line_visualizer_row_load_data_cb (GObject *object,
+sysprof_line_visualizer_load_data_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)object;
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = (SysprofLineVisualizer *)object;
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_autoptr(GError) error = NULL;
g_autoptr(PointCache) cache = NULL;
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
- cache = sysprof_line_visualizer_row_load_data_finish (self, result, &error);
+ cache = sysprof_line_visualizer_load_data_finish (self, result, &error);
if (cache == NULL)
{
@@ -275,20 +290,20 @@ sysprof_line_visualizer_row_load_data_cb (GObject *object,
}
static gboolean
-sysprof_line_visualizer_row_do_reload (gpointer data)
+sysprof_line_visualizer_do_reload (gpointer data)
{
- SysprofLineVisualizerRow *self = data;
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = data;
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
priv->queued_load = 0;
if (priv->reader != NULL)
{
- sysprof_line_visualizer_row_load_data_async (self,
+ sysprof_line_visualizer_load_data_async (self,
NULL,
- sysprof_line_visualizer_row_load_data_cb,
+ sysprof_line_visualizer_load_data_cb,
NULL);
}
@@ -296,29 +311,29 @@ sysprof_line_visualizer_row_do_reload (gpointer data)
}
static void
-sysprof_line_visualizer_row_queue_reload (SysprofLineVisualizerRow *self)
+sysprof_line_visualizer_queue_reload (SysprofLineVisualizer *self)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->queued_load == 0)
{
priv->queued_load = gdk_threads_add_idle_full (G_PRIORITY_LOW,
- sysprof_line_visualizer_row_do_reload,
+ sysprof_line_visualizer_do_reload,
self,
NULL);
}
}
static void
-sysprof_line_visualizer_row_set_reader (SysprofVisualizerRow *row,
- SysprofCaptureReader *reader)
+sysprof_line_visualizer_set_reader (SysprofVisualizer *row,
+ SysprofCaptureReader *reader)
{
- SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)row;
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = (SysprofLineVisualizer *)row;
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->reader != reader)
{
@@ -331,15 +346,15 @@ sysprof_line_visualizer_row_set_reader (SysprofVisualizerRow *row,
if (reader != NULL)
priv->reader = sysprof_capture_reader_ref (reader);
- sysprof_line_visualizer_row_queue_reload (self);
+ sysprof_line_visualizer_queue_reload (self);
}
}
static void
-sysprof_line_visualizer_row_finalize (GObject *object)
+sysprof_line_visualizer_finalize (GObject *object)
{
- SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)object;
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = (SysprofLineVisualizer *)object;
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_clear_pointer (&priv->lines, g_array_unref);
g_clear_pointer (&priv->cache, point_cache_unref);
@@ -351,24 +366,20 @@ sysprof_line_visualizer_row_finalize (GObject *object)
priv->queued_load = 0;
}
- G_OBJECT_CLASS (sysprof_line_visualizer_row_parent_class)->finalize (object);
+ G_OBJECT_CLASS (sysprof_line_visualizer_parent_class)->finalize (object);
}
static void
-sysprof_line_visualizer_row_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+sysprof_line_visualizer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- SysprofLineVisualizerRow *self = SYSPROF_LINE_VISUALIZER_ROW (object);
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
switch (prop_id)
{
- case PROP_TITLE:
- g_object_get_property (G_OBJECT (priv->label), "label", value);
- break;
-
case PROP_Y_LOWER:
g_value_set_double (value, priv->y_lower);
break;
@@ -383,20 +394,16 @@ sysprof_line_visualizer_row_get_property (GObject *object,
}
static void
-sysprof_line_visualizer_row_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+sysprof_line_visualizer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- SysprofLineVisualizerRow *self = SYSPROF_LINE_VISUALIZER_ROW (object);
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
switch (prop_id)
{
- case PROP_TITLE:
- g_object_set_property (G_OBJECT (priv->label), "label", value);
- break;
-
case PROP_Y_LOWER:
priv->y_lower = g_value_get_double (value);
priv->y_lower_set = TRUE;
@@ -415,26 +422,19 @@ sysprof_line_visualizer_row_set_property (GObject *object,
}
static void
-sysprof_line_visualizer_row_class_init (SysprofLineVisualizerRowClass *klass)
+sysprof_line_visualizer_class_init (SysprofLineVisualizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- SysprofVisualizerRowClass *visualizer_class = SYSPROF_VISUALIZER_ROW_CLASS (klass);
+ SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
- object_class->finalize = sysprof_line_visualizer_row_finalize;
- object_class->get_property = sysprof_line_visualizer_row_get_property;
- object_class->set_property = sysprof_line_visualizer_row_set_property;
+ object_class->finalize = sysprof_line_visualizer_finalize;
+ object_class->get_property = sysprof_line_visualizer_get_property;
+ object_class->set_property = sysprof_line_visualizer_set_property;
- widget_class->draw = sysprof_line_visualizer_row_draw;
+ widget_class->draw = sysprof_line_visualizer_draw;
- visualizer_class->set_reader = sysprof_line_visualizer_row_set_reader;
-
- properties [PROP_TITLE] =
- g_param_spec_string ("title",
- "Title",
- "The title of the row",
- NULL,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ visualizer_class->set_reader = sysprof_line_visualizer_set_reader;
properties [PROP_Y_LOWER] =
g_param_spec_double ("y-lower",
@@ -458,35 +458,22 @@ sysprof_line_visualizer_row_class_init (SysprofLineVisualizerRowClass *klass)
}
static void
-sysprof_line_visualizer_row_init (SysprofLineVisualizerRow *self)
+sysprof_line_visualizer_init (SysprofLineVisualizer *self)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
- PangoAttrList *attrs = pango_attr_list_new ();
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo));
-
- pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL * PANGO_SCALE_SMALL));
-
- priv->label = g_object_new (GTK_TYPE_LABEL,
- "attributes", attrs,
- "visible", TRUE,
- "xalign", 0.0f,
- "yalign", 0.0f,
- NULL);
- gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->label));
-
- pango_attr_list_unref (attrs);
}
void
-sysprof_line_visualizer_row_add_counter (SysprofLineVisualizerRow *self,
- guint counter_id,
- const GdkRGBA *color)
+sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
LineInfo line_info = { 0 };
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
g_assert (priv->lines != NULL);
line_info.id = counter_id;
@@ -506,18 +493,18 @@ sysprof_line_visualizer_row_add_counter (SysprofLineVisualizerRow *self,
g_array_append_val (priv->lines, line_info);
- if (SYSPROF_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added)
- SYSPROF_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added (self, counter_id);
+ if (SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added)
+ SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id);
- sysprof_line_visualizer_row_queue_reload (self);
+ sysprof_line_visualizer_queue_reload (self);
}
void
-sysprof_line_visualizer_row_clear (SysprofLineVisualizerRow *self)
+sysprof_line_visualizer_clear (SysprofLineVisualizer *self)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->lines->len > 0)
g_array_remove_range (priv->lines, 0, priv->lines->len);
@@ -580,8 +567,8 @@ calc_y_int64 (gint64 lower,
}
static gboolean
-sysprof_line_visualizer_row_load_data_frame_cb (const SysprofCaptureFrame *frame,
- gpointer user_data)
+sysprof_line_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
{
LoadData *load = user_data;
@@ -622,8 +609,8 @@ sysprof_line_visualizer_row_load_data_frame_cb (const SysprofCaptureFrame *frame
}
static gboolean
-sysprof_line_visualizer_row_load_data_range_cb (const SysprofCaptureFrame *frame,
- gpointer user_data)
+sysprof_line_visualizer_load_data_range_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
{
LoadData *load = user_data;
@@ -689,16 +676,16 @@ sysprof_line_visualizer_row_load_data_range_cb (const SysprofCaptureFrame *frame
}
static void
-sysprof_line_visualizer_row_load_data_worker (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+sysprof_line_visualizer_load_data_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
LoadData *load = task_data;
g_autoptr(GArray) counter_ids = NULL;
g_assert (G_IS_TASK (task));
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (source_object));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (source_object));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
counter_ids = g_array_new (FALSE, FALSE, sizeof (guint));
@@ -716,7 +703,7 @@ sysprof_line_visualizer_row_load_data_worker (GTask *task,
/* If y boundaries are not set, we need to discover them by scaning the data. */
if (!load->y_lower_set || !load->y_upper_set)
{
- sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_row_load_data_range_cb, load);
+ sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_range_cb, load);
sysprof_capture_cursor_reset (load->cursor);
/* Add extra boundary for some space above the graph line */
@@ -724,26 +711,26 @@ sysprof_line_visualizer_row_load_data_worker (GTask *task,
load->y_upper = load->y_upper + ((load->y_upper - load->y_lower) * .25);
}
- sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_row_load_data_frame_cb, load);
+ sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_frame_cb, load);
g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref);
}
static void
-sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_autoptr(GTask) task = NULL;
LoadData *load;
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_priority (task, G_PRIORITY_LOW);
- g_task_set_source_tag (task, sysprof_line_visualizer_row_load_data_async);
+ g_task_set_source_tag (task, sysprof_line_visualizer_load_data_async);
if (priv->reader == NULL)
{
@@ -773,18 +760,18 @@ sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self,
}
g_task_set_task_data (task, load, load_data_free);
- g_task_run_in_thread (task, sysprof_line_visualizer_row_load_data_worker);
+ g_task_run_in_thread (task, sysprof_line_visualizer_load_data_worker);
}
static PointCache *
-sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self,
- GAsyncResult *result,
- GError **error)
+sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self,
+ GAsyncResult *result,
+ GError **error)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
LoadData *state;
- g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
g_assert (G_IS_TASK (result));
state = g_task_get_task_data (G_TASK (result));
@@ -805,13 +792,13 @@ sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self,
}
void
-sysprof_line_visualizer_row_set_line_width (SysprofLineVisualizerRow *self,
- guint counter_id,
- gdouble width)
+sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self,
+ guint counter_id,
+ gdouble width)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
@@ -820,20 +807,20 @@ sysprof_line_visualizer_row_set_line_width (SysprofLineVisualizerRow *self,
if (info->id == counter_id)
{
info->line_width = width;
- sysprof_line_visualizer_row_queue_reload (self);
+ sysprof_line_visualizer_queue_reload (self);
break;
}
}
}
void
-sysprof_line_visualizer_row_set_fill (SysprofLineVisualizerRow *self,
- guint counter_id,
- const GdkRGBA *color)
+sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
@@ -844,20 +831,20 @@ sysprof_line_visualizer_row_set_fill (SysprofLineVisualizerRow *self,
info->fill = !!color;
if (color != NULL)
info->background = *color;
- sysprof_line_visualizer_row_queue_reload (self);
+ sysprof_line_visualizer_queue_reload (self);
break;
}
}
}
void
-sysprof_line_visualizer_row_set_dash (SysprofLineVisualizerRow *self,
- guint counter_id,
- gboolean use_dash)
+sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self,
+ guint counter_id,
+ gboolean use_dash)
{
- SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self);
+ SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
- g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self));
+ g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
@@ -866,7 +853,7 @@ sysprof_line_visualizer_row_set_dash (SysprofLineVisualizerRow *self,
if (info->id == counter_id)
{
info->use_dash = !!use_dash;
- sysprof_line_visualizer_row_queue_reload (self);
+ sysprof_line_visualizer_queue_reload (self);
break;
}
}
diff --git a/src/libsysprof-ui/sysprof-line-visualizer.h b/src/libsysprof-ui/sysprof-line-visualizer.h
new file mode 100644
index 0000000..4fac885
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-line-visualizer.h
@@ -0,0 +1,57 @@
+/* sysprof-line-visualizer.h
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * 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
+ */
+
+#pragma once
+
+#include "sysprof-visualizer.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_LINE_VISUALIZER (sysprof_line_visualizer_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF, LINE_VISUALIZER,
SysprofVisualizer)
+
+struct _SysprofLineVisualizerClass
+{
+ SysprofVisualizerClass parent_class;
+
+ void (*counter_added) (SysprofLineVisualizer *self,
+ guint counter_id);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+GtkWidget *sysprof_line_visualizer_new (void);
+void sysprof_line_visualizer_clear (SysprofLineVisualizer *self);
+void sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color);
+void sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self,
+ guint counter_id,
+ gdouble width);
+void sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color);
+void sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self,
+ guint counter_id,
+ gboolean use_dash);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-logs-aid.c b/src/libsysprof-ui/sysprof-logs-aid.c
new file mode 100644
index 0000000..466eab3
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-logs-aid.c
@@ -0,0 +1,237 @@
+/* sysprof-logs-aid.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-logs-aid"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-color-cycle.h"
+#include "sysprof-logs-aid.h"
+#include "sysprof-logs-page.h"
+#include "sysprof-mark-visualizer.h"
+
+struct _SysprofLogsAid
+{
+ SysprofAid parent_instance;
+};
+
+typedef struct
+{
+ SysprofDisplay *display;
+ SysprofCaptureCursor *cursor;
+ GArray *log_marks;
+} Present;
+
+G_DEFINE_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF_TYPE_AID)
+
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->log_marks, g_array_unref);
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
+static void
+on_group_activated_cb (SysprofVisualizerGroup *group,
+ SysprofPage *page)
+{
+ SysprofDisplay *display;
+
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
+ g_assert (SYSPROF_IS_PAGE (page));
+
+ display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
+ sysprof_display_set_visible_page (display, page);
+}
+
+/**
+ * sysprof_logs_aid_new:
+ *
+ * Create a new #SysprofLogsAid.
+ *
+ * Returns: (transfer full): a newly created #SysprofLogsAid
+ */
+SysprofAid *
+sysprof_logs_aid_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_LOGS_AID, NULL);
+}
+
+static gboolean
+find_marks_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ Present *p = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (p != NULL);
+
+ if (frame->type == SYSPROF_CAPTURE_FRAME_LOG)
+ {
+ SysprofMarkTimeSpan span = { frame->time, frame->time };
+ g_array_append_val (p->log_marks, span);
+ }
+
+ return TRUE;
+}
+
+static gint
+compare_span (const SysprofMarkTimeSpan *a,
+ const SysprofMarkTimeSpan *b)
+{
+ if (a->kind < b->kind)
+ return -1;
+
+ if (b->kind < a->kind)
+ return 1;
+
+ if (a->begin < b->begin)
+ return -1;
+
+ if (b->begin < a->begin)
+ return 1;
+
+ if (b->end > a->end)
+ return -1;
+
+ return 0;
+}
+
+static void
+sysprof_logs_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *p = task_data;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (p != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (p->display));
+ g_assert (p->cursor != NULL);
+ g_assert (SYSPROF_IS_LOGS_AID (source_object));
+
+ sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p);
+ g_array_sort (p->log_marks, (GCompareFunc)compare_span);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_logs_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType logs[] = {
+ SYSPROF_CAPTURE_FRAME_LOG,
+ };
+ SysprofLogsAid *self = (SysprofLogsAid *)aid;
+ g_autoptr(GTask) task = NULL;
+ Present p = {0};
+
+ g_assert (SYSPROF_IS_LOGS_AID (self));
+
+ p.display = g_object_ref (display);
+ p.log_marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan));
+ p.cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (p.cursor,
+ sysprof_capture_condition_new_where_type_in (1, logs));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_logs_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &p),
+ present_free);
+ g_task_run_in_thread (task, sysprof_logs_aid_present_worker);
+}
+
+static gboolean
+sysprof_logs_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ Present *p;
+
+ g_assert (SYSPROF_IS_LOGS_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ p = g_task_get_task_data (G_TASK (result));
+
+ if (p->log_marks->len > 0)
+ {
+ g_autoptr(GHashTable) items = NULL;
+ SysprofVisualizerGroup *group;
+ SysprofVisualizer *marks;
+ SysprofPage *page;
+
+ items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) g_array_unref);
+ g_hash_table_insert (items, g_strdup (_("Logs")), g_array_ref (p->log_marks));
+
+ group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "title", _("Logs"),
+ "visible", TRUE,
+ NULL);
+
+ marks = sysprof_mark_visualizer_new (items);
+ sysprof_visualizer_set_title (marks, _("Logs"));
+ gtk_widget_show (GTK_WIDGET (marks));
+ sysprof_visualizer_group_insert (group, marks, 0, FALSE);
+ sysprof_display_add_group (p->display, group);
+
+ page = g_object_new (SYSPROF_TYPE_LOGS_PAGE,
+ "title", _("Logs"),
+ "visible", TRUE,
+ NULL);
+ sysprof_display_add_page (p->display, page);
+
+ g_signal_connect_object (group,
+ "group-activated",
+ G_CALLBACK (on_group_activated_cb),
+ page,
+ 0);
+ }
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_logs_aid_class_init (SysprofLogsAidClass *klass)
+{
+ SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
+
+ aid_class->present_async = sysprof_logs_aid_present_async;
+ aid_class->present_finish = sysprof_logs_aid_present_finish;
+}
+
+static void
+sysprof_logs_aid_init (SysprofLogsAid *self)
+{
+}
diff --git a/src/libsysprof-ui/sysprof-logs-aid.h b/src/libsysprof-ui/sysprof-logs-aid.h
new file mode 100644
index 0000000..2f2d13a
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-logs-aid.h
@@ -0,0 +1,33 @@
+/* sysprof-logs-aid.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include "sysprof-aid.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_LOGS_AID (sysprof_logs_aid_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF, LOGS_AID, SysprofAid)
+
+SysprofAid *sysprof_logs_aid_new (void);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-logs-page.c b/src/libsysprof-ui/sysprof-logs-page.c
new file mode 100644
index 0000000..f2b46af
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-logs-page.c
@@ -0,0 +1,116 @@
+/* sysprof-logs-page.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-logs-page"
+
+#include "config.h"
+
+#include "sysprof-log-model.h"
+#include "sysprof-logs-page.h"
+
+struct _SysprofLogsPage
+{
+ SysprofPage parent_instance;
+
+ /* Template Widgets */
+ GtkTreeView *tree_view;
+};
+
+G_DEFINE_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF_TYPE_PAGE)
+
+static void
+sysprof_logs_page_load_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofLogsPage *self;
+ g_autoptr(SysprofLogModel) model = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!(model = sysprof_log_model_new_finish (result, &error)))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+
+ self = g_task_get_source_object (task);
+
+ gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (model));
+}
+
+static void
+sysprof_logs_page_load_async (SysprofPage *page,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *filter,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SysprofLogsPage *self = (SysprofLogsPage *)page;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (SYSPROF_IS_LOGS_PAGE (self));
+ g_assert (reader != NULL);
+ g_assert (!selection || SYSPROF_IS_SELECTION (selection));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_logs_page_load_async);
+
+ sysprof_log_model_new_async (reader,
+ selection,
+ cancellable,
+ sysprof_logs_page_load_cb,
+ g_steal_pointer (&task));
+}
+
+static gboolean
+sysprof_logs_page_load_finish (SysprofPage *page,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (SYSPROF_IS_LOGS_PAGE (page));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_logs_page_class_init (SysprofLogsPageClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass);
+
+ page_class->load_async = sysprof_logs_page_load_async;
+ page_class->load_finish = sysprof_logs_page_load_finish;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-logs-page.ui");
+ gtk_widget_class_bind_template_child (widget_class, SysprofLogsPage, tree_view);
+}
+
+static void
+sysprof_logs_page_init (SysprofLogsPage *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/libsysprof-ui/sysprof-visualizer-row-private.h b/src/libsysprof-ui/sysprof-logs-page.h
similarity index 74%
rename from src/libsysprof-ui/sysprof-visualizer-row-private.h
rename to src/libsysprof-ui/sysprof-logs-page.h
index a364056..97bb245 100644
--- a/src/libsysprof-ui/sysprof-visualizer-row-private.h
+++ b/src/libsysprof-ui/sysprof-logs-page.h
@@ -1,6 +1,6 @@
-/* sysprof-visualizer-row-private.h
+/* sysprof-logs-page.h
*
- * Copyright 2016-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2019 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
@@ -20,10 +20,12 @@
#pragma once
-#include "sysprof-visualizer-row.h"
+#include "sysprof-page.h"
G_BEGIN_DECLS
-gint _sysprof_visualizer_row_get_graph_width (SysprofVisualizerRow *self);
+#define SYSPROF_TYPE_LOGS_PAGE (sysprof_logs_page_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF, LOGS_PAGE, SysprofPage)
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-logs-page.ui b/src/libsysprof-ui/sysprof-logs-page.ui
new file mode 100644
index 0000000..6760253
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-logs-page.ui
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="SysprofLogsPage" parent="SysprofPage">
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="tree_view">
+ <property name="tooltip-column">3</property>
+ <property name="headers-visible">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">false</property>
+ <property name="title" translatable="yes">Time</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">false</property>
+ <property name="title" translatable="yes">Severity</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">false</property>
+ <property name="resizable">true</property>
+ <property name="title" translatable="yes">Domain</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">true</property>
+ <property name="title" translatable="yes">Message</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="ellipsize">end</property>
+ <property name="xalign">0.0</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.c b/src/libsysprof-ui/sysprof-mark-visualizer.c
new file mode 100644
index 0000000..216cf62
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-mark-visualizer.c
@@ -0,0 +1,287 @@
+/* sysprof-mark-visualizer.c
+ *
+ * Copyright 2018-2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-mark-visualizer"
+
+#include "config.h"
+
+#include "sysprof-mark-visualizer.h"
+
+#define RECT_HEIGHT (4)
+#define RECT_MIN_WIDTH (3)
+#define RECT_OVERLAP (-1)
+
+struct _SysprofMarkVisualizer
+{
+ SysprofVisualizer parent_instance;
+ GHashTable *spans_by_group;
+ GHashTable *rgba_by_group;
+ GHashTable *rgba_by_kind;
+ GHashTable *row_by_kind;
+ guint x_is_dirty : 1;
+};
+
+G_DEFINE_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF_TYPE_VISUALIZER)
+
+static void
+reset_positions (SysprofMarkVisualizer *self)
+{
+ g_assert (SYSPROF_IS_MARK_VISUALIZER (self));
+
+ self->x_is_dirty = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+SysprofVisualizer *
+sysprof_mark_visualizer_new (GHashTable *groups)
+{
+ SysprofMarkVisualizer *self;
+ guint n_items;
+ gint height;
+
+ g_return_val_if_fail (groups != NULL, NULL);
+
+ self = g_object_new (SYSPROF_TYPE_MARK_VISUALIZER, NULL);
+ self->spans_by_group = g_hash_table_ref (groups);
+
+ reset_positions (self);
+
+ n_items = g_hash_table_size (groups);
+ height = MAX (35, n_items * (RECT_HEIGHT - RECT_OVERLAP));
+ gtk_widget_set_size_request (GTK_WIDGET (self), -1, height);
+
+ return SYSPROF_VISUALIZER (g_steal_pointer (&self));
+}
+
+static gboolean
+sysprof_mark_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget;
+ SysprofVisualizer *vis = (SysprofVisualizer *)widget;
+ GHashTableIter iter;
+ GtkAllocation alloc;
+ gpointer k, v;
+ gboolean ret;
+ gint n_groups = 0;
+ gint y = 0;
+
+ g_assert (SYSPROF_IS_MARK_VISUALIZER (self));
+ g_assert (cr != NULL);
+
+ ret = GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->draw (widget, cr);
+ if (self->spans_by_group == NULL)
+ return ret;
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ /* Pre-calculate all time slots so we can join later */
+ if (self->x_is_dirty)
+ {
+ g_hash_table_iter_init (&iter, self->spans_by_group);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ const GArray *spans = v;
+
+ for (guint i = 0; i < spans->len; i++)
+ {
+ SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i);
+
+ span->x = sysprof_visualizer_get_x_for_time (vis, span->begin);
+ span->x2 = sysprof_visualizer_get_x_for_time (vis, span->end);
+ }
+ }
+
+ self->x_is_dirty = FALSE;
+ }
+
+ n_groups = g_hash_table_size (self->spans_by_group);
+
+ g_hash_table_iter_init (&iter, self->spans_by_group);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ static const GdkRGBA black = {0, 0, 0, 1};
+ SysprofMarkTimeSpan *span;
+ const gchar *group = k;
+ const GArray *spans = v;
+ const GdkRGBA *rgba;
+ const GdkRGBA *kindrgba;
+ const GdkRGBA *grouprgba;
+
+ if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group)))
+ {
+ rgba = grouprgba;
+ gdk_cairo_set_source_rgba (cr, rgba);
+ }
+
+ for (guint i = 0; i < spans->len; i++)
+ {
+ gint x1, x2;
+
+ span = &g_array_index (spans, SysprofMarkTimeSpan, i);
+
+ if (n_groups == 1)
+ {
+ rgba = &black;
+ if ((kindrgba = g_hash_table_lookup (self->rgba_by_kind, GUINT_TO_POINTER (span->kind))))
+ rgba = kindrgba;
+ else if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group)))
+ rgba = grouprgba;
+ gdk_cairo_set_source_rgba (cr, rgba);
+ }
+
+ x1 = span->x;
+ x2 = x1 + RECT_MIN_WIDTH;
+
+ if (span->x2 > x2)
+ x2 = span->x2;
+
+ /* If we are limited to one group, we might need to get the row
+ * height for the kind of span this is.
+ */
+ if (n_groups == 1)
+ {
+ gint row = GPOINTER_TO_INT (g_hash_table_lookup (self->row_by_kind, GUINT_TO_POINTER
(span->kind)));
+ y = row * (RECT_HEIGHT - RECT_OVERLAP);
+ }
+
+ for (guint j = i + 1; j < spans->len; j++)
+ {
+ const SysprofMarkTimeSpan *next = &g_array_index (spans, SysprofMarkTimeSpan, j);
+
+ /* Don't join this if we are about to draw a different kind */
+ if (n_groups == 1 && next->kind != span->kind)
+ break;
+
+ if (next->x <= x2)
+ {
+ x2 = MAX (x2, next->x2);
+ i++;
+ continue;
+ }
+
+ break;
+ }
+
+ cairo_rectangle (cr, x1, y, x2 - x1, RECT_HEIGHT);
+
+ if (n_groups == 1)
+ cairo_fill (cr);
+ }
+
+ if (n_groups > 1)
+ cairo_fill (cr);
+
+ y += RECT_HEIGHT + RECT_OVERLAP;
+ }
+
+ return ret;
+}
+
+static void
+sysprof_mark_visualizer_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
+{
+ SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget;
+
+ g_assert (SYSPROF_IS_MARK_VISUALIZER (self));
+ g_assert (alloc != NULL);
+
+ GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->size_allocate (widget, alloc);
+
+ reset_positions (self);
+}
+
+static void
+sysprof_mark_visualizer_finalize (GObject *object)
+{
+ SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)object;
+
+ g_clear_pointer (&self->spans_by_group, g_hash_table_unref);
+ g_clear_pointer (&self->rgba_by_group, g_hash_table_unref);
+ g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref);
+ g_clear_pointer (&self->row_by_kind, g_hash_table_unref);
+
+ G_OBJECT_CLASS (sysprof_mark_visualizer_parent_class)->finalize (object);
+}
+
+static void
+sysprof_mark_visualizer_class_init (SysprofMarkVisualizerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = sysprof_mark_visualizer_finalize;
+
+ widget_class->draw = sysprof_mark_visualizer_draw;
+ widget_class->size_allocate = sysprof_mark_visualizer_size_allocate;
+}
+
+static void
+sysprof_mark_visualizer_init (SysprofMarkVisualizer *self)
+{
+ self->rgba_by_kind = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+ self->row_by_kind = g_hash_table_new (NULL, NULL);
+ self->rgba_by_group = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self,
+ const gchar *group,
+ const GdkRGBA *rgba)
+{
+ g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self));
+ g_return_if_fail (group != NULL);
+
+ g_hash_table_insert (self->rgba_by_group,
+ g_strdup (group),
+ g_memdup (rgba, sizeof *rgba));
+}
+
+void
+sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self,
+ GHashTable *rgba_by_kind)
+{
+ g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self));
+
+ if (rgba_by_kind != self->rgba_by_kind)
+ {
+ g_hash_table_remove_all (self->row_by_kind);
+
+ g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref);
+
+ if (rgba_by_kind)
+ {
+ GHashTableIter iter;
+ guint row = 0;
+ gpointer k;
+
+ self->rgba_by_kind = g_hash_table_ref (rgba_by_kind);
+
+ g_hash_table_iter_init (&iter, rgba_by_kind);
+ while (g_hash_table_iter_next (&iter, &k, NULL))
+ g_hash_table_insert (self->row_by_kind, k, GUINT_TO_POINTER (row++));
+
+ gtk_widget_set_size_request (GTK_WIDGET (self),
+ -1,
+ MAX (35, row * (RECT_HEIGHT - RECT_OVERLAP)));
+ }
+ }
+}
diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.h b/src/libsysprof-ui/sysprof-mark-visualizer.h
new file mode 100644
index 0000000..36327e1
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-mark-visualizer.h
@@ -0,0 +1,47 @@
+/* sysprof-mark-visualizer.h
+ *
+ * Copyright 2018-2019 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
+ */
+
+#pragma once
+
+#include "sysprof-visualizer.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gint64 begin;
+ gint64 end;
+ guint kind;
+ gint x;
+ gint x2;
+} SysprofMarkTimeSpan;
+
+#define SYSPROF_TYPE_MARK_VISUALIZER (sysprof_mark_visualizer_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF, MARK_VISUALIZER,
SysprofVisualizer)
+
+SysprofVisualizer *sysprof_mark_visualizer_new (GHashTable *groups);
+void sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self,
+ const gchar *group,
+ const GdkRGBA *rgba);
+void sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self,
+ GHashTable *rgba_by_kind);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-marks-aid.c b/src/libsysprof-ui/sysprof-marks-aid.c
new file mode 100644
index 0000000..a8f79b4
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-marks-aid.c
@@ -0,0 +1,319 @@
+/* sysprof-marks-aid.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-marks-aid"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-color-cycle.h"
+#include "sysprof-marks-aid.h"
+#include "sysprof-marks-page.h"
+#include "sysprof-mark-visualizer.h"
+
+struct _SysprofMarksAid
+{
+ SysprofAid parent_instance;
+};
+
+typedef struct
+{
+ SysprofDisplay *display;
+ SysprofCaptureCursor *cursor;
+ GHashTable *categories;
+ GHashTable *kinds;
+ guint last_kind;
+ guint has_marks : 1;
+} Present;
+
+G_DEFINE_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF_TYPE_AID)
+
+static void
+present_free (gpointer data)
+{
+ Present *p = data;
+
+ g_clear_pointer (&p->categories, g_hash_table_unref);
+ g_clear_pointer (&p->kinds, g_hash_table_unref);
+ g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
+ g_clear_object (&p->display);
+ g_slice_free (Present, p);
+}
+
+static void
+on_group_activated_cb (SysprofVisualizerGroup *group,
+ SysprofPage *page)
+{
+ SysprofDisplay *display;
+
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
+ g_assert (SYSPROF_IS_PAGE (page));
+
+ display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
+ sysprof_display_set_visible_page (display, page);
+}
+
+/**
+ * sysprof_marks_aid_new:
+ *
+ * Create a new #SysprofMarksAid.
+ *
+ * Returns: (transfer full): a newly created #SysprofMarksAid
+ */
+SysprofAid *
+sysprof_marks_aid_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_MARKS_AID, NULL);
+}
+
+static gboolean
+find_marks_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ Present *p = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (p != NULL);
+
+ if (frame->type == SYSPROF_CAPTURE_FRAME_MARK)
+ {
+ const SysprofCaptureMark *mark = (const SysprofCaptureMark *)frame;
+ SysprofMarkTimeSpan span = { frame->time, frame->time + mark->duration };
+ gchar joined[64];
+ gpointer kptr;
+ GArray *items;
+
+ p->has_marks = TRUE;
+
+ if G_UNLIKELY (!(items = g_hash_table_lookup (p->categories, mark->group)))
+ {
+ items = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan));
+ g_hash_table_insert (p->categories, g_strdup (mark->group), items);
+ }
+
+ g_snprintf (joined, sizeof joined, "%s:%s", mark->group, mark->name);
+
+ if G_UNLIKELY (!(kptr = g_hash_table_lookup (p->kinds, joined)))
+ {
+ p->last_kind++;
+ kptr = GINT_TO_POINTER (p->last_kind);
+ g_hash_table_insert (p->kinds, g_strdup (joined), kptr);
+ }
+
+ span.kind = GPOINTER_TO_INT (kptr);
+
+ g_array_append_val (items, span);
+ }
+
+ return TRUE;
+}
+
+static gint
+compare_span (const SysprofMarkTimeSpan *a,
+ const SysprofMarkTimeSpan *b)
+{
+ if (a->kind < b->kind)
+ return -1;
+
+ if (b->kind < a->kind)
+ return 1;
+
+ if (a->begin < b->begin)
+ return -1;
+
+ if (b->begin < a->begin)
+ return 1;
+
+ if (b->end > a->end)
+ return -1;
+
+ return 0;
+}
+
+static void
+sysprof_marks_aid_present_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Present *p = task_data;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (p != NULL);
+ g_assert (SYSPROF_IS_DISPLAY (p->display));
+ g_assert (p->cursor != NULL);
+ g_assert (SYSPROF_IS_MARKS_AID (source_object));
+
+ sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p);
+
+ g_hash_table_iter_init (&iter, p->categories);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ GArray *spans = v;
+
+ g_array_sort (spans, (GCompareFunc)compare_span);
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_marks_aid_present_async (SysprofAid *aid,
+ SysprofCaptureReader *reader,
+ SysprofDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const SysprofCaptureFrameType marks[] = {
+ SYSPROF_CAPTURE_FRAME_MARK,
+ };
+ SysprofMarksAid *self = (SysprofMarksAid *)aid;
+ g_autoptr(GTask) task = NULL;
+ Present p = {0};
+
+ g_assert (SYSPROF_IS_MARKS_AID (self));
+
+ p.display = g_object_ref (display);
+ p.categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) g_array_unref);
+ p.kinds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ p.cursor = sysprof_capture_cursor_new (reader);
+ sysprof_capture_cursor_add_condition (p.cursor,
+ sysprof_capture_condition_new_where_type_in (1, marks));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_marks_aid_present_async);
+ g_task_set_task_data (task,
+ g_slice_dup (Present, &p),
+ present_free);
+ g_task_run_in_thread (task, sysprof_marks_aid_present_worker);
+}
+
+static gboolean
+sysprof_marks_aid_present_finish (SysprofAid *aid,
+ GAsyncResult *result,
+ GError **error)
+{
+ Present *p;
+
+ g_assert (SYSPROF_IS_MARKS_AID (aid));
+ g_assert (G_IS_TASK (result));
+
+ p = g_task_get_task_data (G_TASK (result));
+
+ if (p->has_marks)
+ {
+ g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
+ SysprofVisualizerGroup *group;
+ SysprofVisualizer *marks;
+ SysprofPage *page;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
+ "can-focus", TRUE,
+ "has-page", TRUE,
+ "title", _("Timings"),
+ "visible", TRUE,
+ NULL);
+
+ marks = sysprof_mark_visualizer_new (p->categories);
+ sysprof_visualizer_set_title (marks, _("Timings"));
+ gtk_widget_show (GTK_WIDGET (marks));
+
+ g_hash_table_iter_init (&iter, p->categories);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ g_autoptr(GHashTable) seen = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+ g_autoptr(GHashTable) scoped = NULL;
+ SysprofVisualizer *scoped_vis;
+ GArray *spans = v;
+ const gchar *name = k;
+ GdkRGBA rgba;
+ GdkRGBA kind_rgba;
+ gdouble ratio;
+
+ sysprof_color_cycle_next (cycle, &rgba);
+ sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (marks), name, &rgba);
+
+ /* Now make a scoped row just for this group */
+ scoped = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)g_array_unref);
+ g_hash_table_insert (scoped, g_strdup (name), g_array_ref (spans));
+
+ scoped_vis = sysprof_mark_visualizer_new (scoped);
+ sysprof_visualizer_set_title (scoped_vis, name);
+ sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), name, &rgba);
+ sysprof_visualizer_group_insert (group, scoped_vis, -1, TRUE);
+
+ ratio = .4 / p->last_kind;
+
+ for (guint i = 0; i < spans->len; i++)
+ {
+ const SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i);
+
+ if (!g_hash_table_contains (seen, GUINT_TO_POINTER (span->kind)))
+ {
+ dzl_rgba_shade (&rgba, &kind_rgba, 1 + (ratio * span->kind));
+ g_hash_table_insert (seen,
+ GUINT_TO_POINTER (span->kind),
+ g_memdup (&kind_rgba, sizeof kind_rgba));
+ }
+ }
+
+ sysprof_mark_visualizer_set_kind_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), seen);
+ }
+
+ page = g_object_new (SYSPROF_TYPE_MARKS_PAGE,
+ "zoom-manager", sysprof_display_get_zoom_manager (p->display),
+ "visible", TRUE,
+ NULL);
+
+ g_signal_connect_object (group,
+ "group-activated",
+ G_CALLBACK (on_group_activated_cb),
+ page,
+ 0);
+
+ sysprof_visualizer_group_insert (group, marks, 0, FALSE);
+ sysprof_display_add_group (p->display, group);
+ sysprof_display_add_page (p->display, page);
+ }
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_marks_aid_class_init (SysprofMarksAidClass *klass)
+{
+ SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
+
+ aid_class->present_async = sysprof_marks_aid_present_async;
+ aid_class->present_finish = sysprof_marks_aid_present_finish;
+}
+
+static void
+sysprof_marks_aid_init (SysprofMarksAid *self)
+{
+}
diff --git a/src/libsysprof-ui/sysprof-marks-aid.h b/src/libsysprof-ui/sysprof-marks-aid.h
new file mode 100644
index 0000000..f201f3a
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-marks-aid.h
@@ -0,0 +1,33 @@
+/* sysprof-marks-aid.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include "sysprof-aid.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_MARKS_AID (sysprof_marks_aid_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF, MARKS_AID, SysprofAid)
+
+SysprofAid *sysprof_marks_aid_new (void);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-marks-view.c b/src/libsysprof-ui/sysprof-marks-page.c
similarity index 64%
rename from src/libsysprof-ui/sysprof-marks-view.c
rename to src/libsysprof-ui/sysprof-marks-page.c
index c09d71d..3c4ce2f 100644
--- a/src/libsysprof-ui/sysprof-marks-view.c
+++ b/src/libsysprof-ui/sysprof-marks-page.c
@@ -1,4 +1,4 @@
-/* sysprof-marks-view.c
+/* sysprof-marks-page.c
*
* Copyright 2019 Christian Hergert <chergert redhat com>
*
@@ -18,13 +18,13 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "sysprof-marks-view"
+#define G_LOG_DOMAIN "sysprof-marks-page"
#include "config.h"
#include "sysprof-cell-renderer-duration.h"
#include "sysprof-marks-model.h"
-#include "sysprof-marks-view.h"
+#include "sysprof-marks-page.h"
#include "sysprof-ui-private.h"
#include "sysprof-zoom-manager.h"
@@ -42,10 +42,16 @@ typedef struct
/* Template objects */
GtkScrolledWindow *scroller;
GtkTreeView *tree_view;
+ GtkBox *details_box;
GtkTreeViewColumn *duration_column;
SysprofCellRendererDuration *duration_cell;
GtkStack *stack;
-} SysprofMarksViewPrivate;
+ GtkLabel *group;
+ GtkLabel *mark;
+ GtkLabel *time;
+ GtkLabel *duration;
+ GtkTextView *message;
+} SysprofMarksPagePrivate;
enum {
PROP_0,
@@ -56,17 +62,17 @@ enum {
static GParamSpec *properties [N_PROPS];
-G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarksView, sysprof_marks_view, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarksPage, sysprof_marks_page, SYSPROF_TYPE_PAGE)
static gboolean
-sysprof_marks_view_tree_view_key_press_event_cb (SysprofMarksView *self,
+sysprof_marks_page_tree_view_key_press_event_cb (SysprofMarksPage *self,
const GdkEventKey *key,
GtkTreeView *tree_view)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
gint dir = 0;
- g_assert (SYSPROF_MARKS_VIEW (self));
+ g_assert (SYSPROF_MARKS_PAGE (self));
g_assert (key != NULL);
if (key->state == 0)
@@ -127,21 +133,29 @@ get_first_selected (GtkTreeSelection *selection,
}
static void
-sysprof_marks_view_selection_changed_cb (SysprofMarksView *self,
+sysprof_marks_page_selection_changed_cb (SysprofMarksPage *self,
GtkTreeSelection *selection)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
GtkTreeModel *model;
GtkTreeIter iter;
- g_assert (SYSPROF_IS_MARKS_VIEW (self));
+ g_assert (SYSPROF_IS_MARKS_PAGE (self));
g_assert (GTK_IS_TREE_SELECTION (selection));
if (get_first_selected (selection, &model, &iter))
{
+ g_autofree gchar *group = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *duration_str = NULL;
+ g_autofree gchar *time_str = NULL;
+ g_autofree gchar *text = NULL;
GtkAdjustment *adj;
gdouble x;
gint64 begin_time;
+ gint64 end_time;
+ gint64 duration;
+ gint64 otime;
gdouble lower;
gdouble upper;
gdouble value;
@@ -149,9 +163,26 @@ sysprof_marks_view_selection_changed_cb (SysprofMarksView *self,
gint width;
gtk_tree_model_get (model, &iter,
+ SYSPROF_MARKS_MODEL_COLUMN_GROUP, &group,
+ SYSPROF_MARKS_MODEL_COLUMN_NAME, &name,
SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, &begin_time,
+ SYSPROF_MARKS_MODEL_COLUMN_END_TIME, &end_time,
+ SYSPROF_MARKS_MODEL_COLUMN_TEXT, &text,
-1);
+ duration = end_time - begin_time;
+ duration_str = _sysprof_format_duration (duration);
+
+ otime = begin_time - priv->capture_begin_time;
+ time_str = _sysprof_format_duration (otime);
+
+ gtk_label_set_label (priv->group, group);
+ gtk_label_set_label (priv->mark, name);
+ gtk_label_set_label (priv->duration, duration_str);
+ gtk_label_set_label (priv->time, time_str);
+
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (priv->message), text, -1);
+
adj = gtk_scrolled_window_get_hadjustment (priv->scroller);
width = gtk_tree_view_column_get_width (priv->duration_column);
x = sysprof_zoom_manager_get_offset_at_time (priv->zoom_manager,
@@ -173,19 +204,19 @@ sysprof_marks_view_selection_changed_cb (SysprofMarksView *self,
}
static gboolean
-sysprof_marks_view_tree_view_query_tooltip_cb (SysprofMarksView *self,
+sysprof_marks_page_tree_view_query_tooltip_cb (SysprofMarksPage *self,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip,
GtkTreeView *tree_view)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
g_autoptr(GtkTreePath) path = NULL;
GtkTreeViewColumn *column;
gint cell_x, cell_y;
- g_assert (SYSPROF_IS_MARKS_VIEW (self));
+ g_assert (SYSPROF_IS_MARKS_PAGE (self));
g_assert (GTK_IS_TOOLTIP (tooltip));
g_assert (GTK_IS_TREE_VIEW (tree_view));
@@ -229,24 +260,139 @@ sysprof_marks_view_tree_view_query_tooltip_cb (SysprofMarksView *self,
}
static void
-sysprof_marks_view_finalize (GObject *object)
+sysprof_marks_page_load_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- SysprofMarksView *self = (SysprofMarksView *)object;
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ g_autoptr(SysprofMarksModel) model = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+ SysprofMarksPagePrivate *priv;
+ SysprofCaptureReader *reader;
+ SysprofMarksPage *self;
+
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+ priv = sysprof_marks_page_get_instance_private (self);
+
+ if (!(model = sysprof_marks_model_new_finish (result, &error)))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ reader = g_task_get_task_data (task);
+ g_assert (reader != NULL);
+
+ priv->capture_begin_time = sysprof_capture_reader_get_start_time (reader);
+ priv->capture_end_time = sysprof_capture_reader_get_end_time (reader);
+
+ g_object_set (priv->duration_cell,
+ "capture-begin-time", priv->capture_begin_time,
+ "capture-end-time", priv->capture_end_time,
+ "zoom-manager", priv->zoom_manager,
+ NULL);
+
+ gtk_tree_view_set_model (priv->tree_view, GTK_TREE_MODEL (model));
+
+ if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL) == 0)
+ gtk_stack_set_visible_child_name (priv->stack, "empty-state");
+ else
+ gtk_stack_set_visible_child_name (priv->stack, "marks");
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_marks_page_load_async (SysprofPage *page,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *filter,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SysprofMarksPage *self = (SysprofMarksPage *)page;
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_marks_page_load_async);
+ g_task_set_task_data (task,
+ sysprof_capture_reader_ref (reader),
+ (GDestroyNotify) sysprof_capture_reader_unref);
+
+ sysprof_marks_model_new_async (reader,
+ priv->kind,
+ selection,
+ cancellable,
+ sysprof_marks_page_load_cb,
+ g_steal_pointer (&task));
+}
+
+static gboolean
+sysprof_marks_page_load_finish (SysprofPage *page,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_MARKS_PAGE (page), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_marks_page_set_hadjustment (SysprofPage *page,
+ GtkAdjustment *hadjustment)
+{
+ SysprofMarksPage *self = (SysprofMarksPage *)page;
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_MARKS_PAGE (self));
+ g_assert (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment));
+
+ gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment);
+}
+
+static void
+sysprof_marks_page_set_size_group (SysprofPage *page,
+ GtkSizeGroup *size_group)
+{
+ SysprofMarksPage *self = (SysprofMarksPage *)page;
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_MARKS_PAGE (self));
+ g_assert (GTK_IS_SIZE_GROUP (size_group));
+
+ gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->details_box));
+}
+
+static void
+sysprof_marks_page_finalize (GObject *object)
+{
+ SysprofMarksPage *self = (SysprofMarksPage *)object;
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
g_clear_object (&priv->zoom_manager);
- G_OBJECT_CLASS (sysprof_marks_view_parent_class)->finalize (object);
+ G_OBJECT_CLASS (sysprof_marks_page_parent_class)->finalize (object);
}
static void
-sysprof_marks_view_get_property (GObject *object,
+sysprof_marks_page_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
- SysprofMarksView *self = SYSPROF_MARKS_VIEW (object);
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
switch (prop_id)
{
@@ -264,13 +410,13 @@ sysprof_marks_view_get_property (GObject *object,
}
static void
-sysprof_marks_view_set_property (GObject *object,
+sysprof_marks_page_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
- SysprofMarksView *self = SYSPROF_MARKS_VIEW (object);
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
switch (prop_id)
{
@@ -299,21 +445,33 @@ sysprof_marks_view_set_property (GObject *object,
}
static void
-sysprof_marks_view_class_init (SysprofMarksViewClass *klass)
+sysprof_marks_page_class_init (SysprofMarksPageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
- object_class->finalize = sysprof_marks_view_finalize;
- object_class->get_property = sysprof_marks_view_get_property;
- object_class->set_property = sysprof_marks_view_set_property;
-
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-marks-view.ui");
- gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, scroller);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, tree_view);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, duration_cell);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, duration_column);
- gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, stack);
+ SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass);
+
+ object_class->finalize = sysprof_marks_page_finalize;
+ object_class->get_property = sysprof_marks_page_get_property;
+ object_class->set_property = sysprof_marks_page_set_property;
+
+ page_class->load_async = sysprof_marks_page_load_async;
+ page_class->load_finish = sysprof_marks_page_load_finish;
+ page_class->set_hadjustment = sysprof_marks_page_set_hadjustment;
+ page_class->set_size_group = sysprof_marks_page_set_size_group;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-marks-page.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, details_box);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_cell);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_column);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, scroller);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, stack);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, tree_view);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, group);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, mark);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, time);
+ gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, message);
properties [PROP_KIND] =
g_param_spec_enum ("kind", NULL, NULL,
@@ -332,9 +490,9 @@ sysprof_marks_view_class_init (SysprofMarksViewClass *klass)
}
static void
-sysprof_marks_view_init (SysprofMarksView *self)
+sysprof_marks_page_init (SysprofMarksPage *self)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
priv->kind = SYSPROF_MARKS_MODEL_MARKS;
@@ -345,123 +503,48 @@ sysprof_marks_view_init (SysprofMarksView *self)
g_signal_connect_object (priv->tree_view,
"key-press-event",
- G_CALLBACK (sysprof_marks_view_tree_view_key_press_event_cb),
+ G_CALLBACK (sysprof_marks_page_tree_view_key_press_event_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->tree_view,
"query-tooltip",
- G_CALLBACK (sysprof_marks_view_tree_view_query_tooltip_cb),
+ G_CALLBACK (sysprof_marks_page_tree_view_query_tooltip_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (gtk_tree_view_get_selection (priv->tree_view),
"changed",
- G_CALLBACK (sysprof_marks_view_selection_changed_cb),
+ G_CALLBACK (sysprof_marks_page_selection_changed_cb),
self,
G_CONNECT_SWAPPED);
}
GtkWidget *
-sysprof_marks_view_new (void)
-{
- return g_object_new (SYSPROF_TYPE_MARKS_VIEW, NULL);
-}
-
-static void
-sysprof_marks_view_load_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- g_autoptr(SysprofMarksModel) model = NULL;
- g_autoptr(GError) error = NULL;
- g_autoptr(GTask) task = user_data;
- SysprofMarksViewPrivate *priv;
- SysprofCaptureReader *reader;
- SysprofMarksView *self;
-
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (G_IS_TASK (task));
-
- self = g_task_get_source_object (task);
- priv = sysprof_marks_view_get_instance_private (self);
-
- if (!(model = sysprof_marks_model_new_finish (result, &error)))
- {
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
-
- reader = g_task_get_task_data (task);
- g_assert (reader != NULL);
-
- priv->capture_begin_time = sysprof_capture_reader_get_start_time (reader);
- priv->capture_end_time = sysprof_capture_reader_get_end_time (reader);
-
- g_object_set (priv->duration_cell,
- "capture-begin-time", priv->capture_begin_time,
- "capture-end-time", priv->capture_end_time,
- "zoom-manager", priv->zoom_manager,
- NULL);
-
- gtk_tree_view_set_model (priv->tree_view, GTK_TREE_MODEL (model));
-
- if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL) == 0)
- gtk_stack_set_visible_child_name (priv->stack, "empty-state");
- else
- gtk_stack_set_visible_child_name (priv->stack, "marks");
-
- g_task_return_boolean (task, TRUE);
-}
-
-void
-sysprof_marks_view_load_async (SysprofMarksView *self,
- SysprofCaptureReader *reader,
- SysprofSelection *selection,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+sysprof_marks_page_new (SysprofZoomManager *zoom_manager,
+ SysprofMarksModelKind kind)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
- g_autoptr(GTask) task = NULL;
+ SysprofMarksPage *self;
+ SysprofMarksPagePrivate *priv;
- g_return_if_fail (SYSPROF_IS_MARKS_VIEW (self));
- g_return_if_fail (reader != NULL);
- g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (zoom_manager), NULL);
- task = g_task_new (self, cancellable, callback, user_data);
- g_task_set_source_tag (task, sysprof_marks_view_load_async);
- g_task_set_task_data (task,
- sysprof_capture_reader_ref (reader),
- (GDestroyNotify) sysprof_capture_reader_unref);
+ self = g_object_new (SYSPROF_TYPE_MARKS_PAGE,
+ "zoom-manager", zoom_manager,
+ NULL);
+ priv = sysprof_marks_page_get_instance_private (self);
+ priv->kind = kind;
- sysprof_marks_model_new_async (reader,
- priv->kind,
- selection,
- cancellable,
- sysprof_marks_view_load_cb,
- g_steal_pointer (&task));
-}
-
-gboolean
-sysprof_marks_view_load_finish (SysprofMarksView *self,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (SYSPROF_IS_MARKS_VIEW (self), FALSE);
- g_return_val_if_fail (G_IS_TASK (result), FALSE);
-
- return g_task_propagate_boolean (G_TASK (result), error);
+ return GTK_WIDGET (self);
}
void
-_sysprof_marks_view_set_hadjustment (SysprofMarksView *self,
+_sysprof_marks_page_set_hadjustment (SysprofMarksPage *self,
GtkAdjustment *hadjustment)
{
- SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self);
+ SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self);
- g_return_if_fail (SYSPROF_IS_MARKS_VIEW (self));
+ g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self));
g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment);
diff --git a/src/libsysprof-ui/sysprof-cpu-visualizer-row.h b/src/libsysprof-ui/sysprof-marks-page.h
similarity index 55%
rename from src/libsysprof-ui/sysprof-cpu-visualizer-row.h
rename to src/libsysprof-ui/sysprof-marks-page.h
index c8293c4..5070b1a 100644
--- a/src/libsysprof-ui/sysprof-cpu-visualizer-row.h
+++ b/src/libsysprof-ui/sysprof-marks-page.h
@@ -1,6 +1,6 @@
-/* sysprof-cpu-visualizer-row.h
+/* sysprof-marks-page.h
*
- * Copyright 2016-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2019 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
@@ -20,22 +20,25 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
+#include "sysprof-marks-model.h"
+#include "sysprof-page.h"
+#include "sysprof-zoom-manager.h"
-#include "sysprof-line-visualizer-row.h"
+G_BEGIN_DECLS
-#include "sysprof-version-macros.h"
+#define SYSPROF_TYPE_MARKS_PAGE (sysprof_marks_page_get_type())
-G_BEGIN_DECLS
+G_DECLARE_DERIVABLE_TYPE (SysprofMarksPage, sysprof_marks_page, SYSPROF, MARKS_PAGE, SysprofPage)
-#define SYSPROF_TYPE_CPU_VISUALIZER_ROW (sysprof_cpu_visualizer_row_get_type())
+struct _SysprofMarksPageClass
+{
+ SysprofPageClass parent_class;
-SYSPROF_AVAILABLE_IN_ALL
-G_DECLARE_FINAL_TYPE (SysprofCpuVisualizerRow, sysprof_cpu_visualizer_row, SYSPROF, CPU_VISUALIZER_ROW,
SysprofLineVisualizerRow)
+ /*< private >*/
+ gpointer _reserved[16];
+};
-SYSPROF_AVAILABLE_IN_ALL
-GtkWidget *sysprof_cpu_visualizer_row_new (void);
+GtkWidget *sysprof_marks_page_new (SysprofZoomManager *zoom_manager,
+ SysprofMarksModelKind kind);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-marks-page.ui b/src/libsysprof-ui/sysprof-marks-page.ui
new file mode 100644
index 0000000..a0a8491
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-marks-page.ui
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="SysprofMarksPage" parent="SysprofPage">
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="homogeneous">false</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox" id="details_box">
+ <property name="orientation">vertical</property>
+ <property name="margin">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Details</property>
+ <property name="xalign">0</property>
+ <property name="visible">true</property>
+ <property name="margin-bottom">6</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="hexpand">false</property>
+ <property name="vexpand">true</property>
+ <property name="margin-start">6</property>
+ <property name="visible">true</property>
+ <property name="column-spacing">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Group</property>
+ <property name="visible">true</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">0</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Mark</property>
+ <property name="visible">true</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">1</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Time</property>
+ <property name="visible">true</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">2</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Duration</property>
+ <property name="visible">true</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">3</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Message</property>
+ <property name="visible">true</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">4</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="group">
+ <property name="visible">true</property>
+ <property name="xalign">0</property>
+ <property name="wrap">true</property>
+ </object>
+ <packing>
+ <property name="top-attach">0</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="mark">
+ <property name="visible">true</property>
+ <property name="xalign">0</property>
+ <property name="wrap">true</property>
+ </object>
+ <packing>
+ <property name="top-attach">1</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="time">
+ <property name="visible">true</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top-attach">2</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="duration">
+ <property name="visible">true</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top-attach">3</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hexpand">true</property>
+ <property name="shadow-type">in</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTextView" id="message">
+ <property name="editable">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="top-attach">4</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroller">
+ <property name="hscrollbar-policy">external</property>
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="tree_view">
+ <property name="headers-visible">false</property>
+ <property name="enable-grid-lines">horizontal</property>
+ <property name="has-tooltip">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="duration_column">
+ <property name="title" translatable="yes">Duration</property>
+ <property name="expand">true</property>
+ <child>
+ <object class="SysprofCellRendererDuration" id="duration_cell">
+ <property name="xalign">0</property>
+ <property name="ypad">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">5</attribute>
+ <attribute name="begin-time">2</attribute>
+ <attribute name="end-time">3</attribute>
+ </attributes>
+ <cell-packing>
+ <property name="expand">true</property>
+ </cell-packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">marks</property>
+ </packing>
+ </child>
+ <child>
+ <object class="DzlEmptyState">
+ <property name="icon-name">computer-fail-symbolic</property>
+ <property name="title" translatable="yes">No Timings Available</property>
+ <property name="subtitle" translatable="yes">No timing data was found for the current
selection</property>
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="name">empty-state</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libsysprof-ui/sysprof-memory-aid.h b/src/libsysprof-ui/sysprof-memory-aid.h
index 424bd65..d33a1d8 100644
--- a/src/libsysprof-ui/sysprof-memory-aid.h
+++ b/src/libsysprof-ui/sysprof-memory-aid.h
@@ -26,10 +26,8 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_MEMORY_AID (sysprof_memory_aid_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofMemoryAid, sysprof_memory_aid, SYSPROF, MEMORY_AID, SysprofAid)
-SYSPROF_AVAILABLE_IN_ALL
SysprofAid *sysprof_memory_aid_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-model-filter.h b/src/libsysprof-ui/sysprof-model-filter.h
index ddbc2b9..a1b97bd 100644
--- a/src/libsysprof-ui/sysprof-model-filter.h
+++ b/src/libsysprof-ui/sysprof-model-filter.h
@@ -33,7 +33,7 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_MODEL_FILTER (sysprof_model_filter_get_type())
typedef gboolean (*SysprofModelFilterFunc) (GObject *object,
- gpointer user_data);
+ gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofModelFilter, sysprof_model_filter, SYSPROF, MODEL_FILTER, GObject)
diff --git a/src/libsysprof-ui/sysprof-notebook.h b/src/libsysprof-ui/sysprof-notebook.h
index 2af9d86..676f4f5 100644
--- a/src/libsysprof-ui/sysprof-notebook.h
+++ b/src/libsysprof-ui/sysprof-notebook.h
@@ -20,6 +20,10 @@
#pragma once
+#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
+# error "Only <sysprof-ui.h> can be included directly."
+#endif
+
#include <gtk/gtk.h>
#include <sysprof.h>
diff --git a/src/libsysprof-ui/sysprof-page.c b/src/libsysprof-ui/sysprof-page.c
new file mode 100644
index 0000000..af5f927
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-page.c
@@ -0,0 +1,235 @@
+/* sysprof-page.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-page"
+
+#include "config.h"
+
+#include "sysprof-page.h"
+
+typedef struct
+{
+ gchar *title;
+} SysprofPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofPage, sysprof_page, GTK_TYPE_BIN)
+
+enum {
+ PROP_0,
+ PROP_TITLE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/**
+ * sysprof_page_new:
+ *
+ * Create a new #SysprofPage.
+ *
+ * Returns: (transfer full) (type SysprofPage): a newly created #SysprofPage
+ */
+GtkWidget *
+sysprof_page_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_PAGE, NULL);
+}
+
+const gchar *
+sysprof_page_get_title (SysprofPage *self)
+{
+ SysprofPagePrivate *priv = sysprof_page_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_PAGE (self), NULL);
+
+ return priv->title;
+}
+
+void
+sysprof_page_set_title (SysprofPage *self,
+ const gchar *title)
+{
+ SysprofPagePrivate *priv = sysprof_page_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_PAGE (self));
+
+ if (g_strcmp0 (priv->title, title) != 0)
+ {
+ g_free (priv->title);
+ priv->title = g_strdup (title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ }
+}
+
+static void
+sysprof_page_real_load_async (SysprofPage *self,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *condition,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data,
+ sysprof_page_load_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Operation not supported");
+}
+
+static gboolean
+sysprof_page_real_load_finish (SysprofPage *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+sysprof_page_finalize (GObject *object)
+{
+ SysprofPage *self = (SysprofPage *)object;
+ SysprofPagePrivate *priv = sysprof_page_get_instance_private (self);
+
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (sysprof_page_parent_class)->finalize (object);
+}
+
+static void
+sysprof_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofPage *self = SYSPROF_PAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, sysprof_page_get_title (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofPage *self = SYSPROF_PAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_TITLE:
+ sysprof_page_set_title (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_page_class_init (SysprofPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = sysprof_page_finalize;
+ object_class->get_property = sysprof_page_get_property;
+ object_class->set_property = sysprof_page_set_property;
+
+ klass->load_async = sysprof_page_real_load_async;
+ klass->load_finish = sysprof_page_real_load_finish;
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title for the page",
+ 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_page_init (SysprofPage *self)
+{
+}
+
+/**
+ * sysprof_page_load_async:
+ * @condition: (nullable): a #sysprofCaptureCondition or %NULL
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ *
+ * Since: 3.34
+ */
+void
+sysprof_page_load_async (SysprofPage *self,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *condition,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (SYSPROF_IS_PAGE (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ SYSPROF_PAGE_GET_CLASS (self)->load_async (self, reader, selection, condition, cancellable, callback,
user_data);
+}
+
+gboolean
+sysprof_page_load_finish (SysprofPage *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (SYSPROF_IS_PAGE (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return SYSPROF_PAGE_GET_CLASS (self)->load_finish (self, result, error);
+}
+
+void
+sysprof_page_set_size_group (SysprofPage *self,
+ GtkSizeGroup *size_group)
+{
+ g_return_if_fail (SYSPROF_IS_PAGE (self));
+ g_return_if_fail (!size_group || GTK_IS_SIZE_GROUP (size_group));
+
+ if (SYSPROF_PAGE_GET_CLASS (self)->set_size_group)
+ SYSPROF_PAGE_GET_CLASS (self)->set_size_group (self, size_group);
+}
+
+void
+sysprof_page_set_hadjustment (SysprofPage *self,
+ GtkAdjustment *hadjustment)
+{
+ g_return_if_fail (SYSPROF_IS_PAGE (self));
+ g_return_if_fail (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment));
+
+ if (SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment)
+ SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment (self, hadjustment);
+}
diff --git a/src/libsysprof-ui/sysprof-page.h b/src/libsysprof-ui/sysprof-page.h
new file mode 100644
index 0000000..783d998
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-page.h
@@ -0,0 +1,86 @@
+/* sysprof-page.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
+# error "Only <sysprof-ui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <sysprof.h>
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_PAGE (sysprof_page_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (SysprofPage, sysprof_page, SYSPROF, PAGE, GtkBin)
+
+struct _SysprofPageClass
+{
+ GtkBinClass parent_class;
+
+ void (*load_async) (SysprofPage *self,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *condition,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*load_finish) (SysprofPage *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*set_hadjustment) (SysprofPage *self,
+ GtkAdjustment *hadjustment);
+ void (*set_size_group) (SysprofPage *self,
+ GtkSizeGroup *size_group);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+SYSPROF_AVAILABLE_IN_ALL
+GtkWidget *sysprof_page_new (void);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_page_load_async (SysprofPage *self,
+ SysprofCaptureReader *reader,
+ SysprofSelection *selection,
+ SysprofCaptureCondition *condition,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+SYSPROF_AVAILABLE_IN_ALL
+gboolean sysprof_page_load_finish (SysprofPage *self,
+ GAsyncResult *result,
+ GError **error);
+SYSPROF_AVAILABLE_IN_ALL
+const gchar *sysprof_page_get_title (SysprofPage *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_page_set_title (SysprofPage *self,
+ const gchar *title);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_page_set_hadjustment (SysprofPage *self,
+ GtkAdjustment *hadjustment);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_page_set_size_group (SysprofPage *self,
+ GtkSizeGroup *size_group);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.c b/src/libsysprof-ui/sysprof-profiler-assistant.c
index e3db2bf..a8a6622 100644
--- a/src/libsysprof-ui/sysprof-profiler-assistant.c
+++ b/src/libsysprof-ui/sysprof-profiler-assistant.c
@@ -27,13 +27,17 @@
#include "sysprof-platform.h"
#include "sysprof-aid-icon.h"
-#include "sysprof-cpu-aid.h"
#include "sysprof-environ-editor.h"
#include "sysprof-profiler-assistant.h"
-#include "sysprof-proxy-aid.h"
#include "sysprof-process-model-row.h"
#include "sysprof-ui-private.h"
+#include "sysprof-battery-aid.h"
+#include "sysprof-callgraph-aid.h"
+#include "sysprof-cpu-aid.h"
+#include "sysprof-memory-aid.h"
+#include "sysprof-proxy-aid.h"
+
struct _SysprofProfilerAssistant
{
GtkBin parent_instance;
@@ -298,9 +302,12 @@ sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass)
gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, inherit_switch);
g_type_ensure (SYSPROF_TYPE_AID_ICON);
+ g_type_ensure (SYSPROF_TYPE_BATTERY_AID);
g_type_ensure (SYSPROF_TYPE_CPU_AID);
+ g_type_ensure (SYSPROF_TYPE_MEMORY_AID);
g_type_ensure (SYSPROF_TYPE_PROXY_AID);
g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR);
+ g_type_ensure (SYSPROF_TYPE_CALLGRAPH_AID);
}
static void
diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.h b/src/libsysprof-ui/sysprof-profiler-assistant.h
index 8bc6df5..f57498a 100644
--- a/src/libsysprof-ui/sysprof-profiler-assistant.h
+++ b/src/libsysprof-ui/sysprof-profiler-assistant.h
@@ -28,10 +28,8 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_PROFILER_ASSISTANT (sysprof_profiler_assistant_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofProfilerAssistant, sysprof_profiler_assistant, SYSPROF, PROFILER_ASSISTANT,
GtkBin)
-SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_profiler_assistant_new (void);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui
index 7a08003..2a15f30 100644
--- a/src/libsysprof-ui/sysprof-profiler-assistant.ui
+++ b/src/libsysprof-ui/sysprof-profiler-assistant.ui
@@ -43,13 +43,7 @@
</object>
</child>
</object>
- <object class="SysprofAid" id="battery_aid">
- <property name="display-name">Battery</property>
- <property name="icon-name">battery-low-charging-symbolic</property>
- <child>
- <object class="SysprofBatterySource"/>
- </child>
- </object>
+ <object class="SysprofBatteryAid" id="battery_aid"/>
<template class="SysprofProfilerAssistant" parent="GtkBin">
<child>
<object class="GtkScrolledWindow">
diff --git a/src/libsysprof-ui/sysprof-proxy-aid.h b/src/libsysprof-ui/sysprof-proxy-aid.h
index 4f6a79d..218ba11 100644
--- a/src/libsysprof-ui/sysprof-proxy-aid.h
+++ b/src/libsysprof-ui/sysprof-proxy-aid.h
@@ -36,13 +36,10 @@ struct _SysprofProxyAidClass
gpointer _reserved[8];
};
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_proxy_aid_set_bus_type (SysprofProxyAid *self,
GBusType bus_type);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_proxy_aid_set_bus_name (SysprofProxyAid *self,
const gchar *bus_name);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_proxy_aid_set_object_path (SysprofProxyAid *self,
const gchar *obj_path);
diff --git a/src/libsysprof-ui/sysprof-recording-state-view.h
b/src/libsysprof-ui/sysprof-recording-state-view.h
index 82d3b8e..ffc149c 100644
--- a/src/libsysprof-ui/sysprof-recording-state-view.h
+++ b/src/libsysprof-ui/sysprof-recording-state-view.h
@@ -20,10 +20,6 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
#include <sysprof.h>
@@ -31,7 +27,6 @@ G_BEGIN_DECLS
#define SYSPROF_TYPE_RECORDING_STATE_VIEW (sysprof_recording_state_view_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofRecordingStateView, sysprof_recording_state_view, SYSPROF,
RECORDING_STATE_VIEW, GtkBin)
struct _SysprofRecordingStateViewClass
@@ -41,9 +36,7 @@ struct _SysprofRecordingStateViewClass
gpointer padding[4];
};
-SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_recording_state_view_new (void);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_recording_state_view_set_profiler (SysprofRecordingStateView *self,
SysprofProfiler *profiler);
diff --git a/src/libsysprof-ui/sysprof-scrollmap.c b/src/libsysprof-ui/sysprof-scrollmap.c
new file mode 100644
index 0000000..41d4478
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-scrollmap.c
@@ -0,0 +1,306 @@
+/* sysprof-scrollmap.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-scrollmap"
+
+#include "config.h"
+
+#include "sysprof-scrollmap.h"
+
+#define BOX_SIZE 4
+
+struct _SysprofScrollmap
+{
+ GtkScrollbar parent_instance;
+
+ gint64 begin_time;
+ gint64 end_time;
+
+ GArray *timings;
+ GArray *buckets;
+ GCancellable *cancellable;
+
+ gint most;
+};
+
+typedef struct
+{
+ gint64 begin_time;
+ gint64 end_time;
+ GArray *timings;
+ gint width;
+ gint height;
+} Recalculate;
+
+G_DEFINE_TYPE (SysprofScrollmap, sysprof_scrollmap, GTK_TYPE_SCROLLBAR)
+
+static void
+recalculate_free (gpointer data)
+{
+ Recalculate *state = data;
+
+ g_clear_pointer (&state->timings, g_array_unref);
+ g_slice_free (Recalculate, state);
+}
+
+static void
+sysprof_scrollmap_recalculate_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ Recalculate *state = task_data;
+ g_autoptr(GArray) buckets = NULL;
+ gint64 duration;
+ gint n_buckets;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_SCROLLMAP (source_object));
+ g_assert (state != NULL);
+ g_assert (state->timings != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ duration = state->end_time - state->begin_time;
+ n_buckets = MAX (1, state->width / (BOX_SIZE + 1));
+ buckets = g_array_sized_new (FALSE, TRUE, sizeof (gint), n_buckets);
+ g_array_set_size (buckets, n_buckets);
+
+ for (guint i = 0; i < state->timings->len; i++)
+ {
+ gint64 t = g_array_index (state->timings, gint64, i);
+ gint n;
+
+ if (t < state->begin_time || t > state->end_time)
+ continue;
+
+ n = ((t - state->begin_time) / (gdouble)duration) * n_buckets;
+
+ g_assert (n < n_buckets);
+
+ g_array_index (buckets, gint, n)++;
+ }
+
+ g_task_return_pointer (task,
+ g_steal_pointer (&buckets),
+ (GDestroyNotify) g_array_unref);
+}
+
+static void
+sysprof_scrollmap_recalculate_async (SysprofScrollmap *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ GtkAllocation alloc;
+ Recalculate state;
+
+ g_assert (SYSPROF_IS_SCROLLMAP (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_scrollmap_recalculate_async);
+
+ if (self->timings == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "The operation was cancelled");
+ return;
+ }
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ state.begin_time = self->begin_time;
+ state.end_time = self->end_time;
+ state.width = alloc.width;
+ state.height = alloc.height;
+ state.timings = g_array_ref (self->timings);
+
+ g_task_set_task_data (task,
+ g_slice_dup (Recalculate, &state),
+ recalculate_free);
+ g_task_run_in_thread (task, sysprof_scrollmap_recalculate_worker);
+}
+
+static GArray *
+sysprof_scrollmap_recalculate_finish (SysprofScrollmap *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (SYSPROF_IS_SCROLLMAP (self));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+draw_boxes (const GtkAllocation *alloc,
+ cairo_t *cr,
+ gint x,
+ gint n_boxes)
+{
+ gint y;
+
+ g_assert (cr != NULL);
+
+ y = alloc->height - BOX_SIZE;
+
+ for (gint i = 0; i < n_boxes; i++)
+ {
+ cairo_rectangle (cr, x, y, BOX_SIZE, -BOX_SIZE);
+ y -= (BOX_SIZE + 1);
+ }
+
+ cairo_fill (cr);
+}
+
+static gboolean
+sysprof_scrollmap_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ SysprofScrollmap *self = (SysprofScrollmap *)widget;
+ GtkStyleContext *style_context;
+ GtkAllocation alloc;
+ GdkRGBA color;
+ gint max_boxes;
+
+ g_assert (SYSPROF_IS_SCROLLMAP (self));
+ g_assert (cr != NULL);
+
+ if (self->buckets == NULL)
+ goto chainup;
+
+ gtk_widget_get_allocation (widget, &alloc);
+ max_boxes = alloc.height / (BOX_SIZE + 1) - 1;
+
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_get_color (style_context,
+ gtk_style_context_get_state (style_context),
+ &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ for (guint i = 0; i < self->buckets->len; i++)
+ {
+ gint n = g_array_index (self->buckets, gint, i);
+ gint x = 1 + i * (BOX_SIZE + 1);
+ gint b = max_boxes * (n / (gdouble)self->most);
+
+#if 1
+ if (n > 0)
+ b = MAX (b, 1);
+#endif
+
+ draw_boxes (&alloc, cr, x, b);
+ }
+
+chainup:
+ return GTK_WIDGET_CLASS (sysprof_scrollmap_parent_class)->draw (widget, cr);
+}
+
+static void
+sysprof_scrollmap_recalculate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofScrollmap *self = (SysprofScrollmap *)object;
+ g_autoptr(GArray) buckets = NULL;
+
+ g_assert (SYSPROF_IS_SCROLLMAP (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (user_data == NULL);
+
+ if ((buckets = sysprof_scrollmap_recalculate_finish (self, result, NULL)))
+ {
+ self->most = 0;
+
+ for (guint i = 0; i < buckets->len; i++)
+ {
+ gint n = g_array_index (buckets, gint, i);
+ self->most = MAX (self->most, n);
+ }
+
+ g_clear_pointer (&self->buckets, g_array_unref);
+ self->buckets = g_steal_pointer (&buckets);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+sysprof_scrollmap_finalize (GObject *object)
+{
+ SysprofScrollmap *self = (SysprofScrollmap *)object;
+
+ g_clear_pointer (&self->buckets, g_array_unref);
+ g_clear_pointer (&self->timings, g_array_unref);
+
+ G_OBJECT_CLASS (sysprof_scrollmap_parent_class)->finalize (object);
+}
+
+static void
+sysprof_scrollmap_class_init (SysprofScrollmapClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = sysprof_scrollmap_finalize;
+
+ widget_class->draw = sysprof_scrollmap_draw;
+}
+
+static void
+sysprof_scrollmap_init (SysprofScrollmap *self)
+{
+}
+
+void
+sysprof_scrollmap_set_timings (SysprofScrollmap *self,
+ GArray *timings)
+{
+ g_return_if_fail (SYSPROF_IS_SCROLLMAP (self));
+
+ if (timings != self->timings)
+ {
+ g_clear_pointer (&self->timings, g_array_unref);
+ self->timings = timings ? g_array_ref (timings) : NULL;
+ }
+}
+
+void
+sysprof_scrollmap_set_time_range (SysprofScrollmap *self,
+ gint64 begin_time,
+ gint64 end_time)
+{
+ g_return_if_fail (SYSPROF_IS_SCROLLMAP (self));
+
+ self->begin_time = begin_time;
+ self->end_time = end_time;
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ sysprof_scrollmap_recalculate_async (self,
+ self->cancellable,
+ sysprof_scrollmap_recalculate_cb,
+ NULL);
+}
diff --git a/src/libsysprof-ui/sysprof-scrollmap.h b/src/libsysprof-ui/sysprof-scrollmap.h
new file mode 100644
index 0000000..e7884aa
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-scrollmap.h
@@ -0,0 +1,37 @@
+/* sysprof-scrollmap.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_SCROLLMAP (sysprof_scrollmap_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofScrollmap, sysprof_scrollmap, SYSPROF, SCROLLMAP, GtkScrollbar)
+
+void sysprof_scrollmap_set_timings (SysprofScrollmap *self,
+ GArray *timings);
+void sysprof_scrollmap_set_time_range (SysprofScrollmap *self,
+ gint64 begin_time,
+ gint64 end_time);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-theme-manager.c b/src/libsysprof-ui/sysprof-theme-manager.c
index b1ebfe5..1094893 100644
--- a/src/libsysprof-ui/sysprof-theme-manager.c
+++ b/src/libsysprof-ui/sysprof-theme-manager.c
@@ -202,9 +202,9 @@ sysprof_theme_manager_get_default (void)
guint
sysprof_theme_manager_register_resource (SysprofThemeManager *self,
- const gchar *theme_name,
- const gchar *variant,
- const gchar *resource)
+ const gchar *theme_name,
+ const gchar *variant,
+ const gchar *resource)
{
ThemeResource *theme_resource;
static guint counter;
diff --git a/src/libsysprof-ui/sysprof-time-visualizer.c b/src/libsysprof-ui/sysprof-time-visualizer.c
new file mode 100644
index 0000000..cb1d01a
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-time-visualizer.c
@@ -0,0 +1,543 @@
+/* sysprof-time-visualizer.c
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-time-visualizer"
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysprof.h>
+
+#include "pointcache.h"
+#include "sysprof-time-visualizer.h"
+
+typedef struct
+{
+ /*
+ * Our reader as assigned by the visualizer system.
+ */
+ SysprofCaptureReader *reader;
+
+ /*
+ * An array of LineInfo which contains information about the counters
+ * we need to render.
+ */
+ GArray *lines;
+
+ /*
+ * This is our set of cached points to render. Once it is assigned here,
+ * it is immutable (and therefore may be shared with worker processes
+ * that are rendering the points).
+ */
+ PointCache *cache;
+
+ /*
+ * If we have a new counter discovered or the reader is set, we might
+ * want to delay loading until we return to the main loop. This can
+ * help us avoid doing duplicate work.
+ */
+ guint queued_load;
+} SysprofTimeVisualizerPrivate;
+
+typedef struct
+{
+ guint id;
+ gdouble line_width;
+ GdkRGBA rgba;
+ guint use_default_style : 1;
+ guint use_dash : 1;
+} LineInfo;
+
+typedef struct
+{
+ SysprofCaptureCursor *cursor;
+ GArray *lines;
+ PointCache *cache;
+ gint64 begin_time;
+ gint64 end_time;
+} LoadData;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofTimeVisualizer, sysprof_time_visualizer, SYSPROF_TYPE_VISUALIZER)
+
+static void sysprof_time_visualizer_load_data_async (SysprofTimeVisualizer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static PointCache *sysprof_time_visualizer_load_data_finish (SysprofTimeVisualizer *self,
+ GAsyncResult *result,
+ GError **error);
+
+static gdouble dashes[] = { 1.0, 2.0 };
+
+static void
+load_data_free (gpointer data)
+{
+ LoadData *load = data;
+
+ if (load != NULL)
+ {
+ g_clear_pointer (&load->lines, g_array_unref);
+ g_clear_pointer (&load->cursor, sysprof_capture_cursor_unref);
+ g_clear_pointer (&load->cache, point_cache_unref);
+ g_slice_free (LoadData, load);
+ }
+}
+
+static GArray *
+copy_array (GArray *ar)
+{
+ GArray *ret;
+
+ ret = g_array_sized_new (FALSE, FALSE, g_array_get_element_size (ar), ar->len);
+ g_array_set_size (ret, ar->len);
+ memcpy (ret->data, ar->data, ar->len * g_array_get_element_size (ret));
+
+ return ret;
+}
+
+static gboolean
+sysprof_time_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)widget;
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+ GtkStyleContext *style_context;
+ GtkStateFlags flags;
+ GtkAllocation alloc;
+ GdkRectangle clip;
+ GdkRGBA foreground;
+ gboolean ret;
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (widget));
+ g_assert (cr != NULL);
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ ret = GTK_WIDGET_CLASS (sysprof_time_visualizer_parent_class)->draw (widget, cr);
+
+ if (priv->cache == NULL)
+ return ret;
+
+ if (!gdk_cairo_get_clip_rectangle (cr, &clip))
+ return ret;
+
+ style_context = gtk_widget_get_style_context (widget);
+ flags = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_color (style_context, flags, &foreground);
+
+ gdk_cairo_set_source_rgba (cr, &foreground);
+
+ for (guint line = 0; line < priv->lines->len; line++)
+ {
+ g_autofree SysprofVisualizerAbsolutePoint *points = NULL;
+ const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line);
+ const Point *fpoints;
+ guint n_fpoints = 0;
+
+ fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints);
+
+ if (n_fpoints > 0)
+ {
+ guint last_x = G_MAXUINT;
+
+ points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints);
+
+ sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self),
+ (const SysprofVisualizerRelativePoint *)fpoints,
+ n_fpoints,
+ points,
+ n_fpoints);
+
+ cairo_set_line_width (cr, 1.0);
+
+ for (guint i = 0; i < n_fpoints; i++)
+ {
+ if ((guint)points[i].x != last_x)
+ last_x = (guint)points[i].x;
+ else
+ continue;
+
+ cairo_move_to (cr, (guint)points[i].x + .5, alloc.height / 3);
+ cairo_line_to (cr, (guint)points[i].x + .5, alloc.height / 3 * 2);
+ }
+
+ if (line_info->use_dash)
+ cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0);
+
+ cairo_stroke (cr);
+ }
+ }
+
+ return ret;
+}
+
+static void
+sysprof_time_visualizer_load_data_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)object;
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+ g_autoptr(GError) error = NULL;
+ g_autoptr(PointCache) cache = NULL;
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ cache = sysprof_time_visualizer_load_data_finish (self, result, &error);
+
+ if (cache == NULL)
+ {
+ g_warning ("%s", error->message);
+ return;
+ }
+
+ g_clear_pointer (&priv->cache, point_cache_unref);
+ priv->cache = g_steal_pointer (&cache);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static gboolean
+sysprof_time_visualizer_do_reload (gpointer data)
+{
+ SysprofTimeVisualizer *self = data;
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ priv->queued_load = 0;
+ if (priv->reader != NULL)
+ sysprof_time_visualizer_load_data_async (self,
+ NULL,
+ sysprof_time_visualizer_load_data_cb,
+ NULL);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+sysprof_time_visualizer_queue_reload (SysprofTimeVisualizer *self)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ if (priv->queued_load == 0)
+ priv->queued_load =
+ gdk_threads_add_idle_full (G_PRIORITY_LOW,
+ sysprof_time_visualizer_do_reload,
+ self,
+ NULL);
+}
+
+static void
+sysprof_time_visualizer_set_reader (SysprofVisualizer *row,
+ SysprofCaptureReader *reader)
+{
+ SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)row;
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ if (priv->reader != reader)
+ {
+ if (priv->reader != NULL)
+ {
+ sysprof_capture_reader_unref (priv->reader);
+ priv->reader = NULL;
+ }
+
+ if (reader != NULL)
+ priv->reader = sysprof_capture_reader_ref (reader);
+
+ sysprof_time_visualizer_queue_reload (self);
+ }
+}
+
+static void
+sysprof_time_visualizer_finalize (GObject *object)
+{
+ SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)object;
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_clear_pointer (&priv->lines, g_array_unref);
+ g_clear_pointer (&priv->cache, point_cache_unref);
+ g_clear_pointer (&priv->reader, sysprof_capture_reader_unref);
+
+ if (priv->queued_load != 0)
+ {
+ g_source_remove (priv->queued_load);
+ priv->queued_load = 0;
+ }
+
+ G_OBJECT_CLASS (sysprof_time_visualizer_parent_class)->finalize (object);
+}
+
+static void
+sysprof_time_visualizer_class_init (SysprofTimeVisualizerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
+
+ object_class->finalize = sysprof_time_visualizer_finalize;
+
+ widget_class->draw = sysprof_time_visualizer_draw;
+
+ visualizer_class->set_reader = sysprof_time_visualizer_set_reader;
+}
+
+static void
+sysprof_time_visualizer_init (SysprofTimeVisualizer *self)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo));
+}
+
+void
+sysprof_time_visualizer_add_counter (SysprofTimeVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+ LineInfo line_info = {0};
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+ g_assert (priv->lines != NULL);
+
+ line_info.id = counter_id;
+ line_info.line_width = 1.0;
+
+ if (color != NULL)
+ {
+ line_info.rgba = *color;
+ line_info.use_default_style = FALSE;
+ }
+ else
+ {
+ line_info.use_default_style = TRUE;
+ }
+
+ g_array_append_val (priv->lines, line_info);
+
+ if (SYSPROF_TIME_VISUALIZER_GET_CLASS (self)->counter_added)
+ SYSPROF_TIME_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id);
+
+ sysprof_time_visualizer_queue_reload (self);
+}
+
+void
+sysprof_time_visualizer_clear (SysprofTimeVisualizer *self)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ if (priv->lines->len > 0)
+ g_array_remove_range (priv->lines, 0, priv->lines->len);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static inline gboolean
+contains_id (GArray *ar,
+ guint id)
+{
+ for (guint i = 0; i < ar->len; i++)
+ {
+ const LineInfo *info = &g_array_index (ar, LineInfo, i);
+
+ if (info->id == id)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline gdouble
+calc_x (gint64 lower,
+ gint64 upper,
+ gint64 value)
+{
+ return (gdouble)(value - lower) / (gdouble)(upper - lower);
+}
+
+static gboolean
+sysprof_time_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ LoadData *load = user_data;
+
+ g_assert (frame != NULL);
+ g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET ||
+ frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
+ g_assert (load != NULL);
+
+ if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET)
+ {
+ const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame;
+ gdouble x = calc_x (load->begin_time, load->end_time, frame->time);
+
+ for (guint i = 0; i < set->n_values; i++)
+ {
+ const SysprofCaptureCounterValues *group = &set->values[i];
+
+ for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++)
+ {
+ guint counter_id = group->ids[j];
+
+ if (counter_id != 0 && contains_id (load->lines, counter_id))
+ point_cache_add_point_to_set (load->cache, counter_id, x, 0);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+sysprof_time_visualizer_load_data_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ LoadData *load = task_data;
+ g_autoptr(GArray) counter_ids = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (source_object));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ counter_ids = g_array_new (FALSE, FALSE, sizeof (guint));
+
+ for (guint i = 0; i < load->lines->len; i++)
+ {
+ const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i);
+ g_array_append_val (counter_ids, line_info->id);
+ }
+
+ sysprof_capture_cursor_add_condition (load->cursor,
+ sysprof_capture_condition_new_where_counter_in (counter_ids->len,
+ (guint
*)(gpointer)counter_ids->data));
+ sysprof_capture_cursor_foreach (load->cursor, sysprof_time_visualizer_load_data_frame_cb, load);
+ g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref);
+}
+
+static void
+sysprof_time_visualizer_load_data_async (SysprofTimeVisualizer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+ LoadData *load;
+
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_priority (task, G_PRIORITY_LOW);
+ g_task_set_source_tag (task, sysprof_time_visualizer_load_data_async);
+
+ if (priv->reader == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "No data loaded");
+ return;
+ }
+
+ load = g_slice_new0 (LoadData);
+ load->cache = point_cache_new ();
+ load->begin_time = sysprof_capture_reader_get_start_time (priv->reader);
+ load->end_time = sysprof_capture_reader_get_end_time (priv->reader);
+ load->cursor = sysprof_capture_cursor_new (priv->reader);
+ load->lines = copy_array (priv->lines);
+
+ for (guint i = 0; i < load->lines->len; i++)
+ {
+ const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i);
+
+ point_cache_add_set (load->cache, line_info->id);
+ }
+
+ g_task_set_task_data (task, load, load_data_free);
+ g_task_run_in_thread (task, sysprof_time_visualizer_load_data_worker);
+}
+
+static PointCache *
+sysprof_time_visualizer_load_data_finish (SysprofTimeVisualizer *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (SYSPROF_IS_TIME_VISUALIZER (self));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+void
+sysprof_time_visualizer_set_line_width (SysprofTimeVisualizer *self,
+ guint counter_id,
+ gdouble width)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ for (guint i = 0; i < priv->lines->len; i++)
+ {
+ LineInfo *info = &g_array_index (priv->lines, LineInfo, i);
+
+ if (info->id == counter_id)
+ {
+ info->line_width = width;
+ sysprof_time_visualizer_queue_reload (self);
+ break;
+ }
+ }
+}
+
+void
+sysprof_time_visualizer_set_dash (SysprofTimeVisualizer *self,
+ guint counter_id,
+ gboolean use_dash)
+{
+ SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self));
+
+ for (guint i = 0; i < priv->lines->len; i++)
+ {
+ LineInfo *info = &g_array_index (priv->lines, LineInfo, i);
+
+ if (info->id == counter_id)
+ {
+ info->use_dash = !!use_dash;
+ sysprof_time_visualizer_queue_reload (self);
+ break;
+ }
+ }
+}
diff --git a/src/libsysprof-ui/sysprof-time-visualizer.h b/src/libsysprof-ui/sysprof-time-visualizer.h
new file mode 100644
index 0000000..1b9160a
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-time-visualizer.h
@@ -0,0 +1,54 @@
+/* sysprof-time-visualizer.h
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * 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
+ */
+
+#pragma once
+
+#include "sysprof-visualizer.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_TIME_VISUALIZER (sysprof_time_visualizer_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (SysprofTimeVisualizer, sysprof_time_visualizer, SYSPROF, TIME_VISUALIZER,
SysprofVisualizer)
+
+struct _SysprofTimeVisualizerClass
+{
+ SysprofVisualizerClass parent_class;
+
+ void (*counter_added) (SysprofTimeVisualizer *self,
+ guint counter_id);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+GtkWidget *sysprof_time_visualizer_new (void);
+void sysprof_time_visualizer_clear (SysprofTimeVisualizer *self);
+void sysprof_time_visualizer_add_counter (SysprofTimeVisualizer *self,
+ guint counter_id,
+ const GdkRGBA *color);
+void sysprof_time_visualizer_set_line_width (SysprofTimeVisualizer *self,
+ guint counter_id,
+ gdouble width);
+void sysprof_time_visualizer_set_dash (SysprofTimeVisualizer *self,
+ guint counter_id,
+ gboolean use_dash);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-ui-private.h b/src/libsysprof-ui/sysprof-ui-private.h
index 3895683..aef6e8a 100644
--- a/src/libsysprof-ui/sysprof-ui-private.h
+++ b/src/libsysprof-ui/sysprof-ui-private.h
@@ -20,42 +20,18 @@
#pragma once
-#include "sysprof-callgraph-view.h"
+#include "sysprof-callgraph-page.h"
#include "sysprof-display.h"
-#include "sysprof-marks-view.h"
#include "sysprof-profiler-assistant.h"
-#include "sysprof-visualizer-view.h"
G_BEGIN_DECLS
-typedef struct
-{
- gchar *name;
- guint64 count;
- gint64 max;
- gint64 min;
- gint64 avg;
- guint64 avg_count;
-} SysprofMarkStat;
-
-SysprofMarkStat *_sysprof_mark_stat_new (const gchar *name);
-void _sysprof_mark_stat_free (SysprofMarkStat *self);
-void _sysprof_marks_view_set_hadjustment (SysprofMarksView *self,
- GtkAdjustment *hadjustment);
-void _sysprof_visualizer_view_set_hadjustment (SysprofVisualizerView *self,
- GtkAdjustment *hadjustment);
-void _sysprof_rounded_rectangle (cairo_t *cr,
- const GdkRectangle *rect,
- gint x_radius,
- gint y_radius);
-gchar *_sysprof_format_duration (gint64 duration);
-void _sysprof_callgraph_view_set_failed (SysprofCallgraphView *self);
-void _sysprof_callgraph_view_set_loading (SysprofCallgraphView *self,
- gboolean loading);
-void _sysprof_display_focus_record (SysprofDisplay *self);
-void _sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self);
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMarkStat, _sysprof_mark_stat_free)
+void _sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self);
+void _sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self,
+ gboolean loading);
+void _sysprof_display_focus_record (SysprofDisplay *self);
+void _sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self);
+gchar *_sysprof_format_duration (gint64 duration);
#if !GLIB_CHECK_VERSION(2, 56, 0)
# define g_clear_weak_pointer(ptr) \
diff --git a/src/libsysprof-ui/sysprof-ui.h b/src/libsysprof-ui/sysprof-ui.h
index 4f020d5..c63a712 100644
--- a/src/libsysprof-ui/sysprof-ui.h
+++ b/src/libsysprof-ui/sysprof-ui.h
@@ -27,31 +27,14 @@ G_BEGIN_DECLS
#define SYSPROF_UI_INSIDE
-# include "sysprof-aid.h"
-# include "sysprof-callgraph-view.h"
-# include "sysprof-capture-view.h"
-# include "sysprof-callgraph-aid.h"
-# include "sysprof-cell-renderer-percent.h"
# include "sysprof-check.h"
-# include "sysprof-cpu-aid.h"
-# include "sysprof-cpu-visualizer-row.h"
# include "sysprof-display.h"
-# include "sysprof-empty-state-view.h"
-# include "sysprof-failed-state-view.h"
-# include "sysprof-line-visualizer-row.h"
-# include "sysprof-marks-model.h"
-# include "sysprof-marks-view.h"
-# include "sysprof-mark-visualizer-row.h"
-# include "sysprof-memory-aid.h"
# include "sysprof-model-filter.h"
# include "sysprof-notebook.h"
+# include "sysprof-page.h"
# include "sysprof-process-model-row.h"
-# include "sysprof-profiler-assistant.h"
-# include "sysprof-proxy-aid.h"
-# include "sysprof-recording-state-view.h"
-# include "sysprof-visualizer-row.h"
-# include "sysprof-visualizer-view.h"
-# include "sysprof-zoom-manager.h"
+# include "sysprof-visualizer-group.h"
+# include "sysprof-visualizer.h"
#undef SYSPROF_UI_INSIDE
diff --git a/src/libsysprof-ui/sysprof-visualizer-group-header.c
b/src/libsysprof-ui/sysprof-visualizer-group-header.c
new file mode 100644
index 0000000..9fc1a43
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer-group-header.c
@@ -0,0 +1,159 @@
+/* sysprof-visualizer-group-header.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-visualizer-group-header"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-visualizer.h"
+#include "sysprof-visualizer-group.h"
+#include "sysprof-visualizer-group-header.h"
+
+struct _SysprofVisualizerGroupHeader
+{
+ GtkListBoxRow parent_instance;
+
+ GtkBox *box;
+};
+
+G_DEFINE_TYPE (SysprofVisualizerGroupHeader, sysprof_visualizer_group_header, GTK_TYPE_LIST_BOX_ROW)
+
+static void
+sysprof_visualizer_group_header_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (sysprof_visualizer_group_header_parent_class)->finalize (object);
+}
+
+static void
+sysprof_visualizer_group_header_class_init (SysprofVisualizerGroupHeaderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = sysprof_visualizer_group_header_finalize;
+}
+
+static void
+sysprof_visualizer_group_header_init (SysprofVisualizerGroupHeader *self)
+{
+ self->box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->box));
+}
+
+void
+_sysprof_visualizer_group_header_add_row (SysprofVisualizerGroupHeader *self,
+ guint position,
+ const gchar *title,
+ GMenuModel *menu,
+ GtkWidget *widget)
+{
+ GtkBox *box;
+ GtkWidget *group;
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP_HEADER (self));
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (widget));
+ g_return_if_fail (!menu || G_IS_MENU_MODEL (menu));
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "spacing", 6,
+ "visible", TRUE,
+ NULL);
+ g_object_bind_property (widget, "visible", box, "visible", G_BINDING_SYNC_CREATE);
+ gtk_container_add_with_properties (GTK_CONTAINER (self->box), GTK_WIDGET (box),
+ "position", position,
+ NULL);
+
+ if (title != NULL)
+ {
+ g_autoptr(GtkSizeGroup) size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+ PangoAttrList *attrs = pango_attr_list_new ();
+ GtkLabel *label;
+
+ pango_attr_list_insert (attrs, pango_attr_scale_new (0.83333));
+ label = g_object_new (GTK_TYPE_LABEL,
+ "attributes", attrs,
+ "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+ "margin", 6,
+ "hexpand", TRUE,
+ "label", title,
+ "visible", TRUE,
+ "xalign", 0.0f,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (label));
+ pango_attr_list_unref (attrs);
+
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_size_group_add_widget (size_group, GTK_WIDGET (box));
+ }
+
+ group = gtk_widget_get_ancestor (widget, SYSPROF_TYPE_VISUALIZER_GROUP);
+
+ if (position == 0 && sysprof_visualizer_group_get_has_page (SYSPROF_VISUALIZER_GROUP (group)))
+ {
+ GtkImage *image;
+
+ image = g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "view-paged-symbolic",
+ "tooltip-text", _("Select for more details"),
+ "pixel-size", 16,
+ "visible", TRUE,
+ NULL);
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (image), "dim-label");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (image));
+ }
+
+ if (menu != NULL)
+ {
+ GtkStyleContext *style_context;
+ GtkMenuButton *button;
+
+ button = g_object_new (GTK_TYPE_MENU_BUTTON,
+ "child", g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "view-more-symbolic",
+ "visible", TRUE,
+ NULL),
+ "margin-right", 6,
+ "direction", GTK_ARROW_RIGHT,
+ "halign", GTK_ALIGN_CENTER,
+ "menu-model", menu,
+ "tooltip-text", _("Display supplimental graphs"),
+ "use-popover", FALSE,
+ "valign", GTK_ALIGN_CENTER,
+ "visible", TRUE,
+ NULL);
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (button));
+ gtk_style_context_add_class (style_context, "image-button");
+ gtk_style_context_add_class (style_context, "small-button");
+ gtk_style_context_add_class (style_context, "flat");
+
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (button));
+ }
+}
+
+SysprofVisualizerGroupHeader *
+_sysprof_visualizer_group_header_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP_HEADER, NULL);
+}
diff --git a/src/libsysprof-ui/sysprof-visualizer-group-header.h
b/src/libsysprof-ui/sysprof-visualizer-group-header.h
new file mode 100644
index 0000000..2a1e9c8
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer-group-header.h
@@ -0,0 +1,31 @@
+/* sysprof-visualizer-group-header.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_VISUALIZER_GROUP_HEADER (sysprof_visualizer_group_header_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofVisualizerGroupHeader, sysprof_visualizer_group_header, SYSPROF,
VISUALIZER_GROUP_HEADER, GtkListBoxRow)
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizer-group-private.h
b/src/libsysprof-ui/sysprof-visualizer-group-private.h
new file mode 100644
index 0000000..850c960
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer-group-private.h
@@ -0,0 +1,48 @@
+/* sysprof-visualizers-group-private.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include <sysprof-capture.h>
+
+#include "sysprof-visualizer-group.h"
+#include "sysprof-visualizer-group-header.h"
+
+G_BEGIN_DECLS
+
+void _sysprof_visualizer_set_data_width (SysprofVisualizer
*self,
+ gint
width);
+void _sysprof_visualizer_group_set_data_width (SysprofVisualizerGroup
*self,
+ gint
width);
+void _sysprof_visualizer_group_set_reader (SysprofVisualizerGroup
*self,
+ SysprofCaptureReader
*reader);
+SysprofVisualizerGroupHeader *_sysprof_visualizer_group_header_new (void);
+void _sysprof_visualizer_group_header_add_row (SysprofVisualizerGroupHeader
*self,
+ guint
position,
+ const gchar
*title,
+ GMenuModel
*menu,
+ GtkWidget
*row);
+void _sysprof_visualizer_group_header_remove_row (SysprofVisualizerGroupHeader
*self,
+ guint
row);
+void _sysprof_visualizer_group_set_header (SysprofVisualizerGroup
*self,
+ SysprofVisualizerGroupHeader
*header);
+
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizer-group.c b/src/libsysprof-ui/sysprof-visualizer-group.c
new file mode 100644
index 0000000..851ffba
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer-group.c
@@ -0,0 +1,511 @@
+/* sysprof-visualizer-group.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-visualizer-group"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "sysprof-visualizer.h"
+#include "sysprof-visualizer-group.h"
+#include "sysprof-visualizer-group-private.h"
+
+typedef struct
+{
+ /* Owned pointers */
+ GMenuModel *menu;
+ GMenu *default_menu;
+ GMenu *rows_menu;
+ gchar *title;
+ GtkSizeGroup *size_group;
+ GSimpleActionGroup *actions;
+
+ gint priority;
+
+ guint has_page : 1;
+
+ /* Weak pointers */
+ SysprofVisualizerGroupHeader *header;
+
+ /* Child Widgets */
+ GtkBox *visualizers;
+} SysprofVisualizerGroupPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofVisualizerGroup, sysprof_visualizer_group, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_HAS_PAGE,
+ PROP_MENU,
+ PROP_PRIORITY,
+ PROP_TITLE,
+ N_PROPS
+};
+
+enum {
+ GROUP_ACTIVATED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+/**
+ * sysprof_visualizer_group_new:
+ *
+ * Create a new #SysprofVisualizerGroup.
+ *
+ * Returns: (transfer full): a newly created #SysprofVisualizerGroup
+ */
+SysprofVisualizerGroup *
+sysprof_visualizer_group_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, NULL);
+}
+
+const gchar *
+sysprof_visualizer_group_get_title (SysprofVisualizerGroup *self)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), NULL);
+
+ return priv->title;
+}
+
+void
+sysprof_visualizer_group_set_title (SysprofVisualizerGroup *self,
+ const gchar *title)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+
+ if (g_strcmp0 (priv->title, title) != 0)
+ {
+ g_free (priv->title);
+ priv->title = g_strdup (title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ }
+}
+
+/**
+ * sysprof_visualizer_group_get_menu:
+ *
+ * Gets the menu for the group.
+ *
+ * Returns: (transfer none) (nullable): a #GMenuModel or %NULL
+ *
+ * Since: 3.34
+ */
+GMenuModel *
+sysprof_visualizer_group_get_menu (SysprofVisualizerGroup *self)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), NULL);
+
+ return priv->menu;
+}
+
+void
+sysprof_visualizer_group_set_menu (SysprofVisualizerGroup *self,
+ GMenuModel *menu)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+ g_return_if_fail (!menu || G_IS_MENU_MODEL (menu));
+
+ if (g_set_object (&priv->menu, menu))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MENU]);
+}
+
+static gchar *
+create_action_name (const gchar *str)
+{
+ GString *ret = g_string_new (NULL);
+
+ for (; *str; str = g_utf8_next_char (str))
+ {
+ gunichar ch = g_utf8_get_char (str);
+
+ if (g_unichar_isalnum (ch))
+ g_string_append_unichar (ret, ch);
+ else
+ g_string_append_c (ret, '_');
+ }
+
+ return g_string_free (ret, FALSE);
+}
+
+static void
+sysprof_visualizer_group_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ SysprofVisualizerGroup *self = (SysprofVisualizerGroup *)container;
+
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ if (SYSPROF_IS_VISUALIZER (child))
+ sysprof_visualizer_group_insert (self, SYSPROF_VISUALIZER (child), -1, FALSE);
+ else
+ GTK_CONTAINER_CLASS (sysprof_visualizer_group_parent_class)->add (container, child);
+}
+
+static void
+sysprof_visualizer_group_finalize (GObject *object)
+{
+ SysprofVisualizerGroup *self = (SysprofVisualizerGroup *)object;
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_clear_pointer (&priv->title, g_free);
+ g_clear_object (&priv->menu);
+ g_clear_object (&priv->size_group);
+ g_clear_object (&priv->default_menu);
+ g_clear_object (&priv->rows_menu);
+ g_clear_object (&priv->actions);
+
+ g_clear_weak_pointer (&priv->header);
+
+ G_OBJECT_CLASS (sysprof_visualizer_group_parent_class)->finalize (object);
+}
+
+static void
+sysprof_visualizer_group_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofVisualizerGroup *self = SYSPROF_VISUALIZER_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_HAS_PAGE:
+ g_value_set_boolean (value, sysprof_visualizer_group_get_has_page (self));
+ break;
+
+ case PROP_MENU:
+ g_value_set_object (value, sysprof_visualizer_group_get_menu (self));
+ break;
+
+ case PROP_PRIORITY:
+ g_value_set_int (value, sysprof_visualizer_group_get_priority (self));
+ break;
+
+ case PROP_TITLE:
+ g_value_set_string (value, sysprof_visualizer_group_get_title (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_visualizer_group_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofVisualizerGroup *self = SYSPROF_VISUALIZER_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_HAS_PAGE:
+ sysprof_visualizer_group_set_has_page (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_MENU:
+ sysprof_visualizer_group_set_menu (self, g_value_get_object (value));
+ break;
+
+ case PROP_PRIORITY:
+ sysprof_visualizer_group_set_priority (self, g_value_get_int (value));
+ break;
+
+ case PROP_TITLE:
+ sysprof_visualizer_group_set_title (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_visualizer_group_class_init (SysprofVisualizerGroupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->finalize = sysprof_visualizer_group_finalize;
+ object_class->get_property = sysprof_visualizer_group_get_property;
+ object_class->set_property = sysprof_visualizer_group_set_property;
+
+ container_class->add = sysprof_visualizer_group_add;
+
+ properties [PROP_HAS_PAGE] =
+ g_param_spec_boolean ("has-page",
+ "Has Page",
+ "Has Page",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MENU] =
+ g_param_spec_object ("menu",
+ "Menu",
+ "Menu",
+ G_TYPE_MENU_MODEL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PRIORITY] =
+ g_param_spec_int ("priority",
+ "Priority",
+ "The Priority of the group, used for sorting",
+ G_MININT, G_MAXINT, 0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title of the row",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [GROUP_ACTIVATED] =
+ g_signal_new ("group-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_css_name (widget_class, "SysprofVisualizerGroup");
+}
+
+static void
+sysprof_visualizer_group_init (SysprofVisualizerGroup *self)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+ g_autoptr(GMenuItem) item = NULL;
+
+ priv->actions = g_simple_action_group_new ();
+
+ priv->default_menu = g_menu_new ();
+ priv->rows_menu = g_menu_new ();
+
+ item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->rows_menu));
+ g_menu_append_item (priv->default_menu, item);
+
+ priv->menu = g_object_ref (G_MENU_MODEL (priv->default_menu));
+
+ priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+ gtk_size_group_add_widget (priv->size_group, GTK_WIDGET (self));
+
+ priv->visualizers = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->visualizers));
+}
+
+void
+_sysprof_visualizer_group_set_header (SysprofVisualizerGroup *self,
+ SysprofVisualizerGroupHeader *header)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+ g_return_if_fail (!header || SYSPROF_IS_VISUALIZER_GROUP_HEADER (header));
+
+ if (g_set_weak_pointer (&priv->header, header))
+ {
+ if (header != NULL)
+ {
+ GList *children;
+ guint position = 0;
+
+ gtk_widget_insert_action_group (GTK_WIDGET (header),
+ "group",
+ G_ACTION_GROUP (priv->actions));
+ gtk_size_group_add_widget (priv->size_group, GTK_WIDGET (header));
+
+ children = gtk_container_get_children (GTK_CONTAINER (priv->visualizers));
+
+ for (const GList *iter = children; iter; iter = iter->next)
+ {
+ SysprofVisualizer *vis = iter->data;
+ const gchar *title;
+ GMenuModel *menu = NULL;
+
+ g_assert (SYSPROF_IS_VISUALIZER (vis));
+
+ if (position == 0)
+ menu = priv->menu;
+
+ title = sysprof_visualizer_get_title (vis);
+
+ if (title == NULL)
+ title = priv->title;
+
+ _sysprof_visualizer_group_header_add_row (header,
+ position,
+ title,
+ menu,
+ GTK_WIDGET (vis));
+
+ position++;
+ }
+
+ g_list_free (children);
+ }
+ }
+}
+
+static void
+sysprof_visualizer_group_set_reader_cb (SysprofVisualizer *visualizer,
+ SysprofCaptureReader *reader)
+{
+ sysprof_visualizer_set_reader (visualizer, reader);
+}
+
+void
+_sysprof_visualizer_group_set_reader (SysprofVisualizerGroup *self,
+ SysprofCaptureReader *reader)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+ g_return_if_fail (reader != NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (priv->visualizers),
+ (GtkCallback) sysprof_visualizer_group_set_reader_cb,
+ reader);
+}
+
+void
+sysprof_visualizer_group_insert (SysprofVisualizerGroup *self,
+ SysprofVisualizer *visualizer,
+ gint position,
+ gboolean can_toggle)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (visualizer));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->visualizers), GTK_WIDGET (visualizer),
+ "position", position,
+ NULL);
+
+ if (can_toggle)
+ {
+ const gchar *title = sysprof_visualizer_get_title (visualizer);
+ g_autofree gchar *action_name = create_action_name (title);
+ g_autofree gchar *full_action_name = g_strdup_printf ("group.%s", action_name);
+ g_autoptr(GMenuItem) item = g_menu_item_new (title, full_action_name);
+ g_autoptr(GPropertyAction) action = NULL;
+
+ action = g_property_action_new (action_name, visualizer, "visible");
+ g_action_map_add_action (G_ACTION_MAP (priv->actions), G_ACTION (action));
+ g_menu_item_set_attribute (item, "role", "s", "check");
+ g_menu_append_item (priv->rows_menu, item);
+ }
+}
+
+static void
+propagate_data_width_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ _sysprof_visualizer_set_data_width (SYSPROF_VISUALIZER (widget),
+ GPOINTER_TO_INT (user_data));
+}
+
+void
+_sysprof_visualizer_group_set_data_width (SysprofVisualizerGroup *self,
+ gint width)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+
+ gtk_container_foreach (GTK_CONTAINER (priv->visualizers),
+ propagate_data_width_cb,
+ GINT_TO_POINTER (width));
+}
+
+gint
+sysprof_visualizer_group_get_priority (SysprofVisualizerGroup *self)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), 0);
+
+ return priv->priority;
+}
+
+void
+sysprof_visualizer_group_set_priority (SysprofVisualizerGroup *self,
+ gint priority)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+
+ if (priv->priority != priority)
+ {
+ priv->priority = priority;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]);
+ }
+}
+
+gboolean
+sysprof_visualizer_group_get_has_page (SysprofVisualizerGroup *self)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), FALSE);
+
+ return priv->has_page;
+}
+
+void
+sysprof_visualizer_group_set_has_page (SysprofVisualizerGroup *self,
+ gboolean has_page)
+{
+ SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self));
+
+ has_page = !!has_page;
+
+ if (has_page != priv->has_page)
+ {
+ priv->has_page = has_page;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_PAGE]);
+ }
+}
diff --git a/src/libsysprof-ui/sysprof-visualizer-group.h b/src/libsysprof-ui/sysprof-visualizer-group.h
new file mode 100644
index 0000000..cac1c66
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer-group.h
@@ -0,0 +1,76 @@
+/* sysprof-visualizer-group.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
+# error "Only <sysprof-ui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+#include "sysprof-visualizer.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_VISUALIZER_GROUP (sysprof_visualizer_group_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (SysprofVisualizerGroup, sysprof_visualizer_group, SYSPROF, VISUALIZER_GROUP,
GtkListBoxRow)
+
+struct _SysprofVisualizerGroupClass
+{
+ GtkListBoxRowClass parent_class;
+
+ void (*group_activated) (SysprofVisualizerGroup *self);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+SYSPROF_AVAILABLE_IN_ALL
+SysprofVisualizerGroup *sysprof_visualizer_group_new (void);
+SYSPROF_AVAILABLE_IN_ALL
+GMenuModel *sysprof_visualizer_group_get_menu (SysprofVisualizerGroup *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_group_set_menu (SysprofVisualizerGroup *self,
+ GMenuModel *menu);
+SYSPROF_AVAILABLE_IN_ALL
+gint sysprof_visualizer_group_get_priority (SysprofVisualizerGroup *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_group_set_priority (SysprofVisualizerGroup *self,
+ gint priority);
+SYSPROF_AVAILABLE_IN_ALL
+const gchar *sysprof_visualizer_group_get_title (SysprofVisualizerGroup *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_group_set_title (SysprofVisualizerGroup *self,
+ const gchar *title);
+SYSPROF_AVAILABLE_IN_ALL
+gboolean sysprof_visualizer_group_get_has_page (SysprofVisualizerGroup *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_group_set_has_page (SysprofVisualizerGroup *self,
+ gboolean has_page);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_group_insert (SysprofVisualizerGroup *self,
+ SysprofVisualizer *visualizer,
+ gint position,
+ gboolean can_toggle);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizer-ticks.c b/src/libsysprof-ui/sysprof-visualizer-ticks.c
index 4cbf291..fc3a60a 100644
--- a/src/libsysprof-ui/sysprof-visualizer-ticks.c
+++ b/src/libsysprof-ui/sysprof-visualizer-ticks.c
@@ -18,19 +18,24 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+#define G_LOG_DOMAIN "sysprof-visualizer-ticks"
+
#include "config.h"
#include <glib/gi18n.h>
+#include <sysprof.h>
#include "sysprof-visualizer-ticks.h"
#define NSEC_PER_SEC G_GINT64_CONSTANT(1000000000)
-#define NSEC_PER_HOUR (NSEC_PER_SEC * 60 * 60)
-#define NSEC_PER_MIN (NSEC_PER_SEC * 60)
+#define NSEC_PER_DAY (NSEC_PER_SEC * 60L * 60L * 24L)
+#define NSEC_PER_HOUR (NSEC_PER_SEC * 60L * 60L)
+#define NSEC_PER_MIN (NSEC_PER_SEC * 60L)
#define NSEC_PER_MSEC (NSEC_PER_SEC/G_GINT64_CONSTANT(1000))
#define MIN_TICK_DISTANCE 20
-#define LABEL_HEIGHT_PX 8
+#define LABEL_HEIGHT_PX 10
+SYSPROF_ALIGNED_BEGIN (8)
struct _SysprofVisualizerTicks
{
GtkDrawingArea parent_instance;
@@ -38,7 +43,7 @@ struct _SysprofVisualizerTicks
gint64 epoch;
gint64 begin_time;
gint64 end_time;
-} __attribute__((aligned(8)));
+} SYSPROF_ALIGNED_END (8);
enum {
TICK_MINUTES,
@@ -50,6 +55,7 @@ enum {
TICK_TENTHS,
TICK_HUNDREDTHS,
TICK_THOUSANDTHS,
+ TICK_TEN_THOUSANDTHS,
N_TICKS
};
@@ -67,6 +73,7 @@ struct {
{ 1, 5, NSEC_PER_SEC / 10 },
{ 1, 4, NSEC_PER_SEC / 100 },
{ 1, 3, NSEC_PER_SEC / 1000 },
+ { 1, 1, NSEC_PER_SEC / 10000 },
};
G_DEFINE_TYPE (SysprofVisualizerTicks, sysprof_visualizer_ticks, GTK_TYPE_DRAWING_AREA)
@@ -82,6 +89,7 @@ update_label_text (PangoLayout *layout,
gint hours = 0;
gint min = 0;
gint sec = 0;
+ G_GNUC_UNUSED gint days = 0;
g_assert (PANGO_IS_LAYOUT (layout));
@@ -89,6 +97,12 @@ update_label_text (PangoLayout *layout,
time -= tmp;
msec = tmp / 100000L;
+ if (time >= NSEC_PER_DAY)
+ {
+ days = time / NSEC_PER_DAY;
+ time %= NSEC_PER_DAY;
+ }
+
if (time >= NSEC_PER_HOUR)
{
hours = time / NSEC_PER_HOUR;
@@ -135,18 +149,6 @@ get_x_for_time (SysprofVisualizerTicks *self,
return alloc->width * x_ratio;
}
-#if 0
-static inline gint64
-get_time_at_x (SysprofVisualizerTicks *self,
- const GtkAllocation *alloc,
- gdouble x)
-{
- return self->begin_time
- - self->epoch
- + ((self->end_time - self->begin_time) / (gdouble)alloc->width * x);
-}
-#endif
-
static gboolean
draw_ticks (SysprofVisualizerTicks *self,
cairo_t *cr,
@@ -186,8 +188,10 @@ draw_ticks (SysprofVisualizerTicks *self,
PangoLayout *layout;
PangoFontDescription *font_desc;
gboolean want_msec;
+ gint last_x2 = G_MININT;
+ gint w, h;
- layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "00:10:00");
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "00:10:00.0000");
font_desc = pango_font_description_new ();
pango_font_description_set_family_static (font_desc, "Monospace");
@@ -195,6 +199,8 @@ draw_ticks (SysprofVisualizerTicks *self,
pango_layout_set_font_description (layout, font_desc);
pango_font_description_free (font_desc);
+ pango_layout_get_pixel_size (layout, &w, &h);
+
/* If we are operating on smaller than seconds here, then we want
* to ensure we include msec with the timestamps.
*/
@@ -206,9 +212,15 @@ draw_ticks (SysprofVisualizerTicks *self,
{
gdouble x = get_x_for_time (self, &alloc, t);
- cairo_move_to (cr, (gint)x + .5 - (gint)half, alloc.height - LABEL_HEIGHT_PX);
+ if (x < (last_x2 + MIN_TICK_DISTANCE))
+ continue;
+
+ cairo_move_to (cr, (gint)x + 2.5 - (gint)half, 2);
update_label_text (layout, t - self->epoch, want_msec);
+ pango_layout_get_pixel_size (layout, &w, &h);
pango_cairo_show_layout (cr, layout);
+
+ last_x2 = x + w;
}
g_clear_object (&layout);
@@ -221,8 +233,8 @@ draw_ticks (SysprofVisualizerTicks *self,
{
gdouble x = get_x_for_time (self, &alloc, t);
- cairo_move_to (cr, (gint)x - .5 - (gint)half, 0);
- cairo_line_to (cr, (gint)x - .5 - (gint)half, tick_sizing[ticks].height);
+ cairo_move_to (cr, (gint)x - .5 - (gint)half, alloc.height);
+ cairo_line_to (cr, (gint)x - .5 - (gint)half, alloc.height - tick_sizing[ticks].height);
count++;
}
@@ -235,7 +247,7 @@ draw_ticks (SysprofVisualizerTicks *self,
static gboolean
sysprof_visualizer_ticks_draw (GtkWidget *widget,
- cairo_t *cr)
+ cairo_t *cr)
{
SysprofVisualizerTicks *self = SYSPROF_VISUALIZER_TICKS (widget);
GtkStyleContext *style;
@@ -250,9 +262,12 @@ sysprof_visualizer_ticks_draw (GtkWidget *widget,
if (0 == (timespan = self->end_time - self->begin_time))
return GDK_EVENT_PROPAGATE;
+ style = gtk_widget_get_style_context (widget);
+
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
- style = gtk_widget_get_style_context (widget);
+ gtk_render_background (style, cr, 0, 0, alloc.width, alloc.height);
+
state = gtk_widget_get_state_flags (widget);
gtk_style_context_get_color (style, state, &color);
@@ -290,8 +305,8 @@ sysprof_visualizer_ticks_draw (GtkWidget *widget,
static void
sysprof_visualizer_ticks_get_preferred_height (GtkWidget *widget,
- gint *min_height,
- gint *nat_height)
+ gint *min_height,
+ gint *nat_height)
{
g_assert (SYSPROF_IS_VISUALIZER_TICKS (widget));
@@ -306,7 +321,7 @@ sysprof_visualizer_ticks_class_init (SysprofVisualizerTicksClass *klass)
widget_class->draw = sysprof_visualizer_ticks_draw;
widget_class->get_preferred_height = sysprof_visualizer_ticks_get_preferred_height;
- gtk_widget_class_set_css_name (widget_class, "ticks");
+ gtk_widget_class_set_css_name (widget_class, "SysprofVisualizerTicks");
}
static void
@@ -325,8 +340,8 @@ sysprof_visualizer_ticks_new (void)
void
sysprof_visualizer_ticks_get_time_range (SysprofVisualizerTicks *self,
- gint64 *begin_time,
- gint64 *end_time)
+ gint64 *begin_time,
+ gint64 *end_time)
{
g_return_if_fail (SYSPROF_IS_VISUALIZER_TICKS (self));
g_return_if_fail (begin_time != NULL || end_time != NULL);
@@ -340,8 +355,8 @@ sysprof_visualizer_ticks_get_time_range (SysprofVisualizerTicks *self,
void
sysprof_visualizer_ticks_set_time_range (SysprofVisualizerTicks *self,
- gint64 begin_time,
- gint64 end_time)
+ gint64 begin_time,
+ gint64 end_time)
{
g_return_if_fail (SYSPROF_IS_VISUALIZER_TICKS (self));
@@ -390,3 +405,11 @@ sysprof_visualizer_ticks_set_epoch (SysprofVisualizerTicks *self,
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
+
+gint64
+sysprof_visualizer_ticks_get_duration (SysprofVisualizerTicks *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER_TICKS (self), 0);
+
+ return self->end_time - self->begin_time;
+}
diff --git a/src/libsysprof-ui/sysprof-visualizer-ticks.h b/src/libsysprof-ui/sysprof-visualizer-ticks.h
index 2ef71e7..aa963fa 100644
--- a/src/libsysprof-ui/sysprof-visualizer-ticks.h
+++ b/src/libsysprof-ui/sysprof-visualizer-ticks.h
@@ -20,10 +20,6 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
#include <gtk/gtk.h>
G_BEGIN_DECLS
@@ -42,5 +38,6 @@ void sysprof_visualizer_ticks_get_time_range (SysprofVisualizerTicks *self
void sysprof_visualizer_ticks_set_time_range (SysprofVisualizerTicks *self,
gint64 begin_time,
gint64 end_time);
+gint64 sysprof_visualizer_ticks_get_duration (SysprofVisualizerTicks *self);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizer.c b/src/libsysprof-ui/sysprof-visualizer.c
new file mode 100644
index 0000000..41ffba5
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer.c
@@ -0,0 +1,346 @@
+/* sysprof-visualizer.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-visualizer"
+
+#include "config.h"
+
+#include "sysprof-visualizer.h"
+
+typedef struct
+{
+ gchar *title;
+
+ gint64 begin_time;
+ gint64 end_time;
+ gint64 duration;
+
+ /* The width for [begin_time..end_time] which may be less
+ * than what the widgets allocation is.
+ */
+ gint data_width;
+} SysprofVisualizerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofVisualizer, sysprof_visualizer, DZL_TYPE_BIN)
+
+enum {
+ PROP_0,
+ PROP_BEGIN_TIME,
+ PROP_END_TIME,
+ PROP_TITLE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+sysprof_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ g_assert (SYSPROF_IS_VISUALIZER (widget));
+ g_assert (cr != NULL);
+
+ GTK_WIDGET_CLASS (sysprof_visualizer_parent_class)->draw (widget, cr);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+sysprof_visualizer_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *nat_width)
+{
+ SysprofVisualizer *self = (SysprofVisualizer *)widget;
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_assert (SYSPROF_IS_VISUALIZER (self));
+ g_assert (min_width != NULL);
+ g_assert (nat_width != NULL);
+
+ *min_width = *nat_width = priv->data_width ? priv->data_width : 1;
+}
+
+static void
+sysprof_visualizer_finalize (GObject *object)
+{
+ SysprofVisualizer *self = (SysprofVisualizer *)object;
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (sysprof_visualizer_parent_class)->finalize (object);
+}
+
+static void
+sysprof_visualizer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofVisualizer *self = SYSPROF_VISUALIZER (object);
+
+ switch (prop_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, sysprof_visualizer_get_title (self));
+ break;
+
+ case PROP_BEGIN_TIME:
+ g_value_set_int64 (value, sysprof_visualizer_get_begin_time (self));
+ break;
+
+ case PROP_END_TIME:
+ g_value_set_int64 (value, sysprof_visualizer_get_end_time (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_visualizer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofVisualizer *self = SYSPROF_VISUALIZER (object);
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_TITLE:
+ sysprof_visualizer_set_title (self, g_value_get_string (value));
+ break;
+
+ case PROP_BEGIN_TIME:
+ priv->begin_time = g_value_get_int64 (value);
+ priv->duration = priv->end_time - priv->begin_time;
+ break;
+
+ case PROP_END_TIME:
+ priv->end_time = g_value_get_int64 (value);
+ priv->duration = priv->end_time - priv->begin_time;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_visualizer_class_init (SysprofVisualizerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = sysprof_visualizer_finalize;
+ object_class->get_property = sysprof_visualizer_get_property;
+ object_class->set_property = sysprof_visualizer_set_property;
+
+ widget_class->draw = sysprof_visualizer_draw;
+ widget_class->get_preferred_width = sysprof_visualizer_get_preferred_width;
+
+ properties [PROP_BEGIN_TIME] =
+ g_param_spec_int64 ("begin-time",
+ "Begin Time",
+ "Begin Time",
+ G_MININT64,
+ G_MAXINT64,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_END_TIME] =
+ g_param_spec_int64 ("end-time",
+ "End Time",
+ "End Time",
+ G_MININT64,
+ G_MAXINT64,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title for the row",
+ NULL,
+ (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, "SysprofVisualizer");
+}
+
+static void
+sysprof_visualizer_init (SysprofVisualizer *self)
+{
+}
+
+const gchar *
+sysprof_visualizer_get_title (SysprofVisualizer *self)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0);
+
+ return priv->title;
+}
+
+void
+sysprof_visualizer_set_title (SysprofVisualizer *self,
+ const gchar *title)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (self));
+
+ if (g_strcmp0 (priv->title, title) != 0)
+ {
+ g_free (priv->title);
+ priv->title = g_strdup (title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ }
+}
+
+gint64
+sysprof_visualizer_get_begin_time (SysprofVisualizer *self)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0);
+
+ return priv->begin_time;
+}
+
+gint64
+sysprof_visualizer_get_end_time (SysprofVisualizer *self)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0);
+
+ return priv->end_time;
+}
+
+void
+sysprof_visualizer_set_reader (SysprofVisualizer *self,
+ SysprofCaptureReader *reader)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (self));
+ g_return_if_fail (reader != NULL);
+
+ if (priv->begin_time == 0 || priv->end_time == 0)
+ {
+ priv->begin_time = sysprof_capture_reader_get_start_time (reader);
+ priv->end_time = sysprof_capture_reader_get_end_time (reader);
+ priv->duration = priv->end_time - priv->begin_time;
+ }
+
+ if (SYSPROF_VISUALIZER_GET_CLASS (self)->set_reader)
+ SYSPROF_VISUALIZER_GET_CLASS (self)->set_reader (self, reader);
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static inline void
+subtract_border (GtkAllocation *alloc,
+ GtkBorder *border)
+{
+#if 0
+ g_print ("Border; %d %d %d %d\n", border->top, border->left, border->bottom, border->right);
+#endif
+
+ alloc->x += border->left;
+ alloc->y += border->top;
+ alloc->width -= border->left + border->right;
+ alloc->height -= border->top + border->bottom;
+}
+
+static void
+adjust_alloc_for_borders (SysprofVisualizer *self,
+ GtkAllocation *alloc)
+{
+ GtkStyleContext *style_context;
+ GtkBorder border;
+ GtkStateFlags state;
+
+ g_assert (SYSPROF_IS_VISUALIZER (self));
+ g_assert (alloc != NULL);
+
+ state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_get_border (style_context, state, &border);
+
+ subtract_border (alloc, &border);
+}
+
+void
+sysprof_visualizer_translate_points (SysprofVisualizer *self,
+ const SysprofVisualizerRelativePoint *in_points,
+ guint n_in_points,
+ SysprofVisualizerAbsolutePoint *out_points,
+ guint n_out_points)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+ GtkAllocation alloc;
+ gint graph_width;
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (self));
+ g_return_if_fail (in_points != NULL);
+ g_return_if_fail (out_points != NULL);
+ g_return_if_fail (n_in_points == n_out_points);
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+ adjust_alloc_for_borders (self, &alloc);
+
+ graph_width = priv->data_width;
+
+ for (guint i = 0; i < n_in_points; i++)
+ {
+ out_points[i].x = (in_points[i].x * graph_width);
+ out_points[i].y = alloc.height - (ABS (in_points[i].y) * alloc.height);
+ }
+}
+
+void
+_sysprof_visualizer_set_data_width (SysprofVisualizer *self,
+ gint data_width)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZER (self));
+
+ if (priv->data_width != data_width)
+ {
+ priv->data_width = data_width;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+}
+
+gint
+sysprof_visualizer_get_x_for_time (SysprofVisualizer *self,
+ gint64 time)
+{
+ SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self);
+
+ return ((time - priv->begin_time) / (gdouble)priv->duration) * priv->data_width;
+}
diff --git a/src/libsysprof-ui/sysprof-visualizer.h b/src/libsysprof-ui/sysprof-visualizer.h
new file mode 100644
index 0000000..618f82d
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizer.h
@@ -0,0 +1,82 @@
+/* sysprof-visualizer.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
+# error "Only <sysprof-ui.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <sysprof.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gdouble x;
+ gdouble y;
+} SysprofVisualizerRelativePoint;
+
+typedef struct
+{
+ gint x;
+ gint y;
+} SysprofVisualizerAbsolutePoint;
+
+#define SYSPROF_TYPE_VISUALIZER (sysprof_visualizer_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (SysprofVisualizer, sysprof_visualizer, SYSPROF, VISUALIZER, DzlBin)
+
+struct _SysprofVisualizerClass
+{
+ DzlBinClass parent_class;
+
+ void (*set_reader) (SysprofVisualizer *self,
+ SysprofCaptureReader *reader);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+SYSPROF_AVAILABLE_IN_ALL
+const gchar *sysprof_visualizer_get_title (SysprofVisualizer *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_set_title (SysprofVisualizer *self,
+ const gchar *title);
+SYSPROF_AVAILABLE_IN_ALL
+gint64 sysprof_visualizer_get_begin_time (SysprofVisualizer *self);
+SYSPROF_AVAILABLE_IN_ALL
+gint64 sysprof_visualizer_get_end_time (SysprofVisualizer *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_set_reader (SysprofVisualizer *self,
+ SysprofCaptureReader *reader);
+SYSPROF_AVAILABLE_IN_ALL
+gint sysprof_visualizer_get_x_for_time (SysprofVisualizer *self,
+ gint64 time);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_visualizer_translate_points (SysprofVisualizer *self,
+ const SysprofVisualizerRelativePoint *in_points,
+ guint n_in_points,
+ SysprofVisualizerAbsolutePoint *out_points,
+ guint n_out_points);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.c b/src/libsysprof-ui/sysprof-visualizers-frame.c
new file mode 100644
index 0000000..a116920
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizers-frame.c
@@ -0,0 +1,767 @@
+/* sysprof-visualizers-frame.c
+ *
+ * Copyright 2019 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
+ */
+
+#define G_LOG_DOMAIN "sysprof-visualizers-frame"
+
+#include "config.h"
+
+#include "sysprof-scrollmap.h"
+#include "sysprof-visualizer-group-private.h"
+#include "sysprof-visualizer-ticks.h"
+#include "sysprof-visualizers-frame.h"
+#include "sysprof-zoom-manager.h"
+
+struct _SysprofVisualizersFrame
+{
+ GtkBin parent_instance;
+
+ /* Drag selection tracking */
+ SysprofSelection *selection;
+ gint64 drag_begin_at;
+ gint64 drag_selection_at;
+ guint button_pressed : 1;
+
+ /* Help avoid over-resizing/allocating */
+ GtkAllocation last_alloc;
+ gdouble last_zoom;
+
+ /* Known time range from the capture */
+ gint64 begin_time;
+ gint64 end_time;
+
+ /* Template Widgets */
+ GtkListBox *groups;
+ GtkListBox *visualizers;
+ SysprofScrollmap *hscrollbar;
+ SysprofVisualizerTicks *ticks;
+ GtkScrolledWindow *ticks_scroller;
+ GtkScrolledWindow *hscroller;
+ GtkScrolledWindow *vscroller;
+ SysprofZoomManager *zoom_manager;
+ GtkScale *zoom_scale;
+ GtkSizeGroup *left_column;
+};
+
+typedef struct
+{
+ GtkListBox *list;
+ GtkStyleContext *style_context;
+ cairo_t *cr;
+ GtkAllocation alloc;
+ gint64 begin_time;
+ gint64 duration;
+} SelectionDraw;
+
+G_DEFINE_TYPE (SysprofVisualizersFrame, sysprof_visualizers_frame, GTK_TYPE_BIN)
+
+enum {
+ PROP_0,
+ PROP_SELECTED_GROUP,
+ PROP_SELECTION,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gint64
+get_time_from_x (SysprofVisualizersFrame *self,
+ gdouble x)
+{
+ GtkAllocation alloc;
+ gdouble ratio;
+ gint64 duration;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->ticks), &alloc);
+ duration = sysprof_visualizer_ticks_get_duration (self->ticks);
+
+ if (alloc.width < 1)
+ return 0;
+
+ ratio = x / alloc.width;
+
+ return self->begin_time + (ratio * duration);
+}
+
+static void
+draw_selection_cb (SysprofSelection *selection,
+ gint64 range_begin,
+ gint64 range_end,
+ gpointer user_data)
+{
+ SelectionDraw *draw = user_data;
+ GdkRectangle area;
+ gdouble x, x2;
+
+ g_assert (SYSPROF_IS_SELECTION (selection));
+ g_assert (draw != NULL);
+ g_assert (draw->cr != NULL);
+ g_assert (GTK_IS_LIST_BOX (draw->list));
+
+ x = (range_begin - draw->begin_time) / (gdouble)draw->duration;
+ x2 = (range_end - draw->begin_time) / (gdouble)draw->duration;
+
+ area.x = x * draw->alloc.width;
+ area.width = (x2 * draw->alloc.width) - area.x;
+ area.y = 0;
+ area.height = draw->alloc.height;
+
+ if (area.width < 0)
+ {
+ area.width = ABS (area.width);
+ area.x -= area.width;
+ }
+
+ gtk_render_background (draw->style_context, draw->cr, area.x + 2, area.y + 2, area.width - 4, area.height
- 4);
+}
+
+static gboolean
+visualizers_draw_after_cb (SysprofVisualizersFrame *self,
+ cairo_t *cr,
+ GtkListBox *list)
+{
+ SelectionDraw draw;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (GTK_IS_LIST_BOX (list));
+
+ draw.style_context = gtk_widget_get_style_context (GTK_WIDGET (list));
+ draw.list = list;
+ draw.cr = cr;
+ draw.begin_time = self->begin_time;
+ draw.duration = sysprof_visualizer_ticks_get_duration (self->ticks);
+
+ if (draw.duration == 0)
+ return GDK_EVENT_PROPAGATE;
+
+ gtk_widget_get_allocation (GTK_WIDGET (list), &draw.alloc);
+
+ if (sysprof_selection_get_has_selection (self->selection) || self->button_pressed)
+ {
+ gtk_style_context_add_class (draw.style_context, "selection");
+ sysprof_selection_foreach (self->selection, draw_selection_cb, &draw);
+ if (self->button_pressed)
+ draw_selection_cb (self->selection, self->drag_begin_at, self->drag_selection_at, &draw);
+ gtk_style_context_remove_class (draw.style_context, "selection");
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+visualizers_realize_after_cb (SysprofVisualizersFrame *self,
+ GtkListBox *list)
+{
+ GdkDisplay *display;
+ GdkWindow *window;
+ GdkCursor *cursor;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (GTK_IS_LIST_BOX (list));
+
+ window = gtk_widget_get_window (GTK_WIDGET (list));
+ display = gdk_window_get_display (window);
+ cursor = gdk_cursor_new_from_name (display, "text");
+ gdk_window_set_cursor (window, cursor);
+ g_clear_object (&cursor);
+}
+
+static gboolean
+visualizers_button_press_event_cb (SysprofVisualizersFrame *self,
+ GdkEventButton *ev,
+ GtkListBox *visualizers)
+{
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (ev != NULL);
+ g_assert (GTK_IS_LIST_BOX (visualizers));
+
+ if (ev->button != GDK_BUTTON_PRIMARY)
+ {
+ if (sysprof_selection_get_has_selection (self->selection))
+ {
+ sysprof_selection_unselect_all (self->selection);
+ return GDK_EVENT_STOP;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+ }
+
+ if ((ev->state & GDK_SHIFT_MASK) == 0)
+ sysprof_selection_unselect_all (self->selection);
+
+ self->button_pressed = TRUE;
+
+ self->drag_begin_at = get_time_from_x (self, ev->x);
+ self->drag_selection_at = self->drag_begin_at;
+
+ gtk_widget_queue_draw (GTK_WIDGET (visualizers));
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+visualizers_button_release_event_cb (SysprofVisualizersFrame *self,
+ GdkEventButton *ev,
+ GtkListBox *list)
+{
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (ev != NULL);
+ g_assert (GTK_IS_LIST_BOX (list));
+
+ if (!self->button_pressed || ev->button != GDK_BUTTON_PRIMARY)
+ return GDK_EVENT_PROPAGATE;
+
+ self->button_pressed = FALSE;
+
+ if (self->drag_begin_at != self->drag_selection_at)
+ {
+ sysprof_selection_select_range (self->selection,
+ self->drag_begin_at,
+ self->drag_selection_at);
+ self->drag_begin_at = -1;
+ self->drag_selection_at = -1;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (list));
+
+ return GDK_EVENT_STOP;
+}
+
+static gboolean
+visualizers_motion_notify_event_cb (SysprofVisualizersFrame *self,
+ GdkEventMotion *ev,
+ GtkListBox *list)
+{
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (ev != NULL);
+ g_assert (GTK_IS_LIST_BOX (list));
+
+ if (!self->button_pressed)
+ return GDK_EVENT_PROPAGATE;
+
+ self->drag_selection_at = get_time_from_x (self, ev->x);
+
+ gtk_widget_queue_draw (GTK_WIDGET (list));
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+propagate_data_width_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ _sysprof_visualizer_group_set_data_width (SYSPROF_VISUALIZER_GROUP (widget),
+ GPOINTER_TO_INT (user_data));
+}
+
+static void
+sysprof_visualizers_frame_notify_zoom (SysprofVisualizersFrame *self,
+ GParamSpec *pspec,
+ SysprofZoomManager *zoom_manager)
+{
+ gint data_width;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (SYSPROF_IS_ZOOM_MANAGER (zoom_manager));
+
+ data_width = sysprof_zoom_manager_get_width_for_duration (self->zoom_manager,
+ self->end_time - self->begin_time);
+ gtk_container_foreach (GTK_CONTAINER (self->visualizers),
+ propagate_data_width_cb,
+ GINT_TO_POINTER (data_width));
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+static void
+sysprof_visualizers_frame_apply_zoom (SysprofVisualizersFrame *self,
+ const GtkAllocation *alloc)
+{
+ gint64 duration;
+ gint64 end_time;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+
+ duration = sysprof_zoom_manager_get_duration_for_width (self->zoom_manager, alloc->width);
+ end_time = self->begin_time + duration;
+
+ sysprof_scrollmap_set_time_range (self->hscrollbar,
+ self->begin_time,
+ MAX (self->end_time, end_time));
+ sysprof_visualizer_ticks_set_epoch (self->ticks, self->begin_time);
+ sysprof_visualizer_ticks_set_time_range (self->ticks, self->begin_time, end_time);
+}
+
+static gint
+find_pos (SysprofVisualizersFrame *self,
+ const gchar *title,
+ gint priority)
+{
+ GList *list;
+ gint pos = 0;
+
+ if (title == NULL)
+ return -1;
+
+ list = gtk_container_get_children (GTK_CONTAINER (self->visualizers));
+
+ for (const GList *iter = list; iter; iter = iter->next)
+ {
+ SysprofVisualizerGroup *group = iter->data;
+ gint prio = sysprof_visualizer_group_get_priority (group);
+ const gchar *item = sysprof_visualizer_group_get_title (group);
+
+ if (priority < prio ||
+ (priority == prio && g_strcmp0 (title, item) < 0))
+ break;
+
+ pos++;
+ }
+
+ g_list_free (list);
+
+ return pos;
+}
+
+static void
+sysprof_visualizers_frame_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ SysprofVisualizersFrame *self = (SysprofVisualizersFrame *)container;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ if (SYSPROF_IS_VISUALIZER_GROUP (child))
+ {
+ SysprofVisualizerGroupHeader *header;
+ const gchar *title = sysprof_visualizer_group_get_title (SYSPROF_VISUALIZER_GROUP (child));
+ gint priority = sysprof_visualizer_group_get_priority (SYSPROF_VISUALIZER_GROUP (child));
+ gint pos = find_pos (self, title, priority);
+
+ gtk_list_box_insert (self->visualizers, child, pos);
+
+ header = _sysprof_visualizer_group_header_new ();
+ g_object_set_data (G_OBJECT (header), "VISUALIZER_GROUP", child);
+ gtk_list_box_insert (self->groups, GTK_WIDGET (header), pos);
+ _sysprof_visualizer_group_set_header (SYSPROF_VISUALIZER_GROUP (child), header);
+ gtk_widget_show (GTK_WIDGET (header));
+
+ sysprof_visualizers_frame_notify_zoom (self, NULL, self->zoom_manager);
+
+ return;
+ }
+
+ GTK_CONTAINER_CLASS (sysprof_visualizers_frame_parent_class)->add (container, child);
+}
+
+static void
+sysprof_visualizers_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
+{
+ SysprofVisualizersFrame *self = (SysprofVisualizersFrame *)widget;
+ gdouble zoom;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (alloc != NULL);
+
+ GTK_WIDGET_CLASS (sysprof_visualizers_frame_parent_class)->size_allocate (widget, alloc);
+
+ zoom = sysprof_zoom_manager_get_zoom (self->zoom_manager);
+
+ if (alloc->width != self->last_alloc.width || zoom != self->last_zoom)
+ sysprof_visualizers_frame_apply_zoom (self, alloc);
+
+ self->last_alloc = *alloc;
+ self->last_zoom = zoom;
+}
+
+static void
+sysprof_visualizers_frame_selection_changed (SysprofVisualizersFrame *self,
+ SysprofSelection *selection)
+{
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (SYSPROF_IS_SELECTION (selection));
+
+ gtk_widget_queue_draw (GTK_WIDGET (self->visualizers));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTION]);
+}
+
+static void
+sysprof_visualizers_frame_group_activated_cb (SysprofVisualizersFrame *self,
+ SysprofVisualizerGroupHeader *row,
+ GtkListBox *list)
+{
+ SysprofVisualizerGroup *group;
+
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP_HEADER (row));
+
+ group = g_object_get_data (G_OBJECT (row), "VISUALIZER_GROUP");
+ g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
+
+ g_signal_emit_by_name (group, "group-activated");
+}
+
+static void
+sysprof_visualizers_frame_finalize (GObject *object)
+{
+ SysprofVisualizersFrame *self = (SysprofVisualizersFrame *)object;
+
+ g_clear_object (&self->selection);
+
+ G_OBJECT_CLASS (sysprof_visualizers_frame_parent_class)->finalize (object);
+}
+
+static void
+sysprof_visualizers_frame_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofVisualizersFrame *self = SYSPROF_VISUALIZERS_FRAME (object);
+
+ switch (prop_id)
+ {
+ case PROP_SELECTED_GROUP:
+ g_value_set_object (value, sysprof_visualizers_frame_get_selected_group (self));
+ break;
+
+ case PROP_SELECTION:
+ g_value_set_object (value, sysprof_visualizers_frame_get_selection (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_visualizers_frame_class_init (SysprofVisualizersFrameClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->finalize = sysprof_visualizers_frame_finalize;
+ object_class->get_property = sysprof_visualizers_frame_get_property;
+
+ widget_class->size_allocate = sysprof_visualizers_frame_size_allocate;
+
+ container_class->add = sysprof_visualizers_frame_add;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/sysprof/ui/sysprof-visualizers-frame.ui");
+ gtk_widget_class_set_css_name (widget_class, "SysprofVisualizersFrame");
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, groups);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, hscrollbar);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, hscroller);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, left_column);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, ticks);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, ticks_scroller);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, visualizers);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, vscroller);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, zoom_manager);
+ gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, zoom_scale);
+
+ properties [PROP_SELECTED_GROUP] =
+ g_param_spec_object ("selected-group",
+ "Selected Group",
+ "The selected group",
+ SYSPROF_TYPE_VISUALIZER_GROUP,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SELECTION] =
+ g_param_spec_object ("selection",
+ "Selection",
+ "The time selection",
+ SYSPROF_TYPE_SELECTION,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ g_type_ensure (SYSPROF_TYPE_SCROLLMAP);
+ g_type_ensure (SYSPROF_TYPE_VISUALIZER_TICKS);
+ g_type_ensure (SYSPROF_TYPE_ZOOM_MANAGER);
+}
+
+static void
+sysprof_visualizers_frame_init (SysprofVisualizersFrame *self)
+{
+ GtkAdjustment *hadj;
+ GtkAdjustment *zadj;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->selection = g_object_new (SYSPROF_TYPE_SELECTION, NULL);
+
+ zadj = sysprof_zoom_manager_get_adjustment (self->zoom_manager);
+ hadj = gtk_scrolled_window_get_hadjustment (self->hscroller);
+
+ gtk_scrolled_window_set_hadjustment (self->ticks_scroller, hadj);
+ gtk_range_set_adjustment (GTK_RANGE (self->hscrollbar), hadj);
+ gtk_range_set_adjustment (GTK_RANGE (self->zoom_scale), zadj);
+
+ gtk_widget_insert_action_group (GTK_WIDGET (self),
+ "zoom",
+ G_ACTION_GROUP (self->zoom_manager));
+
+ g_signal_connect_object (self->groups,
+ "row-activated",
+ G_CALLBACK (sysprof_visualizers_frame_group_activated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->selection,
+ "changed",
+ G_CALLBACK (sysprof_visualizers_frame_selection_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->visualizers,
+ "draw",
+ G_CALLBACK (visualizers_draw_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ g_signal_connect_object (self->visualizers,
+ "realize",
+ G_CALLBACK (visualizers_realize_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ g_signal_connect_object (self->visualizers,
+ "button-press-event",
+ G_CALLBACK (visualizers_button_press_event_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->visualizers,
+ "button-release-event",
+ G_CALLBACK (visualizers_button_release_event_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->visualizers,
+ "motion-notify-event",
+ G_CALLBACK (visualizers_motion_notify_event_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->zoom_manager,
+ "notify::zoom",
+ G_CALLBACK (sysprof_visualizers_frame_notify_zoom),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+}
+
+/**
+ * sysprof_visualizers_frame_get_selected_group:
+ *
+ * Gets the currently selected group.
+ *
+ * Returns: (transfer none) (nullable): the selected row
+ *
+ * Since: 3.34
+ */
+SysprofVisualizerGroup *
+sysprof_visualizers_frame_get_selected_group (SysprofVisualizersFrame *self)
+{
+ GtkListBoxRow *row;
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL);
+
+ row = gtk_list_box_get_selected_row (self->groups);
+
+ return SYSPROF_VISUALIZER_GROUP (row);
+}
+
+/**
+ * sysprof_visualizers_frame_get_selection:
+ *
+ * Get the time selection
+ *
+ * Returns: (transfer none): a #SysprofSelection
+ *
+ * Since: 3.34
+ */
+SysprofSelection *
+sysprof_visualizers_frame_get_selection (SysprofVisualizersFrame *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL);
+
+ return self->selection;
+}
+
+static gint
+compare_gint64 (const gint64 *a,
+ const gint64 *b)
+{
+ if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return 1;
+ else
+ return 0;
+}
+
+static gboolean
+index_frame_times_frame_cb (const SysprofCaptureFrame *frame,
+ gpointer user_data)
+{
+ GArray *array = user_data;
+
+ /* Track timing, but ignore some common types at startup */
+ if (frame->type != SYSPROF_CAPTURE_FRAME_MAP &&
+ frame->type != SYSPROF_CAPTURE_FRAME_PROCESS)
+ g_array_append_val (array, frame->time);
+
+ return TRUE;
+}
+
+static void
+index_frame_times_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ SysprofCaptureCursor *cursor = task_data;
+ GArray *timings = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (SYSPROF_IS_VISUALIZERS_FRAME (source_object));
+ g_assert (cursor != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ timings = g_array_new (FALSE, FALSE, sizeof (gint64));
+ sysprof_capture_cursor_foreach (cursor, index_frame_times_frame_cb, timings);
+ g_array_sort (timings, (GCompareFunc) compare_gint64);
+
+ g_task_return_pointer (task,
+ g_steal_pointer (&timings),
+ (GDestroyNotify) g_array_unref);
+}
+
+void
+sysprof_visualizers_frame_load_async (SysprofVisualizersFrame *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ GtkAllocation alloc;
+
+ g_return_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self));
+ g_return_if_fail (reader != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->ticks), &alloc);
+
+ /* At this point, the SysprofDisplay should have already scanned the
+ * reader for all of the events and therefore we can trust the begin
+ * and end time for the capture.
+ */
+ self->begin_time = sysprof_capture_reader_get_start_time (reader);
+ self->end_time = sysprof_capture_reader_get_end_time (reader);
+
+ if (alloc.width)
+ sysprof_visualizers_frame_apply_zoom (self, &alloc);
+
+ /* Now we need to run through the frames and index their times
+ * so that we can calculate the number of items per bucket when
+ * drawing the scrollbar.
+ */
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, sysprof_visualizers_frame_load_async);
+ g_task_set_task_data (task,
+ sysprof_capture_cursor_new (reader),
+ (GDestroyNotify) sysprof_capture_cursor_unref);
+ g_task_run_in_thread (task, index_frame_times_worker);
+}
+
+gboolean
+sysprof_visualizers_frame_load_finish (SysprofVisualizersFrame *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_autoptr(GArray) timings = NULL;
+
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ if ((timings = g_task_propagate_pointer (G_TASK (result), error)))
+ {
+ GtkAllocation alloc;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->ticks), &alloc);
+ sysprof_scrollmap_set_timings (self->hscrollbar, timings);
+ sysprof_visualizers_frame_apply_zoom (self, &alloc);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * sysprof_visualizers_frame_get_zoom_manager:
+ *
+ * Gets the zoom manager for the frame.
+ *
+ * Returns: (transfer none): a #SysprofZoomManager
+ */
+SysprofZoomManager *
+sysprof_visualizers_frame_get_zoom_manager (SysprofVisualizersFrame *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL);
+
+ return self->zoom_manager;
+}
+
+/**
+ * sysprof_visualizers_frame_get_size_group:
+ *
+ * gets the left column size group.
+ *
+ * Returns: (transfer none): a size group
+ */
+GtkSizeGroup *
+sysprof_visualizers_frame_get_size_group (SysprofVisualizersFrame *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL);
+
+ return self->left_column;
+}
+
+/**
+ * sysprof_visualizers_frame_get_hadjustment:
+ *
+ * Gets the scroll adjustment used for horizontal scrolling
+ *
+ * Returns: (transfer none): a #GtkAdjustment
+ */
+GtkAdjustment *
+sysprof_visualizers_frame_get_hadjustment (SysprofVisualizersFrame *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL);
+
+ return gtk_range_get_adjustment (GTK_RANGE (self->hscrollbar));
+}
diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.h b/src/libsysprof-ui/sysprof-visualizers-frame.h
new file mode 100644
index 0000000..d0501ce
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizers-frame.h
@@ -0,0 +1,49 @@
+/* sysprof-visualizers-frame.h
+ *
+ * Copyright 2019 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
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <sysprof.h>
+
+#include "sysprof-visualizer-group.h"
+#include "sysprof-zoom-manager.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_VISUALIZERS_FRAME (sysprof_visualizers_frame_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofVisualizersFrame, sysprof_visualizers_frame, SYSPROF, VISUALIZERS_FRAME, GtkBin)
+
+SysprofSelection *sysprof_visualizers_frame_get_selection (SysprofVisualizersFrame *self);
+SysprofVisualizerGroup *sysprof_visualizers_frame_get_selected_group (SysprofVisualizersFrame *self);
+SysprofZoomManager *sysprof_visualizers_frame_get_zoom_manager (SysprofVisualizersFrame *self);
+void sysprof_visualizers_frame_load_async (SysprofVisualizersFrame *self,
+ SysprofCaptureReader *reader,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean sysprof_visualizers_frame_load_finish (SysprofVisualizersFrame *self,
+ GAsyncResult *result,
+ GError **error);
+GtkSizeGroup *sysprof_visualizers_frame_get_size_group (SysprofVisualizersFrame *self);
+GtkAdjustment *sysprof_visualizers_frame_get_hadjustment (SysprofVisualizersFrame *self);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.ui b/src/libsysprof-ui/sysprof-visualizers-frame.ui
new file mode 100644
index 0000000..11cda09
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-visualizers-frame.ui
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+ <requires lib="gtk+" version="3.22"/>
+ <template class="SysprofVisualizersFrame" parent="GtkBin">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="width_request">125</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <property name="margin-top">3</property>
+ <property name="margin-bottom">3</property>
+ <property name="margin-start">7</property>
+ <property name="margin-end">7</property>
+ <style>
+ <class name="left-column"/>
+ </style>
+ <child type="center">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">3</property>
+ <property name="margin_bottom">3</property>
+ <property name="label" translatable="yes">Instruments</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">display.page</property>
+ <property name="action-target">'details'</property>
+ <property name="tooltip-text" translatable="yes">Select for more details</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ <class name="small-button"/>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">preferences-system-details-symbolic</property>
+ <property name="pixel-size">16</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="ticks_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="ticks_scroller">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">external</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="SysprofVisualizerTicks" id="ticks">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="vscroller">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="propagate_natural_height">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkListBox" id="groups">
+ <property name="width_request">125</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">False</property>
+ <property name="selection_mode">browse</property>
+ <style>
+ <class name="left-column"/>
+ <class name="visualizer-groups"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="hscroller">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">external</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="propagate_natural_height">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkListBox" id="visualizers">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <property name="activate_on_single_click">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <style>
+ <class name="inline-toolbar"/>
+ </style>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="width_request">125</property>
+ <property name="visible">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">False</property>
+ <style>
+ <class name="left-column"/>
+ </style>
+ <child>
+ <object class="GtkButton" id="zoom_out">
+ <property name="action-name">zoom.zoom-out</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">zoom-out-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ <class name="small-button"/>
+ <class name="flat"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="zoom_scale">
+ <property name="width_request">175</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="round_digits">1</property>
+ <property name="draw_value">False</property>
+ <marks>
+ <mark value="0.0" position="bottom"/>
+ </marks>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="zoom_in">
+ <property name="action-name">zoom.zoom-in</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">zoom-in-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ <class name="small-button"/>
+ <class name="flat"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="SysprofScrollmap" id="hscrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup" id="left_column">
+ <widgets>
+ <widget name="box1"/>
+ <widget name="groups"/>
+ <widget name="box2"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="top_row">
+ <property name="mode">vertical</property>
+ <widgets>
+ <widget name="box1"/>
+ <widget name="ticks_box"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="row_size_group">
+ <widgets>
+ <widget name="ticks"/>
+ <widget name="visualizers"/>
+ </widgets>
+ </object>
+ <object class="SysprofZoomManager" id="zoom_manager"/>
+</interface>
diff --git a/src/libsysprof-ui/sysprof-zoom-manager.h b/src/libsysprof-ui/sysprof-zoom-manager.h
index 8b84953..d25742f 100644
--- a/src/libsysprof-ui/sysprof-zoom-manager.h
+++ b/src/libsysprof-ui/sysprof-zoom-manager.h
@@ -20,61 +20,38 @@
#pragma once
-#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION)
-# error "Only <sysprof-ui.h> can be included directly."
-#endif
-
-#include <glib-object.h>
-
-#include "sysprof-version-macros.h"
+#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_ZOOM_MANAGER (sysprof_zoom_manager_get_type())
-SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofZoomManager, sysprof_zoom_manager, SYSPROF, ZOOM_MANAGER, GObject)
-SYSPROF_AVAILABLE_IN_ALL
SysprofZoomManager *sysprof_zoom_manager_new (void);
-SYSPROF_AVAILABLE_IN_ALL
+GtkAdjustment *sysprof_zoom_manager_get_adjustment (SysprofZoomManager *self);
gboolean sysprof_zoom_manager_get_can_zoom_in (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_zoom_manager_get_can_zoom_out (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_zoom_manager_get_min_zoom (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_zoom_manager_get_max_zoom (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_set_min_zoom (SysprofZoomManager *self,
gdouble min_zoom);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_set_max_zoom (SysprofZoomManager *self,
gdouble max_zoom);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_zoom_in (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_zoom_out (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_reset (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
gdouble sysprof_zoom_manager_get_zoom (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
void sysprof_zoom_manager_set_zoom (SysprofZoomManager *self,
gdouble zoom);
-SYSPROF_AVAILABLE_IN_ALL
gchar *sysprof_zoom_manager_get_zoom_label (SysprofZoomManager *self);
-SYSPROF_AVAILABLE_IN_ALL
gint sysprof_zoom_manager_get_width_for_duration (SysprofZoomManager *self,
gint64 duration);
-SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_zoom_manager_get_duration_for_width (SysprofZoomManager *self,
gint width);
-SYSPROF_AVAILABLE_IN_ALL
gdouble sysprof_zoom_manager_fit_zoom_for_duration (SysprofZoomManager *self,
gint64 duration,
gint width);
-SYSPROF_AVAILABLE_IN_ALL
gdouble sysprof_zoom_manager_get_offset_at_time (SysprofZoomManager *self,
gint64 offset,
gint width);
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 7cebc3c..53a3785 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -61,7 +61,8 @@ if get_option('enable_gtk')
dependencies: test_ui_deps,
)
- test_zoom = executable('test-zoom', 'test-zoom.c',
+ test_zoom = executable('test-zoom',
+ ['test-zoom.c', '../libsysprof-ui/sysprof-zoom-manager.c'],
c_args: test_cflags,
dependencies: test_ui_deps,
)
diff --git a/src/tests/test-capture-view.c b/src/tests/test-capture-view.c
index 192b111..1dca19a 100644
--- a/src/tests/test-capture-view.c
+++ b/src/tests/test-capture-view.c
@@ -25,7 +25,7 @@ main (gint argc,
gchar *argv[])
{
GtkWindow *window;
- SysprofCaptureView *view;
+ SysprofDisplay *view;
SysprofCaptureReader *reader;
g_autoptr(GError) error = NULL;
@@ -44,16 +44,16 @@ main (gint argc,
}
window = g_object_new (GTK_TYPE_WINDOW,
- "title", "SysprofCaptureView",
+ "title", "SysprofDisplay",
"default-width", 800,
"default-height", 600,
NULL);
- view = g_object_new (SYSPROF_TYPE_CAPTURE_VIEW,
+ view = g_object_new (SYSPROF_TYPE_DISPLAY,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (view));
- sysprof_capture_view_load_async (view, reader, NULL, NULL, NULL);
+ sysprof_display_load_async (view, reader, NULL, NULL, NULL);
g_signal_connect (window, "delete-event", gtk_main_quit, NULL);
gtk_window_present (GTK_WINDOW (window));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]