mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
tree: start on massive tree refactor
The big thing going on here is that we are going to split up the libraries a bit better, and remove GObject from the capture library. The libsysprof library will bring in the capture library statically, so we can export the symbols we want. Eventually, we will bump the version to sysprof-3, but not yet.
This commit is contained in:
6
src/libsysprof-ui/css/SpVisualizerView-Adwaita-dark.css
Normal file
6
src/libsysprof-ui/css/SpVisualizerView-Adwaita-dark.css
Normal file
@ -0,0 +1,6 @@
|
||||
visualizers list row {
|
||||
background-color: #201f21;
|
||||
background-size: 8px 8px;
|
||||
background-image: repeating-linear-gradient(0deg, #232224, #232224 1px, transparent 1px, transparent 8px),
|
||||
repeating-linear-gradient(-90deg, #232224, #232224 1px, transparent 1px, transparent 8px);
|
||||
}
|
||||
7
src/libsysprof-ui/css/SpVisualizerView-Adwaita.css
Normal file
7
src/libsysprof-ui/css/SpVisualizerView-Adwaita.css
Normal file
@ -0,0 +1,7 @@
|
||||
visualizers list row {
|
||||
background-color: #f6f7f8;
|
||||
background-size: 8px 8px;
|
||||
background-image: repeating-linear-gradient(0deg, #f0f1f2, #f0f1f2 1px, transparent 1px, transparent 8px),
|
||||
repeating-linear-gradient(-90deg, #f0f1f2, #f0f1f2 1px, transparent 1px, transparent 8px);
|
||||
}
|
||||
|
||||
13
src/libsysprof-ui/css/SpVisualizerView-shared.css
Normal file
13
src/libsysprof-ui/css/SpVisualizerView-shared.css
Normal file
@ -0,0 +1,13 @@
|
||||
visualizers.selection {
|
||||
background: none;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.35);
|
||||
border: 1px solid @theme_selected_bg_color;
|
||||
}
|
||||
visualizers.selection:backdrop {
|
||||
background: none;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.15);
|
||||
border: none;
|
||||
}
|
||||
visualizers list row:not(:first-child) {
|
||||
border-top: 1px solid alpha(@borders, 0.4);
|
||||
}
|
||||
20
src/libsysprof-ui/libsysprof-ui.gresource.xml
Normal file
20
src/libsysprof-ui/libsysprof-ui.gresource.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gnome/sysprof">
|
||||
<file compressed="true">css/SpVisualizerView-shared.css</file>
|
||||
<file compressed="true">css/SpVisualizerView-Adwaita.css</file>
|
||||
<file compressed="true">css/SpVisualizerView-Adwaita-dark.css</file>
|
||||
|
||||
<!-- Application icons -->
|
||||
<file alias="icons/scalable/apps/org.gnome.Sysprof.svg">../../data/icons/scalable/apps/org.gnome.Sysprof.svg</file>
|
||||
<file alias="icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg">../../data/icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg</file>
|
||||
|
||||
<file compressed="true">ui/sp-callgraph-view.ui</file>
|
||||
<file compressed="true">ui/sp-empty-state-view.ui</file>
|
||||
<file compressed="true">ui/sp-failed-state-view.ui</file>
|
||||
<file compressed="true">ui/sp-process-model-row.ui</file>
|
||||
<file compressed="true">ui/sp-profiler-menu-button.ui</file>
|
||||
<file compressed="true">ui/sp-recording-state-view.ui</file>
|
||||
<file compressed="true">ui/sp-visualizer-view.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
85
src/libsysprof-ui/meson.build
Normal file
85
src/libsysprof-ui/meson.build
Normal file
@ -0,0 +1,85 @@
|
||||
libsysprof_ui_public_sources = [
|
||||
'sp-callgraph-view.c',
|
||||
'sp-color-cycle.c',
|
||||
'sp-cpu-visualizer-row.c',
|
||||
'sp-empty-state-view.c',
|
||||
'sp-failed-state-view.c',
|
||||
'sp-line-visualizer-row.c',
|
||||
'sp-mark-visualizer-row.c',
|
||||
'sp-model-filter.c',
|
||||
'sp-multi-paned.c',
|
||||
'sp-process-model-row.c',
|
||||
'sp-profiler-menu-button.c',
|
||||
'sp-recording-state-view.c',
|
||||
'sp-visualizer-list.c',
|
||||
'sp-visualizer-row.c',
|
||||
'sp-visualizer-ticks.c',
|
||||
'sp-visualizer-view.c',
|
||||
'sp-zoom-manager.c',
|
||||
]
|
||||
|
||||
libsysprof_ui_private_sources = [
|
||||
'pointcache.c',
|
||||
'rectangles.c',
|
||||
'sp-cell-renderer-percent.c',
|
||||
'sp-theme-manager.c',
|
||||
]
|
||||
|
||||
libsysprof_ui_public_headers = [
|
||||
'sp-callgraph-view.h',
|
||||
'sp-cell-renderer-percent.h',
|
||||
'sp-cpu-visualizer-row.h',
|
||||
'sp-empty-state-view.h',
|
||||
'sp-failed-state-view.h',
|
||||
'sp-line-visualizer-row.h',
|
||||
'sp-mark-visualizer-row.h',
|
||||
'sp-model-filter.h',
|
||||
'sp-multi-paned.h',
|
||||
'sp-process-model-row.h',
|
||||
'sp-profiler-menu-button.h',
|
||||
'sp-recording-state-view.h',
|
||||
'sp-visualizer-list.h',
|
||||
'sp-visualizer-row.h',
|
||||
'sp-visualizer-ticks.h',
|
||||
'sp-visualizer-view.h',
|
||||
'sp-zoom-manager.h',
|
||||
'sysprof-ui.h',
|
||||
]
|
||||
|
||||
libsysprof_ui_resources = gnome.compile_resources(
|
||||
'libsysprof-ui-resources',
|
||||
'libsysprof-ui.gresource.xml',
|
||||
c_name: 'lisysprof_ui',
|
||||
)
|
||||
|
||||
libsysprof_ui_deps = [
|
||||
dependency('gio-2.0', version: glib_req_version),
|
||||
dependency('gtk+-3.0', version: gtk_req_version),
|
||||
libsysprof_dep,
|
||||
libshared_dep,
|
||||
]
|
||||
|
||||
libsysprof_ui = shared_library('sysprof-ui-@0@'.format(libsysprof_api_version),
|
||||
libsysprof_ui_public_sources + libsysprof_ui_private_sources + libsysprof_ui_resources,
|
||||
dependencies: libsysprof_ui_deps,
|
||||
install_dir: get_option('libdir'),
|
||||
install: true,
|
||||
)
|
||||
|
||||
libsysprof_ui_dep = declare_dependency(
|
||||
link_with: libsysprof_ui,
|
||||
dependencies: libsysprof_ui_deps,
|
||||
include_directories: include_directories('.'),
|
||||
)
|
||||
|
||||
pkgconfig.generate(
|
||||
subdirs: [ sysprof_header_subdir ],
|
||||
version: meson.project_version(),
|
||||
name: 'sysprof-ui-@0@'.format(libsysprof_api_version),
|
||||
filebase: 'sysprof-ui-@0@'.format(libsysprof_api_version),
|
||||
description: 'The UI library for GTK applications embedding sysprof',
|
||||
install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
|
||||
requires: [ 'gio-2.0', 'gtk+-3.0' ],
|
||||
)
|
||||
|
||||
install_headers(libsysprof_ui_public_headers, subdir: sysprof_header_subdir)
|
||||
105
src/libsysprof-ui/pointcache.c
Normal file
105
src/libsysprof-ui/pointcache.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* pointcache.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "pointcache"
|
||||
|
||||
#include "pointcache.h"
|
||||
|
||||
struct _PointCache
|
||||
{
|
||||
volatile gint ref_count;
|
||||
GHashTable *sets;
|
||||
};
|
||||
|
||||
static void
|
||||
point_cache_destroy (PointCache *self)
|
||||
{
|
||||
g_clear_pointer (&self->sets, g_hash_table_unref);
|
||||
g_slice_free (PointCache, self);
|
||||
}
|
||||
|
||||
PointCache *
|
||||
point_cache_ref (PointCache *self)
|
||||
{
|
||||
g_return_val_if_fail (self->ref_count > 0, NULL);
|
||||
g_atomic_int_inc (&self->ref_count);
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
point_cache_unref (PointCache *self)
|
||||
{
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
if (g_atomic_int_dec_and_test (&self->ref_count))
|
||||
point_cache_destroy (self);
|
||||
}
|
||||
|
||||
PointCache *
|
||||
point_cache_new (void)
|
||||
{
|
||||
PointCache *self;
|
||||
|
||||
self = g_slice_new0 (PointCache);
|
||||
self->ref_count = 1;
|
||||
self->sets = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
point_cache_add_set (PointCache *self,
|
||||
guint set_id)
|
||||
{
|
||||
g_hash_table_insert (self->sets,
|
||||
GUINT_TO_POINTER (set_id),
|
||||
g_array_new (FALSE, FALSE, sizeof (Point)));
|
||||
}
|
||||
|
||||
gboolean
|
||||
point_cache_contains_set (PointCache *self,
|
||||
guint set_id)
|
||||
{
|
||||
return g_hash_table_contains (self->sets, GUINT_TO_POINTER (set_id));
|
||||
}
|
||||
|
||||
void
|
||||
point_cache_add_point_to_set (PointCache *self,
|
||||
guint set_id,
|
||||
gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
GArray *ar;
|
||||
Point point = { x, y };
|
||||
|
||||
ar = g_hash_table_lookup (self->sets, GUINT_TO_POINTER (set_id));
|
||||
g_array_append_val (ar, point);
|
||||
}
|
||||
|
||||
const Point *
|
||||
point_cache_get_points (PointCache *self,
|
||||
guint set_id,
|
||||
guint *n_points)
|
||||
{
|
||||
GArray *ar;
|
||||
|
||||
ar = g_hash_table_lookup (self->sets, GUINT_TO_POINTER (set_id));
|
||||
*n_points = ar->len;
|
||||
return &g_array_index (ar, const Point, 0);
|
||||
}
|
||||
55
src/libsysprof-ui/pointcache.h
Normal file
55
src/libsysprof-ui/pointcache.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* pointcache.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
|
||||
*/
|
||||
|
||||
#ifndef POINT_CACHE_H
|
||||
#define POINT_CACHE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PointCache PointCache;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
} Point;
|
||||
|
||||
PointCache *point_cache_new (void);
|
||||
PointCache *point_cache_ref (PointCache *self);
|
||||
void point_cache_unref (PointCache *self);
|
||||
void point_cache_add_set (PointCache *self,
|
||||
guint set_id);
|
||||
gboolean point_cache_contains_set (PointCache *self,
|
||||
guint set_id);
|
||||
void point_cache_add_point_to_set (PointCache *self,
|
||||
guint set_id,
|
||||
gdouble x,
|
||||
gdouble y);
|
||||
const Point *point_cache_get_points (PointCache *self,
|
||||
guint set_id,
|
||||
guint *n_points);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PointCache, point_cache_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* POINT_CACHE_H */
|
||||
248
src/libsysprof-ui/rectangles.c
Normal file
248
src/libsysprof-ui/rectangles.c
Normal file
@ -0,0 +1,248 @@
|
||||
/* rectangles.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
|
||||
*/
|
||||
|
||||
#include "rectangles.h"
|
||||
#include "sp-color-cycle.h"
|
||||
#include "sp-visualizer-row.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *name;
|
||||
const gchar *message;
|
||||
gint64 begin;
|
||||
gint64 end;
|
||||
GdkRectangle area;
|
||||
} Rectangle;
|
||||
|
||||
struct _Rectangles
|
||||
{
|
||||
GStringChunk *strings;
|
||||
GArray *rectangles;
|
||||
GHashTable *y_indexes;
|
||||
GHashTable *colors;
|
||||
SpColorCycle *cycle;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
guint sorted : 1;
|
||||
};
|
||||
|
||||
Rectangles *
|
||||
rectangles_new (gint64 begin_time,
|
||||
gint64 end_time)
|
||||
{
|
||||
Rectangles *self;
|
||||
|
||||
self = g_slice_new0 (Rectangles);
|
||||
self->strings = g_string_chunk_new (4096);
|
||||
self->rectangles = g_array_new (FALSE, FALSE, sizeof (Rectangle));
|
||||
self->y_indexes = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
self->colors = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
|
||||
self->cycle = sp_color_cycle_new ();
|
||||
self->begin_time = begin_time;
|
||||
self->end_time = end_time;
|
||||
self->sorted = FALSE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
rectangles_add (Rectangles *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time,
|
||||
const gchar *name,
|
||||
const gchar *message)
|
||||
{
|
||||
Rectangle rect = {0};
|
||||
|
||||
g_assert (self != NULL);
|
||||
|
||||
if (message != NULL)
|
||||
rect.message = g_string_chunk_insert_const (self->strings, message);
|
||||
|
||||
if (name != NULL)
|
||||
rect.name = g_string_chunk_insert_const (self->strings, name);
|
||||
|
||||
rect.begin = begin_time;
|
||||
rect.end = end_time;
|
||||
|
||||
if (rect.end == rect.begin)
|
||||
rect.area.width = 1;
|
||||
|
||||
g_array_append_val (self->rectangles, rect);
|
||||
|
||||
self->sorted = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
rectangles_free (Rectangles *self)
|
||||
{
|
||||
g_string_chunk_free (self->strings);
|
||||
g_array_unref (self->rectangles);
|
||||
g_hash_table_unref (self->colors);
|
||||
g_hash_table_unref (self->y_indexes);
|
||||
sp_color_cycle_unref (self->cycle);
|
||||
g_slice_free (Rectangles, self);
|
||||
}
|
||||
|
||||
static gint
|
||||
sort_rectangles (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const Rectangle *r1 = a;
|
||||
const Rectangle *r2 = b;
|
||||
gint64 r = r1->begin - r2->begin;
|
||||
if (r == 0)
|
||||
r = r1->end - r2->end;
|
||||
if (r > 0) return 1;
|
||||
else if (r < 0) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rectangles_sort (Rectangles *self)
|
||||
{
|
||||
guint sequence = 0;
|
||||
|
||||
g_assert (self != NULL);
|
||||
|
||||
if (self->sorted)
|
||||
return;
|
||||
|
||||
g_array_sort (self->rectangles, sort_rectangles);
|
||||
|
||||
g_hash_table_remove_all (self->y_indexes);
|
||||
|
||||
for (guint i = 0; i < self->rectangles->len; i++)
|
||||
{
|
||||
const Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i);
|
||||
|
||||
if (!g_hash_table_contains (self->y_indexes, rect->name))
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
sp_color_cycle_next (self->cycle, &rgba);
|
||||
g_hash_table_insert (self->y_indexes, (gchar *)rect->name, GUINT_TO_POINTER (++sequence));
|
||||
g_hash_table_insert (self->colors, (gchar *)rect->name, g_memdup (&rgba, sizeof rgba));
|
||||
}
|
||||
}
|
||||
|
||||
self->sorted = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
rectangles_draw (Rectangles *self,
|
||||
GtkWidget *row,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
gdouble range;
|
||||
guint n_rows;
|
||||
|
||||
g_assert (self != NULL);
|
||||
g_assert (SP_IS_VISUALIZER_ROW (row));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
if (!self->sorted)
|
||||
rectangles_sort (self);
|
||||
|
||||
gtk_widget_get_allocation (row, &alloc);
|
||||
n_rows = g_hash_table_size (self->y_indexes);
|
||||
if (n_rows == 0 || alloc.height == 0)
|
||||
return;
|
||||
|
||||
range = self->end_time - self->begin_time;
|
||||
|
||||
for (guint i = 0; i < self->rectangles->len; i++)
|
||||
{
|
||||
Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i);
|
||||
guint y_index = GPOINTER_TO_UINT (g_hash_table_lookup (self->y_indexes, rect->name));
|
||||
SpVisualizerRowRelativePoint in_points[2];
|
||||
SpVisualizerRowAbsolutePoint out_points[2];
|
||||
GdkRectangle r;
|
||||
GdkRGBA *rgba;
|
||||
|
||||
g_assert (y_index > 0);
|
||||
g_assert (y_index <= n_rows);
|
||||
|
||||
in_points[0].x = (rect->begin - self->begin_time) / range;
|
||||
in_points[0].y = (y_index - 1) / (gdouble)n_rows;
|
||||
in_points[1].x = (rect->end - self->begin_time) / range;
|
||||
in_points[1].y = 0;
|
||||
|
||||
sp_visualizer_row_translate_points (SP_VISUALIZER_ROW (row),
|
||||
in_points, G_N_ELEMENTS (in_points),
|
||||
out_points, G_N_ELEMENTS (out_points));
|
||||
|
||||
r.height = alloc.height / (gdouble)n_rows;
|
||||
r.x = out_points[0].x;
|
||||
r.y = out_points[0].y - r.height;
|
||||
|
||||
if (rect->end <= rect->begin)
|
||||
r.width = 1;
|
||||
else
|
||||
r.width = MAX (1, out_points[1].x - out_points[0].x);
|
||||
|
||||
rect->area = r;
|
||||
|
||||
rgba = g_hash_table_lookup (self->colors, rect->name);
|
||||
|
||||
gdk_cairo_rectangle (cr, &r);
|
||||
gdk_cairo_set_source_rgba (cr, rgba);
|
||||
cairo_fill (cr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rectangles_set_end_time (Rectangles *self,
|
||||
gint64 end_time)
|
||||
{
|
||||
/* We might not know the real end time until we've exhausted the stream */
|
||||
self->end_time = end_time;
|
||||
}
|
||||
|
||||
gboolean
|
||||
rectangles_query_tooltip (Rectangles *self,
|
||||
GtkTooltip *tooltip,
|
||||
const gchar *group,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
g_assert (self != NULL);
|
||||
g_assert (GTK_IS_TOOLTIP (tooltip));
|
||||
|
||||
/* TODO: Sort, binary search, etc. */
|
||||
|
||||
for (guint i = 0; i < self->rectangles->len; i++)
|
||||
{
|
||||
const Rectangle *r = &g_array_index (self->rectangles, Rectangle, i);
|
||||
|
||||
if (r->area.x <= x &&
|
||||
r->area.y <= y &&
|
||||
r->area.x + r->area.width >= x &&
|
||||
r->area.y + r->area.height >= y)
|
||||
{
|
||||
g_autofree gchar *text = g_strdup_printf ("%s:%s: %s", group, r->name, r->message);
|
||||
gtk_tooltip_set_text (tooltip, text);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
48
src/libsysprof-ui/rectangles.h
Normal file
48
src/libsysprof-ui/rectangles.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* rectangles.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 <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _Rectangles Rectangles;
|
||||
|
||||
Rectangles *rectangles_new (gint64 begin_time,
|
||||
gint64 end_time);
|
||||
void rectangles_free (Rectangles *self);
|
||||
void rectangles_draw (Rectangles *self,
|
||||
GtkWidget *widget,
|
||||
cairo_t *cr);
|
||||
void rectangles_add (Rectangles *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time,
|
||||
const gchar *name,
|
||||
const gchar *message);
|
||||
void rectangles_set_end_time (Rectangles *self,
|
||||
gint64 end_time);
|
||||
gboolean rectangles_query_tooltip (Rectangles *self,
|
||||
GtkTooltip *tooltip,
|
||||
const gchar *group,
|
||||
gint x,
|
||||
gint y);
|
||||
|
||||
G_END_DECLS
|
||||
1085
src/libsysprof-ui/sp-callgraph-view.c
Normal file
1085
src/libsysprof-ui/sp-callgraph-view.c
Normal file
File diff suppressed because it is too large
Load Diff
52
src/libsysprof-ui/sp-callgraph-view.h
Normal file
52
src/libsysprof-ui/sp-callgraph-view.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* sp-callgraph-view.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
|
||||
*/
|
||||
|
||||
#ifndef SP_CALLGRAPH_VIEW_H
|
||||
#define SP_CALLGRAPH_VIEW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-callgraph-profile.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_CALLGRAPH_VIEW (sp_callgraph_view_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpCallgraphView, sp_callgraph_view, SP, CALLGRAPH_VIEW, GtkBin)
|
||||
|
||||
struct _SpCallgraphViewClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
|
||||
void (*go_previous) (SpCallgraphView *self);
|
||||
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
GtkWidget *sp_callgraph_view_new (void);
|
||||
SpCallgraphProfile *sp_callgraph_view_get_profile (SpCallgraphView *self);
|
||||
void sp_callgraph_view_set_profile (SpCallgraphView *self,
|
||||
SpCallgraphProfile *profile);
|
||||
gchar *sp_callgraph_view_screenshot (SpCallgraphView *self);
|
||||
guint sp_callgraph_view_get_n_functions (SpCallgraphView *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_CALLGRAPH_VIEW_H */
|
||||
137
src/libsysprof-ui/sp-cell-renderer-percent.c
Normal file
137
src/libsysprof-ui/sp-cell-renderer-percent.c
Normal file
@ -0,0 +1,137 @@
|
||||
/* sp-cell-renderer-percent.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
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "sp-cell-renderer-percent.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble percent;
|
||||
} SpCellRendererPercentPrivate;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PERCENT,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpCellRendererPercent, sp_cell_renderer_percent, GTK_TYPE_CELL_RENDERER_TEXT)
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
sp_cell_renderer_percent_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpCellRendererPercent *self = SP_CELL_RENDERER_PERCENT (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PERCENT:
|
||||
g_value_set_double (value, sp_cell_renderer_percent_get_percent (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cell_renderer_percent_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpCellRendererPercent *self = SP_CELL_RENDERER_PERCENT (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PERCENT:
|
||||
sp_cell_renderer_percent_set_percent (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cell_renderer_percent_class_init (SpCellRendererPercentClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = sp_cell_renderer_percent_get_property;
|
||||
object_class->set_property = sp_cell_renderer_percent_set_property;
|
||||
|
||||
properties [PROP_PERCENT] =
|
||||
g_param_spec_double ("percent",
|
||||
"Percent",
|
||||
"Percent",
|
||||
0.0,
|
||||
100.0,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cell_renderer_percent_init (SpCellRendererPercent *self)
|
||||
{
|
||||
g_object_set (self, "xalign", 1.0f, NULL);
|
||||
}
|
||||
|
||||
gdouble
|
||||
sp_cell_renderer_percent_get_percent (SpCellRendererPercent *self)
|
||||
{
|
||||
SpCellRendererPercentPrivate *priv = sp_cell_renderer_percent_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_CELL_RENDERER_PERCENT (self), 0.0);
|
||||
|
||||
return priv->percent;
|
||||
}
|
||||
|
||||
void
|
||||
sp_cell_renderer_percent_set_percent (SpCellRendererPercent *self,
|
||||
gdouble percent)
|
||||
{
|
||||
SpCellRendererPercentPrivate *priv = sp_cell_renderer_percent_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_CELL_RENDERER_PERCENT (self));
|
||||
g_return_if_fail (percent >= 0.0);
|
||||
g_return_if_fail (percent <= 100.0);
|
||||
|
||||
if (percent != priv->percent)
|
||||
{
|
||||
gchar text[128];
|
||||
|
||||
priv->percent = percent;
|
||||
|
||||
g_snprintf (text, sizeof text, "%.2lf<span size='smaller'><span size='smaller'> </span>%%</span>", percent);
|
||||
text [sizeof text - 1] = '\0';
|
||||
|
||||
g_object_set (self, "markup", text, NULL);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PERCENT]);
|
||||
}
|
||||
}
|
||||
59
src/libsysprof-ui/sp-cell-renderer-percent.h
Normal file
59
src/libsysprof-ui/sp-cell-renderer-percent.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* sp-cell-renderer-percent.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
|
||||
*/
|
||||
|
||||
#ifndef SP_CELL_RENDERER_PERCENT_H
|
||||
#define SP_CELL_RENDERER_PERCENT_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_CELL_RENDERER_PERCENT (sp_cell_renderer_percent_get_type())
|
||||
#define SP_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercent))
|
||||
#define SP_CELL_RENDERER_PERCENT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercent const))
|
||||
#define SP_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercentClass))
|
||||
#define SP_IS_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CELL_RENDERER_PERCENT))
|
||||
#define SP_IS_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CELL_RENDERER_PERCENT))
|
||||
#define SP_CELL_RENDERER_PERCENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercentClass))
|
||||
|
||||
typedef struct _SpCellRendererPercent SpCellRendererPercent;
|
||||
typedef struct _SpCellRendererPercentClass SpCellRendererPercentClass;
|
||||
|
||||
struct _SpCellRendererPercent
|
||||
{
|
||||
GtkCellRendererText parent;
|
||||
};
|
||||
|
||||
struct _SpCellRendererPercentClass
|
||||
{
|
||||
GtkCellRendererTextClass parent_class;
|
||||
|
||||
gpointer padding[4];
|
||||
};
|
||||
|
||||
GType sp_cell_renderer_percent_get_type (void);
|
||||
GtkCellRenderer *sp_cell_renderer_percent_new (void);
|
||||
gdouble sp_cell_renderer_percent_get_percent (SpCellRendererPercent *self);
|
||||
void sp_cell_renderer_percent_set_percent (SpCellRendererPercent *self,
|
||||
gdouble percent);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_CELL_RENDERER_PERCENT_H */
|
||||
138
src/libsysprof-ui/sp-color-cycle.c
Normal file
138
src/libsysprof-ui/sp-color-cycle.c
Normal file
@ -0,0 +1,138 @@
|
||||
/* sp-color-cycle.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-color-cycle"
|
||||
|
||||
#include "sp-color-cycle.h"
|
||||
|
||||
G_DEFINE_BOXED_TYPE (SpColorCycle, sp_color_cycle, sp_color_cycle_ref, sp_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",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct _SpColorCycle
|
||||
{
|
||||
volatile gint ref_count;
|
||||
GdkRGBA *colors;
|
||||
gsize n_colors;
|
||||
guint position;
|
||||
};
|
||||
|
||||
static void
|
||||
sp_color_cycle_destroy (SpColorCycle *self)
|
||||
{
|
||||
g_free (self->colors);
|
||||
g_slice_free (SpColorCycle, self);
|
||||
}
|
||||
|
||||
SpColorCycle *
|
||||
sp_color_cycle_new (void)
|
||||
{
|
||||
SpColorCycle *self;
|
||||
|
||||
self = g_slice_new0 (SpColorCycle);
|
||||
self->ref_count = 1;
|
||||
self->n_colors = g_strv_length ((gchar **)default_colors);
|
||||
self->colors = g_new0 (GdkRGBA, self->n_colors);
|
||||
|
||||
for (guint i = 0; default_colors[i]; i++)
|
||||
{
|
||||
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
|
||||
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
SpColorCycle *
|
||||
sp_color_cycle_ref (SpColorCycle *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (self->ref_count > 0, NULL);
|
||||
g_atomic_int_inc (&self->ref_count);
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_unref (SpColorCycle *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
if (g_atomic_int_dec_and_test (&self->ref_count))
|
||||
sp_color_cycle_destroy (self);
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_next (SpColorCycle *self,
|
||||
GdkRGBA *rgba)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->position < self->n_colors);
|
||||
|
||||
*rgba = self->colors[self->position];
|
||||
|
||||
/*
|
||||
* TODO: Adjust color HSV/etc
|
||||
*
|
||||
* We could simply adjust the brightness/etc after we dispatch
|
||||
* a color so that we get darker as we go.
|
||||
*/
|
||||
|
||||
self->position = (self->position + 1) % self->n_colors;
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_reset (SpColorCycle *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
for (guint i = 0; default_colors[i]; i++)
|
||||
{
|
||||
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
|
||||
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
|
||||
}
|
||||
|
||||
self->position = 0;
|
||||
}
|
||||
42
src/libsysprof-ui/sp-color-cycle.h
Normal file
42
src/libsysprof-ui/sp-color-cycle.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* sp-color-cycle.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
|
||||
*/
|
||||
|
||||
#ifndef SP_COLOR_CYCLE_H
|
||||
#define SP_COLOR_CYCLE_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_COLOR_CYCLE (sp_color_cycle_get_type())
|
||||
|
||||
typedef struct _SpColorCycle SpColorCycle;
|
||||
|
||||
GType sp_color_cycle_get_type (void);
|
||||
SpColorCycle *sp_color_cycle_ref (SpColorCycle *self);
|
||||
void sp_color_cycle_unref (SpColorCycle *self);
|
||||
SpColorCycle *sp_color_cycle_new (void);
|
||||
void sp_color_cycle_reset (SpColorCycle *self);
|
||||
void sp_color_cycle_next (SpColorCycle *self,
|
||||
GdkRGBA *rgba);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_COLOR_CYCLE_H */
|
||||
171
src/libsysprof-ui/sp-cpu-visualizer-row.c
Normal file
171
src/libsysprof-ui/sp-cpu-visualizer-row.c
Normal file
@ -0,0 +1,171 @@
|
||||
/* sp-cpu-visualizer-row.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-cpu-visualizer-row"
|
||||
|
||||
#include "sp-capture-condition.h"
|
||||
#include "sp-capture-cursor.h"
|
||||
#include "sp-color-cycle.h"
|
||||
#include "sp-cpu-visualizer-row.h"
|
||||
|
||||
struct _SpCpuVisualizerRow
|
||||
{
|
||||
SpLineVisualizerRow parent_instance;
|
||||
SpColorCycle *colors;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SpCpuVisualizerRow, sp_cpu_visualizer_row, SP_TYPE_LINE_VISUALIZER_ROW)
|
||||
|
||||
static gboolean
|
||||
sp_cpu_visualizer_counter_found (const SpCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
const SpCaptureFrameCounterDefine *def = (SpCaptureFrameCounterDefine *)frame;
|
||||
GArray *counters = user_data;
|
||||
gboolean found = FALSE;
|
||||
|
||||
g_assert (frame->type == SP_CAPTURE_FRAME_CTRDEF);
|
||||
|
||||
/*
|
||||
* In practice, all the CPU counters are defined at once, so we can avoid
|
||||
* walking the rest of the capture by returning after we find our CTRDEF.
|
||||
*/
|
||||
|
||||
for (guint i = 0; i < def->n_counters; i++)
|
||||
{
|
||||
if (g_str_equal (def->counters[i].category, "CPU Percent"))
|
||||
{
|
||||
guint id = def->counters[i].id;
|
||||
g_array_append_val (counters, id);
|
||||
found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return !found;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cpu_visualizer_row_discover_counters (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *canellable)
|
||||
{
|
||||
const SpCaptureFrameType types[] = { SP_CAPTURE_FRAME_CTRDEF };
|
||||
SpCaptureReader *reader = task_data;
|
||||
g_autoptr(SpCaptureCursor) cursor = NULL;
|
||||
g_autoptr(GArray) counters = NULL;
|
||||
|
||||
g_assert (G_IS_TASK (task));
|
||||
g_assert (SP_IS_CPU_VISUALIZER_ROW (source_object));
|
||||
g_assert (reader != NULL);
|
||||
|
||||
counters = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||
cursor = sp_capture_cursor_new (reader);
|
||||
sp_capture_cursor_add_condition (cursor, sp_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types));
|
||||
sp_capture_cursor_foreach (cursor, sp_cpu_visualizer_counter_found, counters);
|
||||
g_task_return_pointer (task, g_steal_pointer (&counters), (GDestroyNotify)g_array_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
complete_counters (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpCpuVisualizerRow *self = (SpCpuVisualizerRow *)object;
|
||||
g_autoptr(GArray) counters = NULL;
|
||||
|
||||
g_assert (SP_IS_CPU_VISUALIZER_ROW (self));
|
||||
g_assert (G_IS_TASK (result));
|
||||
|
||||
counters = g_task_propagate_pointer (G_TASK (result), NULL);
|
||||
|
||||
if (counters != NULL)
|
||||
{
|
||||
for (guint i = 0; i < counters->len; i++)
|
||||
{
|
||||
guint counter_id = g_array_index (counters, guint, i);
|
||||
GdkRGBA color;
|
||||
|
||||
sp_color_cycle_next (self->colors, &color);
|
||||
sp_line_visualizer_row_add_counter (SP_LINE_VISUALIZER_ROW (self), counter_id, &color);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide ourself if we failed to locate counters */
|
||||
gtk_widget_set_visible (GTK_WIDGET (self), counters != NULL && counters->len > 0);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cpu_visualizer_row_set_reader (SpVisualizerRow *row,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpCpuVisualizerRow *self = (SpCpuVisualizerRow *)row;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
|
||||
g_assert (SP_IS_CPU_VISUALIZER_ROW (row));
|
||||
|
||||
sp_color_cycle_reset (self->colors);
|
||||
|
||||
sp_line_visualizer_row_clear (SP_LINE_VISUALIZER_ROW (row));
|
||||
|
||||
SP_VISUALIZER_ROW_CLASS (sp_cpu_visualizer_row_parent_class)->set_reader (row, reader);
|
||||
|
||||
if (reader != NULL)
|
||||
{
|
||||
task = g_task_new (self, NULL, complete_counters, NULL);
|
||||
g_task_set_source_tag (task, sp_cpu_visualizer_row_set_reader);
|
||||
g_task_set_task_data (task, sp_capture_reader_copy (reader),
|
||||
(GDestroyNotify)sp_capture_reader_unref);
|
||||
g_task_run_in_thread (task, sp_cpu_visualizer_row_discover_counters);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cpu_visualizer_row_finalize (GObject *object)
|
||||
{
|
||||
SpCpuVisualizerRow *self = (SpCpuVisualizerRow *)object;
|
||||
|
||||
g_clear_pointer (&self->colors, sp_color_cycle_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_cpu_visualizer_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cpu_visualizer_row_class_init (SpCpuVisualizerRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
SpVisualizerRowClass *row_class = SP_VISUALIZER_ROW_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_cpu_visualizer_row_finalize;
|
||||
|
||||
row_class->set_reader = sp_cpu_visualizer_row_set_reader;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_cpu_visualizer_row_init (SpCpuVisualizerRow *self)
|
||||
{
|
||||
self->colors = sp_color_cycle_new ();
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
sp_cpu_visualizer_row_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_CPU_VISUALIZER_ROW, NULL);
|
||||
}
|
||||
36
src/libsysprof-ui/sp-cpu-visualizer-row.h
Normal file
36
src/libsysprof-ui/sp-cpu-visualizer-row.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* sp-cpu-visualizer-row.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
|
||||
*/
|
||||
|
||||
#ifndef SP_CPU_VISUALIZER_ROW_H
|
||||
#define SP_CPU_VISUALIZER_ROW_H
|
||||
|
||||
#include "sp-line-visualizer-row.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_CPU_VISUALIZER_ROW (sp_cpu_visualizer_row_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpCpuVisualizerRow, sp_cpu_visualizer_row, SP, CPU_VISUALIZER_ROW, SpLineVisualizerRow)
|
||||
|
||||
GtkWidget *sp_cpu_visualizer_row_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_CPU_VISUALIZER_ROW_H */
|
||||
207
src/libsysprof-ui/sp-empty-state-view.c
Normal file
207
src/libsysprof-ui/sp-empty-state-view.c
Normal file
@ -0,0 +1,207 @@
|
||||
/* sp-empty-state-view.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-empty-state-view"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sp-empty-state-view.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkLabel *title;
|
||||
GtkLabel *subtitle;
|
||||
} SpEmptyStateViewPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpEmptyStateView, sp_empty_state_view, GTK_TYPE_BIN)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TITLE,
|
||||
PROP_SUBTITLE,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
GtkWidget *
|
||||
sp_empty_state_view_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_EMPTY_STATE_VIEW, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_empty_state_view_action (GtkWidget *widget,
|
||||
const gchar *prefix,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
GApplication *app;
|
||||
GActionGroup *group = NULL;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
|
||||
g_return_val_if_fail (prefix, FALSE);
|
||||
g_return_val_if_fail (action_name, FALSE);
|
||||
|
||||
app = g_application_get_default ();
|
||||
toplevel = gtk_widget_get_toplevel (widget);
|
||||
|
||||
while ((group == NULL) && (widget != NULL))
|
||||
{
|
||||
group = gtk_widget_get_action_group (widget, prefix);
|
||||
widget = gtk_widget_get_parent (widget);
|
||||
}
|
||||
|
||||
if (!group && g_str_equal (prefix, "win") && G_IS_ACTION_GROUP (toplevel))
|
||||
group = G_ACTION_GROUP (toplevel);
|
||||
|
||||
if (!group && g_str_equal (prefix, "app") && G_IS_ACTION_GROUP (app))
|
||||
group = G_ACTION_GROUP (app);
|
||||
|
||||
if (group && g_action_group_has_action (group, action_name))
|
||||
{
|
||||
g_action_group_activate_action (group, action_name, parameter);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (parameter && g_variant_is_floating (parameter))
|
||||
{
|
||||
parameter = g_variant_ref_sink (parameter);
|
||||
g_variant_unref (parameter);
|
||||
}
|
||||
|
||||
g_warning ("Failed to locate action %s.%s", prefix, action_name);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_empty_state_view_activate_link (SpEmptyStateView *self,
|
||||
const gchar *uri,
|
||||
GtkLabel *label)
|
||||
{
|
||||
g_assert (SP_IS_EMPTY_STATE_VIEW (self));
|
||||
g_assert (uri != NULL);
|
||||
g_assert (GTK_IS_LABEL (label));
|
||||
|
||||
if (g_str_has_prefix (uri, "action://"))
|
||||
{
|
||||
g_autofree gchar *full_name = NULL;
|
||||
g_autofree gchar *action_name = NULL;
|
||||
g_autofree gchar *group_name = NULL;
|
||||
g_autoptr(GVariant) param = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
uri += strlen ("action://");
|
||||
|
||||
if (g_action_parse_detailed_name (uri, &full_name, ¶m, &error))
|
||||
{
|
||||
const gchar *dot = strchr (full_name, '.');
|
||||
|
||||
if (param != NULL && g_variant_is_floating (param))
|
||||
param = g_variant_ref_sink (param);
|
||||
|
||||
if (dot == NULL)
|
||||
return FALSE;
|
||||
|
||||
group_name = g_strndup (full_name, dot - full_name);
|
||||
action_name = g_strdup (++dot);
|
||||
|
||||
sp_empty_state_view_action (GTK_WIDGET (self),
|
||||
group_name,
|
||||
action_name,
|
||||
param);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
g_warning ("%s", error->message);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_empty_state_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpEmptyStateView *self = SP_EMPTY_STATE_VIEW (object);
|
||||
SpEmptyStateViewPrivate *priv = sp_empty_state_view_get_instance_private (self);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TITLE:
|
||||
gtk_label_set_label (priv->title, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_SUBTITLE:
|
||||
gtk_label_set_label (priv->subtitle, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_empty_state_view_class_init (SpEmptyStateViewClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->set_property = sp_empty_state_view_set_property;
|
||||
|
||||
properties [PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_SUBTITLE] =
|
||||
g_param_spec_string ("subtitle",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-empty-state-view.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpEmptyStateView, subtitle);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpEmptyStateView, title);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_empty_state_view_init (SpEmptyStateView *self)
|
||||
{
|
||||
SpEmptyStateViewPrivate *priv = sp_empty_state_view_get_instance_private (self);
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
g_signal_connect_object (priv->subtitle,
|
||||
"activate-link",
|
||||
G_CALLBACK (sp_empty_state_view_activate_link),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
44
src/libsysprof-ui/sp-empty-state-view.h
Normal file
44
src/libsysprof-ui/sp-empty-state-view.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* sp-empty-state-view.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
|
||||
*/
|
||||
|
||||
#ifndef SP_EMPTY_STATE_VIEW_H
|
||||
#define SP_EMPTY_STATE_VIEW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_EMPTY_STATE_VIEW (sp_empty_state_view_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpEmptyStateView, sp_empty_state_view, SP, EMPTY_STATE_VIEW, GtkBin)
|
||||
|
||||
struct _SpEmptyStateViewClass
|
||||
{
|
||||
GtkBinClass parent;
|
||||
|
||||
gpointer padding[4];
|
||||
};
|
||||
|
||||
GtkWidget *sp_empty_state_view_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_EMPTY_STATE_VIEW_H */
|
||||
|
||||
44
src/libsysprof-ui/sp-failed-state-view.c
Normal file
44
src/libsysprof-ui/sp-failed-state-view.c
Normal file
@ -0,0 +1,44 @@
|
||||
/* sp-failed-state-view.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
|
||||
*/
|
||||
|
||||
#include "sp-failed-state-view.h"
|
||||
|
||||
G_DEFINE_TYPE (SpFailedStateView, sp_failed_state_view, GTK_TYPE_BIN)
|
||||
|
||||
GtkWidget *
|
||||
sp_failed_state_view_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_FAILED_STATE_VIEW, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_failed_state_view_class_init (SpFailedStateViewClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class,
|
||||
"/org/gnome/sysprof/ui/sp-failed-state-view.ui");
|
||||
}
|
||||
|
||||
static void
|
||||
sp_failed_state_view_init (SpFailedStateView *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
}
|
||||
47
src/libsysprof-ui/sp-failed-state-view.h
Normal file
47
src/libsysprof-ui/sp-failed-state-view.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* sp-failed-state-view.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
|
||||
*/
|
||||
|
||||
#ifndef SP_FAILED_STATE_VIEW_H
|
||||
#define SP_FAILED_STATE_VIEW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-profiler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_FAILED_STATE_VIEW (sp_failed_state_view_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpFailedStateView, sp_failed_state_view, SP, FAILED_STATE_VIEW, GtkBin)
|
||||
|
||||
struct _SpFailedStateViewClass
|
||||
{
|
||||
GtkBinClass parent;
|
||||
|
||||
gpointer padding[4];
|
||||
};
|
||||
|
||||
GtkWidget *sp_failed_state_view_new (void);
|
||||
void sp_failed_state_view_set_profiler (SpFailedStateView *self,
|
||||
SpProfiler *profiler);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_FAILED_STATE_VIEW_H */
|
||||
819
src/libsysprof-ui/sp-line-visualizer-row.c
Normal file
819
src/libsysprof-ui/sp-line-visualizer-row.c
Normal file
@ -0,0 +1,819 @@
|
||||
/* sp-line-visualizer-row.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 "sp-line-visualizer-row"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pointcache.h"
|
||||
#include "sp-capture-condition.h"
|
||||
#include "sp-capture-cursor.h"
|
||||
#include "sp-line-visualizer-row.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
* Our reader as assigned by the visualizer system.
|
||||
*/
|
||||
SpCaptureReader *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;
|
||||
|
||||
/*
|
||||
* Child widget to display the label in the upper corner.
|
||||
*/
|
||||
GtkLabel *label;
|
||||
|
||||
/*
|
||||
* Range of the scale for lower and upper.
|
||||
*/
|
||||
gdouble y_lower;
|
||||
gdouble y_upper;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
guint y_lower_set : 1;
|
||||
guint y_upper_set : 1;
|
||||
} SpLineVisualizerRowPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint id;
|
||||
gdouble line_width;
|
||||
GdkRGBA foreground;
|
||||
GdkRGBA background;
|
||||
guint use_default_style : 1;
|
||||
guint fill : 1;
|
||||
} LineInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureCursor *cursor;
|
||||
GArray *lines;
|
||||
PointCache *cache;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
gdouble y_lower;
|
||||
gdouble y_upper;
|
||||
guint y_lower_set : 1;
|
||||
guint y_upper_set : 1;
|
||||
} LoadData;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpLineVisualizerRow, sp_line_visualizer_row, SP_TYPE_VISUALIZER_ROW)
|
||||
|
||||
static void sp_line_visualizer_row_load_data_async (SpLineVisualizerRow *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
static PointCache *sp_line_visualizer_row_load_data_finish (SpLineVisualizerRow *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TITLE,
|
||||
PROP_Y_LOWER,
|
||||
PROP_Y_UPPER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
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, sp_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
|
||||
sp_line_visualizer_row_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
GtkStyleContext *style_context;
|
||||
GtkStateFlags flags;
|
||||
GtkAllocation alloc;
|
||||
GdkRGBA foreground;
|
||||
gboolean ret;
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (widget));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
gtk_widget_get_allocation (widget, &alloc);
|
||||
|
||||
ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr);
|
||||
|
||||
if (priv->cache == NULL)
|
||||
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 SpVisualizerRowAbsolutePoint *points = NULL;
|
||||
const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line);
|
||||
const Point *fpoints;
|
||||
guint n_fpoints = 0;
|
||||
GdkRGBA color;
|
||||
|
||||
fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints);
|
||||
|
||||
if (n_fpoints > 0)
|
||||
{
|
||||
gdouble last_x;
|
||||
gdouble last_y;
|
||||
|
||||
points = g_new0 (SpVisualizerRowAbsolutePoint, n_fpoints);
|
||||
|
||||
sp_visualizer_row_translate_points (SP_VISUALIZER_ROW (self),
|
||||
(const SpVisualizerRowRelativePoint *)fpoints,
|
||||
n_fpoints,
|
||||
points,
|
||||
n_fpoints);
|
||||
|
||||
last_x = points[0].x;
|
||||
last_y = points[0].y;
|
||||
|
||||
if (line_info->fill)
|
||||
{
|
||||
cairo_move_to (cr, last_x, alloc.height);
|
||||
cairo_line_to (cr, last_x, last_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_move_to (cr, last_x, last_y);
|
||||
}
|
||||
|
||||
for (guint i = 1; i < n_fpoints; i++)
|
||||
{
|
||||
cairo_curve_to (cr,
|
||||
last_x + ((points[i].x - last_x) / 2),
|
||||
last_y,
|
||||
last_x + ((points[i].x - last_x) / 2),
|
||||
points[i].y,
|
||||
points[i].x,
|
||||
points[i].y);
|
||||
last_x = points[i].x;
|
||||
last_y = points[i].y;
|
||||
}
|
||||
|
||||
if (line_info->fill)
|
||||
{
|
||||
cairo_line_to (cr, last_x, alloc.height);
|
||||
cairo_close_path (cr);
|
||||
}
|
||||
|
||||
cairo_set_line_width (cr, line_info->line_width);
|
||||
|
||||
if (line_info->fill)
|
||||
{
|
||||
gdk_cairo_set_source_rgba (cr, &line_info->background);
|
||||
cairo_fill_preserve (cr);
|
||||
}
|
||||
|
||||
if (line_info->use_default_style)
|
||||
color = foreground;
|
||||
else
|
||||
color = line_info->foreground;
|
||||
|
||||
gdk_cairo_set_source_rgba (cr, &color);
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_load_data_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpLineVisualizerRow *self = (SpLineVisualizerRow *)object;
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(PointCache) cache = NULL;
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
|
||||
cache = sp_line_visualizer_row_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
|
||||
sp_line_visualizer_row_do_reload (gpointer data)
|
||||
{
|
||||
SpLineVisualizerRow *self = data;
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
|
||||
priv->queued_load = 0;
|
||||
|
||||
if (priv->reader != NULL)
|
||||
{
|
||||
sp_line_visualizer_row_load_data_async (self,
|
||||
NULL,
|
||||
sp_line_visualizer_row_load_data_cb,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_queue_reload (SpLineVisualizerRow *self)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
|
||||
if (priv->queued_load == 0)
|
||||
{
|
||||
priv->queued_load = gdk_threads_add_idle_full (G_PRIORITY_LOW,
|
||||
sp_line_visualizer_row_do_reload,
|
||||
self,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_set_reader (SpVisualizerRow *row,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpLineVisualizerRow *self = (SpLineVisualizerRow *)row;
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
|
||||
if (priv->reader != reader)
|
||||
{
|
||||
if (priv->reader != NULL)
|
||||
{
|
||||
sp_capture_reader_unref (priv->reader);
|
||||
priv->reader = NULL;
|
||||
}
|
||||
|
||||
if (reader != NULL)
|
||||
priv->reader = sp_capture_reader_ref (reader);
|
||||
|
||||
sp_line_visualizer_row_queue_reload (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_finalize (GObject *object)
|
||||
{
|
||||
SpLineVisualizerRow *self = (SpLineVisualizerRow *)object;
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_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, sp_capture_reader_unref);
|
||||
|
||||
if (priv->queued_load != 0)
|
||||
{
|
||||
g_source_remove (priv->queued_load);
|
||||
priv->queued_load = 0;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (sp_line_visualizer_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object);
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_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;
|
||||
|
||||
case PROP_Y_UPPER:
|
||||
g_value_set_double (value, priv->y_upper);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object);
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_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;
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
case PROP_Y_UPPER:
|
||||
priv->y_upper = g_value_get_double (value);
|
||||
priv->y_upper_set = TRUE;
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
SpVisualizerRowClass *visualizer_class = SP_VISUALIZER_ROW_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_line_visualizer_row_finalize;
|
||||
object_class->get_property = sp_line_visualizer_row_get_property;
|
||||
object_class->set_property = sp_line_visualizer_row_set_property;
|
||||
|
||||
widget_class->draw = sp_line_visualizer_row_draw;
|
||||
|
||||
visualizer_class->set_reader = sp_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));
|
||||
|
||||
properties [PROP_Y_LOWER] =
|
||||
g_param_spec_double ("y-lower",
|
||||
"Y Lower",
|
||||
"The lowest Y value for the visualizer",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_Y_UPPER] =
|
||||
g_param_spec_double ("y-upper",
|
||||
"Y Upper",
|
||||
"The highest Y value for the visualizer",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
100.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_init (SpLineVisualizerRow *self)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
PangoAttrList *attrs = pango_attr_list_new ();
|
||||
|
||||
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
|
||||
sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
LineInfo line_info = { 0 };
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
g_assert (priv->lines != NULL);
|
||||
|
||||
line_info.id = counter_id;
|
||||
line_info.line_width = 1.0;
|
||||
|
||||
if (color != NULL)
|
||||
{
|
||||
line_info.foreground = *color;
|
||||
line_info.use_default_style = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_rgba_parse (&line_info.foreground, "#000");
|
||||
line_info.use_default_style = TRUE;
|
||||
}
|
||||
|
||||
g_array_append_val (priv->lines, line_info);
|
||||
|
||||
if (SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added)
|
||||
SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added (self, counter_id);
|
||||
|
||||
sp_line_visualizer_row_queue_reload (self);
|
||||
}
|
||||
|
||||
void
|
||||
sp_line_visualizer_row_clear (SpLineVisualizerRow *self)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_LINE_VISUALIZER_ROW (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 guint8
|
||||
counter_type (LoadData *load,
|
||||
guint counter_id)
|
||||
{
|
||||
/* TODO: Support other counter types
|
||||
*
|
||||
* We need to keep some information on the counter (by id) so that we
|
||||
* can track the counters type (which is a 1-byte type id).
|
||||
*/
|
||||
return SP_CAPTURE_COUNTER_DOUBLE;
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
calc_x (gint64 lower,
|
||||
gint64 upper,
|
||||
gint64 value)
|
||||
{
|
||||
return (gdouble)(value - lower) / (gdouble)(upper - lower);
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
calc_y_double (gdouble lower,
|
||||
gdouble upper,
|
||||
gdouble value)
|
||||
{
|
||||
return (value - lower) / (upper - lower);
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
calc_y_int64 (gint64 lower,
|
||||
gint64 upper,
|
||||
gint64 value)
|
||||
{
|
||||
return (gdouble)(value - lower) / (gdouble)(upper - lower);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_line_visualizer_row_load_data_frame_cb (const SpCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
LoadData *load = user_data;
|
||||
|
||||
g_assert (frame != NULL);
|
||||
g_assert (frame->type == SP_CAPTURE_FRAME_CTRSET ||
|
||||
frame->type == SP_CAPTURE_FRAME_CTRDEF);
|
||||
g_assert (load != NULL);
|
||||
|
||||
if (frame->type == SP_CAPTURE_FRAME_CTRSET)
|
||||
{
|
||||
const SpCaptureFrameCounterSet *set = (SpCaptureFrameCounterSet *)frame;
|
||||
gdouble x = calc_x (load->begin_time, load->end_time, frame->time);
|
||||
|
||||
for (guint i = 0; i < set->n_values; i++)
|
||||
{
|
||||
const SpCaptureCounterValues *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))
|
||||
{
|
||||
gdouble y;
|
||||
|
||||
if (counter_type (load, counter_id) == SP_CAPTURE_COUNTER_DOUBLE)
|
||||
y = calc_y_double (load->y_lower, load->y_upper, group->values[j].vdbl);
|
||||
else
|
||||
y = calc_y_int64 (load->y_lower, load->y_upper, group->values[j].v64);
|
||||
|
||||
point_cache_add_point_to_set (load->cache, counter_id, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_line_visualizer_row_load_data_range_cb (const SpCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
LoadData *load = user_data;
|
||||
|
||||
g_assert (frame != NULL);
|
||||
g_assert (frame->type == SP_CAPTURE_FRAME_CTRSET ||
|
||||
frame->type == SP_CAPTURE_FRAME_CTRDEF);
|
||||
g_assert (load != NULL);
|
||||
g_assert (load->y_upper_set == FALSE ||
|
||||
load->y_lower_set == FALSE);
|
||||
|
||||
if (frame->type == SP_CAPTURE_FRAME_CTRSET)
|
||||
{
|
||||
const SpCaptureFrameCounterSet *set = (SpCaptureFrameCounterSet *)frame;
|
||||
|
||||
for (guint i = 0; i < set->n_values; i++)
|
||||
{
|
||||
const SpCaptureCounterValues *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))
|
||||
{
|
||||
gdouble y;
|
||||
|
||||
if (counter_type (load, counter_id) == SP_CAPTURE_COUNTER_DOUBLE)
|
||||
y = group->values[j].vdbl;
|
||||
else
|
||||
y = group->values[j].v64;
|
||||
|
||||
if (!load->y_upper_set)
|
||||
load->y_upper = MAX (load->y_upper, y);
|
||||
|
||||
if (!load->y_lower_set)
|
||||
load->y_lower = MIN (load->y_lower, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_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 (SP_IS_LINE_VISUALIZER_ROW (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);
|
||||
}
|
||||
|
||||
sp_capture_cursor_add_condition (load->cursor,
|
||||
sp_capture_condition_new_where_counter_in (counter_ids->len,
|
||||
(guint *)(gpointer)counter_ids->data));
|
||||
|
||||
/* If y boundaries are not set, we need to discover them by scaning the data. */
|
||||
if (!load->y_lower_set || !load->y_upper_set)
|
||||
{
|
||||
sp_capture_cursor_foreach (load->cursor, sp_line_visualizer_row_load_data_range_cb, load);
|
||||
sp_capture_cursor_reset (load->cursor);
|
||||
|
||||
/* Add extra boundary for some space above the graph line */
|
||||
if (G_MAXDOUBLE - load->y_upper > (load->y_upper * .25))
|
||||
load->y_upper *= 1.25;
|
||||
}
|
||||
|
||||
sp_capture_cursor_foreach (load->cursor, sp_line_visualizer_row_load_data_frame_cb, load);
|
||||
g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_line_visualizer_row_load_data_async (SpLineVisualizerRow *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
g_autoptr(GTask) task = NULL;
|
||||
LoadData *load;
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (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, sp_line_visualizer_row_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->y_lower = priv->y_lower;
|
||||
load->y_upper = priv->y_upper;
|
||||
load->y_lower_set = priv->y_lower_set;
|
||||
load->y_upper_set = priv->y_upper_set;
|
||||
load->begin_time = sp_capture_reader_get_start_time (priv->reader);
|
||||
load->end_time = sp_capture_reader_get_end_time (priv->reader);
|
||||
load->cursor = sp_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, sp_line_visualizer_row_load_data_worker);
|
||||
}
|
||||
|
||||
static PointCache *
|
||||
sp_line_visualizer_row_load_data_finish (SpLineVisualizerRow *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
LoadData *state;
|
||||
|
||||
g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
|
||||
g_assert (G_IS_TASK (result));
|
||||
|
||||
state = g_task_get_task_data (G_TASK (result));
|
||||
|
||||
if (!priv->y_lower_set && priv->y_lower != state->y_lower)
|
||||
{
|
||||
priv->y_lower = state->y_lower;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_Y_LOWER]);
|
||||
}
|
||||
|
||||
if (!priv->y_upper_set && priv->y_upper != state->y_upper)
|
||||
{
|
||||
priv->y_upper = state->y_upper;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_Y_UPPER]);
|
||||
}
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
void
|
||||
sp_line_visualizer_row_set_line_width (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
gdouble width)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_LINE_VISUALIZER_ROW (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;
|
||||
sp_line_visualizer_row_queue_reload (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_line_visualizer_row_set_fill (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_LINE_VISUALIZER_ROW (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->fill = !!color;
|
||||
if (color != NULL)
|
||||
info->background = *color;
|
||||
sp_line_visualizer_row_queue_reload (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/libsysprof-ui/sp-line-visualizer-row.h
Normal file
57
src/libsysprof-ui/sp-line-visualizer-row.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* sp-line-visualizer-row.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
|
||||
*/
|
||||
|
||||
#ifndef SP_LINE_VISUALIZER_ROW_H
|
||||
#define SP_LINE_VISUALIZER_ROW_H
|
||||
|
||||
#include "sp-visualizer-row.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_LINE_VISUALIZER_ROW (sp_line_visualizer_row_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpLineVisualizerRow, sp_line_visualizer_row, SP, LINE_VISUALIZER_ROW, SpVisualizerRow)
|
||||
|
||||
struct _SpLineVisualizerRowClass
|
||||
{
|
||||
SpVisualizerRowClass parent_class;
|
||||
|
||||
void (*counter_added) (SpLineVisualizerRow *self,
|
||||
guint counter_id);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _reserved[16];
|
||||
};
|
||||
|
||||
GtkWidget *sp_line_visualizer_row_new (void);
|
||||
void sp_line_visualizer_row_clear (SpLineVisualizerRow *self);
|
||||
void sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
const GdkRGBA *color);
|
||||
void sp_line_visualizer_row_set_line_width (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
gdouble width);
|
||||
void sp_line_visualizer_row_set_fill (SpLineVisualizerRow *self,
|
||||
guint counter_id,
|
||||
const GdkRGBA *color);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_LINE_VISUALIZER_ROW_H */
|
||||
488
src/libsysprof-ui/sp-mark-visualizer-row.c
Normal file
488
src/libsysprof-ui/sp-mark-visualizer-row.c
Normal file
@ -0,0 +1,488 @@
|
||||
/* sp-mark-visualizer-row.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 "sp-mark-visualizer-row"
|
||||
|
||||
#include "sp-capture-condition.h"
|
||||
#include "sp-capture-cursor.h"
|
||||
#include "rectangles.h"
|
||||
#include "sp-mark-visualizer-row.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
* Our reader as assigned by the visualizer system.
|
||||
*/
|
||||
SpCaptureReader *reader;
|
||||
|
||||
/*
|
||||
* The group we care about for displaying marks. The idea is that we only
|
||||
* show one group per-row, so tooling from separate systems can either be
|
||||
* displayed together or not, based on usefulness.
|
||||
*/
|
||||
gchar *group;
|
||||
|
||||
/*
|
||||
* Rectangle information we have built from the marks that belong to this
|
||||
* row of information.
|
||||
*/
|
||||
Rectangles *rectangles;
|
||||
|
||||
/*
|
||||
* Child widget to display the label in the upper corner.
|
||||
*/
|
||||
GtkLabel *label;
|
||||
} SpMarkVisualizerRowPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *group;
|
||||
SpCaptureCursor *cursor;
|
||||
Rectangles *rects;
|
||||
GHashTable *inferred_rects;
|
||||
} BuildState;
|
||||
|
||||
typedef struct {
|
||||
gint64 time;
|
||||
gchar *name;
|
||||
gchar *message;
|
||||
} InferredRect;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_GROUP,
|
||||
PROP_TITLE,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpMarkVisualizerRow, sp_mark_visualizer_row, SP_TYPE_VISUALIZER_ROW)
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
free_inferred_rect (InferredRect *rect)
|
||||
{
|
||||
g_free (rect->name);
|
||||
g_free (rect->message);
|
||||
g_slice_free (InferredRect, rect);
|
||||
}
|
||||
|
||||
static void
|
||||
add_inferred_rect_point (BuildState *state,
|
||||
InferredRect *rect)
|
||||
{
|
||||
rectangles_add (state->rects,
|
||||
rect->time,
|
||||
rect->time,
|
||||
rect->name,
|
||||
rect->message);
|
||||
}
|
||||
|
||||
static void
|
||||
build_state_free (BuildState *state)
|
||||
{
|
||||
g_hash_table_remove_all (state->inferred_rects);
|
||||
g_clear_pointer (&state->inferred_rects, g_hash_table_unref);
|
||||
g_free (state->group);
|
||||
g_object_unref (state->cursor);
|
||||
g_slice_free (BuildState, state);
|
||||
}
|
||||
|
||||
/* Creates rectangles for GPU marks.
|
||||
*
|
||||
* GPU marks come in as a begin and an end, but since those things are
|
||||
* processessed on potentially different CPUs, perf doesn't record
|
||||
* them in sequence order in the mmap ringbuffer. Thus, we have to
|
||||
* shuffle things back around at visualization time.
|
||||
*/
|
||||
static gboolean
|
||||
process_gpu_mark (BuildState *state,
|
||||
const SpCaptureMark *mark)
|
||||
{
|
||||
InferredRect *rect = g_hash_table_lookup (state->inferred_rects,
|
||||
mark->message);
|
||||
|
||||
if (rect)
|
||||
{
|
||||
gboolean ours_begins = strstr (mark->name, "begin") != NULL;
|
||||
gboolean theirs_begins = strstr (rect->name, "begin") != NULL;
|
||||
|
||||
if (ours_begins != theirs_begins)
|
||||
{
|
||||
rectangles_add (state->rects,
|
||||
ours_begins ? mark->frame.time : rect->time,
|
||||
ours_begins ? rect->time : mark->frame.time,
|
||||
ours_begins ? mark->name : rect->name,
|
||||
rect->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Something went weird with the tracking (GPU hang caused
|
||||
* two starts?), so just put up both time points as vertical
|
||||
* bars for now.
|
||||
*/
|
||||
rectangles_add (state->rects,
|
||||
mark->frame.time,
|
||||
mark->frame.time,
|
||||
mark->name,
|
||||
mark->message);
|
||||
|
||||
add_inferred_rect_point (state, rect);
|
||||
}
|
||||
|
||||
g_hash_table_remove (state->inferred_rects,
|
||||
rect->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = g_slice_new0 (InferredRect);
|
||||
rect->name = g_strdup (mark->name);
|
||||
rect->message = g_strdup (mark->message);
|
||||
rect->time = mark->frame.time;
|
||||
|
||||
g_hash_table_insert (state->inferred_rects, rect->message, rect);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
sp_mark_visualizer_row_add_rect (const SpCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
BuildState *state = user_data;
|
||||
const SpCaptureMark *mark = (const SpCaptureMark *)frame;
|
||||
|
||||
g_assert (frame != NULL);
|
||||
g_assert (frame->type == SP_CAPTURE_FRAME_MARK);
|
||||
g_assert (state != NULL);
|
||||
g_assert (state->rects != NULL);
|
||||
|
||||
if (g_strcmp0 (mark->group, state->group) == 0)
|
||||
{
|
||||
if (strstr (mark->name, "gpu begin") != NULL ||
|
||||
strstr (mark->name, "gpu end") != NULL)
|
||||
process_gpu_mark (state, mark);
|
||||
else
|
||||
rectangles_add (state->rects,
|
||||
frame->time,
|
||||
frame->time + mark->duration,
|
||||
mark->name,
|
||||
mark->message);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
BuildState *state = task_data;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
gint64 end_time;
|
||||
|
||||
g_assert (G_IS_TASK (task));
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (source_object));
|
||||
g_assert (state != NULL);
|
||||
g_assert (state->cursor != NULL);
|
||||
|
||||
sp_capture_cursor_foreach (state->cursor, sp_mark_visualizer_row_add_rect, state);
|
||||
|
||||
/* If any inferred rects are left incomplete, just drop them in as
|
||||
* point events for now.
|
||||
*/
|
||||
g_hash_table_iter_init (&iter, state->inferred_rects);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
InferredRect *rect = value;
|
||||
|
||||
add_inferred_rect_point (state, rect);
|
||||
}
|
||||
g_hash_table_remove_all (state->inferred_rects);
|
||||
|
||||
end_time = sp_capture_reader_get_end_time (sp_capture_cursor_get_reader (state->cursor));
|
||||
rectangles_set_end_time (state->rects, end_time);
|
||||
g_task_return_pointer (task, g_steal_pointer (&state->rects), (GDestroyNotify)rectangles_free);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_mark_visualizer_row_query_tooltip (GtkWidget *widget,
|
||||
gint x,
|
||||
gint y,
|
||||
gboolean keyboard_mode,
|
||||
GtkTooltip *tooltip)
|
||||
{
|
||||
SpMarkVisualizerRow *self = (SpMarkVisualizerRow *)widget;
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (self));
|
||||
|
||||
if (priv->rectangles == NULL)
|
||||
return FALSE;
|
||||
|
||||
return rectangles_query_tooltip (priv->rectangles, tooltip, priv->group, x, y);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_mark_visualizer_row_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
SpMarkVisualizerRow *self = (SpMarkVisualizerRow *)widget;
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
GtkStyleContext *style_context;
|
||||
GtkStateFlags flags;
|
||||
GdkRGBA foreground;
|
||||
GtkAllocation alloc;
|
||||
gboolean ret;
|
||||
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (widget));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
gtk_widget_get_allocation (widget, &alloc);
|
||||
|
||||
ret = GTK_WIDGET_CLASS (sp_mark_visualizer_row_parent_class)->draw (widget, cr);
|
||||
|
||||
if (priv->rectangles == NULL)
|
||||
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);
|
||||
|
||||
rectangles_draw (priv->rectangles, GTK_WIDGET (self), cr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
data_load_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpMarkVisualizerRow *self = (SpMarkVisualizerRow *)object;
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (self));
|
||||
g_assert (G_IS_TASK (result));
|
||||
|
||||
g_clear_pointer (&priv->rectangles, rectangles_free);
|
||||
priv->rectangles = g_task_propagate_pointer (G_TASK (result), NULL);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_reload (SpMarkVisualizerRow *self)
|
||||
{
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
g_autoptr(SpCaptureCursor) cursor = NULL;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
SpCaptureCondition *condition;
|
||||
BuildState *state;
|
||||
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (self));
|
||||
|
||||
g_clear_pointer (&priv->rectangles, rectangles_free);
|
||||
|
||||
condition = sp_capture_condition_new_where_type_in (1, (SpCaptureFrameType[]) { SP_CAPTURE_FRAME_MARK });
|
||||
cursor = sp_capture_cursor_new (priv->reader);
|
||||
sp_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
|
||||
|
||||
state = g_slice_new0 (BuildState);
|
||||
state->inferred_rects = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL,
|
||||
(GDestroyNotify)free_inferred_rect);
|
||||
state->group = g_strdup (priv->group);
|
||||
state->cursor = g_steal_pointer (&cursor);
|
||||
state->rects = rectangles_new (sp_capture_reader_get_start_time (priv->reader),
|
||||
sp_capture_reader_get_end_time (priv->reader));
|
||||
|
||||
task = g_task_new (self, NULL, data_load_cb, NULL);
|
||||
g_task_set_task_data (task, state, (GDestroyNotify)build_state_free);
|
||||
g_task_run_in_thread (task, sp_mark_visualizer_row_worker);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_set_reader (SpVisualizerRow *row,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpMarkVisualizerRow *self = (SpMarkVisualizerRow *)row;
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MARK_VISUALIZER_ROW (self));
|
||||
|
||||
if (reader != priv->reader)
|
||||
{
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
if (reader != NULL)
|
||||
priv->reader = sp_capture_reader_ref (reader);
|
||||
sp_mark_visualizer_row_reload (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_finalize (GObject *object)
|
||||
{
|
||||
SpMarkVisualizerRow *self = (SpMarkVisualizerRow *)object;
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->group, g_free);
|
||||
g_clear_pointer (&priv->rectangles, rectangles_free);
|
||||
|
||||
G_OBJECT_CLASS (sp_mark_visualizer_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpMarkVisualizerRow *self = SP_MARK_VISUALIZER_ROW (object);
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_GROUP:
|
||||
g_value_set_string (value, sp_mark_visualizer_row_get_group (self));
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, gtk_label_get_label (priv->label));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpMarkVisualizerRow *self = SP_MARK_VISUALIZER_ROW (object);
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_GROUP:
|
||||
sp_mark_visualizer_row_set_group (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
gtk_label_set_label (priv->label, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_class_init (SpMarkVisualizerRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
SpVisualizerRowClass *visualizer_class = SP_VISUALIZER_ROW_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_mark_visualizer_row_finalize;
|
||||
object_class->get_property = sp_mark_visualizer_row_get_property;
|
||||
object_class->set_property = sp_mark_visualizer_row_set_property;
|
||||
|
||||
widget_class->draw = sp_mark_visualizer_row_draw;
|
||||
widget_class->query_tooltip = sp_mark_visualizer_row_query_tooltip;
|
||||
|
||||
visualizer_class->set_reader = sp_mark_visualizer_row_set_reader;
|
||||
|
||||
properties [PROP_GROUP] =
|
||||
g_param_spec_string ("group",
|
||||
"Group",
|
||||
"The group of the row",
|
||||
NULL,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"Title",
|
||||
"The title of the row",
|
||||
NULL,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_mark_visualizer_row_init (SpMarkVisualizerRow *self)
|
||||
{
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
PangoAttrList *attrs = pango_attr_list_new ();
|
||||
|
||||
gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
sp_mark_visualizer_row_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_MARK_VISUALIZER_ROW, NULL);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
sp_mark_visualizer_row_get_group (SpMarkVisualizerRow *self)
|
||||
{
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_MARK_VISUALIZER_ROW (self), NULL);
|
||||
|
||||
return priv->group;
|
||||
}
|
||||
|
||||
void
|
||||
sp_mark_visualizer_row_set_group (SpMarkVisualizerRow *self,
|
||||
const gchar *group)
|
||||
{
|
||||
SpMarkVisualizerRowPrivate *priv = sp_mark_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_MARK_VISUALIZER_ROW (self));
|
||||
|
||||
if (g_strcmp0 (priv->group, group) != 0)
|
||||
{
|
||||
g_free (priv->group);
|
||||
priv->group = g_strdup (group);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_GROUP]);
|
||||
}
|
||||
}
|
||||
44
src/libsysprof-ui/sp-mark-visualizer-row.h
Normal file
44
src/libsysprof-ui/sp-mark-visualizer-row.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* sp-mark-visualizer-row.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 "sp-visualizer-row.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_MARK_VISUALIZER_ROW (sp_mark_visualizer_row_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpMarkVisualizerRow, sp_mark_visualizer_row, SP, MARK_VISUALIZER_ROW, SpVisualizerRow)
|
||||
|
||||
struct _SpMarkVisualizerRowClass
|
||||
{
|
||||
SpVisualizerRowClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _reserved[16];
|
||||
};
|
||||
|
||||
GtkWidget *sp_mark_visualizer_row_new (void);
|
||||
const gchar *sp_mark_visualizer_row_get_group (SpMarkVisualizerRow *self);
|
||||
void sp_mark_visualizer_row_set_group (SpMarkVisualizerRow *self,
|
||||
const gchar *group);
|
||||
|
||||
G_END_DECLS
|
||||
495
src/libsysprof-ui/sp-model-filter.c
Normal file
495
src/libsysprof-ui/sp-model-filter.c
Normal file
@ -0,0 +1,495 @@
|
||||
/* sp-model-filter.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
|
||||
*/
|
||||
|
||||
#include "sp-model-filter.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSequenceIter *child_iter;
|
||||
GSequenceIter *filter_iter;
|
||||
} SpModelFilterItem;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* The list we are filtering */
|
||||
GListModel *child_model;
|
||||
|
||||
/*
|
||||
* Both sequences point to the same SpModelFilterItem which
|
||||
* contains cross-referencing stable GSequenceIter pointers.
|
||||
* The child_seq is considered the "owner" and used to release
|
||||
* allocated resources.
|
||||
*/
|
||||
GSequence *child_seq;
|
||||
GSequence *filter_seq;
|
||||
|
||||
/*
|
||||
* Typical set of callback/closure/free function pointers and data.
|
||||
* Called for child items to determine visibility state.
|
||||
*/
|
||||
SpModelFilterFunc filter_func;
|
||||
gpointer filter_func_data;
|
||||
GDestroyNotify filter_func_data_destroy;
|
||||
|
||||
/*
|
||||
* If set, we will not emit items-changed. This is useful during
|
||||
* invalidation so that we can do a single emission for all items
|
||||
* that have changed.
|
||||
*/
|
||||
guint supress_items_changed : 1;
|
||||
} SpModelFilterPrivate;
|
||||
|
||||
static void list_model_iface_init (GListModelInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpModelFilter, sp_model_filter, G_TYPE_OBJECT, 0,
|
||||
G_ADD_PRIVATE (SpModelFilter)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
list_model_iface_init))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CHILD_MODEL,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
static guint signal_id;
|
||||
|
||||
static void
|
||||
sp_model_filter_item_free (gpointer data)
|
||||
{
|
||||
SpModelFilterItem *item = data;
|
||||
|
||||
g_clear_pointer (&item->filter_iter, g_sequence_remove);
|
||||
item->child_iter = NULL;
|
||||
g_slice_free (SpModelFilterItem, item);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_model_filter_default_filter_func (GObject *item,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locates the next item in the filter sequence starting from
|
||||
* the cross-reference found at @iter. If none are found, the
|
||||
* end_iter for the filter sequence is returned.
|
||||
*
|
||||
* This returns an iter in the filter_sequence, not the child_seq.
|
||||
*
|
||||
* Returns: a #GSequenceIter from the filter sequence.
|
||||
*/
|
||||
static GSequenceIter *
|
||||
find_next_visible_filter_iter (SpModelFilter *self,
|
||||
GSequenceIter *iter)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (iter != NULL);
|
||||
|
||||
for (; !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
SpModelFilterItem *item = g_sequence_get (iter);
|
||||
|
||||
g_assert (item->child_iter == iter);
|
||||
g_assert (item->filter_iter == NULL ||
|
||||
g_sequence_iter_get_sequence (item->filter_iter) == priv->filter_seq);
|
||||
|
||||
if (item->filter_iter != NULL)
|
||||
return item->filter_iter;
|
||||
}
|
||||
|
||||
return g_sequence_get_end_iter (priv->filter_seq);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_child_model_items_changed (SpModelFilter *self,
|
||||
guint position,
|
||||
guint n_removed,
|
||||
guint n_added,
|
||||
GListModel *child_model)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
gboolean unblocked;
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (G_IS_LIST_MODEL (child_model));
|
||||
g_assert (priv->child_model == child_model);
|
||||
g_assert (position <= (guint)g_sequence_get_length (priv->child_seq));
|
||||
g_assert ((g_sequence_get_length (priv->child_seq) - n_removed + n_added) ==
|
||||
g_list_model_get_n_items (child_model));
|
||||
|
||||
unblocked = !priv->supress_items_changed;
|
||||
|
||||
if (n_removed > 0)
|
||||
{
|
||||
GSequenceIter *iter = g_sequence_get_iter_at_pos (priv->child_seq, position);
|
||||
gint first_position = -1;
|
||||
guint count = 0;
|
||||
|
||||
g_assert (!g_sequence_iter_is_end (iter));
|
||||
|
||||
/* Small shortcut when all items are removed */
|
||||
if (n_removed == (guint)g_sequence_get_length (priv->child_seq))
|
||||
{
|
||||
g_sequence_remove_range (g_sequence_get_begin_iter (priv->child_seq),
|
||||
g_sequence_get_end_iter (priv->child_seq));
|
||||
g_assert (g_sequence_is_empty (priv->child_seq));
|
||||
g_assert (g_sequence_is_empty (priv->filter_seq));
|
||||
goto add_new_items;
|
||||
}
|
||||
|
||||
for (guint i = 0; i < n_removed; i++)
|
||||
{
|
||||
GSequenceIter *to_remove = iter;
|
||||
SpModelFilterItem *item = g_sequence_get (iter);
|
||||
|
||||
g_assert (item != NULL);
|
||||
g_assert (item->child_iter == iter);
|
||||
g_assert (item->filter_iter == NULL ||
|
||||
g_sequence_iter_get_sequence (item->filter_iter) == priv->filter_seq);
|
||||
|
||||
/* If this is visible, we need to notify about removal */
|
||||
if (unblocked && item->filter_iter != NULL)
|
||||
{
|
||||
if (first_position < 0)
|
||||
first_position = g_sequence_iter_get_position (item->filter_iter);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Fetch the next while the iter is still valid */
|
||||
iter = g_sequence_iter_next (iter);
|
||||
|
||||
/* Cascades into also removing from filter_seq. */
|
||||
g_sequence_remove (to_remove);
|
||||
}
|
||||
|
||||
if (unblocked && first_position >= 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), first_position, count, 0);
|
||||
}
|
||||
|
||||
add_new_items:
|
||||
|
||||
if (n_added > 0)
|
||||
{
|
||||
GSequenceIter *iter = g_sequence_get_iter_at_pos (priv->child_seq, position);
|
||||
GSequenceIter *filter_iter = find_next_visible_filter_iter (self, iter);
|
||||
guint filter_position = g_sequence_iter_get_position (filter_iter);
|
||||
guint count = 0;
|
||||
|
||||
/* Walk backwards to insert items into the filter list so that
|
||||
* we can use the same filter_position for each items-changed
|
||||
* signal emission.
|
||||
*/
|
||||
for (guint i = position + n_added; i > position; i--)
|
||||
{
|
||||
g_autoptr(GObject) instance = NULL;
|
||||
SpModelFilterItem *item;
|
||||
|
||||
item = g_slice_new0 (SpModelFilterItem);
|
||||
item->filter_iter = NULL;
|
||||
item->child_iter = g_sequence_insert_before (iter, item);
|
||||
|
||||
instance = g_list_model_get_item (child_model, i - 1);
|
||||
g_assert (G_IS_OBJECT (instance));
|
||||
|
||||
/* Check if this item is visible */
|
||||
if (priv->filter_func (instance, priv->filter_func_data))
|
||||
{
|
||||
item->filter_iter = g_sequence_insert_before (filter_iter, item);
|
||||
|
||||
/* Use this in the future for relative positioning */
|
||||
filter_iter = item->filter_iter;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Insert next item before this */
|
||||
iter = item->child_iter;
|
||||
}
|
||||
|
||||
if (unblocked && count)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), filter_position, 0, count);
|
||||
}
|
||||
|
||||
g_assert ((guint)g_sequence_get_length (priv->child_seq) ==
|
||||
g_list_model_get_n_items (child_model));
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_finalize (GObject *object)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)object;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->child_seq, g_sequence_free);
|
||||
g_clear_pointer (&priv->filter_seq, g_sequence_free);
|
||||
|
||||
if (priv->filter_func_data_destroy)
|
||||
{
|
||||
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
|
||||
priv->filter_func_data_destroy = NULL;
|
||||
}
|
||||
|
||||
g_clear_object (&priv->child_model);
|
||||
|
||||
G_OBJECT_CLASS (sp_model_filter_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpModelFilter *self = SP_MODEL_FILTER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CHILD_MODEL:
|
||||
g_value_set_object (value, sp_model_filter_get_child_model (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_class_init (SpModelFilterClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_model_filter_finalize;
|
||||
object_class->get_property = sp_model_filter_get_property;
|
||||
|
||||
properties [PROP_CHILD_MODEL] =
|
||||
g_param_spec_object ("child-model",
|
||||
"Child Model",
|
||||
"The child model being filtered.",
|
||||
G_TYPE_LIST_MODEL,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
signal_id = g_signal_lookup ("items-changed", SP_TYPE_MODEL_FILTER);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_init (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
priv->filter_func = sp_model_filter_default_filter_func;
|
||||
priv->child_seq = g_sequence_new (sp_model_filter_item_free);
|
||||
priv->filter_seq = g_sequence_new (NULL);
|
||||
}
|
||||
|
||||
static GType
|
||||
sp_model_filter_get_item_type (GListModel *model)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
return g_list_model_get_item_type (priv->child_model);
|
||||
}
|
||||
|
||||
static guint
|
||||
sp_model_filter_get_n_items (GListModel *model)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (priv->filter_seq != NULL);
|
||||
|
||||
return g_sequence_get_length (priv->filter_seq);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sp_model_filter_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
SpModelFilterItem *item;
|
||||
GSequenceIter *iter;
|
||||
guint child_position;
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (position < (guint)g_sequence_get_length (priv->filter_seq));
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (priv->filter_seq, position);
|
||||
g_assert (!g_sequence_iter_is_end (iter));
|
||||
|
||||
item = g_sequence_get (iter);
|
||||
g_assert (item != NULL);
|
||||
g_assert (item->filter_iter == iter);
|
||||
g_assert (item->child_iter != NULL);
|
||||
g_assert (g_sequence_iter_get_sequence (item->child_iter) == priv->child_seq);
|
||||
|
||||
child_position = g_sequence_iter_get_position (item->child_iter);
|
||||
|
||||
return g_list_model_get_item (priv->child_model, child_position);
|
||||
}
|
||||
|
||||
static void
|
||||
list_model_iface_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sp_model_filter_get_item_type;
|
||||
iface->get_n_items = sp_model_filter_get_n_items;
|
||||
iface->get_item = sp_model_filter_get_item;
|
||||
}
|
||||
|
||||
SpModelFilter *
|
||||
sp_model_filter_new (GListModel *child_model)
|
||||
{
|
||||
SpModelFilter *ret;
|
||||
SpModelFilterPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (child_model), NULL);
|
||||
|
||||
ret = g_object_new (SP_TYPE_MODEL_FILTER, NULL);
|
||||
priv = sp_model_filter_get_instance_private (ret);
|
||||
priv->child_model = g_object_ref (child_model);
|
||||
|
||||
g_signal_connect_object (child_model,
|
||||
"items-changed",
|
||||
G_CALLBACK (sp_model_filter_child_model_items_changed),
|
||||
ret,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
sp_model_filter_invalidate (ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_model_filter_get_child_model:
|
||||
* @self: A #SpModelFilter
|
||||
*
|
||||
* Gets the child model that is being filtered.
|
||||
*
|
||||
* Returns: (transfer none): A #GListModel.
|
||||
*/
|
||||
GListModel *
|
||||
sp_model_filter_get_child_model (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_MODEL_FILTER (self), NULL);
|
||||
|
||||
return priv->child_model;
|
||||
}
|
||||
|
||||
void
|
||||
sp_model_filter_invalidate (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
/* We block emission while in invalidate so that we can use
|
||||
* a single larger items-changed rather lots of small emissions.
|
||||
*/
|
||||
priv->supress_items_changed = TRUE;
|
||||
|
||||
/* First determine how many items we need to synthesize as a removal */
|
||||
n_items = g_sequence_get_length (priv->filter_seq);
|
||||
|
||||
/*
|
||||
* If we have a child store, we want to rebuild our list of items
|
||||
* from scratch, so just remove everything.
|
||||
*/
|
||||
if (!g_sequence_is_empty (priv->child_seq))
|
||||
g_sequence_remove_range (g_sequence_get_begin_iter (priv->child_seq),
|
||||
g_sequence_get_end_iter (priv->child_seq));
|
||||
|
||||
g_assert (g_sequence_is_empty (priv->child_seq));
|
||||
g_assert (g_sequence_is_empty (priv->filter_seq));
|
||||
g_assert (!priv->child_model || G_IS_LIST_MODEL (priv->child_model));
|
||||
|
||||
/*
|
||||
* Now add the new items by synthesizing the addition of all the
|
||||
* itmes in the list.
|
||||
*/
|
||||
if (priv->child_model != NULL)
|
||||
{
|
||||
guint child_n_items;
|
||||
|
||||
/*
|
||||
* Now add all the items as one shot to our list so that
|
||||
* we get populate our sequence and filter sequence.
|
||||
*/
|
||||
child_n_items = g_list_model_get_n_items (priv->child_model);
|
||||
sp_model_filter_child_model_items_changed (self, 0, 0, child_n_items, priv->child_model);
|
||||
|
||||
g_assert ((guint)g_sequence_get_length (priv->child_seq) == child_n_items);
|
||||
g_assert ((guint)g_sequence_get_length (priv->filter_seq) <= child_n_items);
|
||||
}
|
||||
|
||||
priv->supress_items_changed = FALSE;
|
||||
|
||||
/* Now that we've updated our sequences, notify of all the changes
|
||||
* as a single series of updates to the consumers.
|
||||
*/
|
||||
if (n_items > 0 || !g_sequence_is_empty (priv->filter_seq))
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
0,
|
||||
n_items,
|
||||
g_sequence_get_length (priv->filter_seq));
|
||||
}
|
||||
|
||||
void
|
||||
sp_model_filter_set_filter_func (SpModelFilter *self,
|
||||
SpModelFilterFunc filter_func,
|
||||
gpointer filter_func_data,
|
||||
GDestroyNotify filter_func_data_destroy)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_MODEL_FILTER (self));
|
||||
g_return_if_fail (filter_func || (!filter_func_data && !filter_func_data_destroy));
|
||||
|
||||
if (priv->filter_func_data_destroy != NULL)
|
||||
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
|
||||
|
||||
if (filter_func != NULL)
|
||||
{
|
||||
priv->filter_func = filter_func;
|
||||
priv->filter_func_data = filter_func_data;
|
||||
priv->filter_func_data_destroy = filter_func_data_destroy;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->filter_func = sp_model_filter_default_filter_func;
|
||||
priv->filter_func_data = NULL;
|
||||
priv->filter_func_data_destroy = NULL;
|
||||
}
|
||||
|
||||
sp_model_filter_invalidate (self);
|
||||
}
|
||||
52
src/libsysprof-ui/sp-model-filter.h
Normal file
52
src/libsysprof-ui/sp-model-filter.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* sp-model-filter.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
|
||||
*/
|
||||
|
||||
#ifndef SP_MODEL_FILTER_H
|
||||
#define SP_MODEL_FILTER_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_MODEL_FILTER (sp_model_filter_get_type())
|
||||
|
||||
typedef gboolean (*SpModelFilterFunc) (GObject *object,
|
||||
gpointer user_data);
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpModelFilter, sp_model_filter, SP, MODEL_FILTER, GObject)
|
||||
|
||||
struct _SpModelFilterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
SpModelFilter *sp_model_filter_new (GListModel *child_model);
|
||||
GListModel *sp_model_filter_get_child_model (SpModelFilter *self);
|
||||
void sp_model_filter_invalidate (SpModelFilter *self);
|
||||
void sp_model_filter_set_filter_func (SpModelFilter *self,
|
||||
SpModelFilterFunc filter_func,
|
||||
gpointer filter_func_data,
|
||||
GDestroyNotify filter_func_data_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_MODEL_FILTER_H */
|
||||
1915
src/libsysprof-ui/sp-multi-paned.c
Normal file
1915
src/libsysprof-ui/sp-multi-paned.c
Normal file
File diff suppressed because it is too large
Load Diff
57
src/libsysprof-ui/sp-multi-paned.h
Normal file
57
src/libsysprof-ui/sp-multi-paned.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* sp-multi-paned.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 Lesser 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SP_MULTI_PANED_H
|
||||
#define SP_MULTI_PANED_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_MULTI_PANED (sp_multi_paned_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpMultiPaned, sp_multi_paned, SP, MULTI_PANED, GtkContainer)
|
||||
|
||||
struct _SpMultiPanedClass
|
||||
{
|
||||
GtkContainerClass parent;
|
||||
|
||||
void (*resize_drag_begin) (SpMultiPaned *self,
|
||||
GtkWidget *child);
|
||||
void (*resize_drag_end) (SpMultiPaned *self,
|
||||
GtkWidget *child);
|
||||
|
||||
gpointer _reserved1;
|
||||
gpointer _reserved2;
|
||||
gpointer _reserved3;
|
||||
gpointer _reserved4;
|
||||
gpointer _reserved5;
|
||||
gpointer _reserved6;
|
||||
gpointer _reserved7;
|
||||
gpointer _reserved8;
|
||||
};
|
||||
|
||||
GtkWidget *sp_multi_paned_new (void);
|
||||
guint sp_multi_paned_get_n_children (SpMultiPaned *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_MULTI_PANED_H */
|
||||
254
src/libsysprof-ui/sp-process-model-row.c
Normal file
254
src/libsysprof-ui/sp-process-model-row.c
Normal file
@ -0,0 +1,254 @@
|
||||
/* sp-process-model-row.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
|
||||
*/
|
||||
|
||||
#include "sp-process-model-row.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpProcessModelItem *item;
|
||||
|
||||
GtkLabel *args_label;
|
||||
GtkLabel *label;
|
||||
GtkLabel *pid;
|
||||
GtkImage *image;
|
||||
GtkImage *check;
|
||||
} SpProcessModelRowPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpProcessModelRow, sp_process_model_row, GTK_TYPE_LIST_BOX_ROW)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ITEM,
|
||||
PROP_SELECTED,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
GtkWidget *
|
||||
sp_process_model_row_new (SpProcessModelItem *item)
|
||||
{
|
||||
return g_object_new (SP_TYPE_PROCESS_MODEL_ROW,
|
||||
"item", item,
|
||||
NULL);
|
||||
}
|
||||
|
||||
SpProcessModelItem *
|
||||
sp_process_model_row_get_item (SpProcessModelRow *self)
|
||||
{
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ROW (self), NULL);
|
||||
|
||||
return priv->item;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_set_item (SpProcessModelRow *self,
|
||||
SpProcessModelItem *item)
|
||||
{
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL_ROW (self));
|
||||
g_assert (SP_IS_PROCESS_MODEL_ITEM (item));
|
||||
|
||||
if (g_set_object (&priv->item, item))
|
||||
{
|
||||
const gchar *command_line;
|
||||
g_auto(GStrv) parts = NULL;
|
||||
g_autofree gchar *pidstr = NULL;
|
||||
const gchar * const *argv;
|
||||
GPid pid;
|
||||
|
||||
command_line = sp_process_model_item_get_command_line (item);
|
||||
parts = g_strsplit (command_line ?: "", "\n", 0);
|
||||
gtk_label_set_label (priv->label, parts [0]);
|
||||
|
||||
if ((NULL != (argv = sp_process_model_item_get_argv (item))) && (argv[0] != NULL))
|
||||
{
|
||||
g_autofree gchar *argvstr = g_strjoinv (" ", (gchar **)&argv[1]);
|
||||
g_autofree gchar *escaped = g_markup_escape_text (argvstr, -1);
|
||||
|
||||
gtk_label_set_label (priv->args_label, escaped);
|
||||
}
|
||||
|
||||
pid = sp_process_model_item_get_pid (item);
|
||||
pidstr = g_strdup_printf ("<small>%u</small>", pid);
|
||||
gtk_label_set_label (priv->pid, pidstr);
|
||||
gtk_label_set_use_markup (priv->pid, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_process_model_row_get_selected (SpProcessModelRow *self)
|
||||
{
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ROW (self), FALSE);
|
||||
|
||||
return gtk_widget_get_visible (GTK_WIDGET (priv->check));
|
||||
}
|
||||
|
||||
void
|
||||
sp_process_model_row_set_selected (SpProcessModelRow *self,
|
||||
gboolean selected)
|
||||
{
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_PROCESS_MODEL_ROW (self));
|
||||
|
||||
selected = !!selected;
|
||||
|
||||
if (selected != sp_process_model_row_get_selected (self))
|
||||
{
|
||||
gtk_widget_set_visible (GTK_WIDGET (priv->check), selected);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED]);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_process_model_row_query_tooltip (GtkWidget *widget,
|
||||
gint x,
|
||||
gint y,
|
||||
gboolean keyboard_mode,
|
||||
GtkTooltip *tooltip)
|
||||
{
|
||||
SpProcessModelRow *self = (SpProcessModelRow *)widget;
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL_ROW (self));
|
||||
g_assert (GTK_IS_TOOLTIP (tooltip));
|
||||
|
||||
if (priv->item != NULL)
|
||||
{
|
||||
const gchar * const *argv = sp_process_model_item_get_argv (priv->item);
|
||||
|
||||
if (argv != NULL)
|
||||
{
|
||||
g_autofree gchar *str = g_strjoinv (" ", (gchar **)argv);
|
||||
gtk_tooltip_set_text (tooltip, str);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_finalize (GObject *object)
|
||||
{
|
||||
SpProcessModelRow *self = (SpProcessModelRow *)object;
|
||||
SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self);
|
||||
|
||||
g_clear_object (&priv->item);
|
||||
|
||||
G_OBJECT_CLASS (sp_process_model_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProcessModelRow *self = SP_PROCESS_MODEL_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM:
|
||||
g_value_set_object (value, sp_process_model_row_get_item (self));
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_boolean (value, sp_process_model_row_get_selected (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProcessModelRow *self = SP_PROCESS_MODEL_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM:
|
||||
sp_process_model_row_set_item (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
sp_process_model_row_set_selected (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_class_init (SpProcessModelRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_process_model_row_finalize;
|
||||
object_class->get_property = sp_process_model_row_get_property;
|
||||
object_class->set_property = sp_process_model_row_set_property;
|
||||
|
||||
widget_class->query_tooltip = sp_process_model_row_query_tooltip;
|
||||
|
||||
properties [PROP_ITEM] =
|
||||
g_param_spec_object ("item",
|
||||
"Item",
|
||||
"Item",
|
||||
SP_TYPE_PROCESS_MODEL_ITEM,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_SELECTED] =
|
||||
g_param_spec_boolean ("selected",
|
||||
"Selected",
|
||||
"Selected",
|
||||
FALSE,
|
||||
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class,
|
||||
"/org/gnome/sysprof/ui/sp-process-model-row.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, args_label);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, image);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, label);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, pid);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, check);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_row_init (SpProcessModelRow *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
50
src/libsysprof-ui/sp-process-model-row.h
Normal file
50
src/libsysprof-ui/sp-process-model-row.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* sp-process-model-row.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
|
||||
*/
|
||||
|
||||
#ifndef SP_PROCESS_MODEL_ROW_H
|
||||
#define SP_PROCESS_MODEL_ROW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-process-model-item.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_PROCESS_MODEL_ROW (sp_process_model_row_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpProcessModelRow, sp_process_model_row, SP, PROCESS_MODEL_ROW, GtkListBoxRow)
|
||||
|
||||
struct _SpProcessModelRowClass
|
||||
{
|
||||
GtkListBoxRowClass parent;
|
||||
|
||||
gpointer padding[4];
|
||||
};
|
||||
|
||||
GtkWidget *sp_process_model_row_new (SpProcessModelItem *item);
|
||||
SpProcessModelItem *sp_process_model_row_get_item (SpProcessModelRow *self);
|
||||
gboolean sp_process_model_row_get_selected (SpProcessModelRow *self);
|
||||
void sp_process_model_row_set_selected (SpProcessModelRow *self,
|
||||
gboolean selected);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_PROCESS_MODEL_ROW_H */
|
||||
|
||||
892
src/libsysprof-ui/sp-profiler-menu-button.c
Normal file
892
src/libsysprof-ui/sp-profiler-menu-button.c
Normal file
@ -0,0 +1,892 @@
|
||||
/* sp-profiler-menu-button.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
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sp-model-filter.h"
|
||||
#include "sp-process-model.h"
|
||||
#include "sp-process-model-item.h"
|
||||
#include "sp-process-model-row.h"
|
||||
#include "sp-profiler-menu-button.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpProfiler *profiler;
|
||||
SpModelFilter *process_filter;
|
||||
|
||||
/* Gtk template widgets */
|
||||
GtkTreeModel *environment_model;
|
||||
GtkLabel *label;
|
||||
GtkPopover *popover;
|
||||
GtkEntry *process_filter_entry;
|
||||
GtkListBox *process_list_box;
|
||||
SpProcessModel *process_model;
|
||||
GtkBox *processes_box;
|
||||
GtkEntry *spawn_entry;
|
||||
GtkStack *stack;
|
||||
GtkSwitch *whole_system_switch;
|
||||
GtkTreeView *env_tree_view;
|
||||
GtkTreeViewColumn *env_key_column;
|
||||
GtkTreeViewColumn *env_value_column;
|
||||
GtkCellRenderer *key_cell;
|
||||
GtkCellRenderer *value_cell;
|
||||
GtkCheckButton *inherit_environ;
|
||||
|
||||
/* Property Bindings */
|
||||
GBinding *inherit_binding;
|
||||
GBinding *list_sensitive_binding;
|
||||
GBinding *mutable_binding;
|
||||
GBinding *whole_system_binding;
|
||||
|
||||
/* Signal handlers */
|
||||
gulong notify_whole_system_handler;
|
||||
|
||||
/* GSources */
|
||||
guint save_env_source;
|
||||
} SpProfilerMenuButtonPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpProfilerMenuButton, sp_profiler_menu_button, GTK_TYPE_MENU_BUTTON)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PROFILER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static void sp_profiler_menu_button_env_row_changed (SpProfilerMenuButton *self,
|
||||
GtkTreePath *tree_path,
|
||||
GtkTreeIter *tree_iter,
|
||||
gpointer user_data);
|
||||
static void sp_profiler_menu_button_validate_spawn (SpProfilerMenuButton *self,
|
||||
GtkEntry *entry);
|
||||
static gboolean save_environ_to_gsettings (gpointer data);
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
GtkWidget *
|
||||
sp_profiler_menu_button_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_PROFILER_MENU_BUTTON, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_update_label (SpProfilerMenuButton *self)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
g_autofree gchar *str = NULL;
|
||||
const gchar *visible_child;
|
||||
const GPid *pids;
|
||||
guint n_pids = 0;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
|
||||
if (priv->profiler == NULL)
|
||||
{
|
||||
gtk_label_set_label (priv->label, "");
|
||||
return;
|
||||
}
|
||||
|
||||
visible_child = gtk_stack_get_visible_child_name (priv->stack);
|
||||
|
||||
if (g_strcmp0 (visible_child, "spawn") == 0)
|
||||
{
|
||||
const gchar *text;
|
||||
|
||||
text = gtk_entry_get_text (priv->spawn_entry);
|
||||
|
||||
if (text && *text)
|
||||
gtk_label_set_label (priv->label, text);
|
||||
else if (sp_profiler_get_whole_system (priv->profiler))
|
||||
gtk_label_set_label (priv->label, _("All Processes"));
|
||||
else
|
||||
gtk_label_set_label (priv->label, _("New Process"));
|
||||
|
||||
sp_profiler_set_spawn (priv->profiler, text && *text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sp_profiler_set_spawn (priv->profiler, FALSE);
|
||||
|
||||
pids = sp_profiler_get_pids (priv->profiler, &n_pids);
|
||||
|
||||
if (n_pids == 0 || sp_profiler_get_whole_system (priv->profiler))
|
||||
{
|
||||
gtk_label_set_label (priv->label, _("All Processes"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (n_pids == 1)
|
||||
{
|
||||
/* Translators: %d is the PID of the process. */
|
||||
str = g_strdup_printf (_("Process %d"), pids[0]);
|
||||
gtk_label_set_label (priv->label, str);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Translators: %u is the number (amount) of processes. */
|
||||
str = g_strdup_printf (ngettext("%u Process", "%u Processes", n_pids), n_pids);
|
||||
gtk_label_set_label (priv->label, str);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_selected_flags (GtkWidget *widget,
|
||||
gpointer user_data)
|
||||
{
|
||||
sp_process_model_row_set_selected (SP_PROCESS_MODEL_ROW (widget), FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
add_binding (GBinding **binding,
|
||||
gpointer src,
|
||||
const gchar *src_property,
|
||||
gpointer dst,
|
||||
const gchar *dst_property,
|
||||
GBindingFlags flags)
|
||||
{
|
||||
g_assert (binding != NULL);
|
||||
g_assert (*binding == NULL);
|
||||
g_assert (src != NULL);
|
||||
g_assert (src_property != NULL);
|
||||
g_assert (dst != NULL);
|
||||
g_assert (dst_property != NULL);
|
||||
|
||||
*binding = g_object_bind_property (src, src_property,
|
||||
dst, dst_property,
|
||||
flags);
|
||||
g_object_add_weak_pointer (G_OBJECT (*binding), (gpointer *)binding);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_binding (GBinding **binding)
|
||||
{
|
||||
g_assert (binding != NULL);
|
||||
g_assert (!*binding || G_IS_BINDING (*binding));
|
||||
|
||||
if (*binding != NULL)
|
||||
{
|
||||
g_object_remove_weak_pointer (G_OBJECT (*binding), (gpointer *)binding);
|
||||
g_binding_unbind (*binding);
|
||||
*binding = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_connect (SpProfilerMenuButton *self)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (SP_IS_PROFILER (priv->profiler));
|
||||
|
||||
add_binding (&priv->mutable_binding,
|
||||
priv->profiler, "is-mutable",
|
||||
self, "sensitive",
|
||||
G_BINDING_SYNC_CREATE);
|
||||
|
||||
add_binding (&priv->whole_system_binding,
|
||||
priv->profiler, "whole-system",
|
||||
priv->whole_system_switch, "active",
|
||||
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
|
||||
|
||||
add_binding (&priv->list_sensitive_binding,
|
||||
priv->profiler, "whole-system",
|
||||
priv->processes_box, "visible",
|
||||
G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
|
||||
|
||||
add_binding (&priv->inherit_binding,
|
||||
priv->inherit_environ, "active",
|
||||
priv->profiler, "spawn-inherit-environ",
|
||||
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
|
||||
|
||||
priv->notify_whole_system_handler =
|
||||
g_signal_connect_object (priv->profiler,
|
||||
"notify::whole-system",
|
||||
G_CALLBACK (sp_profiler_menu_button_update_label),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
sp_profiler_menu_button_update_label (self);
|
||||
|
||||
sp_profiler_menu_button_validate_spawn (self, priv->spawn_entry);
|
||||
sp_profiler_menu_button_env_row_changed (self, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_disconnect (SpProfilerMenuButton *self)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (SP_IS_PROFILER (priv->profiler));
|
||||
|
||||
clear_binding (&priv->mutable_binding);
|
||||
clear_binding (&priv->whole_system_binding);
|
||||
clear_binding (&priv->list_sensitive_binding);
|
||||
clear_binding (&priv->inherit_binding);
|
||||
|
||||
if (priv->save_env_source != 0)
|
||||
save_environ_to_gsettings (self);
|
||||
|
||||
g_signal_handler_disconnect (priv->profiler, priv->notify_whole_system_handler);
|
||||
priv->notify_whole_system_handler = 0;
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
|
||||
|
||||
g_clear_object (&priv->profiler);
|
||||
|
||||
gtk_container_foreach (GTK_CONTAINER (priv->process_list_box),
|
||||
clear_selected_flags,
|
||||
NULL);
|
||||
|
||||
sp_profiler_menu_button_update_label (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_profiler_menu_button_get_profiler:
|
||||
* @self: An #SpProfilerMenuButton
|
||||
*
|
||||
* Gets the profiler instance that is being configured.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): An #SpProfiler or %NULL.
|
||||
*/
|
||||
SpProfiler *
|
||||
sp_profiler_menu_button_get_profiler (SpProfilerMenuButton *self)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_PROFILER_MENU_BUTTON (self), NULL);
|
||||
|
||||
return priv->profiler;
|
||||
}
|
||||
|
||||
void
|
||||
sp_profiler_menu_button_set_profiler (SpProfilerMenuButton *self,
|
||||
SpProfiler *profiler)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_return_if_fail (!profiler || SP_IS_PROFILER (profiler));
|
||||
|
||||
if (priv->profiler != profiler)
|
||||
{
|
||||
if (priv->profiler != NULL)
|
||||
sp_profiler_menu_button_disconnect (self);
|
||||
|
||||
if (profiler != NULL)
|
||||
{
|
||||
priv->profiler = g_object_ref (profiler);
|
||||
sp_profiler_menu_button_connect (self);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILER]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_row_activated (SpProfilerMenuButton *self,
|
||||
SpProcessModelRow *row,
|
||||
GtkListBox *list_box)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
gboolean selected;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (SP_IS_PROCESS_MODEL_ROW (row));
|
||||
g_assert (GTK_IS_LIST_BOX (list_box));
|
||||
|
||||
selected = !sp_process_model_row_get_selected (row);
|
||||
sp_process_model_row_set_selected (row, selected);
|
||||
|
||||
if (priv->profiler != NULL)
|
||||
{
|
||||
SpProcessModelItem *item = sp_process_model_row_get_item (row);
|
||||
GPid pid = sp_process_model_item_get_pid (item);
|
||||
|
||||
if (selected)
|
||||
sp_profiler_add_pid (priv->profiler, pid);
|
||||
else
|
||||
sp_profiler_remove_pid (priv->profiler, pid);
|
||||
}
|
||||
|
||||
sp_profiler_menu_button_update_label (self);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
sp_profiler_menu_button_create_row (gpointer itemptr,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpProcessModelItem *item = itemptr;
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL_ITEM (item));
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (user_data));
|
||||
|
||||
return g_object_new (SP_TYPE_PROCESS_MODEL_ROW,
|
||||
"item", item,
|
||||
"visible", TRUE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_clicked (GtkButton *button)
|
||||
{
|
||||
SpProfilerMenuButton *self = (SpProfilerMenuButton *)button;
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
|
||||
sp_process_model_queue_reload (priv->process_model);
|
||||
|
||||
GTK_BUTTON_CLASS (sp_profiler_menu_button_parent_class)->clicked (button);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_profiler_menu_button_filter_func (GObject *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
const gchar *needle = user_data;
|
||||
const gchar *haystack;
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL_ITEM (object));
|
||||
|
||||
if (needle == NULL)
|
||||
return TRUE;
|
||||
|
||||
haystack = sp_process_model_item_get_command_line (SP_PROCESS_MODEL_ITEM (object));
|
||||
|
||||
if (haystack == NULL)
|
||||
return FALSE;
|
||||
|
||||
return strstr (haystack, needle) != NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_filter_changed (SpProfilerMenuButton *self,
|
||||
GtkEntry *entry)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
const gchar *text;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (GTK_IS_ENTRY (entry));
|
||||
|
||||
text = gtk_entry_get_text (entry);
|
||||
if (text && *text == 0)
|
||||
text = NULL;
|
||||
|
||||
sp_model_filter_set_filter_func (priv->process_filter,
|
||||
sp_profiler_menu_button_filter_func,
|
||||
g_strdup (text),
|
||||
g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_constructed (GObject *object)
|
||||
{
|
||||
SpProfilerMenuButton *self = (SpProfilerMenuButton *)object;
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
|
||||
priv->process_filter = sp_model_filter_new (G_LIST_MODEL (priv->process_model));
|
||||
|
||||
gtk_list_box_bind_model (priv->process_list_box,
|
||||
G_LIST_MODEL (priv->process_filter),
|
||||
sp_profiler_menu_button_create_row,
|
||||
self, NULL);
|
||||
|
||||
G_OBJECT_CLASS (sp_profiler_menu_button_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_realize (GtkWidget *widget)
|
||||
{
|
||||
SpProfilerMenuButton *self = (SpProfilerMenuButton *)widget;
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
g_autoptr(GSettings) settings = NULL;
|
||||
g_auto(GStrv) env = NULL;
|
||||
|
||||
GTK_WIDGET_CLASS (sp_profiler_menu_button_parent_class)->realize (widget);
|
||||
|
||||
settings = g_settings_new ("org.gnome.sysprof2");
|
||||
|
||||
env = g_settings_get_strv (settings, "last-spawn-env");
|
||||
|
||||
g_settings_bind (settings, "last-spawn-argv",
|
||||
priv->spawn_entry, "text",
|
||||
G_SETTINGS_BIND_DEFAULT);
|
||||
g_settings_bind (settings, "last-spawn-inherit-env",
|
||||
priv->inherit_environ, "active",
|
||||
G_SETTINGS_BIND_DEFAULT);
|
||||
|
||||
if (env)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
guint i;
|
||||
|
||||
model = gtk_tree_view_get_model (priv->env_tree_view);
|
||||
gtk_list_store_clear (GTK_LIST_STORE (model));
|
||||
|
||||
for (i = 0; env [i]; i++)
|
||||
{
|
||||
const gchar *key = env [i];
|
||||
const gchar *value = NULL;
|
||||
gchar *eq = strchr (env[i], '=');
|
||||
|
||||
if (eq)
|
||||
{
|
||||
*eq = '\0';
|
||||
value = eq + 1;
|
||||
}
|
||||
|
||||
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
||||
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
|
||||
0, key,
|
||||
1, value,
|
||||
-1);
|
||||
}
|
||||
|
||||
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
save_environ_to_gsettings (gpointer data)
|
||||
{
|
||||
SpProfilerMenuButton *self = data;
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
g_autoptr(GPtrArray) ar = NULL;
|
||||
g_autoptr(GSettings) settings = NULL;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
|
||||
priv->save_env_source = 0;
|
||||
|
||||
if (priv->environment_model == NULL)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
settings = g_settings_new ("org.gnome.sysprof2");
|
||||
|
||||
ar = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (gtk_tree_model_get_iter_first (priv->environment_model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
g_autofree gchar *key = NULL;
|
||||
g_autofree gchar *value = NULL;
|
||||
|
||||
gtk_tree_model_get (priv->environment_model, &iter,
|
||||
0, &key,
|
||||
1, &value,
|
||||
-1);
|
||||
|
||||
if (!key || !*key)
|
||||
continue;
|
||||
|
||||
g_ptr_array_add (ar, g_strdup_printf ("%s=%s", key, value ? value : ""));
|
||||
}
|
||||
while (gtk_tree_model_iter_next (priv->environment_model, &iter));
|
||||
}
|
||||
|
||||
g_ptr_array_add (ar, NULL);
|
||||
|
||||
g_settings_set_strv (settings, "last-spawn-env", (const gchar * const *)ar->pdata);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_destroy (GtkWidget *widget)
|
||||
{
|
||||
SpProfilerMenuButton *self = (SpProfilerMenuButton *)widget;
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
if (priv->profiler != NULL)
|
||||
{
|
||||
sp_profiler_menu_button_disconnect (self);
|
||||
g_clear_object (&priv->profiler);
|
||||
}
|
||||
|
||||
g_clear_object (&priv->process_filter);
|
||||
|
||||
if (priv->save_env_source)
|
||||
{
|
||||
g_source_remove (priv->save_env_source);
|
||||
priv->save_env_source = 0;
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS (sp_profiler_menu_button_parent_class)->destroy (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProfilerMenuButton *self = SP_PROFILER_MENU_BUTTON (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PROFILER:
|
||||
g_value_set_object (value, sp_profiler_menu_button_get_profiler (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProfilerMenuButton *self = SP_PROFILER_MENU_BUTTON (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PROFILER:
|
||||
sp_profiler_menu_button_set_profiler (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_class_init (SpProfilerMenuButtonClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
|
||||
|
||||
object_class->constructed = sp_profiler_menu_button_constructed;
|
||||
object_class->get_property = sp_profiler_menu_button_get_property;
|
||||
object_class->set_property = sp_profiler_menu_button_set_property;
|
||||
|
||||
widget_class->destroy = sp_profiler_menu_button_destroy;
|
||||
widget_class->realize = sp_profiler_menu_button_realize;
|
||||
|
||||
button_class->clicked = sp_profiler_menu_button_clicked;
|
||||
|
||||
properties [PROP_PROFILER] =
|
||||
g_param_spec_object ("profiler",
|
||||
"Profiler",
|
||||
"Profiler",
|
||||
SP_TYPE_PROFILER,
|
||||
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-profiler-menu-button.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_key_column);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_tree_view);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_value_column);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, environment_model);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, inherit_environ);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, key_cell);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, label);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, popover);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_filter_entry);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_list_box);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_model);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, processes_box);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, spawn_entry);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, stack);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, value_cell);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, whole_system_switch);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_env_row_changed (SpProfilerMenuButton *self,
|
||||
GtkTreePath *tree_path,
|
||||
GtkTreeIter *tree_iter,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
g_autoptr(GPtrArray) env = NULL;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (GTK_IS_TREE_MODEL (priv->environment_model));
|
||||
|
||||
/* queue saving settings to gsettings */
|
||||
if (priv->save_env_source)
|
||||
g_source_remove (priv->save_env_source);
|
||||
priv->save_env_source = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
|
||||
1,
|
||||
save_environ_to_gsettings,
|
||||
g_object_ref (self),
|
||||
g_object_unref);
|
||||
|
||||
if (priv->profiler == NULL)
|
||||
return;
|
||||
|
||||
/* sync the environ to the profiler */
|
||||
env = g_ptr_array_new_with_free_func (g_free);
|
||||
model = priv->environment_model;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
g_autofree gchar *key = NULL;
|
||||
g_autofree gchar *value = NULL;
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
0, &key,
|
||||
1, &value,
|
||||
-1);
|
||||
|
||||
if (key && *key)
|
||||
g_ptr_array_add (env, g_strdup_printf ("%s=%s", key, value));
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
g_ptr_array_add (env, NULL);
|
||||
sp_profiler_set_spawn_env (priv->profiler, (const gchar * const *)env->pdata);
|
||||
}
|
||||
|
||||
static void
|
||||
on_backspace (SpProfilerMenuButton *self,
|
||||
GtkEntry *entry)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
if (g_object_get_data (G_OBJECT (entry), "CELL_WAS_EMPTY"))
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter iter;
|
||||
|
||||
model = gtk_tree_view_get_model (priv->env_tree_view);
|
||||
selection = gtk_tree_view_get_selection (priv->env_tree_view);
|
||||
|
||||
if (gtk_tree_selection_get_selected (selection, NULL, &iter))
|
||||
{
|
||||
gtk_cell_renderer_stop_editing (priv->key_cell, TRUE);
|
||||
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
|
||||
}
|
||||
}
|
||||
else
|
||||
g_object_set_data (G_OBJECT (entry), "CELL_WAS_EMPTY",
|
||||
GINT_TO_POINTER (*gtk_entry_get_text (entry) == '\0'));
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_env_key_editing_started (SpProfilerMenuButton *self,
|
||||
GtkCellEditable *editable,
|
||||
const gchar *path,
|
||||
GtkCellRenderer *cell)
|
||||
{
|
||||
g_signal_connect_object (editable,
|
||||
"backspace",
|
||||
G_CALLBACK (on_backspace),
|
||||
self,
|
||||
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_env_key_edited (SpProfilerMenuButton *self,
|
||||
const gchar *path,
|
||||
const gchar *new_text,
|
||||
GtkCellRendererText *cell)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
GtkTreeModel *model;
|
||||
GtkTreePath *tree_path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (path != NULL);
|
||||
g_assert (new_text != NULL);
|
||||
g_assert (GTK_IS_CELL_RENDERER_TEXT (cell));
|
||||
|
||||
model = gtk_tree_view_get_model (priv->env_tree_view);
|
||||
|
||||
tree_path = gtk_tree_path_new_from_string (path);
|
||||
|
||||
if (gtk_tree_model_get_iter (model, &iter, tree_path))
|
||||
{
|
||||
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
|
||||
0, new_text,
|
||||
-1);
|
||||
|
||||
if (!gtk_tree_model_iter_next (model, &iter))
|
||||
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
||||
|
||||
gtk_tree_view_set_cursor_on_cell (priv->env_tree_view,
|
||||
tree_path,
|
||||
priv->env_value_column,
|
||||
priv->value_cell,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
gtk_tree_path_free (tree_path);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_env_value_edited (SpProfilerMenuButton *self,
|
||||
const gchar *path,
|
||||
const gchar *new_text,
|
||||
GtkCellRendererText *cell)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
GtkTreeModel *model;
|
||||
GtkTreePath *tree_path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (path != NULL);
|
||||
g_assert (new_text != NULL);
|
||||
g_assert (GTK_IS_CELL_RENDERER_TEXT (cell));
|
||||
|
||||
model = gtk_tree_view_get_model (priv->env_tree_view);
|
||||
|
||||
tree_path = gtk_tree_path_new_from_string (path);
|
||||
|
||||
if (gtk_tree_model_get_iter (model, &iter, tree_path))
|
||||
{
|
||||
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
|
||||
1, new_text,
|
||||
-1);
|
||||
|
||||
if (!gtk_tree_model_iter_next (model, &iter))
|
||||
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
||||
|
||||
gtk_tree_path_next (tree_path);
|
||||
|
||||
gtk_tree_view_set_cursor_on_cell (priv->env_tree_view,
|
||||
tree_path,
|
||||
priv->env_key_column,
|
||||
priv->key_cell,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
gtk_tree_path_free (tree_path);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_validate_spawn (SpProfilerMenuButton *self,
|
||||
GtkEntry *entry)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
g_auto(GStrv) argv = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
const gchar *text;
|
||||
gint argc;
|
||||
|
||||
g_assert (SP_IS_PROFILER_MENU_BUTTON (self));
|
||||
g_assert (GTK_IS_ENTRY (entry));
|
||||
|
||||
text = gtk_entry_get_text (entry);
|
||||
|
||||
if (text && *text && !g_shell_parse_argv (text, &argc, &argv, &error))
|
||||
{
|
||||
sp_profiler_set_spawn_argv (priv->profiler, NULL);
|
||||
g_object_set (entry,
|
||||
"secondary-icon-name", "dialog-warning-symbolic",
|
||||
"secondary-icon-tooltip-text", _("The command line arguments provided are invalid"),
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sp_profiler_set_spawn_argv (priv->profiler, (const gchar * const *)argv);
|
||||
g_object_set (entry,
|
||||
"secondary-icon-name", NULL,
|
||||
"secondary-icon-tooltip-text", NULL,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_profiler_menu_button_init (SpProfilerMenuButton *self)
|
||||
{
|
||||
SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self);
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
g_signal_connect_object (priv->process_filter_entry,
|
||||
"changed",
|
||||
G_CALLBACK (sp_profiler_menu_button_filter_changed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->spawn_entry,
|
||||
"changed",
|
||||
G_CALLBACK (sp_profiler_menu_button_update_label),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->spawn_entry,
|
||||
"changed",
|
||||
G_CALLBACK (sp_profiler_menu_button_validate_spawn),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->stack,
|
||||
"notify::visible-child",
|
||||
G_CALLBACK (sp_profiler_menu_button_update_label),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->process_list_box,
|
||||
"row-activated",
|
||||
G_CALLBACK (sp_profiler_menu_button_row_activated),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->key_cell,
|
||||
"edited",
|
||||
G_CALLBACK (sp_profiler_menu_button_env_key_edited),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->value_cell,
|
||||
"edited",
|
||||
G_CALLBACK (sp_profiler_menu_button_env_value_edited),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (gtk_tree_view_get_model (priv->env_tree_view),
|
||||
"row-changed",
|
||||
G_CALLBACK (sp_profiler_menu_button_env_row_changed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->key_cell,
|
||||
"editing-started",
|
||||
G_CALLBACK (sp_profiler_menu_button_env_key_editing_started),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
|
||||
}
|
||||
48
src/libsysprof-ui/sp-profiler-menu-button.h
Normal file
48
src/libsysprof-ui/sp-profiler-menu-button.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* sp-profiler-menu-button.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
|
||||
*/
|
||||
|
||||
#ifndef SP_PROFILER_MENU_BUTTON_H
|
||||
#define SP_PROFILER_MENU_BUTTON_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-profiler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_PROFILER_MENU_BUTTON (sp_profiler_menu_button_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpProfilerMenuButton, sp_profiler_menu_button, SP, PROFILER_MENU_BUTTON, GtkMenuButton)
|
||||
|
||||
struct _SpProfilerMenuButtonClass
|
||||
{
|
||||
GtkMenuButtonClass parent_class;
|
||||
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
GtkWidget *sp_profiler_menu_button_new (void);
|
||||
void sp_profiler_menu_button_set_profiler (SpProfilerMenuButton *self,
|
||||
SpProfiler *profiler);
|
||||
SpProfiler *sp_profiler_menu_button_get_profiler (SpProfilerMenuButton *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_PROFILER_MENU_BUTTON_H */
|
||||
195
src/libsysprof-ui/sp-recording-state-view.c
Normal file
195
src/libsysprof-ui/sp-recording-state-view.c
Normal file
@ -0,0 +1,195 @@
|
||||
/* sp-recording-state-view.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
|
||||
*/
|
||||
|
||||
#include "sp-recording-state-view.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpProfiler *profiler;
|
||||
gulong notify_elapsed_handler;
|
||||
GtkLabel *elapsed;
|
||||
} SpRecordingStateViewPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpRecordingStateView, sp_recording_state_view, GTK_TYPE_BIN)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PROFILER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
GtkWidget *
|
||||
sp_recording_state_view_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_RECORDING_STATE_VIEW, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_notify_elapsed (SpRecordingStateView *self,
|
||||
GParamSpec *pspec,
|
||||
SpProfiler *profiler)
|
||||
{
|
||||
SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self);
|
||||
g_autofree gchar *str = NULL;
|
||||
gint64 elapsed;
|
||||
guint hours;
|
||||
guint minutes;
|
||||
guint seconds;
|
||||
|
||||
g_assert (SP_IS_RECORDING_STATE_VIEW (self));
|
||||
g_assert (SP_IS_PROFILER (profiler));
|
||||
|
||||
elapsed = (gint64)sp_profiler_get_elapsed (profiler);
|
||||
|
||||
hours = elapsed / (60 * 60);
|
||||
if (hours > 0)
|
||||
minutes = (elapsed % (hours * 60 * 60)) / 60;
|
||||
else
|
||||
minutes = elapsed / 60;
|
||||
seconds = elapsed % 60;
|
||||
|
||||
if (hours == 0)
|
||||
str = g_strdup_printf ("%02u:%02u", minutes, seconds);
|
||||
else
|
||||
str = g_strdup_printf ("%02u:%02u:%02u", hours, minutes, seconds);
|
||||
|
||||
gtk_label_set_label (priv->elapsed, str);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_destroy (GtkWidget *widget)
|
||||
{
|
||||
SpRecordingStateView *self = (SpRecordingStateView *)widget;
|
||||
SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self);
|
||||
|
||||
if (priv->profiler != NULL)
|
||||
{
|
||||
g_signal_handler_disconnect (priv->profiler, priv->notify_elapsed_handler);
|
||||
g_clear_object (&priv->profiler);
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS (sp_recording_state_view_parent_class)->destroy (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpRecordingStateView *self = SP_RECORDING_STATE_VIEW (object);
|
||||
SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PROFILER:
|
||||
g_value_set_object (value, priv->profiler);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpRecordingStateView *self = SP_RECORDING_STATE_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PROFILER:
|
||||
sp_recording_state_view_set_profiler (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_class_init (SpRecordingStateViewClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->get_property = sp_recording_state_view_get_property;
|
||||
object_class->set_property = sp_recording_state_view_set_property;
|
||||
|
||||
widget_class->destroy = sp_recording_state_view_destroy;
|
||||
|
||||
properties [PROP_PROFILER] =
|
||||
g_param_spec_object ("profiler",
|
||||
"Profiler",
|
||||
"Profiler",
|
||||
SP_TYPE_PROFILER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class,
|
||||
"/org/gnome/sysprof/ui/sp-recording-state-view.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpRecordingStateView, elapsed);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_recording_state_view_init (SpRecordingStateView *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
void
|
||||
sp_recording_state_view_set_profiler (SpRecordingStateView *self,
|
||||
SpProfiler *profiler)
|
||||
{
|
||||
SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_RECORDING_STATE_VIEW (self));
|
||||
g_assert (!profiler || SP_IS_PROFILER (profiler));
|
||||
|
||||
gtk_label_set_label (priv->elapsed, "00:00");
|
||||
|
||||
if (profiler != priv->profiler)
|
||||
{
|
||||
if (priv->profiler != NULL)
|
||||
{
|
||||
g_signal_handler_disconnect (priv->profiler, priv->notify_elapsed_handler);
|
||||
g_clear_object (&priv->profiler);
|
||||
}
|
||||
|
||||
gtk_label_set_label (priv->elapsed, "00:00");
|
||||
|
||||
if (profiler != NULL)
|
||||
{
|
||||
priv->profiler = g_object_ref (profiler);
|
||||
priv->notify_elapsed_handler =
|
||||
g_signal_connect_object (profiler,
|
||||
"notify::elapsed",
|
||||
G_CALLBACK (sp_recording_state_view_notify_elapsed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/libsysprof-ui/sp-recording-state-view.h
Normal file
47
src/libsysprof-ui/sp-recording-state-view.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* sp-recording-state-view.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
|
||||
*/
|
||||
|
||||
#ifndef SP_RECORDING_STATE_VIEW_H
|
||||
#define SP_RECORDING_STATE_VIEW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-profiler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_RECORDING_STATE_VIEW (sp_recording_state_view_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpRecordingStateView, sp_recording_state_view, SP, RECORDING_STATE_VIEW, GtkBin)
|
||||
|
||||
struct _SpRecordingStateViewClass
|
||||
{
|
||||
GtkBinClass parent;
|
||||
|
||||
gpointer padding[4];
|
||||
};
|
||||
|
||||
GtkWidget *sp_recording_state_view_new (void);
|
||||
void sp_recording_state_view_set_profiler (SpRecordingStateView *self,
|
||||
SpProfiler *profiler);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_RECORDING_STATE_VIEW_H */
|
||||
266
src/libsysprof-ui/sp-theme-manager.c
Normal file
266
src/libsysprof-ui/sp-theme-manager.c
Normal file
@ -0,0 +1,266 @@
|
||||
/* sp-theme-manager.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-theme-manager"
|
||||
|
||||
#include "sp-theme-manager.h"
|
||||
|
||||
struct _SpThemeManager
|
||||
{
|
||||
GObject parent_instance;
|
||||
GHashTable *theme_resources;
|
||||
guint reload_source;
|
||||
guint registered_signals : 1;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint id;
|
||||
gchar *key;
|
||||
gchar *theme_name;
|
||||
gchar *variant;
|
||||
gchar *resource;
|
||||
GtkCssProvider *provider;
|
||||
} ThemeResource;
|
||||
|
||||
G_DEFINE_TYPE (SpThemeManager, sp_theme_manager, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
theme_resource_free (gpointer data)
|
||||
{
|
||||
ThemeResource *theme_resource = data;
|
||||
|
||||
if (theme_resource != NULL)
|
||||
{
|
||||
g_clear_pointer (&theme_resource->key, g_free);
|
||||
g_clear_pointer (&theme_resource->theme_name, g_free);
|
||||
g_clear_pointer (&theme_resource->variant, g_free);
|
||||
g_clear_pointer (&theme_resource->resource, g_free);
|
||||
|
||||
if (theme_resource->provider != NULL)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider));
|
||||
g_clear_object (&theme_resource->provider);
|
||||
}
|
||||
|
||||
g_slice_free (ThemeResource, theme_resource);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_resource_matches (ThemeResource *theme_resource,
|
||||
GtkSettings *settings)
|
||||
{
|
||||
g_autofree gchar *theme_name = NULL;
|
||||
gboolean dark_theme = FALSE;
|
||||
|
||||
g_assert (theme_resource != NULL);
|
||||
g_assert (GTK_IS_SETTINGS (settings));
|
||||
|
||||
if (theme_resource->theme_name == NULL)
|
||||
return TRUE;
|
||||
|
||||
g_object_get (settings,
|
||||
"gtk-theme-name", &theme_name,
|
||||
"gtk-application-prefer-dark-theme", &dark_theme,
|
||||
NULL);
|
||||
|
||||
if (g_strcmp0 (theme_name, theme_resource->theme_name) == 0)
|
||||
{
|
||||
if (dark_theme && g_strcmp0 ("dark", theme_resource->variant) == 0)
|
||||
return TRUE;
|
||||
|
||||
if (!dark_theme && (!theme_resource->variant || g_strcmp0 ("light", theme_resource->variant) == 0))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_theme_manager_do_reload (gpointer data)
|
||||
{
|
||||
SpThemeManager *self = data;
|
||||
ThemeResource *theme_resource;
|
||||
GHashTableIter iter;
|
||||
GtkSettings *settings;
|
||||
|
||||
g_assert (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
self->reload_source = 0;
|
||||
|
||||
settings = gtk_settings_get_default ();
|
||||
|
||||
g_hash_table_iter_init (&iter, self->theme_resources);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
|
||||
{
|
||||
if (theme_resource_matches (theme_resource, settings))
|
||||
{
|
||||
if (theme_resource->provider == NULL)
|
||||
{
|
||||
theme_resource->provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_resource (theme_resource->provider, theme_resource->resource);
|
||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (theme_resource->provider != NULL)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider));
|
||||
g_clear_object (&theme_resource->provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_queue_reload (SpThemeManager *self)
|
||||
{
|
||||
g_assert (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
if (self->reload_source == 0)
|
||||
self->reload_source = gdk_threads_add_idle_full (G_PRIORITY_LOW,
|
||||
sp_theme_manager_do_reload,
|
||||
self,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_finalize (GObject *object)
|
||||
{
|
||||
SpThemeManager *self = (SpThemeManager *)object;
|
||||
|
||||
if (self->reload_source != 0)
|
||||
{
|
||||
g_source_remove (self->reload_source);
|
||||
self->reload_source = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->theme_resources, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_theme_manager_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_class_init (SpThemeManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_theme_manager_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_init (SpThemeManager *self)
|
||||
{
|
||||
self->theme_resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, theme_resource_free);
|
||||
|
||||
gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/gnome/sysprof/icons");
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_theme_manager_get_default:
|
||||
*
|
||||
* Returns: (transfer none): An #SpThemeManager
|
||||
*/
|
||||
SpThemeManager *
|
||||
sp_theme_manager_get_default (void)
|
||||
{
|
||||
static SpThemeManager *instance;
|
||||
|
||||
if (instance == NULL)
|
||||
instance = g_object_new (SP_TYPE_THEME_MANAGER, NULL);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
guint
|
||||
sp_theme_manager_register_resource (SpThemeManager *self,
|
||||
const gchar *theme_name,
|
||||
const gchar *variant,
|
||||
const gchar *resource)
|
||||
{
|
||||
ThemeResource *theme_resource;
|
||||
static guint counter;
|
||||
guint id;
|
||||
|
||||
g_return_val_if_fail (SP_IS_THEME_MANAGER (self), 0);
|
||||
|
||||
theme_resource = g_slice_new0 (ThemeResource);
|
||||
theme_resource->id = id = ++counter;
|
||||
theme_resource->key = g_strdup_printf ("%s-%s-%d",
|
||||
theme_name ? theme_name : "shared",
|
||||
variant ? variant : "light",
|
||||
theme_resource->id);
|
||||
theme_resource->theme_name = g_strdup (theme_name);
|
||||
theme_resource->variant = g_strdup (variant);
|
||||
theme_resource->resource = g_strdup (resource);
|
||||
theme_resource->provider = NULL;
|
||||
|
||||
g_hash_table_insert (self->theme_resources, theme_resource->key, theme_resource);
|
||||
|
||||
if (!self->registered_signals)
|
||||
{
|
||||
self->registered_signals = TRUE;
|
||||
g_signal_connect_object (gtk_settings_get_default (),
|
||||
"notify::gtk-application-prefer-dark-theme",
|
||||
G_CALLBACK (sp_theme_manager_queue_reload),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (gtk_settings_get_default (),
|
||||
"notify::gtk-theme-name",
|
||||
G_CALLBACK (sp_theme_manager_queue_reload),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
sp_theme_manager_queue_reload (self);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
sp_theme_manager_unregister (SpThemeManager *self,
|
||||
guint registration_id)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
ThemeResource *theme_resource;
|
||||
|
||||
g_return_if_fail (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
g_hash_table_iter_init (&iter, self->theme_resources);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
|
||||
{
|
||||
if (theme_resource->id == registration_id)
|
||||
{
|
||||
/* Provider is unregistered during destroy */
|
||||
g_hash_table_iter_remove (&iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/libsysprof-ui/sp-theme-manager.h
Normal file
42
src/libsysprof-ui/sp-theme-manager.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* sp-theme-manager.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
|
||||
*/
|
||||
|
||||
#ifndef SP_THEME_MANAGER_H
|
||||
#define SP_THEME_MANAGER_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_THEME_MANAGER (sp_theme_manager_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpThemeManager, sp_theme_manager, SP, THEME_MANAGER, GObject)
|
||||
|
||||
SpThemeManager *sp_theme_manager_get_default (void);
|
||||
void sp_theme_manager_unregister (SpThemeManager *self,
|
||||
guint registration_id);
|
||||
guint sp_theme_manager_register_resource (SpThemeManager *self,
|
||||
const gchar *theme_name,
|
||||
const gchar *variant,
|
||||
const gchar *resource);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_THEME_MANAGER_H */
|
||||
453
src/libsysprof-ui/sp-visualizer-list.c
Normal file
453
src/libsysprof-ui/sp-visualizer-list.c
Normal file
@ -0,0 +1,453 @@
|
||||
/* sp-visualizer-list.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-visualizer-list"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <sysprof.h>
|
||||
|
||||
#include "sp-cpu-visualizer-row.h"
|
||||
#include "sp-visualizer-list.h"
|
||||
#include "sp-visualizer-row.h"
|
||||
#include "sp-mark-visualizer-row.h"
|
||||
#include "sp-zoom-manager.h"
|
||||
|
||||
#define NSEC_PER_SEC G_GUINT64_CONSTANT(1000000000)
|
||||
#define DEFAULT_PIXELS_PER_SECOND 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureReader *reader;
|
||||
SpZoomManager *zoom_manager;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
} SpVisualizerListPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureCursor *cursor;
|
||||
GHashTable *mark_groups;
|
||||
guint fps_counter;
|
||||
GArray *memory;
|
||||
guint has_cpu : 1;
|
||||
} Discovery;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_READER,
|
||||
PROP_ZOOM_MANAGER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (SpVisualizerList, sp_visualizer_list, GTK_TYPE_LIST_BOX)
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
discovery_free (Discovery *state)
|
||||
{
|
||||
g_clear_pointer (&state->mark_groups, g_hash_table_unref);
|
||||
g_clear_pointer (&state->memory, g_array_unref);
|
||||
g_clear_pointer (&state->cursor, sp_capture_cursor_unref);
|
||||
g_slice_free (Discovery, state);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_add (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
SpVisualizerList *self = (SpVisualizerList *)container;
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
GTK_CONTAINER_CLASS (sp_visualizer_list_parent_class)->add (container, widget);
|
||||
|
||||
if (SP_IS_VISUALIZER_ROW (widget))
|
||||
{
|
||||
sp_visualizer_row_set_reader (SP_VISUALIZER_ROW (widget), priv->reader);
|
||||
sp_visualizer_row_set_zoom_manager (SP_VISUALIZER_ROW (widget), priv->zoom_manager);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_finalize (GObject *object)
|
||||
{
|
||||
SpVisualizerList *self = (SpVisualizerList *)object;
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_visualizer_list_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerList *self = SP_VISUALIZER_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_READER:
|
||||
g_value_set_boxed (value, sp_visualizer_list_get_reader (self));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM_MANAGER:
|
||||
g_value_set_object (value, sp_visualizer_list_get_zoom_manager (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerList *self = SP_VISUALIZER_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_READER:
|
||||
sp_visualizer_list_set_reader (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM_MANAGER:
|
||||
sp_visualizer_list_set_zoom_manager (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_class_init (SpVisualizerListClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_visualizer_list_finalize;
|
||||
object_class->get_property = sp_visualizer_list_get_property;
|
||||
object_class->set_property = sp_visualizer_list_set_property;
|
||||
|
||||
container_class->add = sp_visualizer_list_add;
|
||||
|
||||
properties [PROP_READER] =
|
||||
g_param_spec_boxed ("reader",
|
||||
"Reader",
|
||||
"The capture reader",
|
||||
SP_TYPE_CAPTURE_READER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_ZOOM_MANAGER] =
|
||||
g_param_spec_object ("zoom-manager",
|
||||
"Zoom Manager",
|
||||
"The zoom manager",
|
||||
SP_TYPE_ZOOM_MANAGER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_list_init (SpVisualizerList *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
sp_visualizer_list_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_VISUALIZER_ROW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_list_get_reader:
|
||||
*
|
||||
* Gets the reader that is being used for the #SpVisualizerList.
|
||||
*
|
||||
* Returns: (transfer none): An #SpCaptureReader
|
||||
*/
|
||||
SpCaptureReader *
|
||||
sp_visualizer_list_get_reader (SpVisualizerList *self)
|
||||
{
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_LIST (self), NULL);
|
||||
|
||||
return priv->reader;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
discover_new_rows_frame_cb (const SpCaptureFrame *frame,
|
||||
gpointer user_data)
|
||||
{
|
||||
Discovery *state = user_data;
|
||||
|
||||
g_assert (frame != NULL);
|
||||
g_assert (state != NULL);
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
*
|
||||
* It would be nice if we could redesign this all around the concept of
|
||||
* an "gadget" or something which combines a data collection series
|
||||
* and widget views to be displayed.
|
||||
*/
|
||||
|
||||
if (frame->type == SP_CAPTURE_FRAME_MARK)
|
||||
{
|
||||
const SpCaptureMark *mark = (const SpCaptureMark *)frame;
|
||||
|
||||
if (!g_hash_table_contains (state->mark_groups, mark->group))
|
||||
g_hash_table_add (state->mark_groups, g_strdup (mark->group));
|
||||
}
|
||||
|
||||
if (frame->type == SP_CAPTURE_FRAME_CTRDEF)
|
||||
{
|
||||
const SpCaptureFrameCounterDefine *def = (const SpCaptureFrameCounterDefine *)frame;
|
||||
|
||||
for (guint i = 0; i < def->n_counters; i++)
|
||||
{
|
||||
const SpCaptureCounter *ctr = &def->counters[i];
|
||||
|
||||
if (!state->has_cpu &&
|
||||
strstr (ctr->category, "CPU Percent") != NULL)
|
||||
state->has_cpu = TRUE;
|
||||
else if (!state->fps_counter &&
|
||||
strstr (ctr->category, "gtk") != NULL && strstr (ctr->name, "fps") != NULL)
|
||||
state->fps_counter = ctr->id;
|
||||
else if (strcmp ("Memory", ctr->category) == 0 &&
|
||||
strcmp ("Used", ctr->name) == 0)
|
||||
{
|
||||
guint counter_id = ctr->id;
|
||||
g_array_append_val (state->memory, counter_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
discover_new_rows_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
Discovery *state = task_data;
|
||||
|
||||
g_assert (state != NULL);
|
||||
g_assert (state->cursor != NULL);
|
||||
|
||||
sp_capture_cursor_foreach (state->cursor, discover_new_rows_frame_cb, state);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_capture_results (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpVisualizerList *self = (SpVisualizerList *)object;
|
||||
Discovery *state;
|
||||
const gchar *key;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_LIST (self));
|
||||
g_assert (G_IS_TASK (result));
|
||||
g_assert (user_data == NULL);
|
||||
|
||||
state = g_task_get_task_data (G_TASK (result));
|
||||
|
||||
/*
|
||||
* TODO: It would be really nice if we had a more structured way to do this
|
||||
* so that data collections and visualizations could be mapped
|
||||
* together. One way to do so might be to create the concept of an
|
||||
* "instrument" which represents that pair and allows the user to
|
||||
* select what sort of data collections they'd like to see.
|
||||
*/
|
||||
|
||||
if (state->has_cpu)
|
||||
{
|
||||
GtkWidget *row = g_object_new (SP_TYPE_CPU_VISUALIZER_ROW,
|
||||
/* Translators: CPU is the processor. */
|
||||
"title", _("CPU"),
|
||||
"height-request", 50,
|
||||
"selectable", FALSE,
|
||||
"visible", TRUE,
|
||||
"y-lower", 0.0,
|
||||
"y-upper", 100.0,
|
||||
NULL);
|
||||
gtk_container_add (GTK_CONTAINER (self), row);
|
||||
}
|
||||
|
||||
for (guint i = 0; i < state->memory->len; i++)
|
||||
{
|
||||
guint counter_id = g_array_index (state->memory, guint, i);
|
||||
GdkRGBA rgba;
|
||||
GtkWidget *row = g_object_new (SP_TYPE_LINE_VISUALIZER_ROW,
|
||||
"title", _("Memory Used"),
|
||||
"height-request", 35,
|
||||
"selectable", FALSE,
|
||||
"visible", TRUE,
|
||||
"y-lower", 0.0,
|
||||
NULL);
|
||||
gdk_rgba_parse (&rgba, "#204a87");
|
||||
sp_line_visualizer_row_add_counter (SP_LINE_VISUALIZER_ROW (row), counter_id, &rgba);
|
||||
rgba.alpha = 0.3;
|
||||
sp_line_visualizer_row_set_fill (SP_LINE_VISUALIZER_ROW (row), counter_id, &rgba);
|
||||
gtk_container_add (GTK_CONTAINER (self), row);
|
||||
}
|
||||
|
||||
if (state->fps_counter)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
GtkWidget *row = g_object_new (SP_TYPE_LINE_VISUALIZER_ROW,
|
||||
/* Translators: FPS is frames per second. */
|
||||
"title", _("FPS"),
|
||||
"height-request", 35,
|
||||
"selectable", FALSE,
|
||||
"visible", TRUE,
|
||||
"y-lower", 0.0,
|
||||
"y-upper", 150.0,
|
||||
NULL);
|
||||
gdk_rgba_parse (&rgba, "#204a87");
|
||||
sp_line_visualizer_row_add_counter (SP_LINE_VISUALIZER_ROW (row), state->fps_counter, &rgba);
|
||||
rgba.alpha = 0.3;
|
||||
sp_line_visualizer_row_set_fill (SP_LINE_VISUALIZER_ROW (row), state->fps_counter, &rgba);
|
||||
gtk_container_add (GTK_CONTAINER (self), row);
|
||||
}
|
||||
|
||||
if (g_hash_table_size (state->mark_groups) < 30)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
|
||||
g_hash_table_iter_init (&iter, state->mark_groups);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
|
||||
{
|
||||
GtkWidget *row = g_object_new (SP_TYPE_MARK_VISUALIZER_ROW,
|
||||
"group", key,
|
||||
"title", key,
|
||||
"height-request", 50,
|
||||
"selectable", FALSE,
|
||||
"visible", TRUE,
|
||||
NULL);
|
||||
gtk_container_add (GTK_CONTAINER (self), row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
discover_new_rows (SpVisualizerList *self,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
static const SpCaptureFrameType types[] = { SP_CAPTURE_FRAME_CTRDEF, SP_CAPTURE_FRAME_MARK };
|
||||
g_autoptr(SpCaptureCursor) cursor = NULL;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
SpCaptureCondition *condition;
|
||||
Discovery *state;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_LIST (self));
|
||||
g_assert (reader != NULL);
|
||||
|
||||
/*
|
||||
* The goal here is to automatically discover what rows should be added to
|
||||
* the visualizer list based on events we find in the capture file. In the
|
||||
* future, we might be able to add a UI flow to ask what the user wants or
|
||||
* denote capabilities at the beginning of the capture stream.
|
||||
*/
|
||||
|
||||
cursor = sp_capture_cursor_new (reader);
|
||||
condition = sp_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
|
||||
sp_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
|
||||
|
||||
state = g_slice_new0 (Discovery);
|
||||
state->mark_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
state->cursor = g_steal_pointer (&cursor);
|
||||
state->memory = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||
|
||||
task = g_task_new (self, NULL, handle_capture_results, NULL);
|
||||
g_task_set_task_data (task, g_steal_pointer (&state), (GDestroyNotify)discovery_free);
|
||||
g_task_run_in_thread (task, discover_new_rows_worker);
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_list_set_reader (SpVisualizerList *self,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
|
||||
|
||||
if (reader != priv->reader)
|
||||
{
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
|
||||
if (reader != NULL)
|
||||
{
|
||||
priv->reader = sp_capture_reader_ref (reader);
|
||||
discover_new_rows (self, reader);
|
||||
}
|
||||
|
||||
gtk_container_foreach (GTK_CONTAINER (self),
|
||||
(GtkCallback)sp_visualizer_row_set_reader,
|
||||
reader);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_list_set_zoom_manager (SpVisualizerList *self,
|
||||
SpZoomManager *zoom_manager)
|
||||
{
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (zoom_manager));
|
||||
|
||||
if (g_set_object (&priv->zoom_manager, zoom_manager))
|
||||
{
|
||||
gtk_container_foreach (GTK_CONTAINER (self),
|
||||
(GtkCallback)sp_visualizer_row_set_zoom_manager,
|
||||
zoom_manager);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_list_get_zoom_manager:
|
||||
*
|
||||
* Returns: (nullable) (transfer): A #SpZoomManager or %NULL.
|
||||
*/
|
||||
SpZoomManager *
|
||||
sp_visualizer_list_get_zoom_manager (SpVisualizerList *self)
|
||||
{
|
||||
SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_LIST (self), NULL);
|
||||
|
||||
return priv->zoom_manager;
|
||||
}
|
||||
59
src/libsysprof-ui/sp-visualizer-list.h
Normal file
59
src/libsysprof-ui/sp-visualizer-list.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* sp-visualizer-list.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
|
||||
*/
|
||||
|
||||
#ifndef SP_VISUALIZER_LIST_H
|
||||
#define SP_VISUALIZER_LIST_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-capture-reader.h"
|
||||
#include "sp-zoom-manager.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_VISUALIZER_LIST (sp_visualizer_list_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpVisualizerList, sp_visualizer_list, SP, VISUALIZER_LIST, GtkListBox)
|
||||
|
||||
struct _SpVisualizerListClass
|
||||
{
|
||||
GtkListBoxClass parent_class;
|
||||
|
||||
gpointer _reserved1;
|
||||
gpointer _reserved2;
|
||||
gpointer _reserved3;
|
||||
gpointer _reserved4;
|
||||
gpointer _reserved5;
|
||||
gpointer _reserved6;
|
||||
gpointer _reserved7;
|
||||
gpointer _reserved8;
|
||||
};
|
||||
|
||||
GtkWidget *sp_visualizer_list_new (void);
|
||||
void sp_visualizer_list_set_reader (SpVisualizerList *self,
|
||||
SpCaptureReader *reader);
|
||||
SpCaptureReader *sp_visualizer_list_get_reader (SpVisualizerList *self);
|
||||
SpZoomManager *sp_visualizer_list_get_zoom_manager (SpVisualizerList *self);
|
||||
void sp_visualizer_list_set_zoom_manager (SpVisualizerList *self,
|
||||
SpZoomManager *zoom_manager);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_VISUALIZER_LIST_H */
|
||||
32
src/libsysprof-ui/sp-visualizer-row-private.h
Normal file
32
src/libsysprof-ui/sp-visualizer-row-private.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* sp-visualizer-row-private.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
|
||||
*/
|
||||
|
||||
#ifndef SP_VISUALIZER_ROW_PRIVATE_H
|
||||
#define SP_VISUALIZER_ROW_PRIVATE_H
|
||||
|
||||
#include "sp-visualizer-row.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gint _sp_visualizer_row_get_graph_width (SpVisualizerRow *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_VISUALIZER_ROW_PRIVATE_H */
|
||||
308
src/libsysprof-ui/sp-visualizer-row.c
Normal file
308
src/libsysprof-ui/sp-visualizer-row.c
Normal file
@ -0,0 +1,308 @@
|
||||
/* sp-visualizer-row.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 "sp-visualizer-row"
|
||||
|
||||
#include "sp-visualizer-row.h"
|
||||
#include "sp-visualizer-row-private.h"
|
||||
|
||||
#define NSEC_PER_SEC G_GINT64_CONSTANT(1000000000)
|
||||
#define DEFAULT_PIXELS_PER_SECOND 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureReader *reader;
|
||||
SpZoomManager *zoom_manager;
|
||||
} SpVisualizerRowPrivate;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ZOOM_MANAGER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SpVisualizerRow, sp_visualizer_row, GTK_TYPE_LIST_BOX_ROW)
|
||||
|
||||
gint
|
||||
_sp_visualizer_row_get_graph_width (SpVisualizerRow *self)
|
||||
{
|
||||
SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
|
||||
gdouble zoom_level = 1.0;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_ROW (self));
|
||||
|
||||
if (priv->reader == NULL)
|
||||
return 0;
|
||||
|
||||
if (priv->zoom_manager != NULL)
|
||||
zoom_level = sp_zoom_manager_get_zoom (priv->zoom_manager);
|
||||
|
||||
begin_time = sp_capture_reader_get_start_time (priv->reader);
|
||||
end_time = sp_capture_reader_get_end_time (priv->reader);
|
||||
|
||||
return (end_time - begin_time)
|
||||
/ (gdouble)NSEC_PER_SEC
|
||||
* zoom_level
|
||||
* DEFAULT_PIXELS_PER_SECOND;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_get_preferred_width (GtkWidget *widget,
|
||||
gint *min_width,
|
||||
gint *nat_width)
|
||||
{
|
||||
SpVisualizerRow *self = (SpVisualizerRow *)widget;
|
||||
gint graph_width;
|
||||
gint real_min_width = 0;
|
||||
gint real_nat_width = 0;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_ROW (self));
|
||||
|
||||
GTK_WIDGET_CLASS (sp_visualizer_row_parent_class)->get_preferred_width (widget, &real_min_width, &real_nat_width);
|
||||
|
||||
graph_width = _sp_visualizer_row_get_graph_width (self);
|
||||
|
||||
*min_width = *nat_width = real_min_width + graph_width;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerRow *self = SP_VISUALIZER_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ZOOM_MANAGER:
|
||||
g_value_set_object (value, sp_visualizer_row_get_zoom_manager (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerRow *self = SP_VISUALIZER_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ZOOM_MANAGER:
|
||||
sp_visualizer_row_set_zoom_manager (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_finalize (GObject *object)
|
||||
{
|
||||
SpVisualizerRow *self = (SpVisualizerRow *)object;
|
||||
SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
g_clear_object (&priv->zoom_manager);
|
||||
|
||||
G_OBJECT_CLASS (sp_visualizer_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_class_init (SpVisualizerRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_visualizer_row_finalize;
|
||||
object_class->get_property = sp_visualizer_row_get_property;
|
||||
object_class->set_property = sp_visualizer_row_set_property;
|
||||
|
||||
widget_class->get_preferred_width = sp_visualizer_row_get_preferred_width;
|
||||
|
||||
properties [PROP_ZOOM_MANAGER] =
|
||||
g_param_spec_object ("zoom-manager",
|
||||
"Zoom Manager",
|
||||
"Zoom Manager",
|
||||
SP_TYPE_ZOOM_MANAGER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_init (SpVisualizerRow *self)
|
||||
{
|
||||
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (self), FALSE);
|
||||
gtk_list_box_row_set_selectable (GTK_LIST_BOX_ROW (self), FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_row_zoom_manager_notify_zoom (SpVisualizerRow *self,
|
||||
GParamSpec *pspec,
|
||||
SpZoomManager *zoom_manager)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_ROW (self));
|
||||
g_assert (SP_IS_ZOOM_MANAGER (zoom_manager));
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_row_get_zoom_manager:
|
||||
*
|
||||
* Returns: (transfer none) (nullable): A #SpZoomManager or %NULL.
|
||||
*/
|
||||
SpZoomManager *
|
||||
sp_visualizer_row_get_zoom_manager (SpVisualizerRow *self)
|
||||
{
|
||||
SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_ROW (self), NULL);
|
||||
|
||||
return priv->zoom_manager;
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_row_set_zoom_manager (SpVisualizerRow *self,
|
||||
SpZoomManager *zoom_manager)
|
||||
{
|
||||
SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_ROW (self));
|
||||
g_return_if_fail (!zoom_manager || SP_IS_ZOOM_MANAGER (zoom_manager));
|
||||
|
||||
if (priv->zoom_manager != zoom_manager)
|
||||
{
|
||||
if (priv->zoom_manager != NULL)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (priv->zoom_manager,
|
||||
G_CALLBACK (sp_visualizer_row_zoom_manager_notify_zoom),
|
||||
self);
|
||||
g_clear_object (&priv->zoom_manager);
|
||||
}
|
||||
|
||||
if (zoom_manager != NULL)
|
||||
{
|
||||
priv->zoom_manager = g_object_ref (zoom_manager);
|
||||
g_signal_connect_object (priv->zoom_manager,
|
||||
"notify::zoom",
|
||||
G_CALLBACK (sp_visualizer_row_zoom_manager_notify_zoom),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_row_set_reader (SpVisualizerRow *self,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_ROW (self));
|
||||
|
||||
if (priv->reader != reader)
|
||||
{
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
|
||||
if (reader != NULL)
|
||||
priv->reader = sp_capture_reader_ref (reader);
|
||||
|
||||
if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader)
|
||||
SP_VISUALIZER_ROW_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 (SpVisualizerRow *self,
|
||||
GtkAllocation *alloc)
|
||||
{
|
||||
GtkStyleContext *style_context;
|
||||
GtkBorder border;
|
||||
GtkStateFlags state;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_ROW (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
|
||||
sp_visualizer_row_translate_points (SpVisualizerRow *self,
|
||||
const SpVisualizerRowRelativePoint *in_points,
|
||||
guint n_in_points,
|
||||
SpVisualizerRowAbsolutePoint *out_points,
|
||||
guint n_out_points)
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
gint graph_width;
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_ROW (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 = _sp_visualizer_row_get_graph_width (self);
|
||||
|
||||
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 - (in_points[i].y * alloc.height);
|
||||
}
|
||||
}
|
||||
|
||||
78
src/libsysprof-ui/sp-visualizer-row.h
Normal file
78
src/libsysprof-ui/sp-visualizer-row.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* sp-visualizer-row.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
|
||||
*/
|
||||
|
||||
#ifndef SP_VISUALIZER_ROW_H
|
||||
#define SP_VISUALIZER_ROW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "sp-capture-reader.h"
|
||||
#include "sp-zoom-manager.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_VISUALIZER_ROW (sp_visualizer_row_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpVisualizerRow, sp_visualizer_row, SP, VISUALIZER_ROW, GtkListBoxRow)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
} SpVisualizerRowRelativePoint;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint x;
|
||||
gint y;
|
||||
} SpVisualizerRowAbsolutePoint;
|
||||
|
||||
struct _SpVisualizerRowClass
|
||||
{
|
||||
GtkListBoxRowClass parent_class;
|
||||
|
||||
/**
|
||||
* SpVisualizerRow::set_reader:
|
||||
*
|
||||
* Sets the reader that the row should use to extract counters.
|
||||
* This reader is private to the row and should be freed when
|
||||
* no longer in use with sp_capture_reader_unref().
|
||||
*/
|
||||
void (*set_reader) (SpVisualizerRow *self,
|
||||
SpCaptureReader *reader);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _reserved[16];
|
||||
};
|
||||
|
||||
void sp_visualizer_row_set_reader (SpVisualizerRow *self,
|
||||
SpCaptureReader *reader);
|
||||
SpZoomManager *sp_visualizer_row_get_zoom_manager (SpVisualizerRow *self);
|
||||
void sp_visualizer_row_set_zoom_manager (SpVisualizerRow *self,
|
||||
SpZoomManager *zoom_manager);
|
||||
void sp_visualizer_row_translate_points (SpVisualizerRow *self,
|
||||
const SpVisualizerRowRelativePoint *in_points,
|
||||
guint n_in_points,
|
||||
SpVisualizerRowAbsolutePoint *out_points,
|
||||
guint n_out_points);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_VISUALIZER_ROW_H */
|
||||
390
src/libsysprof-ui/sp-visualizer-ticks.c
Normal file
390
src/libsysprof-ui/sp-visualizer-ticks.c
Normal file
@ -0,0 +1,390 @@
|
||||
/* sp-visualizer-ticks.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
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "sp-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_MSEC (NSEC_PER_SEC/G_GINT64_CONSTANT(1000))
|
||||
#define MIN_TICK_DISTANCE 20
|
||||
#define LABEL_HEIGHT_PX 8
|
||||
|
||||
struct _SpVisualizerTicks
|
||||
{
|
||||
GtkDrawingArea parent_instance;
|
||||
|
||||
gint64 epoch;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
enum {
|
||||
TICK_MINUTES,
|
||||
TICK_HALF_MINUTES,
|
||||
TICK_FIVE_SECONDS,
|
||||
TICK_SECONDS,
|
||||
TICK_HALF_SECONDS,
|
||||
TICK_QUARTER_SECONDS,
|
||||
TICK_TENTHS,
|
||||
TICK_HUNDREDTHS,
|
||||
TICK_THOUSANDTHS,
|
||||
N_TICKS
|
||||
};
|
||||
|
||||
struct {
|
||||
gint width;
|
||||
gint height;
|
||||
gint64 span;
|
||||
} tick_sizing[N_TICKS] = {
|
||||
{ 1, 12, NSEC_PER_SEC * 60 },
|
||||
{ 1, 11, NSEC_PER_SEC * 30 },
|
||||
{ 1, 10, NSEC_PER_SEC * 5 },
|
||||
{ 1, 9, NSEC_PER_SEC },
|
||||
{ 1, 8, NSEC_PER_SEC / 2 },
|
||||
{ 1, 6, NSEC_PER_SEC / 4 },
|
||||
{ 1, 5, NSEC_PER_SEC / 10 },
|
||||
{ 1, 4, NSEC_PER_SEC / 100 },
|
||||
{ 1, 3, NSEC_PER_SEC / 1000 },
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SpVisualizerTicks, sp_visualizer_ticks, GTK_TYPE_DRAWING_AREA)
|
||||
|
||||
static void
|
||||
update_label_text (PangoLayout *layout,
|
||||
gint64 time,
|
||||
gboolean want_msec)
|
||||
{
|
||||
g_autofree gchar *str = NULL;
|
||||
gint64 tmp;
|
||||
gint msec = 0;
|
||||
gint hours = 0;
|
||||
gint min = 0;
|
||||
gint sec = 0;
|
||||
|
||||
g_assert (PANGO_IS_LAYOUT (layout));
|
||||
|
||||
tmp = time % NSEC_PER_SEC;
|
||||
time -= tmp;
|
||||
msec = tmp / 100000L;
|
||||
|
||||
if (time >= NSEC_PER_HOUR)
|
||||
{
|
||||
hours = time / NSEC_PER_HOUR;
|
||||
time %= NSEC_PER_HOUR;
|
||||
}
|
||||
|
||||
if (time >= NSEC_PER_MIN)
|
||||
{
|
||||
min = time / NSEC_PER_MIN;
|
||||
time %= NSEC_PER_MIN;
|
||||
}
|
||||
|
||||
if (time >= NSEC_PER_SEC)
|
||||
{
|
||||
sec = time / NSEC_PER_SEC;
|
||||
time %= NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
if (want_msec || (!hours && !min && !sec && msec))
|
||||
{
|
||||
if (hours > 0)
|
||||
str = g_strdup_printf ("%02u:%02u:%02u.%04u", hours, min, sec, msec);
|
||||
else
|
||||
str = g_strdup_printf ("%02u:%02u.%04u", min, sec, msec);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hours > 0)
|
||||
str = g_strdup_printf ("%02u:%02u:%02u", hours, min, sec);
|
||||
else
|
||||
str = g_strdup_printf ("%02u:%02u", min, sec);
|
||||
}
|
||||
|
||||
pango_layout_set_text (layout, str, -1);
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
get_x_for_time (SpVisualizerTicks *self,
|
||||
const GtkAllocation *alloc,
|
||||
gint64 t)
|
||||
{
|
||||
gint64 timespan = self->end_time - self->begin_time;
|
||||
gdouble x_ratio = (gdouble)(t - self->begin_time) / (gdouble)timespan;
|
||||
return alloc->width * x_ratio;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static inline gint64
|
||||
get_time_at_x (SpVisualizerTicks *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 (SpVisualizerTicks *self,
|
||||
cairo_t *cr,
|
||||
GtkAllocation *area,
|
||||
gint ticks,
|
||||
gboolean label_mode)
|
||||
{
|
||||
GtkAllocation alloc;
|
||||
gdouble half;
|
||||
gint64 x_offset;
|
||||
gint count = 0;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_TICKS (self));
|
||||
g_assert (cr != NULL);
|
||||
g_assert (area != NULL);
|
||||
g_assert (ticks >= 0);
|
||||
g_assert (ticks < N_TICKS);
|
||||
|
||||
/*
|
||||
* If we are in label_model, we don't draw the ticks but only the labels.
|
||||
* That way we can determine which tick level managed to draw a tick in
|
||||
* the visible region so we only show labels for the largest tick level.
|
||||
* (Returning true from this method indicates we successfully drew a tick).
|
||||
*/
|
||||
|
||||
half = tick_sizing[ticks].width / 2.0;
|
||||
|
||||
/* Take our epoch into account to calculate the offset of the
|
||||
* first tick, which might not align perfectly when the beginning
|
||||
* of the visible area.
|
||||
*/
|
||||
x_offset = (self->begin_time - self->epoch) % tick_sizing[ticks].span;
|
||||
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
|
||||
|
||||
if G_UNLIKELY (label_mode)
|
||||
{
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *font_desc;
|
||||
gboolean want_msec;
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "00:10:00");
|
||||
|
||||
font_desc = pango_font_description_new ();
|
||||
pango_font_description_set_family_static (font_desc, "Monospace");
|
||||
pango_font_description_set_absolute_size (font_desc, LABEL_HEIGHT_PX * PANGO_SCALE);
|
||||
pango_layout_set_font_description (layout, font_desc);
|
||||
pango_font_description_free (font_desc);
|
||||
|
||||
/* If we are operating on smaller than seconds here, then we want
|
||||
* to ensure we include msec with the timestamps.
|
||||
*/
|
||||
want_msec = tick_sizing[ticks].span < NSEC_PER_SEC;
|
||||
|
||||
for (gint64 t = self->begin_time - x_offset;
|
||||
t <= self->end_time;
|
||||
t += tick_sizing[ticks].span)
|
||||
{
|
||||
gdouble x = get_x_for_time (self, &alloc, t);
|
||||
|
||||
cairo_move_to (cr, (gint)x + .5 - (gint)half, alloc.height - LABEL_HEIGHT_PX);
|
||||
update_label_text (layout, t - self->epoch, want_msec);
|
||||
pango_cairo_show_layout (cr, layout);
|
||||
}
|
||||
|
||||
g_clear_object (&layout);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (gint64 t = self->begin_time - x_offset;
|
||||
t <= self->end_time;
|
||||
t += tick_sizing[ticks].span)
|
||||
{
|
||||
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);
|
||||
count++;
|
||||
}
|
||||
|
||||
cairo_set_line_width (cr, tick_sizing[ticks].width);
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
|
||||
return count > 2;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_visualizer_ticks_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
SpVisualizerTicks *self = SP_VISUALIZER_TICKS (widget);
|
||||
GtkStyleContext *style;
|
||||
GtkAllocation alloc;
|
||||
GtkStateFlags state;
|
||||
gint64 timespan;
|
||||
GdkRGBA color;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_TICKS (self));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
if (0 == (timespan = self->end_time - self->begin_time))
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
|
||||
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
|
||||
|
||||
style = gtk_widget_get_style_context (widget);
|
||||
state = gtk_widget_get_state_flags (widget);
|
||||
gtk_style_context_get_color (style, state, &color);
|
||||
|
||||
gdk_cairo_set_source_rgba (cr, &color);
|
||||
|
||||
/*
|
||||
* We need to discover up to what level we will draw tick marks.
|
||||
* This is based on the width of the widget and the number of ticks
|
||||
* to draw (which is determined from our timespan). We will skip a
|
||||
* mark if they will end up less than MIN_TICK_DISTANCE px apart.
|
||||
*/
|
||||
|
||||
for (guint i = G_N_ELEMENTS (tick_sizing); i > 0; i--)
|
||||
{
|
||||
gint64 n_ticks = timespan / tick_sizing[i - 1].span;
|
||||
gint largest_match = -1;
|
||||
|
||||
if (n_ticks == 0 || (alloc.width / n_ticks) < MIN_TICK_DISTANCE)
|
||||
continue;
|
||||
|
||||
for (guint j = i; j > 0; j--)
|
||||
{
|
||||
if (draw_ticks (self, cr, &alloc, j - 1, FALSE))
|
||||
largest_match = j - 1;
|
||||
}
|
||||
|
||||
if (largest_match != -1)
|
||||
draw_ticks (self, cr, &alloc, largest_match, TRUE);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_ticks_get_preferred_height (GtkWidget *widget,
|
||||
gint *min_height,
|
||||
gint *nat_height)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_TICKS (widget));
|
||||
|
||||
*min_height = *nat_height = tick_sizing[0].height + LABEL_HEIGHT_PX;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_ticks_class_init (SpVisualizerTicksClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
widget_class->draw = sp_visualizer_ticks_draw;
|
||||
widget_class->get_preferred_height = sp_visualizer_ticks_get_preferred_height;
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, "ticks");
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_ticks_init (SpVisualizerTicks *self)
|
||||
{
|
||||
self->end_time = G_GINT64_CONSTANT (1000000000) * 60;
|
||||
|
||||
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
sp_visualizer_ticks_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_VISUALIZER_TICKS, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_ticks_get_time_range (SpVisualizerTicks *self,
|
||||
gint64 *begin_time,
|
||||
gint64 *end_time)
|
||||
{
|
||||
g_return_if_fail (SP_IS_VISUALIZER_TICKS (self));
|
||||
g_return_if_fail (begin_time != NULL || end_time != NULL);
|
||||
|
||||
if (begin_time != NULL)
|
||||
*begin_time = self->begin_time;
|
||||
|
||||
if (end_time != NULL)
|
||||
*end_time = self->end_time;
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_ticks_set_time_range (SpVisualizerTicks *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time)
|
||||
{
|
||||
g_return_if_fail (SP_IS_VISUALIZER_TICKS (self));
|
||||
|
||||
if (begin_time > end_time)
|
||||
{
|
||||
gint64 tmp = begin_time;
|
||||
begin_time = end_time;
|
||||
end_time = tmp;
|
||||
}
|
||||
|
||||
self->begin_time = begin_time;
|
||||
self->end_time = end_time;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
gint64
|
||||
sp_visualizer_ticks_get_epoch (SpVisualizerTicks *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_TICKS (self), 0);
|
||||
|
||||
return self->epoch;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the epoch for the visualizer ticks.
|
||||
*
|
||||
* The epoch is the "real" starting time of the capture, where as the
|
||||
* sp_visualizer_ticks_set_time_range() function sets the visible range
|
||||
* of the capture.
|
||||
*
|
||||
* This is used to calculate the offset of the beginning of the capture
|
||||
* from begin_time so that the ticks appear in the proper location.
|
||||
*
|
||||
* This function should only need to be called when the reader is changed.
|
||||
*/
|
||||
void
|
||||
sp_visualizer_ticks_set_epoch (SpVisualizerTicks *self,
|
||||
gint64 epoch)
|
||||
{
|
||||
g_return_if_fail (SP_IS_VISUALIZER_TICKS (self));
|
||||
|
||||
if (self->epoch != epoch)
|
||||
{
|
||||
self->epoch = epoch;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
45
src/libsysprof-ui/sp-visualizer-ticks.h
Normal file
45
src/libsysprof-ui/sp-visualizer-ticks.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* sp-visualizer-ticks.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
|
||||
*/
|
||||
|
||||
#ifndef SP_VISUALIZER_TICKS_H
|
||||
#define SP_VISUALIZER_TICKS_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_VISUALIZER_TICKS (sp_visualizer_ticks_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpVisualizerTicks, sp_visualizer_ticks, SP, VISUALIZER_TICKS, GtkDrawingArea)
|
||||
|
||||
GtkWidget *sp_visualizer_ticks_new (void);
|
||||
void sp_visualizer_ticks_set_epoch (SpVisualizerTicks *self,
|
||||
gint64 epoch);
|
||||
gint64 sp_visualizer_ticks_get_epoch (SpVisualizerTicks *self);
|
||||
void sp_visualizer_ticks_get_time_range (SpVisualizerTicks *self,
|
||||
gint64 *begin_time,
|
||||
gint64 *end_time);
|
||||
void sp_visualizer_ticks_set_time_range (SpVisualizerTicks *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_VISUALIZER_TICKS_H */
|
||||
756
src/libsysprof-ui/sp-visualizer-view.c
Normal file
756
src/libsysprof-ui/sp-visualizer-view.c
Normal file
@ -0,0 +1,756 @@
|
||||
/* sp-visualizer-view.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-visualizer-view"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "sp-theme-manager.h"
|
||||
#include "sp-visualizer-list.h"
|
||||
#include "sp-visualizer-row.h"
|
||||
#include "sp-visualizer-row-private.h"
|
||||
#include "sp-selection.h"
|
||||
#include "sp-visualizer-ticks.h"
|
||||
#include "sp-visualizer-view.h"
|
||||
|
||||
#define NSEC_PER_SEC G_GINT64_CONSTANT(1000000000)
|
||||
#define DEFAULT_PIXELS_PER_SECOND 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureReader *reader;
|
||||
SpZoomManager *zoom_manager;
|
||||
SpSelection *selection;
|
||||
|
||||
SpVisualizerList *list;
|
||||
GtkScrolledWindow *scroller;
|
||||
SpVisualizerTicks *ticks;
|
||||
|
||||
gint64 drag_begin_at;
|
||||
gint64 drag_selection_at;
|
||||
|
||||
guint button_pressed : 1;
|
||||
} SpVisualizerViewPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpVisualizerView *self;
|
||||
GtkStyleContext *style_context;
|
||||
cairo_t *cr;
|
||||
GtkAllocation alloc;
|
||||
} SelectionDraw;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_READER,
|
||||
PROP_ZOOM_MANAGER,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
VISUALIZER_ADDED,
|
||||
VISUALIZER_REMOVED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static void buildable_iface_init (GtkBuildableIface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpVisualizerView, sp_visualizer_view, GTK_TYPE_BIN, 0,
|
||||
G_ADD_PRIVATE (SpVisualizerView)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
static guint signals [N_SIGNALS];
|
||||
static GtkBuildableIface *parent_buildable;
|
||||
|
||||
static void
|
||||
find_row1 (GtkWidget *widget,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget **row1 = data;
|
||||
|
||||
if (*row1 == NULL && SP_IS_VISUALIZER_ROW (widget))
|
||||
*row1 = widget;
|
||||
}
|
||||
|
||||
static gint64
|
||||
get_time_from_coordinates (SpVisualizerView *self,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
SpVisualizerRow *row1 = NULL;
|
||||
GtkAllocation alloc;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
gint graph_width;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
|
||||
if (priv->reader == NULL)
|
||||
return 0;
|
||||
|
||||
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
|
||||
|
||||
x -= alloc.x;
|
||||
y -= alloc.y;
|
||||
|
||||
/*
|
||||
* Find the first row so we can get an idea of how wide the graph is
|
||||
* (ignoring spacing caused by the widget being wider than the data points.
|
||||
*/
|
||||
gtk_container_foreach (GTK_CONTAINER (priv->list), find_row1, &row1);
|
||||
if (!SP_IS_VISUALIZER_ROW (row1))
|
||||
return 0;
|
||||
|
||||
begin_time = sp_capture_reader_get_start_time (priv->reader);
|
||||
end_time = sp_capture_reader_get_end_time (priv->reader);
|
||||
graph_width = _sp_visualizer_row_get_graph_width (row1);
|
||||
|
||||
return begin_time + ((end_time - begin_time) * (x / (gdouble)graph_width));
|
||||
}
|
||||
|
||||
static gint
|
||||
get_x_for_time_at (SpVisualizerView *self,
|
||||
const GtkAllocation *alloc,
|
||||
gint64 time_at)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
SpVisualizerRow *row1 = NULL;
|
||||
GtkAdjustment *hadjustment;
|
||||
gdouble nsec_per_pixel;
|
||||
gdouble value;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
gint graph_width;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (alloc != NULL);
|
||||
|
||||
/*
|
||||
* Find the first row so we can get an idea of how wide the graph is
|
||||
* (ignoring spacing caused by the widget being wider than the data points.
|
||||
*/
|
||||
gtk_container_foreach (GTK_CONTAINER (priv->list), find_row1, &row1);
|
||||
if (!SP_IS_VISUALIZER_ROW (row1))
|
||||
return 0;
|
||||
|
||||
hadjustment = gtk_scrolled_window_get_hadjustment (priv->scroller);
|
||||
value = gtk_adjustment_get_value (hadjustment);
|
||||
|
||||
begin_time = sp_capture_reader_get_start_time (priv->reader);
|
||||
end_time = sp_capture_reader_get_end_time (priv->reader);
|
||||
|
||||
graph_width = _sp_visualizer_row_get_graph_width (row1);
|
||||
nsec_per_pixel = (end_time - begin_time) / (gdouble)graph_width;
|
||||
begin_time += value * nsec_per_pixel;
|
||||
|
||||
return ((time_at - begin_time) / nsec_per_pixel);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_row_added (SpVisualizerView *self,
|
||||
GtkWidget *widget,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (GTK_IS_WIDGET (widget));
|
||||
g_assert (SP_IS_VISUALIZER_LIST (list));
|
||||
|
||||
if (SP_IS_VISUALIZER_ROW (widget))
|
||||
g_signal_emit (self, signals [VISUALIZER_ADDED], 0, widget);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_row_removed (SpVisualizerView *self,
|
||||
GtkWidget *widget,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (GTK_IS_WIDGET (widget));
|
||||
g_assert (SP_IS_VISUALIZER_LIST (list));
|
||||
|
||||
if (SP_IS_VISUALIZER_ROW (widget))
|
||||
g_signal_emit (self, signals [VISUALIZER_REMOVED], 0, widget);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_update_ticks (SpVisualizerView *self)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
GtkAdjustment *hadjustment;
|
||||
GtkAllocation alloc;
|
||||
gdouble value;
|
||||
gint64 begin_time;
|
||||
gint64 end_time;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
|
||||
hadjustment = gtk_scrolled_window_get_hadjustment (priv->scroller);
|
||||
value = gtk_adjustment_get_value (hadjustment);
|
||||
|
||||
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
|
||||
|
||||
begin_time = get_time_from_coordinates (self, alloc.x + value, alloc.y);
|
||||
end_time = get_time_from_coordinates (self, alloc.x + value + alloc.width, alloc.y);
|
||||
|
||||
sp_visualizer_ticks_set_time_range (priv->ticks, begin_time, end_time);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_hadjustment_value_changed (SpVisualizerView *self,
|
||||
GtkAdjustment *adjustment)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (GTK_IS_ADJUSTMENT (adjustment));
|
||||
|
||||
sp_visualizer_view_update_ticks (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation)
|
||||
{
|
||||
SpVisualizerView *self = (SpVisualizerView *)widget;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (allocation != NULL);
|
||||
|
||||
GTK_WIDGET_CLASS (sp_visualizer_view_parent_class)->size_allocate (widget, allocation);
|
||||
|
||||
sp_visualizer_view_update_ticks (self);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_selection_cb (SpSelection *selection,
|
||||
gint64 range_begin,
|
||||
gint64 range_end,
|
||||
gpointer user_data)
|
||||
{
|
||||
SelectionDraw *draw = user_data;
|
||||
GdkRectangle area;
|
||||
|
||||
g_assert (SP_IS_SELECTION (selection));
|
||||
g_assert (draw != NULL);
|
||||
g_assert (draw->cr != NULL);
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (draw->self));
|
||||
|
||||
area.x = get_x_for_time_at (draw->self, &draw->alloc, range_begin);
|
||||
area.width = get_x_for_time_at (draw->self, &draw->alloc, range_end) - 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, area.y, area.width, area.height);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_visualizer_view_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
SpVisualizerView *self = (SpVisualizerView *)widget;
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
SelectionDraw draw = { 0 };
|
||||
gboolean ret;
|
||||
|
||||
g_assert (GTK_IS_WIDGET (widget));
|
||||
g_assert (cr != NULL);
|
||||
|
||||
draw.style_context = gtk_widget_get_style_context (widget);
|
||||
draw.self = self;
|
||||
draw.cr = cr;
|
||||
|
||||
gtk_widget_get_allocation (widget, &draw.alloc);
|
||||
|
||||
ret = GTK_WIDGET_CLASS (sp_visualizer_view_parent_class)->draw (widget, cr);
|
||||
|
||||
if (sp_selection_get_has_selection (priv->selection) || priv->button_pressed)
|
||||
{
|
||||
gtk_style_context_add_class (draw.style_context, "selection");
|
||||
sp_selection_foreach (priv->selection, draw_selection_cb, &draw);
|
||||
if (priv->button_pressed)
|
||||
draw_selection_cb (priv->selection, priv->drag_begin_at, priv->drag_selection_at, &draw);
|
||||
gtk_style_context_remove_class (draw.style_context, "selection");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_visualizer_view_list_button_press_event (SpVisualizerView *self,
|
||||
GdkEventButton *ev,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (ev != NULL);
|
||||
g_assert (SP_IS_VISUALIZER_LIST (list));
|
||||
|
||||
if (priv->reader == NULL)
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
|
||||
if (ev->button != GDK_BUTTON_PRIMARY)
|
||||
{
|
||||
if (sp_selection_get_has_selection (priv->selection))
|
||||
{
|
||||
sp_selection_unselect_all (priv->selection);
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
if ((ev->state & GDK_SHIFT_MASK) == 0)
|
||||
sp_selection_unselect_all (priv->selection);
|
||||
|
||||
priv->button_pressed = TRUE;
|
||||
|
||||
priv->drag_begin_at = get_time_from_coordinates (self, ev->x, ev->y);
|
||||
priv->drag_selection_at = priv->drag_begin_at;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_visualizer_view_list_button_release_event (SpVisualizerView *self,
|
||||
GdkEventButton *ev,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (ev != NULL);
|
||||
g_assert (SP_IS_VISUALIZER_LIST (list));
|
||||
|
||||
if (!priv->button_pressed || ev->button != GDK_BUTTON_PRIMARY)
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
|
||||
priv->button_pressed = FALSE;
|
||||
|
||||
if (priv->drag_begin_at != priv->drag_selection_at)
|
||||
{
|
||||
sp_selection_select_range (priv->selection,
|
||||
priv->drag_begin_at,
|
||||
priv->drag_selection_at);
|
||||
priv->drag_begin_at = -1;
|
||||
priv->drag_selection_at = -1;
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
return GDK_EVENT_STOP;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_visualizer_view_list_motion_notify_event (SpVisualizerView *self,
|
||||
GdkEventMotion *ev,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (ev != NULL);
|
||||
g_assert (SP_IS_VISUALIZER_LIST (list));
|
||||
|
||||
if (!priv->button_pressed)
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
|
||||
priv->drag_selection_at = get_time_from_coordinates (self, ev->x, ev->y);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_list_realize_after (SpVisualizerView *self,
|
||||
SpVisualizerList *list)
|
||||
{
|
||||
GdkDisplay *display;
|
||||
GdkWindow *window;
|
||||
GdkCursor *cursor;
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (SP_IS_VISUALIZER_LIST (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 void
|
||||
sp_visualizer_view_selection_changed (SpVisualizerView *self,
|
||||
SpSelection *selection)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (SP_IS_SELECTION (selection));
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_finalize (GObject *object)
|
||||
{
|
||||
SpVisualizerView *self = (SpVisualizerView *)object;
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
g_clear_object (&priv->zoom_manager);
|
||||
g_clear_object (&priv->selection);
|
||||
|
||||
G_OBJECT_CLASS (sp_visualizer_view_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerView *self = SP_VISUALIZER_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_READER:
|
||||
g_value_set_boxed (value, sp_visualizer_view_get_reader (self));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM_MANAGER:
|
||||
g_value_set_object (value, sp_visualizer_view_get_zoom_manager (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpVisualizerView *self = SP_VISUALIZER_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_READER:
|
||||
sp_visualizer_view_set_reader (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM_MANAGER:
|
||||
sp_visualizer_view_set_zoom_manager (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_class_init (SpVisualizerViewClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
SpThemeManager *theme_manager = sp_theme_manager_get_default ();
|
||||
|
||||
object_class->finalize = sp_visualizer_view_finalize;
|
||||
object_class->get_property = sp_visualizer_view_get_property;
|
||||
object_class->set_property = sp_visualizer_view_set_property;
|
||||
|
||||
widget_class->draw = sp_visualizer_view_draw;
|
||||
widget_class->size_allocate = sp_visualizer_view_size_allocate;
|
||||
|
||||
properties [PROP_READER] =
|
||||
g_param_spec_boxed ("reader",
|
||||
"Reader",
|
||||
"The reader for the visualizers",
|
||||
SP_TYPE_CAPTURE_READER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_ZOOM_MANAGER] =
|
||||
g_param_spec_object ("zoom-manager",
|
||||
"Zoom Manager",
|
||||
"The zoom manager for the view",
|
||||
SP_TYPE_ZOOM_MANAGER,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
signals [VISUALIZER_ADDED] =
|
||||
g_signal_new ("visualizer-added",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (SpVisualizerViewClass, visualizer_added),
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, SP_TYPE_VISUALIZER_ROW);
|
||||
|
||||
signals [VISUALIZER_REMOVED] =
|
||||
g_signal_new ("visualizer-removed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (SpVisualizerViewClass, visualizer_removed),
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, SP_TYPE_VISUALIZER_ROW);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-visualizer-view.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpVisualizerView, list);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpVisualizerView, scroller);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, SpVisualizerView, ticks);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, "visualizers");
|
||||
|
||||
sp_theme_manager_register_resource (theme_manager, NULL, NULL, "/org/gnome/sysprof/css/SpVisualizerView-shared.css");
|
||||
sp_theme_manager_register_resource (theme_manager, "Adwaita", NULL, "/org/gnome/sysprof/css/SpVisualizerView-Adwaita.css");
|
||||
sp_theme_manager_register_resource (theme_manager, "Adwaita", "dark", "/org/gnome/sysprof/css/SpVisualizerView-Adwaita-dark.css");
|
||||
|
||||
g_type_ensure (SP_TYPE_VISUALIZER_LIST);
|
||||
g_type_ensure (SP_TYPE_VISUALIZER_TICKS);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_init (SpVisualizerView *self)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
GtkAdjustment *hadjustment;
|
||||
|
||||
priv->drag_begin_at = -1;
|
||||
priv->drag_selection_at = -1;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
priv->selection = g_object_new (SP_TYPE_SELECTION, NULL);
|
||||
|
||||
g_signal_connect_object (priv->selection,
|
||||
"changed",
|
||||
G_CALLBACK (sp_visualizer_view_selection_changed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"button-press-event",
|
||||
G_CALLBACK (sp_visualizer_view_list_button_press_event),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"button-release-event",
|
||||
G_CALLBACK (sp_visualizer_view_list_button_release_event),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"motion-notify-event",
|
||||
G_CALLBACK (sp_visualizer_view_list_motion_notify_event),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"realize",
|
||||
G_CALLBACK (sp_visualizer_view_list_realize_after),
|
||||
self,
|
||||
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"add",
|
||||
G_CALLBACK (sp_visualizer_view_row_added),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
g_signal_connect_object (priv->list,
|
||||
"remove",
|
||||
G_CALLBACK (sp_visualizer_view_row_removed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
hadjustment = gtk_scrolled_window_get_hadjustment (priv->scroller);
|
||||
|
||||
g_signal_connect_object (hadjustment,
|
||||
"value-changed",
|
||||
G_CALLBACK (sp_visualizer_view_hadjustment_value_changed),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_view_get_reader:
|
||||
*
|
||||
* Returns: (transfer none): An #SpCaptureReader
|
||||
*/
|
||||
SpCaptureReader *
|
||||
sp_visualizer_view_get_reader (SpVisualizerView *self)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_VIEW (self), NULL);
|
||||
|
||||
return priv->reader;
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_view_set_reader (SpVisualizerView *self,
|
||||
SpCaptureReader *reader)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_VIEW (self));
|
||||
|
||||
if (priv->reader != reader)
|
||||
{
|
||||
g_clear_pointer (&priv->reader, sp_capture_reader_unref);
|
||||
|
||||
if (reader != NULL)
|
||||
{
|
||||
gint64 begin_time;
|
||||
|
||||
priv->reader = sp_capture_reader_ref (reader);
|
||||
|
||||
begin_time = sp_capture_reader_get_start_time (priv->reader);
|
||||
|
||||
sp_visualizer_ticks_set_epoch (priv->ticks, begin_time);
|
||||
sp_visualizer_ticks_set_time_range (priv->ticks, begin_time, begin_time);
|
||||
|
||||
sp_selection_unselect_all (priv->selection);
|
||||
}
|
||||
|
||||
sp_visualizer_list_set_reader (priv->list, reader);
|
||||
sp_visualizer_view_update_ticks (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_add_child (GtkBuildable *buildable,
|
||||
GtkBuilder *builder,
|
||||
GObject *child,
|
||||
const gchar *type)
|
||||
{
|
||||
SpVisualizerView *self = (SpVisualizerView *)buildable;
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (GTK_IS_BUILDER (builder));
|
||||
g_assert (G_IS_OBJECT (child));
|
||||
|
||||
if (g_strcmp0 (type, "visualizer") == 0 && GTK_IS_WIDGET (child))
|
||||
{
|
||||
gtk_container_add (GTK_CONTAINER (priv->list), GTK_WIDGET (child));
|
||||
return;
|
||||
}
|
||||
|
||||
parent_buildable->add_child (buildable, builder, child, type);
|
||||
}
|
||||
|
||||
static void
|
||||
buildable_iface_init (GtkBuildableIface *iface)
|
||||
{
|
||||
parent_buildable = g_type_interface_peek_parent (iface);
|
||||
iface->add_child = sp_visualizer_view_add_child;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_visualizer_view_zoom_manager_notify_zoom (SpVisualizerView *self,
|
||||
GParamSpec *pspec,
|
||||
SpZoomManager *zoom_manager)
|
||||
{
|
||||
g_assert (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_assert (SP_IS_ZOOM_MANAGER (zoom_manager));
|
||||
|
||||
sp_visualizer_view_update_ticks (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_view_get_zoom_manager:
|
||||
*
|
||||
* Returns: (transfer none) (nullable): An #SpZoomManager or %NULL
|
||||
*/
|
||||
SpZoomManager *
|
||||
sp_visualizer_view_get_zoom_manager (SpVisualizerView *self)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_VIEW (self), NULL);
|
||||
|
||||
return priv->zoom_manager;
|
||||
}
|
||||
|
||||
void
|
||||
sp_visualizer_view_set_zoom_manager (SpVisualizerView *self,
|
||||
SpZoomManager *zoom_manager)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_VISUALIZER_VIEW (self));
|
||||
g_return_if_fail (!zoom_manager || SP_IS_ZOOM_MANAGER (zoom_manager));
|
||||
|
||||
if (priv->zoom_manager != zoom_manager)
|
||||
{
|
||||
if (priv->zoom_manager != NULL)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (priv->zoom_manager,
|
||||
G_CALLBACK (sp_visualizer_view_zoom_manager_notify_zoom),
|
||||
self);
|
||||
g_clear_object (&priv->zoom_manager);
|
||||
}
|
||||
|
||||
if (zoom_manager != NULL)
|
||||
{
|
||||
priv->zoom_manager = g_object_ref (zoom_manager);
|
||||
g_signal_connect_object (priv->zoom_manager,
|
||||
"notify::zoom",
|
||||
G_CALLBACK (sp_visualizer_view_zoom_manager_notify_zoom),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
sp_visualizer_list_set_zoom_manager (priv->list, zoom_manager);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_visualizer_view_get_selection:
|
||||
*
|
||||
* Gets the #SpSelection instance for the visualizer view.
|
||||
* This can be used to alter the selection or selections of the visualizers.
|
||||
*
|
||||
* Returns: (transfer none): An #SpSelection.
|
||||
*/
|
||||
SpSelection *
|
||||
sp_visualizer_view_get_selection (SpVisualizerView *self)
|
||||
{
|
||||
SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_VISUALIZER_VIEW (self), NULL);
|
||||
|
||||
return priv->selection;
|
||||
}
|
||||
75
src/libsysprof-ui/sp-visualizer-view.h
Normal file
75
src/libsysprof-ui/sp-visualizer-view.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* sp-visualizer-view.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
|
||||
*/
|
||||
|
||||
#ifndef SP_VISUALIZER_VIEW_H
|
||||
#define SP_VISUALIZER_VIEW_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <sysprof.h>
|
||||
|
||||
#include "sp-visualizer-row.h"
|
||||
#include "sp-selection.h"
|
||||
#include "sp-zoom-manager.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_VISUALIZER_VIEW (sp_visualizer_view_get_type())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpVisualizerView, sp_visualizer_view, SP, VISUALIZER_VIEW, GtkBin)
|
||||
|
||||
struct _SpVisualizerViewClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
|
||||
void (*visualizer_added) (SpVisualizerView *self,
|
||||
SpVisualizerRow *visualizer);
|
||||
void (*visualizer_removed) (SpVisualizerView *self,
|
||||
SpVisualizerRow *visualizer);
|
||||
|
||||
gpointer _reserved1;
|
||||
gpointer _reserved2;
|
||||
gpointer _reserved3;
|
||||
gpointer _reserved4;
|
||||
gpointer _reserved5;
|
||||
gpointer _reserved6;
|
||||
gpointer _reserved7;
|
||||
gpointer _reserved8;
|
||||
gpointer _reserved9;
|
||||
gpointer _reserved10;
|
||||
gpointer _reserved11;
|
||||
gpointer _reserved12;
|
||||
gpointer _reserved13;
|
||||
gpointer _reserved14;
|
||||
gpointer _reserved15;
|
||||
gpointer _reserved16;
|
||||
};
|
||||
|
||||
GtkWidget *sp_visualizer_view_new (void);
|
||||
SpCaptureReader *sp_visualizer_view_get_reader (SpVisualizerView *self);
|
||||
void sp_visualizer_view_set_reader (SpVisualizerView *self,
|
||||
SpCaptureReader *reader);
|
||||
SpZoomManager *sp_visualizer_view_get_zoom_manager (SpVisualizerView *self);
|
||||
void sp_visualizer_view_set_zoom_manager (SpVisualizerView *self,
|
||||
SpZoomManager *zoom_manager);
|
||||
SpSelection *sp_visualizer_view_get_selection (SpVisualizerView *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_VISUALIZER_VIEW_H */
|
||||
476
src/libsysprof-ui/sp-zoom-manager.c
Normal file
476
src/libsysprof-ui/sp-zoom-manager.c
Normal file
@ -0,0 +1,476 @@
|
||||
/* sp-zoom-manager.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
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-zoom-manager"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "sp-zoom-manager.h"
|
||||
|
||||
struct _SpZoomManager
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GSimpleActionGroup *actions;
|
||||
|
||||
gdouble min_zoom;
|
||||
gdouble max_zoom;
|
||||
gdouble zoom;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CAN_ZOOM_IN,
|
||||
PROP_CAN_ZOOM_OUT,
|
||||
PROP_MIN_ZOOM,
|
||||
PROP_MAX_ZOOM,
|
||||
PROP_ZOOM,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static void action_group_iface_init (GActionGroupInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpZoomManager, sp_zoom_manager, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
static gdouble zoom_levels[] = {
|
||||
0.3,
|
||||
0.5,
|
||||
0.67,
|
||||
0.80,
|
||||
0.90,
|
||||
1.0,
|
||||
1.1,
|
||||
1.2,
|
||||
1.33,
|
||||
1.5,
|
||||
1.7,
|
||||
2.0,
|
||||
2.4,
|
||||
3.0,
|
||||
5.0,
|
||||
10.0,
|
||||
20.0,
|
||||
30.0,
|
||||
50.0,
|
||||
};
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_in_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_zoom_in (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_out_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_zoom_out (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_one_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_reset (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpZoomManager *self = SP_ZOOM_MANAGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MIN_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_min_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_MAX_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_max_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_CAN_ZOOM_IN:
|
||||
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_in (self));
|
||||
break;
|
||||
|
||||
case PROP_CAN_ZOOM_OUT:
|
||||
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_out (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpZoomManager *self = SP_ZOOM_MANAGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MIN_ZOOM:
|
||||
sp_zoom_manager_set_min_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_MAX_ZOOM:
|
||||
sp_zoom_manager_set_max_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM:
|
||||
sp_zoom_manager_set_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_class_init (SpZoomManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = sp_zoom_manager_get_property;
|
||||
object_class->set_property = sp_zoom_manager_set_property;
|
||||
|
||||
properties [PROP_CAN_ZOOM_IN] =
|
||||
g_param_spec_boolean ("can-zoom-in",
|
||||
"Can Zoom In",
|
||||
"Can Zoom In",
|
||||
TRUE,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_CAN_ZOOM_OUT] =
|
||||
g_param_spec_boolean ("can-zoom-out",
|
||||
"Can Zoom Out",
|
||||
"Can Zoom Out",
|
||||
TRUE,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_MIN_ZOOM] =
|
||||
g_param_spec_double ("min-zoom",
|
||||
"Min Zoom",
|
||||
"The minimum zoom to apply",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_MAX_ZOOM] =
|
||||
g_param_spec_double ("max-zoom",
|
||||
"Max Zoom",
|
||||
"The maximum zoom to apply",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_ZOOM] =
|
||||
g_param_spec_double ("zoom",
|
||||
"Zoom",
|
||||
"The current zoom level",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
1.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_init (SpZoomManager *self)
|
||||
{
|
||||
static const GActionEntry entries[] = {
|
||||
{ "zoom-in", sp_zoom_manager_zoom_in_action },
|
||||
{ "zoom-out", sp_zoom_manager_zoom_out_action },
|
||||
{ "zoom-one", sp_zoom_manager_zoom_one_action },
|
||||
};
|
||||
GAction *action;
|
||||
|
||||
self->min_zoom = 0.0;
|
||||
self->max_zoom = 0.0;
|
||||
self->zoom = 1.0;
|
||||
|
||||
self->actions = g_simple_action_group_new ();
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
|
||||
entries,
|
||||
G_N_ELEMENTS (entries),
|
||||
self);
|
||||
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-in");
|
||||
g_object_bind_property (self, "can-zoom-in", action, "enabled", G_BINDING_SYNC_CREATE);
|
||||
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-out");
|
||||
g_object_bind_property (self, "can-zoom-out", action, "enabled", G_BINDING_SYNC_CREATE);
|
||||
}
|
||||
|
||||
SpZoomManager *
|
||||
sp_zoom_manager_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_ZOOM_MANAGER, NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_can_zoom_in (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->max_zoom == 0.0 || self->zoom < self->max_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_can_zoom_out (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->min_zoom == 0.0 || self->zoom > self->min_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_min_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->min_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_max_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->max_zoom;
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_min_zoom (SpZoomManager *self,
|
||||
gdouble min_zoom)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (min_zoom != self->min_zoom)
|
||||
{
|
||||
self->min_zoom = min_zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MIN_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_max_zoom (SpZoomManager *self,
|
||||
gdouble max_zoom)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (max_zoom != self->max_zoom)
|
||||
{
|
||||
self->max_zoom = max_zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_zoom_in (SpZoomManager *self)
|
||||
{
|
||||
gdouble zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (!sp_zoom_manager_get_can_zoom_in (self))
|
||||
return;
|
||||
|
||||
zoom = self->zoom;
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
|
||||
{
|
||||
if (zoom_levels[i] > zoom)
|
||||
{
|
||||
zoom = zoom_levels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zoom == self->zoom)
|
||||
zoom *= 2;
|
||||
|
||||
sp_zoom_manager_set_zoom (self, zoom);
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_zoom_out (SpZoomManager *self)
|
||||
{
|
||||
gdouble zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (!sp_zoom_manager_get_can_zoom_out (self))
|
||||
return;
|
||||
|
||||
zoom = self->zoom;
|
||||
|
||||
for (guint i = G_N_ELEMENTS (zoom_levels); i > 0; i--)
|
||||
{
|
||||
if (zoom_levels[i-1] < zoom)
|
||||
{
|
||||
zoom = zoom_levels[i-1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zoom == self->zoom)
|
||||
zoom /= 2.0;
|
||||
|
||||
sp_zoom_manager_set_zoom (self, zoom);
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_reset (SpZoomManager *self)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
sp_zoom_manager_set_zoom (self, 1.0);
|
||||
}
|
||||
|
||||
gdouble
|
||||
sp_zoom_manager_get_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), 0.0);
|
||||
|
||||
return self->zoom;
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_zoom (SpZoomManager *self,
|
||||
gdouble zoom)
|
||||
{
|
||||
gdouble min_zoom;
|
||||
gdouble max_zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
min_zoom = (self->min_zoom == 0.0) ? -G_MAXDOUBLE : self->min_zoom;
|
||||
max_zoom = (self->max_zoom == 0.0) ? G_MAXDOUBLE : self->max_zoom;
|
||||
|
||||
zoom = CLAMP (zoom, min_zoom, max_zoom);
|
||||
|
||||
if (zoom == 0.0)
|
||||
zoom = 1.0;
|
||||
|
||||
if (zoom != self->zoom)
|
||||
{
|
||||
self->zoom = zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
|
||||
}
|
||||
}
|
||||
|
||||
static gchar **
|
||||
sp_zoom_manager_list_actions (GActionGroup *action_group)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
return g_action_group_list_actions (G_ACTION_GROUP (self->actions));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_zoom_manager_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
return g_action_group_query_action (G_ACTION_GROUP (self->actions),
|
||||
action_name,
|
||||
enabled,
|
||||
parameter_type,
|
||||
state_type,
|
||||
state_hint,
|
||||
state);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (self->actions), action_name, value);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
g_action_group_activate_action (G_ACTION_GROUP (self->actions), action_name, parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
action_group_iface_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->list_actions = sp_zoom_manager_list_actions;
|
||||
iface->query_action = sp_zoom_manager_query_action;
|
||||
iface->change_action_state = sp_zoom_manager_change_action_state;
|
||||
iface->activate_action = sp_zoom_manager_activate_action;
|
||||
}
|
||||
50
src/libsysprof-ui/sp-zoom-manager.h
Normal file
50
src/libsysprof-ui/sp-zoom-manager.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* sp-zoom-manager.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
|
||||
*/
|
||||
|
||||
#ifndef SP_ZOOM_MANAGER_H
|
||||
#define SP_ZOOM_MANAGER_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_ZOOM_MANAGER (sp_zoom_manager_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpZoomManager, sp_zoom_manager, SP, ZOOM_MANAGER, GObject)
|
||||
|
||||
SpZoomManager *sp_zoom_manager_new (void);
|
||||
gboolean sp_zoom_manager_get_can_zoom_in (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_can_zoom_out (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_min_zoom (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_max_zoom (SpZoomManager *self);
|
||||
void sp_zoom_manager_set_min_zoom (SpZoomManager *self,
|
||||
gdouble min_zoom);
|
||||
void sp_zoom_manager_set_max_zoom (SpZoomManager *self,
|
||||
gdouble max_zoom);
|
||||
void sp_zoom_manager_zoom_in (SpZoomManager *self);
|
||||
void sp_zoom_manager_zoom_out (SpZoomManager *self);
|
||||
void sp_zoom_manager_reset (SpZoomManager *self);
|
||||
gdouble sp_zoom_manager_get_zoom (SpZoomManager *self);
|
||||
void sp_zoom_manager_set_zoom (SpZoomManager *self,
|
||||
gdouble zoom);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_ZOOM_MANAGER_H */
|
||||
49
src/libsysprof-ui/sysprof-ui.h
Normal file
49
src/libsysprof-ui/sysprof-ui.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* sysprof-ui.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
|
||||
*/
|
||||
|
||||
#ifndef SYSPROF_UI_H
|
||||
#define SYSPROF_UI_H
|
||||
|
||||
#include <sysprof.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SYSPROF_INSIDE
|
||||
# include "sp-callgraph-view.h"
|
||||
# include "sp-cell-renderer-percent.h"
|
||||
# include "sp-cpu-visualizer-row.h"
|
||||
# include "sp-failed-state-view.h"
|
||||
# include "sp-line-visualizer-row.h"
|
||||
# include "sp-empty-state-view.h"
|
||||
# include "sp-model-filter.h"
|
||||
# include "sp-multi-paned.h"
|
||||
# include "sp-recording-state-view.h"
|
||||
# include "sp-process-model.h"
|
||||
# include "sp-process-model-item.h"
|
||||
# include "sp-process-model-row.h"
|
||||
# include "sp-profiler-menu-button.h"
|
||||
# include "sp-visualizer-row.h"
|
||||
# include "sp-visualizer-view.h"
|
||||
# include "sp-zoom-manager.h"
|
||||
#undef SYSPROF_INSIDE
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SYSPROF_UI_H */
|
||||
190
src/libsysprof-ui/ui/sp-callgraph-view.ui
Normal file
190
src/libsysprof-ui/ui/sp-callgraph-view.ui
Normal file
@ -0,0 +1,190 @@
|
||||
<interface>
|
||||
<template class="SpCallgraphView" parent="GtkBin">
|
||||
<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="SpCellRendererPercent">
|
||||
<property name="xpad">6</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="SpCellRendererPercent">
|
||||
<property name="xpad">6</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="SpCellRendererPercent">
|
||||
<property name="xpad">6</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="SpCellRendererPercent">
|
||||
<property name="xpad">6</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">fixed</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="SpCellRendererPercent">
|
||||
<property name="xpad">6</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">Cumulative</property>
|
||||
<child>
|
||||
<object class="SpCellRendererPercent">
|
||||
<property name="xpad">6</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="percent">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
64
src/libsysprof-ui/ui/sp-empty-state-view.ui
Normal file
64
src/libsysprof-ui/ui/sp-empty-state-view.ui
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="SpEmptyStateView" parent="GtkBin">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="border-width">36</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="visible">true</property>
|
||||
<child type="center">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">org.gnome.Sysprof-symbolic</property>
|
||||
<property name="pixel-size">256</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title">
|
||||
<property name="label" translatable="yes">Welcome to Sysprof</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="scale" value="2"/>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="subtitle">
|
||||
<property name="label" translatable="yes">Start profiling your system with the <b>Record</b> button above</property>
|
||||
<property name="use-markup">true</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="vexpand">true</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
<property name="pack-type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
64
src/libsysprof-ui/ui/sp-failed-state-view.ui
Normal file
64
src/libsysprof-ui/ui/sp-failed-state-view.ui
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<template class="SpFailedStateView" parent="GtkBin">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="border-width">36</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="visible">true</property>
|
||||
<child type="center">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">computer-fail-symbolic</property>
|
||||
<property name="pixel-size">256</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Ouch, that hurt!</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="scale" value="2"/>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Something unexpectedly went wrong while trying to profile your system.</property>
|
||||
<property name="use-markup">true</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="vexpand">true</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
<property name="pack-type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
52
src/libsysprof-ui/ui/sp-process-model-row.ui
Normal file
52
src/libsysprof-ui/ui/sp-process-model-row.ui
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<interface>
|
||||
<template class="SpProcessModelRow" parent="GtkListBoxRow">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">true</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image">
|
||||
<!-- Todo -->
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<property name="hexpand">false</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="ellipsize">middle</property>
|
||||
<property name="xalign">0.0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="args_label">
|
||||
<property name="hexpand">false</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="xalign">0.0</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="check">
|
||||
<property name="hexpand">false</property>
|
||||
<property name="icon-name">object-select-symbolic</property>
|
||||
<property name="visible">false</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pid">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="xalign">1.0</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
235
src/libsysprof-ui/ui/sp-profiler-menu-button.ui
Normal file
235
src/libsysprof-ui/ui/sp-profiler-menu-button.ui
Normal file
@ -0,0 +1,235 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<template class="SpProfilerMenuButton" parent="GtkMenuButton">
|
||||
<property name="popover">popover</property>
|
||||
<property name="width-request">150</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">true</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">pan-down-symbolic</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
<object class="SpProcessModel" id="process_model">
|
||||
</object>
|
||||
<object class="GtkPopover" id="popover">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkStackSwitcher">
|
||||
<property name="border-width">6</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="stack">stack</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="width-request">425</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="margin">10</property>
|
||||
<property name="spacing">12</property>
|
||||
<child type="center">
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="label" translatable="yes" comments="Translators: This is the description for a switch.">Profile my _entire system</property>
|
||||
<property name="use-underline">true</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="mnemonic-widget">whole_system_switch</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="whole_system_switch">
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="hhomogeneous">true</property>
|
||||
<property name="vhomogeneous">false</property>
|
||||
<property name="interpolate-size">true</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="border-width">10</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<!-- box with single child is used so we can toggle visibility of the child -->
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="processes_box">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkEntry" id="process_filter_entry">
|
||||
<property name="placeholder-text" translatable="yes" comments="Translators: This is the placeholder in a search entry.">Search</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="expand">true</property>
|
||||
<property name="min-content-width">100</property>
|
||||
<property name="max-content-width">400</property>
|
||||
<property name="max-content-height">450</property>
|
||||
<property name="propagate-natural-width">true</property>
|
||||
<property name="propagate-natural-height">true</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="process_list_box">
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">existing</property>
|
||||
<property name="title" translatable="yes">Existing Process</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Command Line</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="xalign">0.0</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="spawn_entry">
|
||||
<property name="secondary-icon-activatable">false</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Environment</property>
|
||||
<property name="visible">true</property>
|
||||
<property name="xalign">0.0</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="inherit_environ">
|
||||
<property name="active">true</property>
|
||||
<property name="label" translatable="yes" comments="Translators: This is a check button.">Inherit current environment</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="shadow-type">in</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="max-content-height">400</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="env_tree_view">
|
||||
<property name="model">environment_model</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="env_key_column">
|
||||
<property name="expand">true</property>
|
||||
<property name="resizable">true</property>
|
||||
<property name="title" translatable="yes">Key</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="key_cell">
|
||||
<property name="editable">true</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="env_value_column">
|
||||
<property name="expand">true</property>
|
||||
<property name="resizable">true</property>
|
||||
<property name="title" translatable="yes">Value</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="value_cell">
|
||||
<property name="editable">true</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">spawn</property>
|
||||
<property name="title" translatable="yes">New Process</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="environment_model">
|
||||
<columns>
|
||||
<column type="gchararray"/>
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0"></col>
|
||||
<col id="1"></col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
</interface>
|
||||
64
src/libsysprof-ui/ui/sp-recording-state-view.ui
Normal file
64
src/libsysprof-ui/ui/sp-recording-state-view.ui
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<template class="SpRecordingStateView" parent="GtkBin">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="border-width">36</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="visible">true</property>
|
||||
<child type="center">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">org.gnome.Sysprof-symbolic</property>
|
||||
<property name="pixel-size">256</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="elapsed">
|
||||
<property name="label" translatable="yes">00:00</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="scale" value="4"/>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Did you know you can use <a href="help:sysprof">sysprof-cli</a> to record?</property>
|
||||
<property name="use-markup">true</property>
|
||||
<property name="visible">true</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="vexpand">true</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
<property name="pack-type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
32
src/libsysprof-ui/ui/sp-visualizer-view.ui
Normal file
32
src/libsysprof-ui/ui/sp-visualizer-view.ui
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="SpVisualizerView" parent="GtkBin">
|
||||
<child>
|
||||
<object class="GtkOverlay">
|
||||
<property name="visible">true</property>
|
||||
<child type="overlay">
|
||||
<object class="SpVisualizerTicks" id="ticks">
|
||||
<property name="valign">start</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pass-through">true</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scroller">
|
||||
<property name="propagate-natural-height">true</property>
|
||||
<property name="propagate-natural-width">false</property>
|
||||
<property name="visible">true</property>
|
||||
<child>
|
||||
<object class="SpVisualizerList" id="list">
|
||||
<property name="visible">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
Reference in New Issue
Block a user