Merge branch 'wip/chergert/libsysprof-analyze'

This merges a mostly completed rewrite of Sysprof using all the new GTK
features we can muster.

In particular, we now have SysprofDocument which can be used to read
documents instead of SysprofCaptureReader for a more document-oriented
experience. It's mmap-based and has significantly more indexing
capabilities for new features.

The recording process still needs a bit more work, along with opening
documents and custom debug-dirs.

Some applications are expected to break, such as gnome-builder which
will have to stop using the now-extinct libsysprof-ui library. Instead
applications should open the capture files with Sysprof directly.
This commit is contained in:
Christian Hergert
2023-07-21 17:06:39 -07:00
656 changed files with 74771 additions and 54721 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.flatpak-builder
*.swp
*~
build

View File

@ -16,6 +16,8 @@
#mesondefine HAVE_EXECINFO_H
#mesondefine HAVE_LIBSYSTEMD
#mesondefine HAVE_PERF_CLOCKID
#mesondefine HAVE_POLKIT

202
contrib/eggbitset/COPYING Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,16 @@
Roaring bitmaps implementation
==============================
This directory contains code modified for GTK, based on the Roaring
bitmaps reference implementation
[CRoaring](https://github.com/RoaringBitmap/CRoaring).
It is not necessarily compatible with past or future versions of CRoaring,
and replacing it with a different version or linking to a system copy
is not supported.
See the source files for copyright and licensing information, and the
`COPYING` file for the full text of the Apache license, version 2.0.
When proposing modifications for these files, please consider whether they
are also suitable for submission to CRoaring.

View File

@ -0,0 +1,984 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "eggbitset.h"
#include "roaring.c"
/**
* EggBitset: (ref-func egg_bitset_ref) (unref-func egg_bitset_unref)
*
* A `EggBitset` represents a set of unsigned integers.
*
* Another name for this data structure is "bitmap".
*
* The current implementation is based on [roaring bitmaps](https://roaringbitmap.org/).
*
* A bitset allows adding a set of integers and provides support for set operations
* like unions, intersections and checks for equality or if a value is contained
* in the set. `EggBitset` also contains various functions to query metadata about
* the bitset, such as the minimum or maximum values or its size.
*
* The fastest way to iterate values in a bitset is [struct@Egg.BitsetIter].
*
* The main use case for `EggBitset` is implementing complex selections for
* [iface@Egg.SelectionModel].
*/
struct _EggBitset
{
int ref_count;
roaring_bitmap_t roaring;
};
G_DEFINE_BOXED_TYPE (EggBitset, egg_bitset,
egg_bitset_ref,
egg_bitset_unref)
/**
* egg_bitset_ref:
* @self: (nullable): a `EggBitset`
*
* Acquires a reference on the given `EggBitset`.
*
* Returns: (transfer none): the `EggBitset` with an additional reference
*/
EggBitset *
egg_bitset_ref (EggBitset *self)
{
g_return_val_if_fail (self != NULL, NULL);
self->ref_count += 1;
return self;
}
/**
* egg_bitset_unref:
* @self: (nullable): a `EggBitset`
*
* Releases a reference on the given `EggBitset`.
*
* If the reference was the last, the resources associated to the @self are
* freed.
*/
void
egg_bitset_unref (EggBitset *self)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
self->ref_count -= 1;
if (self->ref_count > 0)
return;
ra_clear (&self->roaring.high_low_container);
g_free (self);
}
/**
* egg_bitset_contains:
* @self: a `EggBitset`
* @value: the value to check
*
* Checks if the given @value has been added to @self
*
* Returns: %TRUE if @self contains @value
**/
gboolean
egg_bitset_contains (const EggBitset *self,
guint value)
{
g_return_val_if_fail (self != NULL, FALSE);
return roaring_bitmap_contains (&self->roaring, value);
}
/**
* egg_bitset_is_empty:
* @self: a `EggBitset`
*
* Check if no value is contained in bitset.
*
* Returns: %TRUE if @self is empty
**/
gboolean
egg_bitset_is_empty (const EggBitset *self)
{
g_return_val_if_fail (self != NULL, TRUE);
return roaring_bitmap_is_empty (&self->roaring);
}
/**
* egg_bitset_equals:
* @self: a `EggBitset`
* @other: another `EggBitset`
*
* Returns %TRUE if @self and @other contain the same values.
*
* Returns: %TRUE if @self and @other contain the same values
**/
gboolean
egg_bitset_equals (const EggBitset *self,
const EggBitset *other)
{
g_return_val_if_fail (self != NULL, other == NULL);
g_return_val_if_fail (other != NULL, FALSE);
if (self == other)
return TRUE;
return roaring_bitmap_equals (&self->roaring, &other->roaring);
}
/**
* egg_bitset_get_minimum:
* @self: a `EggBitset`
*
* Returns the smallest value in @self.
*
* If @self is empty, `G_MAXUINT` is returned.
*
* Returns: The smallest value in @self
**/
guint
egg_bitset_get_minimum (const EggBitset *self)
{
g_return_val_if_fail (self != NULL, G_MAXUINT);
return roaring_bitmap_minimum (&self->roaring);
}
/**
* egg_bitset_get_maximum:
* @self: a `EggBitset`
*
* Returns the largest value in @self.
*
* If @self is empty, 0 is returned.
*
* Returns: The largest value in @self
**/
guint
egg_bitset_get_maximum (const EggBitset *self)
{
g_return_val_if_fail (self != NULL, 0);
return roaring_bitmap_maximum (&self->roaring);
}
/**
* egg_bitset_get_size:
* @self: a `EggBitset`
*
* Gets the number of values that were added to the set.
*
* For example, if the set is empty, 0 is returned.
*
* Note that this function returns a `guint64`, because when all
* values are set, the return value is `G_MAXUINT + 1`. Unless you
* are sure this cannot happen (it can't with `GListModel`), be sure
* to use a 64bit type.
*
* Returns: The number of values in the set.
*/
guint64
egg_bitset_get_size (const EggBitset *self)
{
g_return_val_if_fail (self != NULL, 0);
return roaring_bitmap_get_cardinality (&self->roaring);
}
/**
* egg_bitset_get_size_in_range:
* @self: a `EggBitset`
* @first: the first element to include
* @last: the last element to include
*
* Gets the number of values that are part of the set from @first to @last
* (inclusive).
*
* Note that this function returns a `guint64`, because when all values are
* set, the return value is `G_MAXUINT + 1`. Unless you are sure this cannot
* happen (it can't with `GListModel`), be sure to use a 64bit type.
*
* Returns: The number of values in the set from @first to @last.
*/
guint64
egg_bitset_get_size_in_range (const EggBitset *self,
guint first,
guint last)
{
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (last >= first, 0);
return roaring_bitmap_range_cardinality (&self->roaring, first, ((uint64_t) last) + 1);
}
/**
* egg_bitset_get_nth:
* @self: a `EggBitset`
* @nth: index of the item to get
*
* Returns the value of the @nth item in self.
*
* If @nth is >= the size of @self, 0 is returned.
*
* Returns: the value of the @nth item in @self
*/
guint
egg_bitset_get_nth (const EggBitset *self,
guint nth)
{
uint32_t result;
if (!roaring_bitmap_select (&self->roaring, nth, &result))
return 0;
return result;
}
/**
* egg_bitset_new_empty:
*
* Creates a new empty bitset.
*
* Returns: A new empty bitset
*/
EggBitset *
egg_bitset_new_empty (void)
{
EggBitset *self;
self = g_new0 (EggBitset, 1);
self->ref_count = 1;
ra_init (&self->roaring.high_low_container);
return self;
}
/**
* egg_bitset_new_range:
* @start: first value to add
* @n_items: number of consecutive values to add
*
* Creates a bitset with the given range set.
*
* Returns: A new bitset
**/
EggBitset *
egg_bitset_new_range (guint start,
guint n_items)
{
EggBitset *self;
self = egg_bitset_new_empty ();
egg_bitset_add_range (self, start, n_items);
return self;
}
/**
* egg_bitset_copy:
* @self: a `EggBitset`
*
* Creates a copy of @self.
*
* Returns: (transfer full): A new bitset that contains the same
* values as @self
*/
EggBitset *
egg_bitset_copy (const EggBitset *self)
{
EggBitset *copy;
g_return_val_if_fail (self != NULL, NULL);
copy = egg_bitset_new_empty ();
roaring_bitmap_overwrite (&copy->roaring, &self->roaring);
return copy;
}
/**
* egg_bitset_remove_all:
* @self: a `EggBitset`
*
* Removes all values from the bitset so that it is empty again.
*/
void
egg_bitset_remove_all (EggBitset *self)
{
g_return_if_fail (self != NULL);
roaring_bitmap_clear (&self->roaring);
}
/**
* egg_bitset_add:
* @self: a `EggBitset`
* @value: value to add
*
* Adds @value to @self if it wasn't part of it before.
*
* Returns: %TRUE if @value was not part of @self and @self
* was changed
*/
gboolean
egg_bitset_add (EggBitset *self,
guint value)
{
g_return_val_if_fail (self != NULL, FALSE);
return roaring_bitmap_add_checked (&self->roaring, value);
}
/**
* egg_bitset_remove:
* @self: a `EggBitset`
* @value: value to remove
*
* Removes @value from @self if it was part of it before.
*
* Returns: %TRUE if @value was part of @self and @self
* was changed
*/
gboolean
egg_bitset_remove (EggBitset *self,
guint value)
{
g_return_val_if_fail (self != NULL, FALSE);
return roaring_bitmap_remove_checked (&self->roaring, value);
}
/**
* egg_bitset_add_range:
* @self: a `EggBitset`
* @start: first value to add
* @n_items: number of consecutive values to add
*
* Adds all values from @start (inclusive) to @start + @n_items
* (exclusive) in @self.
*/
void
egg_bitset_add_range (EggBitset *self,
guint start,
guint n_items)
{
g_return_if_fail (self != NULL);
if (n_items == 0)
return;
/* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */
g_return_if_fail (start + n_items == 0 || start + n_items > start);
roaring_bitmap_add_range_closed (&self->roaring, start, start + n_items - 1);
}
/**
* egg_bitset_remove_range:
* @self: a `EggBitset`
* @start: first value to remove
* @n_items: number of consecutive values to remove
*
* Removes all values from @start (inclusive) to @start + @n_items (exclusive)
* in @self.
*/
void
egg_bitset_remove_range (EggBitset *self,
guint start,
guint n_items)
{
g_return_if_fail (self != NULL);
if (n_items == 0)
return;
/* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */
g_return_if_fail (start + n_items == 0 || start + n_items > start);
roaring_bitmap_remove_range_closed (&self->roaring, start, start + n_items - 1);
}
/**
* egg_bitset_add_range_closed:
* @self: a `EggBitset`
* @first: first value to add
* @last: last value to add
*
* Adds the closed range [@first, @last], so @first, @last and all
* values in between. @first must be smaller than @last.
*/
void
egg_bitset_add_range_closed (EggBitset *self,
guint first,
guint last)
{
g_return_if_fail (self != NULL);
g_return_if_fail (first <= last);
roaring_bitmap_add_range_closed (&self->roaring, first, last);
}
/**
* egg_bitset_remove_range_closed:
* @self: a `EggBitset`
* @first: first value to remove
* @last: last value to remove
*
* Removes the closed range [@first, @last], so @first, @last and all
* values in between. @first must be smaller than @last.
*/
void
egg_bitset_remove_range_closed (EggBitset *self,
guint first,
guint last)
{
g_return_if_fail (self != NULL);
g_return_if_fail (first <= last);
roaring_bitmap_remove_range_closed (&self->roaring, first, last);
}
/**
* egg_bitset_add_rectangle:
* @self: a `EggBitset`
* @start: first value to add
* @width: width of the rectangle
* @height: height of the rectangle
* @stride: row stride of the grid
*
* Interprets the values as a 2-dimensional boolean grid with the given @stride
* and inside that grid, adds a rectangle with the given @width and @height.
*/
void
egg_bitset_add_rectangle (EggBitset *self,
guint start,
guint width,
guint height,
guint stride)
{
guint i;
g_return_if_fail (self != NULL);
g_return_if_fail ((start % stride) + width <= stride);
g_return_if_fail (G_MAXUINT - start >= height * stride);
if (width == 0 || height == 0)
return;
for (i = 0; i < height; i++)
egg_bitset_add_range (self, i * stride + start, width);
}
/**
* egg_bitset_remove_rectangle:
* @self: a `EggBitset`
* @start: first value to remove
* @width: width of the rectangle
* @height: height of the rectangle
* @stride: row stride of the grid
*
* Interprets the values as a 2-dimensional boolean grid with the given @stride
* and inside that grid, removes a rectangle with the given @width and @height.
*/
void
egg_bitset_remove_rectangle (EggBitset *self,
guint start,
guint width,
guint height,
guint stride)
{
guint i;
g_return_if_fail (self != NULL);
g_return_if_fail (width <= stride);
g_return_if_fail (G_MAXUINT - start >= height * stride);
if (width == 0 || height == 0)
return;
for (i = 0; i < height; i++)
egg_bitset_remove_range (self, i * stride + start, width);
}
/**
* egg_bitset_union:
* @self: a `EggBitset`
* @other: the `EggBitset` to union with
*
* Sets @self to be the union of @self and @other.
*
* That is, add all values from @other into @self that weren't part of it.
*
* It is allowed for @self and @other to be the same bitset. Nothing will
* happen in that case.
*/
void
egg_bitset_union (EggBitset *self,
const EggBitset *other)
{
g_return_if_fail (self != NULL);
g_return_if_fail (other != NULL);
if (self == other)
return;
roaring_bitmap_or_inplace (&self->roaring, &other->roaring);
}
/**
* egg_bitset_intersect:
* @self: a `EggBitset`
* @other: the `EggBitset` to intersect with
*
* Sets @self to be the intersection of @self and @other.
*
* In other words, remove all values from @self that are not part of @other.
*
* It is allowed for @self and @other to be the same bitset. Nothing will
* happen in that case.
*/
void
egg_bitset_intersect (EggBitset *self,
const EggBitset *other)
{
g_return_if_fail (self != NULL);
g_return_if_fail (other != NULL);
if (self == other)
return;
roaring_bitmap_and_inplace (&self->roaring, &other->roaring);
}
/**
* egg_bitset_subtract:
* @self: a `EggBitset`
* @other: the `EggBitset` to subtract
*
* Sets @self to be the subtraction of @other from @self.
*
* In other words, remove all values from @self that are part of @other.
*
* It is allowed for @self and @other to be the same bitset. The bitset
* will be emptied in that case.
*/
void
egg_bitset_subtract (EggBitset *self,
const EggBitset *other)
{
g_return_if_fail (self != NULL);
g_return_if_fail (other != NULL);
if (self == other)
{
roaring_bitmap_clear (&self->roaring);
return;
}
roaring_bitmap_andnot_inplace (&self->roaring, &other->roaring);
}
/**
* egg_bitset_difference:
* @self: a `EggBitset`
* @other: the `EggBitset` to compute the difference from
*
* Sets @self to be the symmetric difference of @self and @other.
*
* The symmetric difference is set @self to contain all values that
* were either contained in @self or in @other, but not in both.
* This operation is also called an XOR.
*
* It is allowed for @self and @other to be the same bitset. The bitset
* will be emptied in that case.
*/
void
egg_bitset_difference (EggBitset *self,
const EggBitset *other)
{
g_return_if_fail (self != NULL);
g_return_if_fail (other != NULL);
if (self == other)
{
roaring_bitmap_clear (&self->roaring);
return;
}
roaring_bitmap_xor_inplace (&self->roaring, &other->roaring);
}
/**
* egg_bitset_shift_left:
* @self: a `EggBitset`
* @amount: amount to shift all values to the left
*
* Shifts all values in @self to the left by @amount.
*
* Values smaller than @amount are discarded.
*/
void
egg_bitset_shift_left (EggBitset *self,
guint amount)
{
EggBitset *original;
EggBitsetIter iter;
guint value;
gboolean loop;
g_return_if_fail (self != NULL);
if (amount == 0)
return;
original = egg_bitset_copy (self);
egg_bitset_remove_all (self);
for (loop = egg_bitset_iter_init_at (&iter, original, amount, &value);
loop;
loop = egg_bitset_iter_next (&iter, &value))
{
egg_bitset_add (self, value - amount);
}
egg_bitset_unref (original);
}
/**
* egg_bitset_shift_right:
* @self: a `EggBitset`
* @amount: amount to shift all values to the right
*
* Shifts all values in @self to the right by @amount.
*
* Values that end up too large to be held in a #guint are discarded.
*/
void
egg_bitset_shift_right (EggBitset *self,
guint amount)
{
EggBitset *original;
EggBitsetIter iter;
guint value;
gboolean loop;
g_return_if_fail (self != NULL);
if (amount == 0)
return;
original = egg_bitset_copy (self);
egg_bitset_remove_all (self);
for (loop = egg_bitset_iter_init_first (&iter, original, &value);
loop && value <= G_MAXUINT - amount;
loop = egg_bitset_iter_next (&iter, &value))
{
egg_bitset_add (self, value + amount);
}
egg_bitset_unref (original);
}
/**
* egg_bitset_splice:
* @self: a `EggBitset`
* @position: position at which to slice
* @removed: number of values to remove
* @added: number of values to add
*
* This is a support function for `GListModel` handling, by mirroring
* the `GlistModel::items-changed` signal.
*
* First, it "cuts" the values from @position to @removed from
* the bitset. That is, it removes all those values and shifts
* all larger values to the left by @removed places.
*
* Then, it "pastes" new room into the bitset by shifting all values
* larger than @position by @added spaces to the right. This frees
* up space that can then be filled.
*/
void
egg_bitset_splice (EggBitset *self,
guint position,
guint removed,
guint added)
{
g_return_if_fail (self != NULL);
/* overflow */
g_return_if_fail (position + removed >= position);
g_return_if_fail (position + added >= position);
egg_bitset_remove_range (self, position, removed);
if (removed != added)
{
EggBitset *shift = egg_bitset_copy (self);
egg_bitset_remove_range (shift, 0, position);
egg_bitset_remove_range_closed (self, position, G_MAXUINT);
if (added > removed)
egg_bitset_shift_right (shift, added - removed);
else
egg_bitset_shift_left (shift, removed - added);
egg_bitset_union (self, shift);
egg_bitset_unref (shift);
}
}
G_STATIC_ASSERT (sizeof (EggBitsetIter) >= sizeof (roaring_uint32_iterator_t));
static EggBitsetIter *
egg_bitset_iter_copy (EggBitsetIter *iter)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
return (EggBitsetIter *) roaring_copy_uint32_iterator (riter);
}
static void
egg_bitset_iter_free (EggBitsetIter *iter)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
roaring_free_uint32_iterator (riter);
}
G_DEFINE_BOXED_TYPE (EggBitsetIter, egg_bitset_iter, egg_bitset_iter_copy, egg_bitset_iter_free)
/**
* egg_bitset_iter_init_first:
* @iter: (out): a pointer to an uninitialized `EggBitsetIter`
* @set: a `EggBitset`
* @value: (out) (optional): Set to the first value in @set
*
* Initializes an iterator for @set and points it to the first
* value in @set.
*
* If @set is empty, %FALSE is returned and @value is set to %G_MAXUINT.
*
* Returns: %TRUE if @set isn't empty.
*/
gboolean
egg_bitset_iter_init_first (EggBitsetIter *iter,
const EggBitset *set,
guint *value)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (set != NULL, FALSE);
roaring_init_iterator (&set->roaring, riter);
if (value)
*value = riter->has_value ? riter->current_value : 0;
return riter->has_value;
}
/**
* egg_bitset_iter_init_last:
* @iter: (out): a pointer to an uninitialized `EggBitsetIter`
* @set: a `EggBitset`
* @value: (out) (optional): Set to the last value in @set
*
* Initializes an iterator for @set and points it to the last
* value in @set.
*
* If @set is empty, %FALSE is returned.
*
* Returns: %TRUE if @set isn't empty.
**/
gboolean
egg_bitset_iter_init_last (EggBitsetIter *iter,
const EggBitset *set,
guint *value)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (set != NULL, FALSE);
roaring_init_iterator_last (&set->roaring, riter);
if (value)
*value = riter->has_value ? riter->current_value : 0;
return riter->has_value;
}
/**
* egg_bitset_iter_init_at:
* @iter: (out): a pointer to an uninitialized `EggBitsetIter`
* @set: a `EggBitset`
* @target: target value to start iterating at
* @value: (out) (optional): Set to the found value in @set
*
* Initializes @iter to point to @target.
*
* If @target is not found, finds the next value after it.
* If no value >= @target exists in @set, this function returns %FALSE.
*
* Returns: %TRUE if a value was found.
*/
gboolean
egg_bitset_iter_init_at (EggBitsetIter *iter,
const EggBitset *set,
guint target,
guint *value)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (set != NULL, FALSE);
roaring_init_iterator (&set->roaring, riter);
if (!roaring_move_uint32_iterator_equalorlarger (riter, target))
{
if (value)
*value = 0;
return FALSE;
}
if (value)
*value = riter->current_value;
return TRUE;
}
/**
* egg_bitset_iter_next:
* @iter: a pointer to a valid `EggBitsetIter`
* @value: (out) (optional): Set to the next value
*
* Moves @iter to the next value in the set.
*
* If it was already pointing to the last value in the set,
* %FALSE is returned and @iter is invalidated.
*
* Returns: %TRUE if a next value existed
*/
gboolean
egg_bitset_iter_next (EggBitsetIter *iter,
guint *value)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
if (!roaring_advance_uint32_iterator (riter))
{
if (value)
*value = 0;
return FALSE;
}
if (value)
*value = riter->current_value;
return TRUE;
}
/**
* egg_bitset_iter_previous:
* @iter: a pointer to a valid `EggBitsetIter`
* @value: (out) (optional): Set to the previous value
*
* Moves @iter to the previous value in the set.
*
* If it was already pointing to the first value in the set,
* %FALSE is returned and @iter is invalidated.
*
* Returns: %TRUE if a previous value existed
*/
gboolean
egg_bitset_iter_previous (EggBitsetIter *iter,
guint *value)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
if (!roaring_previous_uint32_iterator (riter))
{
if (value)
*value = 0;
return FALSE;
}
if (value)
*value = riter->current_value;
return TRUE;
}
/**
* egg_bitset_iter_get_value:
* @iter: a `EggBitsetIter`
*
* Gets the current value that @iter points to.
*
* If @iter is not valid and [method@Egg.BitsetIter.is_valid]
* returns %FALSE, this function returns 0.
*
* Returns: The current value pointer to by @iter
*/
guint
egg_bitset_iter_get_value (const EggBitsetIter *iter)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, 0);
if (!riter->has_value)
return 0;
return riter->current_value;
}
/**
* egg_bitset_iter_is_valid:
* @iter: a `EggBitsetIter`
*
* Checks if @iter points to a valid value.
*
* Returns: %TRUE if @iter points to a valid value
*/
gboolean
egg_bitset_iter_is_valid (const EggBitsetIter *iter)
{
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
g_return_val_if_fail (iter != NULL, FALSE);
return riter->has_value;
}

View File

@ -0,0 +1,137 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define EGG_TYPE_BITSET (egg_bitset_get_type ())
typedef struct _EggBitset EggBitset;
GType egg_bitset_get_type (void) G_GNUC_CONST;
EggBitset * egg_bitset_ref (EggBitset *self);
void egg_bitset_unref (EggBitset *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(EggBitset, egg_bitset_unref)
gboolean egg_bitset_contains (const EggBitset *self,
guint value);
gboolean egg_bitset_is_empty (const EggBitset *self);
gboolean egg_bitset_equals (const EggBitset *self,
const EggBitset *other);
guint64 egg_bitset_get_size (const EggBitset *self);
guint64 egg_bitset_get_size_in_range (const EggBitset *self,
guint first,
guint last);
guint egg_bitset_get_nth (const EggBitset *self,
guint nth);
guint egg_bitset_get_minimum (const EggBitset *self);
guint egg_bitset_get_maximum (const EggBitset *self);
EggBitset * egg_bitset_new_empty (void);
EggBitset * egg_bitset_copy (const EggBitset *self);
EggBitset * egg_bitset_new_range (guint start,
guint n_items);
void egg_bitset_remove_all (EggBitset *self);
gboolean egg_bitset_add (EggBitset *self,
guint value);
gboolean egg_bitset_remove (EggBitset *self,
guint value);
void egg_bitset_add_range (EggBitset *self,
guint start,
guint n_items);
void egg_bitset_remove_range (EggBitset *self,
guint start,
guint n_items);
void egg_bitset_add_range_closed (EggBitset *self,
guint first,
guint last);
void egg_bitset_remove_range_closed (EggBitset *self,
guint first,
guint last);
void egg_bitset_add_rectangle (EggBitset *self,
guint start,
guint width,
guint height,
guint stride);
void egg_bitset_remove_rectangle (EggBitset *self,
guint start,
guint width,
guint height,
guint stride);
void egg_bitset_union (EggBitset *self,
const EggBitset *other);
void egg_bitset_intersect (EggBitset *self,
const EggBitset *other);
void egg_bitset_subtract (EggBitset *self,
const EggBitset *other);
void egg_bitset_difference (EggBitset *self,
const EggBitset *other);
void egg_bitset_shift_left (EggBitset *self,
guint amount);
void egg_bitset_shift_right (EggBitset *self,
guint amount);
void egg_bitset_splice (EggBitset *self,
guint position,
guint removed,
guint added);
/**
* EggBitsetIter:
*
* An opaque, stack-allocated struct for iterating
* over the elements of a `EggBitset`.
*
* Before a `EggBitsetIter` can be used, it needs to be initialized with
* [func@Egg.BitsetIter.init_first], [func@Egg.BitsetIter.init_last]
* or [func@Egg.BitsetIter.init_at].
*/
typedef struct _EggBitsetIter EggBitsetIter;
struct _EggBitsetIter
{
/*< private >*/
gpointer private_data[10];
};
GType egg_bitset_iter_get_type (void) G_GNUC_CONST;
gboolean egg_bitset_iter_init_first (EggBitsetIter *iter,
const EggBitset *set,
guint *value);
gboolean egg_bitset_iter_init_last (EggBitsetIter *iter,
const EggBitset *set,
guint *value);
gboolean egg_bitset_iter_init_at (EggBitsetIter *iter,
const EggBitset *set,
guint target,
guint *value);
gboolean egg_bitset_iter_next (EggBitsetIter *iter,
guint *value);
gboolean egg_bitset_iter_previous (EggBitsetIter *iter,
guint *value);
guint egg_bitset_iter_get_value (const EggBitsetIter *iter);
gboolean egg_bitset_iter_is_valid (const EggBitsetIter *iter);
G_END_DECLS

View File

@ -0,0 +1,17 @@
libeggbitset_sources = [
'eggbitset.c',
]
libeggbitset_deps = [
dependency('gio-2.0', version: glib_req_version),
]
libeggbitset_static = static_library('eggbitset', libeggbitset_sources,
gnu_symbol_visibility: 'hidden',
dependencies: libeggbitset_deps,
)
libeggbitset_static_dep = declare_dependency(
include_directories: include_directories('.'),
link_with: libeggbitset_static,
)

11475
contrib/eggbitset/roaring.c Normal file

File diff suppressed because it is too large Load Diff

7090
contrib/eggbitset/roaring.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -297,6 +297,37 @@ open_mapped_file (const char *filename,
return file;
}
ElfParser *
elf_parser_new_from_mmap (GMappedFile *file,
GError **error)
{
const guchar *data;
gsize length;
ElfParser *parser;
if (file == NULL)
return NULL;
data = (guchar *)g_mapped_file_get_contents (file);
length = g_mapped_file_get_length (file);
parser = elf_parser_new_from_data (data, length);
if (!parser)
{
g_set_error (error,
G_FILE_ERROR,
G_FILE_ERROR_FAILED,
"Failed to load ELF from mmap region");
g_mapped_file_unref (file);
return NULL;
}
parser->filename = NULL;
parser->file = file;
return parser;
}
ElfParser *
elf_parser_new (const char *filename,
GError **error)

View File

@ -27,6 +27,8 @@ typedef struct ElfParser ElfParser;
ElfParser *elf_parser_new_from_data (const guchar *data,
gsize length);
ElfParser *elf_parser_new_from_mmap (GMappedFile *mapped_file,
GError **err);
ElfParser *elf_parser_new (const char *filename,
GError **err);
void elf_parser_free (ElfParser *parser);

View File

@ -0,0 +1,19 @@
libelfparser_sources = [
'demangle.cpp',
'elfparser.c',
]
libelfparser_deps = [
dependency('glib-2.0', version: glib_req_version),
]
libelfparser_static = static_library('elfparser', libelfparser_sources,
dependencies: libelfparser_deps,
gnu_symbol_visibility: 'hidden',
)
libelfparser_static_dep = declare_dependency(
include_directories: include_directories('.'),
dependencies: libelfparser_deps,
link_with: libelfparser_static,
)

View File

@ -0,0 +1,94 @@
/* line-reader-private.h
*
* Copyright 2015-2023 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib.h>
#include <string.h>
G_BEGIN_DECLS
typedef struct _LineReader
{
char *contents;
gsize length;
gssize pos;
} LineReader;
static inline void
line_reader_init (LineReader *reader,
char *contents,
gssize length)
{
g_assert (reader != NULL);
if (length < 0)
length = strlen (contents);
if (contents != NULL)
{
reader->contents = contents;
reader->length = length;
reader->pos = 0;
}
else
{
reader->contents = NULL;
reader->length = 0;
reader->pos = 0;
}
}
static inline char *
line_reader_next (LineReader *reader,
gsize *length)
{
char *ret = NULL;
g_assert (reader != NULL);
g_assert (length != NULL);
if ((reader->contents == NULL) || (reader->pos >= reader->length))
{
*length = 0;
return NULL;
}
ret = &reader->contents [reader->pos];
for (; reader->pos < reader->length; reader->pos++)
{
if (reader->contents [reader->pos] == '\n')
{
*length = &reader->contents [reader->pos] - ret;
/* Ingore the \r in \r\n if provided */
if (*length > 0 && reader->pos > 0 && reader->contents [reader->pos - 1] == '\r')
(*length)--;
reader->pos++;
return ret;
}
}
*length = &reader->contents [reader->pos] - ret;
return ret;
}
G_END_DECLS

View File

@ -0,0 +1,7 @@
liblinereader_deps = [
dependency('gio-2.0', version: glib_req_version),
]
liblinereader_static_dep = declare_dependency(
include_directories: include_directories('.'),
)

3
contrib/meson.build Normal file
View File

@ -0,0 +1,3 @@
subdir('eggbitset')
subdir('elfparser')
subdir('linereader')

View File

@ -1,9 +1,9 @@
project('sysprof', 'c',
project('sysprof', ['c', 'cpp'],
license: ['GPL3+', 'GPL2+'],
version: '45.alpha',
meson_version: '>=0.59.0',
default_options: [ 'c_std=gnu11',
'cpp_std=c++11',
default_options: [ 'c_std=gnu17',
'cpp_std=gnu++17',
'warning_level=2',
]
)
@ -20,28 +20,48 @@ else
app_id = 'org.gnome.Sysprof'
endif
libsysprof_api_version = 4
libsysprof_ui_api_version = 5
# All libraries share an ABI revision as they are expected
# to be updated as a set. However, we keep libsysprof-capture
# at an older version since it's used as a static library
# from various platform tooling
soname_major_version = 6
libsysprof_capture_api_version = 4
libsysprof_api_version = soname_major_version
version_split = meson.project_version().split('.')
datadir = get_option('datadir')
datadir_for_pc_file = join_paths('${prefix}', datadir)
podir = join_paths(meson.current_source_dir(), 'po')
glib_req = '2.73.0'
gtk_req = '4.6'
# Predetermine some features based on meson_options.txt
need_gtk = get_option('gtk')
need_glib = (need_gtk or
get_option('examples') or
get_option('sysprofd') != 'none' or
get_option('tools') or
get_option('tests'))
need_libsysprof = (need_gtk or
get_option('libsysprof') or
get_option('examples') or
get_option('tools') or
get_option('tests'))
dex_req = '0.3'
glib_req = '2.76.0'
gtk_req = '4.10'
polkit_req = '0.105'
dex_req_version = '>= @0@'.format(dex_req)
glib_req_version = '>= @0@'.format(glib_req)
gtk_req_version = '>= @0@'.format(gtk_req)
polkit_req_version = '>= @0@'.format(polkit_req)
cc = meson.get_compiler('c')
cxx = meson.get_compiler('cpp')
if get_option('libsysprof') or get_option('agent')
add_languages('cpp', native: false)
cxx = meson.get_compiler('cpp')
endif
glib_dep = dependency('glib-2.0', version: glib_req_version, required: need_glib)
gtk_dep = dependency('gtk4', version: gtk_req_version, required: need_gtk)
libsystemd_dep = dependency('libsystemd', required: false)
config_h = configuration_data()
config_h.set_quoted('SYMBOLIC_VERSION', symbolic_version)
@ -72,25 +92,19 @@ if get_option('default_library') != 'static'
endif
endif
debugdir = get_option('debugdir')
if debugdir == ''
debugdir = join_paths(get_option('prefix'), get_option('libdir'), 'debug')
endif
config_h.set_quoted('DEBUGDIR', debugdir)
config_h.set_quoted('GETTEXT_PACKAGE', 'sysprof')
config_h.set10('ENABLE_NLS', true)
config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR')
config_h.set('HAVE_EXECINFO_H', cc.has_header('execinfo.h'))
config_h.set('HAVE_STRLCPY', cc.has_function('strlcpy'))
config_h.set('HAVE_REALLOCARRAY', cc.has_function('reallocarray'))
config_h.set('HAVE_STRLCPY', cc.has_function('strlcpy'))
config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR')
config_h.set10('ENABLE_NLS', true)
config_h.set_quoted('GETTEXT_PACKAGE', 'sysprof')
config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
config_h.set10('HAVE_LIBSYSTEMD', libsystemd_dep.found())
polkit_agent_dep = dependency('polkit-agent-1', required: false)
polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false)
config_h.set10('HAVE_POLKIT_AGENT', polkit_agent_dep.found())
polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false)
config_h.set10('HAVE_POLKIT', polkit_dep.found())
if get_option('libunwind')
@ -98,7 +112,11 @@ if get_option('libunwind')
# and backtrace() showing up in builds
libunwind_dep = dependency('libunwind-generic', required: true)
config_h.set('ENABLE_LIBUNWIND', libunwind_dep.found())
config_h.set('HAVE_UNW_SET_CACHE_SIZE', libunwind_dep.found() and cc.has_header_symbol('libunwind.h', 'unw_set_cache_size', dependencies: [libunwind_dep]))
config_h.set('HAVE_UNW_SET_CACHE_SIZE',
(libunwind_dep.found() and
cc.has_header_symbol('libunwind.h',
'unw_set_cache_size',
dependencies: [libunwind_dep])))
endif
# Development build setup
@ -106,36 +124,49 @@ if get_option('development')
config_h.set10('DEVELOPMENT_BUILD', true)
endif
has_use_clockid = cc.has_member('struct perf_event_attr', 'use_clockid', prefix: '#include <linux/perf_event.h>')
has_clockid = cc.has_member('struct perf_event_attr', 'clockid', prefix: '#include <linux/perf_event.h>')
has_use_clockid = cc.has_member('struct perf_event_attr',
'use_clockid',
prefix: '#include <linux/perf_event.h>')
has_clockid = cc.has_member('struct perf_event_attr',
'clockid', prefix:
'#include <linux/perf_event.h>')
config_h.set('HAVE_PERF_CLOCKID', has_use_clockid and has_clockid)
add_project_arguments([
'-I' + meson.current_build_dir(), # config.h
], language: 'c')
glib_major = glib_req.split('.')[0].to_int()
glib_minor = glib_req.split('.')[1].to_int()
gtk_major = gtk_req.split('.')[0].to_int()
gtk_minor = gtk_req.split('.')[1].to_int()
if glib_minor % 2 == 1
glib_minor = glib_minor + 1
endif
if gtk_minor % 2 == 1
gtk_minor = gtk_minor + 1
endif
# For config.h
add_project_arguments(['-I'+meson.current_build_dir()], language: 'c')
global_c_args = [
'-DSYSPROF_COMPILATION',
'-D_GNU_SOURCE',
'-D_POSIX_C_SOURCE=200809L',
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor),
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor),
'-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
'-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
]
# Enforce GLib symbol access by required version
if need_glib
glib_major = glib_req.split('.')[0].to_int()
glib_minor = glib_req.split('.')[1].to_int()
if glib_minor % 2 == 1
glib_minor = glib_minor + 1
endif
global_c_args += [
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor),
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor),
]
endif
# Enforce GTK symbol access by required version
if need_gtk
gtk_major = gtk_req.split('.')[0].to_int()
gtk_minor = gtk_req.split('.')[1].to_int()
if gtk_minor % 2 == 1
gtk_minor = gtk_minor + 1
endif
global_c_args += [
'-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
'-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor),
]
endif
if host_machine.system() == 'darwin'
global_c_args += ['-D_DARWIN_C_SOURCE']
endif
@ -155,7 +186,7 @@ test_c_args = [
'-Wswitch-default',
'-Wswitch-enum',
'-Wuninitialized',
['-Werror=format-security', '-Werror=format=2' ],
['-Werror=format-security', '-Werror=format=2'],
'-Werror=empty-body',
'-Werror=implicit-function-declaration',
'-Werror=pointer-arith',
@ -171,13 +202,6 @@ test_c_args = [
'-Werror=undef',
]
# Until GLib is fixed with regards to volatile type registration
if cc.get_id() == 'clang'
test_c_args += ['-Wno-incompatible-pointer-types']
else
test_c_args += ['-Werror=incompatible-pointer-types']
endif
foreach arg: test_c_args
if cc.has_multi_arguments(arg)
global_c_args += arg
@ -229,8 +253,9 @@ int main(void) {
config_h.set10('HAVE_STDATOMIC_H', true)
endif
needs_service_access = get_option('libsysprof') or get_option('agent')
install_service_files = needs_service_access or get_option('sysprofd') == 'bundled'
if need_glib
subdir('contrib')
endif
subdir('src')
subdir('data')

View File

@ -8,9 +8,6 @@ option('development', type: 'boolean', value: 'false')
# server scenarios.
option('gtk', type: 'boolean')
# Disable libsysprof/ui (in situations you only want sysprof-capture)
option('libsysprof', type: 'boolean')
# Allow disabling the installation of libsysprof-capture*.a
option('install-static', type: 'boolean')
@ -29,18 +26,15 @@ option('systemdunitdir', type: 'string',
description: 'Directory for systemd service files'
)
# An optional location to specify where to locate debug information. This
# is useful for distributions to set based on their debuginfo setup.
option('debugdir', type: 'string',
description: 'Look for global separate debug info in this path'
)
# If Yelp documentation should be installed
option('help', type: 'boolean')
# Disable use of libunwind
option('libunwind', type: 'boolean')
# Build libsysprof (required by tools, tests, sysprof, etc)
option('libsysprof', type: 'boolean')
# Optionally disable the tools (this is mostly only useful for building only
# libsysprof-capture as a subproject)
option('tools', type: 'boolean')
@ -52,7 +46,3 @@ option('tests', type: 'boolean')
# Optionally disable the examples (this is mostly only useful for building only
# libsysprof-capture as a subproject)
option('examples', type: 'boolean')
# Optionally build the sysprof-agent tool to allow profiling inside of
# containers from external system
option('agent', type: 'boolean', description: 'Build the sysprof-agent utility')

View File

@ -98,6 +98,25 @@
}
]
},
{
"name" : "libdex",
"buildsystem" : "meson",
"config-opts" : [
"--buildtype=debugoptimized",
"-Ddocs=false",
"-Dintrospection=enabled",
"-Dexamples=false",
"-Dtests=false",
"-Dsysprof=false"
],
"sources" : [
{
"type" : "git",
"url" : "https://gitlab.gnome.org/chergert/libdex.git",
"branch" : "main"
}
]
},
{
"name" : "sysprof",
"config-opts" : [

View File

@ -335,6 +335,8 @@ mapped_ring_buffer_finalize (MappedRingBuffer *self)
close (self->fd);
self->fd = -1;
}
free (self);
}
void

View File

@ -19,8 +19,11 @@ if not meson.is_subproject()
install_headers(libsysprof_capture_headers, subdir: sysprof_header_subdir)
endif
libsysprof_capture_sources = files([
mapped_ring_buffer_sources = files([
'mapped-ring-buffer.c',
])
libsysprof_capture_sources = files([
'sysprof-address.c',
'sysprof-capture-condition.c',
'sysprof-capture-cursor.c',
@ -46,8 +49,9 @@ libsysprof_capture_deps = [
]
libsysprof_capture = static_library(
'sysprof-capture-@0@'.format(libsysprof_api_version),
libsysprof_capture_sources,
'sysprof-capture-@0@'.format(libsysprof_capture_api_version),
(libsysprof_capture_sources +
mapped_ring_buffer_sources),
dependencies: libsysprof_capture_deps,
c_args: [ '-DSYSPROF_CAPTURE_COMPILATION' ],
@ -64,7 +68,7 @@ libsysprof_capture_dep = declare_dependency(
dependencies: libsysprof_capture_deps,
include_directories: libsysprof_capture_include_dirs,
)
meson.override_dependency('sysprof-capture-@0@'.format(libsysprof_api_version), libsysprof_capture_dep)
meson.override_dependency('sysprof-capture-@0@'.format(libsysprof_capture_api_version), libsysprof_capture_dep)
if install_static
pkgconfig.generate(
@ -76,3 +80,7 @@ if install_static
libraries_private: libsysprof_capture_deps,
)
endif
if get_option('tests')
subdir('tests')
endif

View File

@ -175,6 +175,10 @@ sysprof_capture_cursor_foreach (SysprofCaptureCursor *self,
delegate = READ_DELEGATE (sysprof_capture_reader_read_sample);
break;
case SYSPROF_CAPTURE_FRAME_TRACE:
delegate = READ_DELEGATE (sysprof_capture_reader_read_trace);
break;
case SYSPROF_CAPTURE_FRAME_LOG:
delegate = READ_DELEGATE (sysprof_capture_reader_read_log);
break;

View File

@ -925,6 +925,52 @@ sysprof_capture_reader_read_sample (SysprofCaptureReader *self)
return sample;
}
const SysprofCaptureTrace *
sysprof_capture_reader_read_trace (SysprofCaptureReader *self)
{
SysprofCaptureTrace *trace;
assert (self != NULL);
assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
assert (self->pos <= self->bufsz);
if (!sysprof_capture_reader_ensure_space_for (self, sizeof *trace))
return NULL;
trace = (SysprofCaptureTrace *)(void *)&self->buf[self->pos];
sysprof_capture_reader_bswap_frame (self, &trace->frame);
if (trace->frame.type != SYSPROF_CAPTURE_FRAME_TRACE)
return NULL;
if (trace->frame.len < sizeof *trace)
return NULL;
if (self->endian != __BYTE_ORDER)
trace->n_addrs = bswap_16 (trace->n_addrs);
if (trace->frame.len < (sizeof *trace + (sizeof(SysprofCaptureAddress) * trace->n_addrs)))
return NULL;
if (!sysprof_capture_reader_ensure_space_for (self, trace->frame.len))
return NULL;
trace = (SysprofCaptureTrace *)(void *)&self->buf[self->pos];
if (SYSPROF_UNLIKELY (self->endian != __BYTE_ORDER))
{
unsigned int i;
for (i = 0; i < trace->n_addrs; i++)
trace->addrs[i] = bswap_64 (trace->addrs[i]);
}
self->pos += trace->frame.len;
return trace;
}
const SysprofCaptureCounterDefine *
sysprof_capture_reader_read_counter_define (SysprofCaptureReader *self)
{
@ -1343,7 +1389,7 @@ array_append (const char ***files,
*files = new_files;
}
(*files)[*n_files] = new_element ? strdup (new_element) : NULL;
(*files)[*n_files] = new_element ? sysprof_strdup (new_element) : NULL;
*n_files = *n_files + 1;
assert (*n_files <= *n_files_allocated);
@ -1362,7 +1408,10 @@ array_deduplicate (const char **files,
for (last_written = 0, next_to_read = 1; last_written <= next_to_read && next_to_read < *n_files;)
{
if (strcmp (files[next_to_read], files[last_written]) == 0)
next_to_read++;
{
free ((char *)files[next_to_read]);
next_to_read++;
}
else
files[++last_written] = files[next_to_read++];
}

View File

@ -111,6 +111,8 @@ const SysprofCaptureProcess *sysprof_capture_reader_read_process (
SYSPROF_AVAILABLE_IN_ALL
const SysprofCaptureSample *sysprof_capture_reader_read_sample (SysprofCaptureReader *self);
SYSPROF_AVAILABLE_IN_ALL
const SysprofCaptureTrace *sysprof_capture_reader_read_trace (SysprofCaptureReader *self);
SYSPROF_AVAILABLE_IN_ALL
const SysprofCaptureJitmap *sysprof_capture_reader_read_jitmap (SysprofCaptureReader *self);
SYSPROF_AVAILABLE_IN_ALL
const SysprofCaptureCounterDefine *sysprof_capture_reader_read_counter_define (SysprofCaptureReader *self);

View File

@ -140,10 +140,11 @@ typedef enum
SYSPROF_CAPTURE_FRAME_FILE_CHUNK = 13,
SYSPROF_CAPTURE_FRAME_ALLOCATION = 14,
SYSPROF_CAPTURE_FRAME_OVERLAY = 15,
SYSPROF_CAPTURE_FRAME_TRACE = 16,
} SysprofCaptureFrameType;
/* Not part of ABI */
#define SYSPROF_CAPTURE_FRAME_LAST 16
#define SYSPROF_CAPTURE_FRAME_LAST 17
SYSPROF_ALIGNED_BEGIN(1)
typedef struct
@ -225,6 +226,18 @@ typedef struct
} SysprofCaptureSample
SYSPROF_ALIGNED_END(1);
SYSPROF_ALIGNED_BEGIN(1)
typedef struct
{
SysprofCaptureFrame frame;
uint32_t n_addrs : 16;
uint32_t entering : 1;
uint32_t padding1 : 15;
int32_t tid;
SysprofCaptureAddress addrs[0];
} SysprofCaptureTrace
SYSPROF_ALIGNED_END(1);
SYSPROF_ALIGNED_BEGIN(1)
typedef struct
{
@ -357,6 +370,7 @@ SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureMap) == 56, "SysprofCaptureMap chan
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureJitmap) == 28, "SysprofCaptureJitmap changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureProcess) == 24, "SysprofCaptureProcess changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureSample) == 32, "SysprofCaptureSample changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureTrace) == 32, "SysprofCaptureTrace changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureFork) == 28, "SysprofCaptureFork changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureExit) == 24, "SysprofCaptureExit changed size");
SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureTimestamp) == 24, "SysprofCaptureTimestamp changed size");
@ -373,6 +387,7 @@ SYSPROF_STATIC_ASSERT (sizeof (SysprofCaptureOverlay) == 32, "SysprofCaptureOver
SYSPROF_STATIC_ASSERT ((offsetof (SysprofCaptureAllocation, addrs) % SYSPROF_CAPTURE_ALIGN) == 0, "SysprofCaptureAllocation.addrs is not aligned");
SYSPROF_STATIC_ASSERT ((offsetof (SysprofCaptureSample, addrs) % SYSPROF_CAPTURE_ALIGN) == 0, "SysprofCaptureSample.addrs is not aligned");
SYSPROF_STATIC_ASSERT ((offsetof (SysprofCaptureTrace, addrs) % SYSPROF_CAPTURE_ALIGN) == 0, "SysprofCaptureTrace.addrs is not aligned");
static inline int
sysprof_capture_address_compare (SysprofCaptureAddress a,

View File

@ -416,6 +416,32 @@ sysprof_capture_writer_cat (SysprofCaptureWriter *self,
break;
}
case SYSPROF_CAPTURE_FRAME_TRACE:
{
const SysprofCaptureTrace *frame;
if (!(frame = sysprof_capture_reader_read_trace (reader)))
goto panic;
{
SysprofCaptureAddress addrs[frame->n_addrs];
for (unsigned int z = 0; z < frame->n_addrs; z++)
addrs[z] = translate_table_translate (tables, TRANSLATE_ADDR, frame->addrs[z]);
sysprof_capture_writer_add_trace (self,
frame->frame.time,
frame->frame.cpu,
frame->frame.pid,
frame->tid,
addrs,
frame->n_addrs,
frame->entering);
}
break;
}
case SYSPROF_CAPTURE_FRAME_CTRDEF:
{
const SysprofCaptureCounterDefine *frame;

View File

@ -798,6 +798,42 @@ sysprof_capture_writer_add_sample (SysprofCaptureWriter *self,
return true;
}
bool
sysprof_capture_writer_add_trace (SysprofCaptureWriter *self,
int64_t time,
int cpu,
int32_t pid,
int32_t tid,
const SysprofCaptureAddress *addrs,
unsigned int n_addrs,
bool entering)
{
SysprofCaptureTrace *ev;
size_t len;
assert (self != NULL);
len = sizeof *ev + (n_addrs * sizeof (SysprofCaptureAddress));
ev = (SysprofCaptureTrace *)sysprof_capture_writer_allocate (self, &len);
if (!ev)
return false;
sysprof_capture_writer_frame_init (&ev->frame,
len,
cpu,
pid,
time,
SYSPROF_CAPTURE_FRAME_SAMPLE);
ev->n_addrs = n_addrs;
ev->tid = tid;
ev->entering = !!entering;
memcpy (ev->addrs, addrs, (n_addrs * sizeof (SysprofCaptureAddress)));
return true;
}
bool
sysprof_capture_writer_add_fork (SysprofCaptureWriter *self,
int64_t time,
@ -1659,7 +1695,7 @@ _sysprof_capture_writer_add_raw (SysprofCaptureWriter *self,
return false;
assert (fr->len == len);
assert (fr->type < 16);
assert (fr->type < SYSPROF_CAPTURE_FRAME_LAST);
memcpy (begin, fr, fr->len);
@ -1668,3 +1704,14 @@ _sysprof_capture_writer_add_raw (SysprofCaptureWriter *self,
return true;
}
int
_sysprof_capture_writer_dup_fd (SysprofCaptureWriter *self)
{
assert (self != NULL);
if (self->fd == -1)
return -1;
return dup (self->fd);
}

View File

@ -143,6 +143,15 @@ bool sysprof_capture_writer_add_sample (Sy
const SysprofCaptureAddress *addrs,
unsigned int n_addrs);
SYSPROF_AVAILABLE_IN_ALL
bool sysprof_capture_writer_add_trace (SysprofCaptureWriter *self,
int64_t time,
int cpu,
int32_t pid,
int32_t tid,
const SysprofCaptureAddress *addrs,
unsigned int n_addrs,
bool entering);
SYSPROF_AVAILABLE_IN_ALL
bool sysprof_capture_writer_add_fork (SysprofCaptureWriter *self,
int64_t time,
int cpu,
@ -235,5 +244,7 @@ SYSPROF_INTERNAL
bool _sysprof_capture_writer_set_time_range (SysprofCaptureWriter *self,
int64_t start_time,
int64_t end_time) SYSPROF_INTERNAL;
SYSPROF_INTERNAL
int _sysprof_capture_writer_dup_fd (SysprofCaptureWriter *self);
SYSPROF_END_DECLS

View File

@ -576,6 +576,43 @@ sysprof_collector_sample (SysprofBacktraceFunc backtrace_func,
} COLLECTOR_END;
}
void
sysprof_collector_trace (SysprofBacktraceFunc backtrace_func,
void *backtrace_data,
bool entering)
{
COLLECTOR_BEGIN {
SysprofCaptureTrace *ev;
size_t len;
len = sizeof *ev + (sizeof (SysprofCaptureTrace) * MAX_UNWIND_DEPTH);
if ((ev = mapped_ring_buffer_allocate (collector->buffer, len)))
{
int n_addrs;
/* See comment from sysprof_collector_allocate(). */
if (backtrace_func)
n_addrs = backtrace_func (ev->addrs, MAX_UNWIND_DEPTH, backtrace_data);
else
n_addrs = 0;
ev->n_addrs = ((n_addrs < 0) ? 0 : (n_addrs > MAX_UNWIND_DEPTH) ? MAX_UNWIND_DEPTH : n_addrs);
ev->frame.len = sizeof *ev + sizeof (SysprofCaptureAddress) * ev->n_addrs;
ev->frame.type = SYSPROF_CAPTURE_FRAME_TRACE;
ev->frame.cpu = _do_getcpu ();
ev->frame.pid = collector->pid;
ev->frame.time = SYSPROF_CAPTURE_CURRENT_TIME;
ev->tid = collector->tid;
ev->entering = !!entering;
ev->padding1 = 0;
mapped_ring_buffer_advance (collector->buffer, ev->frame.len);
}
} COLLECTOR_END;
}
void
sysprof_collector_mark (int64_t time,
int64_t duration,

View File

@ -75,6 +75,10 @@ void sysprof_collector_allocate (SysprofCaptureAddress
SYSPROF_AVAILABLE_IN_3_36
void sysprof_collector_sample (SysprofBacktraceFunc backtrace_func,
void *backtrace_data);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_collector_trace (SysprofBacktraceFunc backtrace_func,
void *backtrace_data,
bool entering);
SYSPROF_AVAILABLE_IN_3_36
void sysprof_collector_mark (int64_t time,
int64_t duration,

View File

@ -24,8 +24,8 @@
#include <glib/gi18n.h>
#include <locale.h>
#include <stddef.h>
#include <rax.h>
#include <sysprof.h>
#include <sysprof-capture.h>
typedef struct
{

View File

@ -23,8 +23,8 @@
#include <errno.h>
#include <glib.h>
#include <stddef.h>
#include <rax.h>
#include <sysprof.h>
#include <sysprof-capture.h>
typedef struct
{

View File

@ -0,0 +1,40 @@
libsysprof_capture_test_env = [
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
'G_DEBUG=gc-friendly',
'GSETTINGS_BACKEND=memory',
'MALLOC_CHECK_=2',
'NO_AT_BRIDGE=1',
]
libsysprof_capture_testsuite_c_args = [
'-DSYSPROF_COMPILATION',
'-DG_ENABLE_DEBUG',
'-UG_DISABLE_ASSERT',
'-UG_DISABLE_CAST_CHECKS',
]
libsysprof_capture_testsuite = {
'allocs-by-size' : {'skip': true},
'cross-thread-frees' : {'skip': true},
'find-temp-allocs' : {'skip': true},
'rewrite-pid' : {'skip': true},
'test-capture' : {},
'test-capture-cursor' : {},
'test-mapped-ring-buffer' : {},
}
libsysprof_capture_testsuite_deps = [
dependency('gio-2.0'),
libsysprof_capture_dep,
]
foreach test, params: libsysprof_capture_testsuite
test_exe = executable(test, '@0@.c'.format(test),
c_args: libsysprof_capture_testsuite_c_args,
dependencies: libsysprof_capture_testsuite_deps,
)
if not params.get('skip', false)
test(test, test_exe, env: libsysprof_capture_test_env)
endif
endforeach

View File

@ -732,7 +732,7 @@ test_reader_writer_file (void)
g_autofree gchar *data = NULL;
g_autofree gchar *testfile = NULL;
GByteArray *buf = g_byte_array_new ();
g_autofree const gchar **files = NULL;
const char **files;
SysprofCaptureWriter *writer;
SysprofCaptureReader *reader;
SysprofCaptureFrameType type;
@ -799,6 +799,7 @@ test_reader_writer_file (void)
g_assert_nonnull (files);
g_assert_cmpstr (files[0], ==, testfile);
g_assert_null (files[1]);
free (files);
sysprof_capture_reader_reset (reader);
new_fd = sysprof_memfd_create ("[sysprof-capture-file]");

View File

@ -163,6 +163,9 @@ test_threaded_movements (void)
g_thread_join (thread1);
g_thread_join (thread2);
mapped_ring_buffer_unref (writer);
mapped_ring_buffer_unref (reader);
}
static void
@ -183,6 +186,8 @@ test_readwrite (void)
mapped_ring_buffer_advance (ring, sizeof *ptr);
}
mapped_ring_buffer_drain (ring, drain_count_cb, NULL);
mapped_ring_buffer_unref (ring);
}
gint

View File

@ -1,86 +0,0 @@
SysprofVisualizer {
background: @content_view_bg;
}
SysprofVisualizer:not(:last-child) {
border-bottom: 1px solid alpha(@borders, 0.3);
}
SysprofVisualizerGroup {
border-bottom: 1px solid @borders;
}
SysprofVisualizerGroup:last-child {
box-shadow: 0 20px 15px 15px alpha(@borders, 0.3);
}
SysprofVisualizersFrame box.horizontal.inline-toolbar {
padding: 0;
margin: 0;
border: none;
border-radius: 0;
border-width: 0;
}
SysprofVisualizersFrame viewport.visualizers {
border-right: 1px solid @borders;
box-shadow: 1px 1px 3px alpha(@borders, 0.5);
}
SysprofVisualizersFrame scrollbar.horizontal {
color: mix(@theme_fg_color, @theme_selected_bg_color, 0.5);
background: transparent;
}
SysprofVisualizersFrame scrollbar.horizontal range trough {
background: transparent;
}
SysprofVisualizersFrame scrollbar.horizontal range trough slider {
margin-left: 1px;
margin-right: 1px;
padding: 6px;
min-height: 14px;
background: alpha(@content_view_bg, 0.2);
border-radius: 3px;
border: 2px solid @theme_selected_bg_color;
box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3),
inset 0 -15px 5px alpha(@content_view_bg,.1),
0px 2px 4px @borders;
}
SysprofVisualizerTicks {
color: mix(@theme_fg_color, @borders, 0.5);
background-color: @content_view_bg;
}
SysprofVisualizersFrame list {
background-color: @theme_bg_color;
}
SysprofVisualizersFrame list.visualizer-groups row {
padding: 0;
border-bottom: 1px solid @borders;
}
SysprofVisualizersFrame list.visualizer-groups row:not(:selected) {
background-color: @theme_bg_color;
}
SysprofVisualizersFrame list.visualizer-groups row:last-child {
box-shadow: 0 20px 15px 15px alpha(@borders, 0.3);
}
SysprofVisualizersFrame .left-column .small-button.flat {
border-color: transparent;
min-height: 8px;
min-width: 8px;
}
SysprofVisualizersFrame .left-column .small-button.flat:checked,
SysprofVisualizersFrame .left-column .small-button.flat:hover {
border-color: @borders;
}
SysprofVisualizersFrame .selection {
border-radius: 3px;
background-color: alpha(@theme_selected_bg_color, 0.35);
box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3),
inset 0 -15px 5px alpha(@content_view_bg,.1),
inset 0 0 1px 1px @theme_selected_bg_color;
}

View File

@ -1,13 +0,0 @@
list.environ-editor row button.flat:last-child {
min-height: 16px;
min-width: 16px;
padding: 0;
margin: 3px;
}
list.environ-editor row button.flat:not(:hover) {
border-color: transparent;
}
list.environ-editor row button.flat image {
padding: 3px;
margin: 0;
}

View File

@ -1,7 +0,0 @@
sysprofaidicon image.right.top {
border-radius: 9999px;
background-color: @theme_selected_bg_color;
color: @theme_selected_fg_color;
padding: 2px;
margin: 0px;
}

View File

@ -1,35 +0,0 @@
/* egg-handle.h
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define EGG_TYPE_HANDLE (egg_handle_get_type())
G_DECLARE_FINAL_TYPE (EggHandle, egg_handle, EGG, HANDLE, GtkWidget)
GtkWidget *egg_handle_new (GtkPositionType position);
void egg_handle_set_position (EggHandle *self,
GtkPositionType position);
G_END_DECLS

View File

@ -1,145 +0,0 @@
/* egg-handle.c
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "config.h"
#include "egg-handle-private.h"
#define EXTRA_SIZE 8
struct _EggHandle
{
GtkWidget parent_instance;
GtkWidget *separator;
GtkPositionType position : 3;
};
G_DEFINE_TYPE (EggHandle, egg_handle, GTK_TYPE_WIDGET)
static gboolean
egg_handle_contains (GtkWidget *widget,
double x,
double y)
{
EggHandle *self = (EggHandle *)widget;
graphene_rect_t area;
g_assert (EGG_IS_HANDLE (self));
if (!gtk_widget_compute_bounds (GTK_WIDGET (self->separator),
GTK_WIDGET (self),
&area))
return FALSE;
switch (self->position)
{
case GTK_POS_LEFT:
area.origin.x -= EXTRA_SIZE;
area.size.width = EXTRA_SIZE;
break;
case GTK_POS_RIGHT:
area.size.width = EXTRA_SIZE;
break;
case GTK_POS_TOP:
area.origin.y -= EXTRA_SIZE;
area.size.height = EXTRA_SIZE;
break;
case GTK_POS_BOTTOM:
area.size.height = EXTRA_SIZE;
break;
default:
g_assert_not_reached ();
break;
}
return graphene_rect_contains_point (&area, &GRAPHENE_POINT_INIT (x, y));
}
static void
egg_handle_dispose (GObject *object)
{
EggHandle *self = (EggHandle *)object;
g_clear_pointer (&self->separator, gtk_widget_unparent);
G_OBJECT_CLASS (egg_handle_parent_class)->dispose (object);
}
static void
egg_handle_class_init (EggHandleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = egg_handle_dispose;
widget_class->contains = egg_handle_contains;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}
static void
egg_handle_init (EggHandle *self)
{
self->separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_parent (GTK_WIDGET (self->separator), GTK_WIDGET (self));
}
void
egg_handle_set_position (EggHandle *self,
GtkPositionType position)
{
g_return_if_fail (EGG_IS_HANDLE (self));
self->position = position;
switch (position)
{
case GTK_POS_LEFT:
case GTK_POS_RIGHT:
gtk_widget_set_cursor_from_name (GTK_WIDGET (self), "col-resize");
gtk_orientable_set_orientation (GTK_ORIENTABLE (self->separator), GTK_ORIENTATION_VERTICAL);
break;
case GTK_POS_TOP:
case GTK_POS_BOTTOM:
gtk_widget_set_cursor_from_name (GTK_WIDGET (self), "row-resize");
gtk_orientable_set_orientation (GTK_ORIENTABLE (self->separator), GTK_ORIENTATION_HORIZONTAL);
break;
default:
g_assert_not_reached ();
}
}
GtkWidget *
egg_handle_new (GtkPositionType position)
{
EggHandle *self;
self = g_object_new (EGG_TYPE_HANDLE, NULL);
egg_handle_set_position (self, position);
return GTK_WIDGET (self);
}

View File

@ -1,48 +0,0 @@
/* egg-paned.h
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define EGG_TYPE_PANED (egg_paned_get_type())
G_DECLARE_FINAL_TYPE (EggPaned, egg_paned, EGG, PANED, GtkWidget)
GtkWidget *egg_paned_new (void);
void egg_paned_append (EggPaned *self,
GtkWidget *child);
void egg_paned_prepend (EggPaned *self,
GtkWidget *child);
void egg_paned_insert (EggPaned *self,
int position,
GtkWidget *child);
void egg_paned_insert_after (EggPaned *self,
GtkWidget *child,
GtkWidget *sibling);
void egg_paned_remove (EggPaned *self,
GtkWidget *child);
guint egg_paned_get_n_children (EggPaned *self);
GtkWidget *egg_paned_get_nth_child (EggPaned *self,
guint nth);
G_END_DECLS

View File

@ -1,593 +0,0 @@
/* egg-paned.c
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "config.h"
#include <string.h>
#include "egg-paned-private.h"
#include "egg-resizer-private.h"
struct _EggPaned
{
GtkWidget parent_instance;
GtkOrientation orientation;
};
static void buildable_iface_init (GtkBuildableIface *iface);
G_DEFINE_TYPE_WITH_CODE (EggPaned, egg_paned, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
enum {
PROP_0,
N_PROPS,
PROP_ORIENTATION,
};
static void
update_orientation (GtkWidget *widget,
GtkOrientation orientation)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gtk_widget_remove_css_class (widget, "vertical");
gtk_widget_add_css_class (widget, "horizontal");
}
else
{
gtk_widget_remove_css_class (widget, "horizontal");
gtk_widget_add_css_class (widget, "vertical");
}
gtk_accessible_update_property (GTK_ACCESSIBLE (widget),
GTK_ACCESSIBLE_PROPERTY_ORIENTATION, orientation,
-1);
}
/**
* egg_paned_new:
*
* Create a new #EggPaned.
*
* Returns: (transfer full): a newly created #EggPaned
*/
GtkWidget *
egg_paned_new (void)
{
return g_object_new (EGG_TYPE_PANED, NULL);
}
static void
egg_paned_set_orientation (EggPaned *self,
GtkOrientation orientation)
{
GtkPositionType pos;
g_assert (EGG_IS_PANED (self));
g_assert (orientation == GTK_ORIENTATION_HORIZONTAL ||
orientation == GTK_ORIENTATION_VERTICAL);
if (self->orientation == orientation)
return;
self->orientation = orientation;
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
pos = GTK_POS_LEFT;
else
pos = GTK_POS_TOP;
for (GtkWidget *child = gtk_widget_get_last_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_prev_sibling (child))
{
g_assert (EGG_IS_RESIZER (child));
egg_resizer_set_position (EGG_RESIZER (child), pos);
}
update_orientation (GTK_WIDGET (self), self->orientation);
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify (G_OBJECT (self), "orientation");
}
static void
egg_paned_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
EggPaned *self = (EggPaned *)widget;
g_assert (EGG_IS_PANED (self));
*minimum = 0;
*natural = 0;
*minimum_baseline = -1;
*natural_baseline = -1;
for (GtkWidget *child = gtk_widget_get_first_child (widget);
child != NULL;
child = gtk_widget_get_next_sibling (child))
{
int child_min, child_nat;
gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL);
if (orientation == self->orientation)
{
*minimum += child_min;
*natural += child_nat;
}
else
{
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
}
}
}
typedef struct
{
GtkWidget *widget;
GtkRequisition min_request;
GtkRequisition nat_request;
GtkAllocation alloc;
} ChildAllocation;
static void
egg_paned_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
EggPaned *self = (EggPaned *)widget;
ChildAllocation *allocs;
ChildAllocation *last_alloc = NULL;
GtkOrientation orientation;
guint n_children = 0;
guint n_expand = 0;
guint i;
int extra_width = width;
int extra_height = height;
int expand_width;
int expand_height;
int x, y;
g_assert (EGG_IS_PANED (self));
GTK_WIDGET_CLASS (egg_paned_parent_class)->size_allocate (widget, width, height, baseline);
n_children = egg_paned_get_n_children (self);
if (n_children == 1)
{
GtkWidget *child = gtk_widget_get_first_child (widget);
GtkAllocation alloc = { 0, 0, width, height };
if (gtk_widget_get_visible (child))
{
gtk_widget_size_allocate (child, &alloc, -1);
return;
}
}
orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self));
allocs = g_newa (ChildAllocation, n_children);
memset (allocs, 0, sizeof *allocs * n_children);
/* Give min size to each of the children */
i = 0;
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child), i++)
{
ChildAllocation *child_alloc = &allocs[i];
if (!gtk_widget_get_visible (child))
continue;
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, height,
&child_alloc->min_request.width,
&child_alloc->nat_request.width,
NULL, NULL);
gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, width,
&child_alloc->min_request.height,
&child_alloc->nat_request.height,
NULL, NULL);
child_alloc->alloc.width = child_alloc->min_request.width;
child_alloc->alloc.height = child_alloc->min_request.height;
n_expand += gtk_widget_compute_expand (child, orientation);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
extra_width -= child_alloc->alloc.width;
child_alloc->alloc.height = height;
}
else
{
extra_height -= child_alloc->alloc.height;
child_alloc->alloc.width = width;
}
}
/* Now try to distribute extra space for natural size */
i = 0;
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child), i++)
{
ChildAllocation *child_alloc = &allocs[i];
if (!gtk_widget_get_visible (child))
continue;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int taken = MIN (extra_width, child_alloc->nat_request.width - child_alloc->alloc.width);
if (taken > 0)
{
child_alloc->alloc.width += taken;
extra_width -= taken;
}
}
else
{
int taken = MIN (extra_height, child_alloc->nat_request.height - child_alloc->alloc.height);
if (taken > 0)
{
child_alloc->alloc.height += taken;
extra_height -= taken;
}
}
last_alloc = child_alloc;
}
/* Now give extra space for those that expand */
expand_width = n_expand ? extra_width / n_expand : 0;
expand_height = n_expand ? extra_height / n_expand : 0;
i = n_children;
for (GtkWidget *child = gtk_widget_get_last_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_prev_sibling (child), i--)
{
ChildAllocation *child_alloc = &allocs[i-1];
if (!gtk_widget_get_visible (child))
continue;
if (!gtk_widget_compute_expand (child, orientation))
continue;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_alloc->alloc.width += expand_width;
extra_width -= expand_width;
}
else
{
child_alloc->alloc.height += expand_height;
extra_height -= expand_height;
}
}
/* Give any leftover to the last visible child */
if (last_alloc)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL)
last_alloc->alloc.width += extra_width;
else
last_alloc->alloc.height += extra_height;
}
i = 0;
x = 0;
y = 0;
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child), i++)
{
ChildAllocation *child_alloc = &allocs[i];
child_alloc->alloc.x = x;
child_alloc->alloc.y = y;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
x += child_alloc->alloc.width;
else
y += child_alloc->alloc.height;
gtk_widget_size_allocate (child, &child_alloc->alloc, -1);
}
}
static void
egg_paned_dispose (GObject *object)
{
EggPaned *self = (EggPaned *)object;
GtkWidget *child;
child = gtk_widget_get_first_child (GTK_WIDGET (self));
while (child)
{
GtkWidget *next = gtk_widget_get_next_sibling (child);
gtk_widget_unparent (child);
child = next;
}
G_OBJECT_CLASS (egg_paned_parent_class)->dispose (object);
}
static void
egg_paned_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EggPaned *self = EGG_PANED (object);
switch (prop_id)
{
case PROP_ORIENTATION:
g_value_set_enum (value, self->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
egg_paned_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EggPaned *self = EGG_PANED (object);
switch (prop_id)
{
case PROP_ORIENTATION:
egg_paned_set_orientation (self, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
egg_paned_class_init (EggPanedClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = egg_paned_dispose;
object_class->get_property = egg_paned_get_property;
object_class->set_property = egg_paned_set_property;
widget_class->measure = egg_paned_measure;
widget_class->size_allocate = egg_paned_size_allocate;
g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
gtk_widget_class_set_css_name (widget_class, "eggpaned");
}
static void
egg_paned_init (EggPaned *self)
{
self->orientation = GTK_ORIENTATION_HORIZONTAL;
update_orientation (GTK_WIDGET (self), self->orientation);
}
static void
egg_paned_update_handles (EggPaned *self)
{
GtkWidget *child;
g_assert (EGG_IS_PANED (self));
for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child))
{
GtkWidget *handle;
g_assert (EGG_IS_RESIZER (child));
if ((handle = egg_resizer_get_handle (EGG_RESIZER (child))))
gtk_widget_show (handle);
}
if ((child = gtk_widget_get_last_child (GTK_WIDGET (self))))
{
GtkWidget *handle = egg_resizer_get_handle (EGG_RESIZER (child));
gtk_widget_hide (handle);
}
}
void
egg_paned_remove (EggPaned *self,
GtkWidget *child)
{
GtkWidget *resizer;
g_return_if_fail (EGG_IS_PANED (self));
g_return_if_fail (GTK_IS_WIDGET (child));
resizer = gtk_widget_get_ancestor (child, EGG_TYPE_RESIZER);
g_return_if_fail (resizer != NULL &&
gtk_widget_get_parent (resizer) == GTK_WIDGET (self));
gtk_widget_unparent (resizer);
egg_paned_update_handles (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
void
egg_paned_insert (EggPaned *self,
int position,
GtkWidget *child)
{
GtkPositionType pos;
GtkWidget *resizer;
g_return_if_fail (EGG_IS_PANED (self));
g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (gtk_widget_get_parent (child) == NULL);
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
pos = GTK_POS_LEFT;
else
pos = GTK_POS_TOP;
resizer = egg_resizer_new (pos);
egg_resizer_set_child (EGG_RESIZER (resizer), child);
if (position < 0)
gtk_widget_insert_before (GTK_WIDGET (resizer), GTK_WIDGET (self), NULL);
else if (position == 0)
gtk_widget_insert_after (GTK_WIDGET (resizer), GTK_WIDGET (self), NULL);
else
{
GtkWidget *sibling = gtk_widget_get_first_child (GTK_WIDGET (self));
for (int i = position; i > 0 && sibling != NULL; i--)
sibling = gtk_widget_get_next_sibling (sibling);
gtk_widget_insert_before (GTK_WIDGET (resizer), GTK_WIDGET (self), sibling);
}
egg_paned_update_handles (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
void
egg_paned_append (EggPaned *self,
GtkWidget *child)
{
egg_paned_insert (self, -1, child);
}
void
egg_paned_prepend (EggPaned *self,
GtkWidget *child)
{
egg_paned_insert (self, 0, child);
}
void
egg_paned_insert_after (EggPaned *self,
GtkWidget *child,
GtkWidget *sibling)
{
int position = 0;
g_return_if_fail (EGG_IS_PANED (self));
g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (!sibling || GTK_IS_WIDGET (sibling));
if (sibling == NULL)
{
egg_paned_prepend (self, child);
return;
}
/* TODO: We should reverse insert() to call this */
for (GtkWidget *ancestor = gtk_widget_get_first_child (GTK_WIDGET (self));
ancestor != NULL;
ancestor = gtk_widget_get_next_sibling (ancestor))
{
position++;
if (sibling == ancestor || gtk_widget_is_ancestor (sibling, ancestor))
break;
}
egg_paned_insert (self, position, child);
}
guint
egg_paned_get_n_children (EggPaned *self)
{
guint count = 0;
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child))
count++;
return count;
}
GtkWidget *
egg_paned_get_nth_child (EggPaned *self,
guint nth)
{
g_return_val_if_fail (EGG_IS_PANED (self), NULL);
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != NULL;
child = gtk_widget_get_next_sibling (child))
{
g_assert (EGG_IS_RESIZER (child));
if (nth == 0)
return egg_resizer_get_child (EGG_RESIZER (child));
nth--;
}
return NULL;
}
static void
egg_paned_add_child (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const char *type)
{
if (GTK_IS_WIDGET (child))
egg_paned_append (EGG_PANED (buildable), GTK_WIDGET (child));
else
g_warning ("Cannot add child of type %s to %s",
G_OBJECT_TYPE_NAME (child),
G_OBJECT_TYPE_NAME (buildable));
}
static void
buildable_iface_init (GtkBuildableIface *iface)
{
iface->add_child = egg_paned_add_child;
}

View File

@ -1,40 +0,0 @@
/* egg-resizer.h
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define EGG_TYPE_RESIZER (egg_resizer_get_type())
G_DECLARE_FINAL_TYPE (EggResizer, egg_resizer, EGG, RESIZER, GtkWidget)
GtkWidget *egg_resizer_new (GtkPositionType position);
GtkPositionType egg_resizer_get_position (EggResizer *self);
void egg_resizer_set_position (EggResizer *self,
GtkPositionType position);
GtkWidget *egg_resizer_get_child (EggResizer *self);
void egg_resizer_set_child (EggResizer *self,
GtkWidget *child);
GtkWidget *egg_resizer_get_handle (EggResizer *self);
G_END_DECLS

View File

@ -1,495 +0,0 @@
/* egg-resizer.c
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file 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 3 of the License, or (at your option)
* any later version.
*
* This file 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 General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "config.h"
#include "egg-handle-private.h"
#include "egg-resizer-private.h"
#define HANDLE_SIZE 8
struct _EggResizer
{
GtkWidget parent_instance;
EggHandle *handle;
GtkWidget *child;
double drag_orig_size;
double drag_position;
GtkPositionType position : 3;
};
G_DEFINE_TYPE (EggResizer, egg_resizer, GTK_TYPE_WIDGET)
enum {
PROP_0,
PROP_CHILD,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
egg_resizer_drag_begin_cb (EggResizer *self,
double start_x,
double start_y,
GtkGestureDrag *drag)
{
GtkAllocation child_alloc;
GtkAllocation handle_alloc;
g_assert (EGG_IS_RESIZER (self));
g_assert (GTK_IS_GESTURE_DRAG (drag));
if (self->child == NULL)
return;
switch (self->position)
{
case GTK_POS_LEFT:
if (start_x > gtk_widget_get_width (GTK_WIDGET (self)) - HANDLE_SIZE)
goto start_drag;
break;
case GTK_POS_RIGHT:
if (start_x <= HANDLE_SIZE)
goto start_drag;
break;
case GTK_POS_TOP:
if (start_y > gtk_widget_get_height (GTK_WIDGET (self)) - HANDLE_SIZE)
goto start_drag;
break;
case GTK_POS_BOTTOM:
if (start_y <= HANDLE_SIZE)
goto start_drag;
break;
default:
g_assert_not_reached ();
break;
}
gtk_gesture_set_state (GTK_GESTURE (drag),
GTK_EVENT_SEQUENCE_DENIED);
return;
start_drag:
gtk_widget_get_allocation (self->child, &child_alloc);
gtk_widget_get_allocation (GTK_WIDGET (self->handle), &handle_alloc);
if (self->position == GTK_POS_LEFT ||
self->position == GTK_POS_RIGHT)
{
self->drag_orig_size = child_alloc.width + handle_alloc.width;
gtk_widget_set_hexpand (self->child, FALSE);
}
else
{
self->drag_orig_size = child_alloc.height + handle_alloc.height;
gtk_widget_set_vexpand (self->child, FALSE);
}
self->drag_position = self->drag_orig_size;
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
egg_resizer_drag_update_cb (EggResizer *self,
double offset_x,
double offset_y,
GtkGestureDrag *drag)
{
g_assert (EGG_IS_RESIZER (self));
g_assert (GTK_IS_GESTURE_DRAG (drag));
if (self->position == GTK_POS_LEFT)
self->drag_position = self->drag_orig_size + offset_x;
else if (self->position == GTK_POS_RIGHT)
self->drag_position = gtk_widget_get_width (GTK_WIDGET (self)) - offset_x;
else if (self->position == GTK_POS_TOP)
self->drag_position = self->drag_orig_size + offset_y;
else if (self->position == GTK_POS_BOTTOM)
self->drag_position = gtk_widget_get_height (GTK_WIDGET (self)) - offset_y;
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
egg_resizer_drag_end_cb (EggResizer *self,
double offset_x,
double offset_y,
GtkGestureDrag *drag)
{
g_assert (EGG_IS_RESIZER (self));
g_assert (GTK_IS_GESTURE_DRAG (drag));
}
GtkWidget *
egg_resizer_new (GtkPositionType position)
{
EggResizer *self;
self = g_object_new (EGG_TYPE_RESIZER, NULL);
self->position = position;
self->handle = EGG_HANDLE (egg_handle_new (position));
gtk_widget_set_parent (GTK_WIDGET (self->handle), GTK_WIDGET (self));
return GTK_WIDGET (self);
}
static void
egg_resizer_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
EggResizer *self = (EggResizer *)widget;
g_assert (EGG_IS_RESIZER (self));
*minimum = 0;
*natural = 0;
*minimum_baseline = -1;
*natural_baseline = -1;
if (self->child != NULL)
gtk_widget_measure (self->child,
orientation,
for_size,
minimum, natural,
NULL, NULL);
if ((orientation == GTK_ORIENTATION_HORIZONTAL &&
(self->position == GTK_POS_LEFT ||
self->position == GTK_POS_RIGHT)) ||
(orientation == GTK_ORIENTATION_VERTICAL &&
(self->position == GTK_POS_TOP ||
self->position == GTK_POS_BOTTOM)))
{
int handle_min, handle_nat;
if (self->drag_position != 0)
{
if (self->drag_position > *minimum)
*natural = self->drag_position;
else if (self->drag_position < *minimum)
*natural = *minimum;
}
if (gtk_widget_get_visible (GTK_WIDGET (self->handle)))
{
gtk_widget_measure (GTK_WIDGET (self->handle),
orientation, for_size,
&handle_min, &handle_nat,
NULL, NULL);
*minimum += handle_min;
*natural += handle_nat;
}
}
}
static void
egg_resizer_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
EggResizer *self = (EggResizer *)widget;
GtkOrientation orientation;
GtkAllocation child_alloc;
GtkAllocation handle_alloc;
int handle_min = 0, handle_nat = 0;
g_assert (EGG_IS_RESIZER (self));
if (self->position == GTK_POS_LEFT ||
self->position == GTK_POS_RIGHT)
orientation = GTK_ORIENTATION_HORIZONTAL;
else
orientation = GTK_ORIENTATION_VERTICAL;
if (gtk_widget_get_visible (GTK_WIDGET (self->handle)))
gtk_widget_measure (GTK_WIDGET (self->handle),
orientation,
-1,
&handle_min, &handle_nat,
NULL, NULL);
switch (self->position)
{
case GTK_POS_LEFT:
handle_alloc.x = width - handle_min;
handle_alloc.width = handle_min;
handle_alloc.y = 0;
handle_alloc.height = height;
child_alloc.x = 0;
child_alloc.y = 0;
child_alloc.width = width - handle_min;
child_alloc.height = height;
break;
case GTK_POS_RIGHT:
handle_alloc.x = 0;
handle_alloc.width = handle_min;
handle_alloc.y = 0;
handle_alloc.height = height;
child_alloc.x = handle_min;
child_alloc.y = 0;
child_alloc.width = width - handle_min;
child_alloc.height = height;
break;
case GTK_POS_TOP:
handle_alloc.x = 0;
handle_alloc.width = width;
handle_alloc.y = height - handle_min;
handle_alloc.height = handle_min;
child_alloc.x = 0;
child_alloc.y = 0;
child_alloc.width = width;
child_alloc.height = height - handle_min;
break;
case GTK_POS_BOTTOM:
handle_alloc.x = 0;
handle_alloc.width = width;
handle_alloc.y = 0;
handle_alloc.height = handle_min;
child_alloc.x = 0;
child_alloc.y = handle_min;
child_alloc.width = width;
child_alloc.height = height - handle_min;
break;
default:
g_assert_not_reached ();
}
if (gtk_widget_get_mapped (GTK_WIDGET (self->handle)))
gtk_widget_size_allocate (GTK_WIDGET (self->handle), &handle_alloc, -1);
if (self->child != NULL &&
gtk_widget_get_mapped (self->child))
gtk_widget_size_allocate (self->child, &child_alloc, -1);
}
static void
egg_resizer_compute_expand (GtkWidget *widget,
gboolean *hexpand,
gboolean *vexpand)
{
EggResizer *self = EGG_RESIZER (widget);
if (self->child != NULL)
{
*hexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_HORIZONTAL);
*vexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_VERTICAL);
}
else
{
*hexpand = FALSE;
*vexpand = FALSE;
}
}
static void
egg_resizer_dispose (GObject *object)
{
EggResizer *self = (EggResizer *)object;
if (self->handle)
gtk_widget_unparent (GTK_WIDGET (self->handle));
self->handle = NULL;
if (self->child)
gtk_widget_unparent (self->child);
self->child = NULL;
G_OBJECT_CLASS (egg_resizer_parent_class)->dispose (object);
}
static void
egg_resizer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EggResizer *self = EGG_RESIZER (object);
switch (prop_id)
{
case PROP_CHILD:
g_value_set_object (value, egg_resizer_get_child (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
egg_resizer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EggResizer *self = EGG_RESIZER (object);
switch (prop_id)
{
case PROP_CHILD:
egg_resizer_set_child (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
egg_resizer_class_init (EggResizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = egg_resizer_dispose;
object_class->get_property = egg_resizer_get_property;
object_class->set_property = egg_resizer_set_property;
widget_class->compute_expand = egg_resizer_compute_expand;
widget_class->measure = egg_resizer_measure;
widget_class->size_allocate = egg_resizer_size_allocate;
properties [PROP_CHILD] =
g_param_spec_object ("child",
"Child",
"Child",
GTK_TYPE_WIDGET,
(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_css_name (widget_class, "eggresizer");
}
static void
egg_resizer_init (EggResizer *self)
{
GtkGesture *gesture;
gesture = gtk_gesture_drag_new ();
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
g_signal_connect_object (gesture,
"drag-begin",
G_CALLBACK (egg_resizer_drag_begin_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (gesture,
"drag-update",
G_CALLBACK (egg_resizer_drag_update_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (gesture,
"drag-end",
G_CALLBACK (egg_resizer_drag_end_cb),
self,
G_CONNECT_SWAPPED);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
}
/**
* egg_resizer_get_child:
* @self: a #EggResizer
*
* Gets the child widget of the resizer.
*
* Returns: (transfer none) (nullable): A #GtkWidget or %NULL
*/
GtkWidget *
egg_resizer_get_child (EggResizer *self)
{
g_return_val_if_fail (EGG_IS_RESIZER (self), NULL);
return self->child;
}
void
egg_resizer_set_child (EggResizer *self,
GtkWidget *child)
{
g_return_if_fail (EGG_IS_RESIZER (self));
g_return_if_fail (!child || GTK_IS_WIDGET (child));
if (child == self->child)
return;
g_clear_pointer (&self->child, gtk_widget_unparent);
self->child = child;
if (self->child != NULL)
gtk_widget_insert_before (self->child,
GTK_WIDGET (self),
GTK_WIDGET (self->handle));
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CHILD]);
}
GtkPositionType
egg_resizer_get_position (EggResizer *self)
{
g_return_val_if_fail (EGG_IS_RESIZER (self), 0);
return self->position;
}
void
egg_resizer_set_position (EggResizer *self,
GtkPositionType position)
{
g_return_if_fail (EGG_IS_RESIZER (self));
if (position != self->position)
{
self->position = position;
egg_handle_set_position (self->handle, position);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
}
GtkWidget *
egg_resizer_get_handle (EggResizer *self)
{
g_return_val_if_fail (EGG_IS_RESIZER (self), NULL);
return GTK_WIDGET (self->handle);
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<gresources>
<gresource prefix="/org/gnome/sysprof">
<file compressed="true">css/SysprofEnvironEditor-shared.css</file>
<file compressed="true">css/SysprofDisplay-shared.css</file>
<file compressed="true">css/SysprofProfilerAssistant-shared.css</file>
<!-- Application icons -->
<file alias="icons/scalable/apps/org.gnome.Sysprof.svg">../../data/icons/org.gnome.Sysprof.svg</file>
<file alias="icons/scalable/apps/org.gnome.Sysprof-symbolic.svg">../../data/icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg</file>
<file alias="icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg">../../data/icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg</file>
</gresource>
<gresource prefix="/org/gnome/sysprof/ui">
<file preprocess="xml-stripblanks">sysprof-aid-icon.ui</file>
<file preprocess="xml-stripblanks">sysprof-callgraph-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-details-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-display.ui</file>
<file preprocess="xml-stripblanks">sysprof-environ-editor-row.ui</file>
<file preprocess="xml-stripblanks">sysprof-failed-state-view.ui</file>
<file preprocess="xml-stripblanks">sysprof-logs-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-marks-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-memprof-page.ui</file>
<file preprocess="xml-stripblanks">sysprof-process-model-row.ui</file>
<file preprocess="xml-stripblanks">sysprof-profiler-assistant.ui</file>
<file preprocess="xml-stripblanks">sysprof-recording-state-view.ui</file>
<file preprocess="xml-stripblanks">sysprof-tab.ui</file>
<file preprocess="xml-stripblanks">sysprof-visualizers-frame.ui</file>
</gresource>
</gresources>

View File

@ -1,136 +0,0 @@
libsysprof_ui_public_sources = [
'sysprof-check.c',
'sysprof-display.c',
'sysprof-model-filter.c',
'sysprof-notebook.c',
'sysprof-page.c',
'sysprof-process-model-row.c',
'sysprof-visualizer.c',
'sysprof-visualizer-group.c',
'sysprof-zoom-manager.c',
]
libsysprof_ui_private_sources = [
'egg-handle.c',
'egg-paned.c',
'egg-resizer.c',
'pointcache.c',
'rectangles.c',
'sysprof-aid.c',
'sysprof-aid-icon.c',
'sysprof-battery-aid.c',
'sysprof-cairo.c',
'sysprof-callgraph-aid.c',
'sysprof-callgraph-page.c',
'sysprof-cell-renderer-duration.c',
'sysprof-cell-renderer-percent.c',
'sysprof-cell-renderer-progress.c',
'sysprof-color-cycle.c',
'sysprof-counters-aid.c',
'sysprof-cpu-aid.c',
'sysprof-depth-visualizer.c',
'sysprof-details-page.c',
'sysprof-diskstat-aid.c',
'sysprof-display.c',
'sysprof-duplex-visualizer.c',
'sysprof-environ.c',
'sysprof-environ-editor.c',
'sysprof-environ-editor-row.c',
'sysprof-environ-variable.c',
'sysprof-failed-state-view.c',
'sysprof-line-visualizer.c',
'sysprof-log-model.c',
'sysprof-logs-aid.c',
'sysprof-logs-page.c',
'sysprof-mark-detail.c',
'sysprof-marks-aid.c',
'sysprof-marks-model.c',
'sysprof-marks-page.c',
'sysprof-mark-visualizer.c',
'sysprof-memory-aid.c',
'sysprof-memprof-aid.c',
'sysprof-memprof-page.c',
'sysprof-memprof-visualizer.c',
'sysprof-netdev-aid.c',
'sysprof-procs-visualizer.c',
'sysprof-profiler-assistant.c',
'sysprof-proxy-aid.c',
'sysprof-rapl-aid.c',
'sysprof-recording-state-view.c',
'sysprof-scrollmap.c',
'sysprof-tab.c',
'sysprof-theme-manager.c',
'sysprof-time-label.c',
'sysprof-time-visualizer.c',
'sysprof-visualizer-group-header.c',
'sysprof-visualizers-frame.c',
'sysprof-visualizer-ticks.c',
'../stackstash.c',
]
libsysprof_ui_public_headers = [
'sysprof-check.h',
'sysprof-display.h',
'sysprof-model-filter.h',
'sysprof-notebook.h',
'sysprof-page.h',
'sysprof-process-model-row.h',
'sysprof-visualizer.h',
'sysprof-visualizer-group.h',
'sysprof-zoom-manager.h',
'sysprof-ui.h',
]
libsysprof_ui_resources = gnome.compile_resources(
'libsysprof-ui-resources',
'libsysprof-ui.gresource.xml',
c_name: 'lisysprof_ui',
)
# Subset of dependencies used in generating the pkg-config file
libsysprof_ui_pkg_deps = [
dependency('gio-2.0', version: glib_req_version),
dependency('gtk4', version: gtk_req_version),
dependency('libadwaita-1'),
]
libsysprof_ui_deps = libsysprof_ui_pkg_deps + [
libsysprof_dep,
]
# Meson's pkgconfig module wants to see a library here, not an internal
# dependency object
libsysprof_ui_pkg_deps += libsysprof
libsysprof_ui = shared_library(
'sysprof-ui-@0@'.format(libsysprof_ui_api_version),
libsysprof_ui_public_sources + libsysprof_ui_private_sources + libsysprof_ui_resources,
dependencies: libsysprof_ui_deps + [librax_dep],
install_dir: get_option('libdir'),
install: true,
c_args: [ '-DSYSPROF_UI_COMPILATION' ],
gnu_symbol_visibility: 'hidden',
)
libsysprof_ui_dep = declare_dependency(
link_with: libsysprof_ui,
dependencies: libsysprof_ui_deps,
include_directories: include_directories('.'),
)
meson.override_dependency('sysprof-ui-@0@'.format(libsysprof_api_version), libsysprof_ui_dep)
pkgconfig.generate(
libsysprof_ui,
subdirs: [ sysprof_ui_header_subdir ],
description: 'The UI library for GTK applications embedding sysprof',
install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
requires: [ 'gio-2.0', 'gtk4' ],
libraries_private: libsysprof_ui_pkg_deps,
variables: [
'datadir=' + datadir_for_pc_file,
],
)
install_headers(libsysprof_ui_public_headers, subdir: sysprof_ui_header_subdir)

View File

@ -1,116 +0,0 @@
/* 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_finalize (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 != NULL, NULL);
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 != NULL);
g_return_if_fail (self->ref_count > 0);
if (g_atomic_int_dec_and_test (&self->ref_count))
point_cache_finalize (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;
*n_points = 0;
if ((ar = g_hash_table_lookup (self->sets, GUINT_TO_POINTER (set_id))))
{
*n_points = ar->len;
return &g_array_index (ar, const Point, 0);
}
return NULL;
}

View File

@ -1,52 +0,0 @@
/* 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
*/
#pragma once
#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

View File

@ -1,248 +0,0 @@
/* 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 "sysprof-color-cycle.h"
#include "sysprof-visualizer.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;
SysprofColorCycle *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 = sysprof_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);
sysprof_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;
sysprof_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_memdup2 (&rgba, sizeof rgba));
}
}
self->sorted = TRUE;
}
void
rectangles_draw (Rectangles *self,
GtkWidget *row,
cairo_t *cr)
{
GtkAllocation alloc;
gdouble range;
guint ns;
g_assert (self != NULL);
g_assert (SYSPROF_IS_VISUALIZER (row));
g_assert (cr != NULL);
if (!self->sorted)
rectangles_sort (self);
gtk_widget_get_allocation (row, &alloc);
ns = g_hash_table_size (self->y_indexes);
if (ns == 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));
SysprofVisualizerRelativePoint in_points[2];
SysprofVisualizerAbsolutePoint out_points[2];
GdkRectangle r;
GdkRGBA *rgba;
g_assert (y_index > 0);
g_assert (y_index <= ns);
in_points[0].x = (rect->begin - self->begin_time) / range;
in_points[0].y = (y_index - 1) / (gdouble)ns;
in_points[1].x = (rect->end - self->begin_time) / range;
in_points[1].y = 0;
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (row),
in_points, G_N_ELEMENTS (in_points),
out_points, G_N_ELEMENTS (out_points));
r.height = alloc.height / (gdouble)ns;
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;
}

View File

@ -1,48 +0,0 @@
/* 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

View File

@ -1,211 +0,0 @@
/* sysprof-aid-icon.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-aid-icon"
#include "config.h"
#include "sysprof-aid-icon.h"
struct _SysprofAidIcon
{
GtkFlowBoxChild parent_instance;
SysprofAid *aid;
/* Template Objects */
GtkLabel *label;
GtkImage *image;
GtkImage *check;
};
G_DEFINE_TYPE (SysprofAidIcon, sysprof_aid_icon, GTK_TYPE_FLOW_BOX_CHILD)
enum {
PROP_0,
PROP_AID,
PROP_SELECTED,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
/**
* sysprof_aid_icon_new:
*
* Create a new #SysprofAidIcon.
*
* Returns: (transfer full): a newly created #SysprofAidIcon
*/
GtkWidget *
sysprof_aid_icon_new (SysprofAid *aid)
{
g_return_val_if_fail (SYSPROF_IS_AID (aid), NULL);
return g_object_new (SYSPROF_TYPE_AID_ICON,
"aid", aid,
NULL);
}
gboolean
sysprof_aid_icon_is_selected (SysprofAidIcon *self)
{
g_return_val_if_fail (SYSPROF_IS_AID_ICON (self), FALSE);
return gtk_widget_get_visible (GTK_WIDGET (self->check));
}
/**
* sysprof_aid_icon_get_aid:
*
* Get the aid that is represented by the icon.
*
* Returns: (transfer none): a #SysprofAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_aid_icon_get_aid (SysprofAidIcon *self)
{
g_return_val_if_fail (SYSPROF_IS_AID_ICON (self), NULL);
return self->aid;
}
static void
sysprof_aid_icon_set_aid (SysprofAidIcon *self,
SysprofAid *aid)
{
g_return_if_fail (SYSPROF_IS_AID_ICON (self));
g_return_if_fail (SYSPROF_IS_AID (aid));
if (g_set_object (&self->aid, aid))
{
GIcon *icon = sysprof_aid_get_icon (aid);
const gchar *title = sysprof_aid_get_display_name (aid);
g_object_set (self->image, "gicon", icon, NULL);
gtk_label_set_label (self->label, title);
}
}
static void
sysprof_aid_icon_finalize (GObject *object)
{
SysprofAidIcon *self = (SysprofAidIcon *)object;
g_clear_object (&self->aid);
G_OBJECT_CLASS (sysprof_aid_icon_parent_class)->finalize (object);
}
static void
sysprof_aid_icon_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofAidIcon *self = SYSPROF_AID_ICON (object);
switch (prop_id)
{
case PROP_AID:
g_value_set_object (value, sysprof_aid_icon_get_aid (self));
break;
case PROP_SELECTED:
g_value_set_boolean (value, gtk_widget_get_visible (GTK_WIDGET (self->check)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_aid_icon_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofAidIcon *self = SYSPROF_AID_ICON (object);
switch (prop_id)
{
case PROP_AID:
sysprof_aid_icon_set_aid (self, g_value_get_object (value));
break;
case PROP_SELECTED:
gtk_widget_set_visible (GTK_WIDGET (self->check), g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_aid_icon_class_init (SysprofAidIconClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = sysprof_aid_icon_finalize;
object_class->get_property = sysprof_aid_icon_get_property;
object_class->set_property = sysprof_aid_icon_set_property;
properties [PROP_AID] =
g_param_spec_object ("aid",
"Aid",
"The aid for the icon",
SYSPROF_TYPE_AID,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties [PROP_SELECTED] =
g_param_spec_boolean ("selected",
"Selected",
"If the item is selected",
FALSE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_css_name (widget_class, "sysprofaidicon");
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-aid-icon.ui");
gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, check);
gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, image);
gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, label);
}
static void
sysprof_aid_icon_init (SysprofAidIcon *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
void
sysprof_aid_icon_toggle (SysprofAidIcon *self)
{
g_return_if_fail (SYSPROF_IS_AID_ICON (self));
gtk_widget_set_visible (GTK_WIDGET (self->check),
!gtk_widget_get_visible (GTK_WIDGET (self->check)));
}

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="SysprofAidIcon" parent="GtkFlowBoxChild">
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkOverlay">
<property name="halign">center</property>
<property name="child">
<object class="GtkImage" id="image">
<property name="pixel-size">48</property>
<property name="width-request">64</property>
<property name="icon-name">org.gnome.Sysprof-symbolic</property>
</object>
</property>
<child type="overlay">
<object class="GtkImage" id="check">
<property name="pixel-size">14</property>
<property name="icon-name">object-select-symbolic</property>
<property name="halign">end</property>
<property name="valign">start</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="use-underline">1</property>
<property name="justify">center</property>
<property name="wrap">1</property>
<property name="mnemonic-widget">SysprofAidIcon</property>
<property name="max-width-chars">12</property>
</object>
</child>
</object>
</property>
</template>
</interface>

View File

@ -1,353 +0,0 @@
/* sysprof-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-aid"
#include "config.h"
#include <gtk/gtk.h>
#include "sysprof-aid.h"
typedef struct
{
GPtrArray *sources;
gchar *display_name;
GIcon *icon;
} SysprofAidPrivate;
static void buildable_iface_init (GtkBuildableIface *iface);
G_DEFINE_TYPE_WITH_CODE (SysprofAid, sysprof_aid, G_TYPE_OBJECT,
G_ADD_PRIVATE (SysprofAid)
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
enum {
PROP_0,
PROP_DISPLAY_NAME,
PROP_ICON,
PROP_ICON_NAME,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
sysprof_aid_real_present_async (SysprofAid *self,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_task_report_new_error (self, callback, user_data,
sysprof_aid_real_present_async,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Not supported");
}
static gboolean
sysprof_aid_real_present_finish (SysprofAid *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
sysprof_aid_finalize (GObject *object)
{
SysprofAid *self = (SysprofAid *)object;
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_clear_pointer (&priv->sources, g_ptr_array_unref);
g_clear_pointer (&priv->display_name, g_free);
g_clear_object (&priv->icon);
G_OBJECT_CLASS (sysprof_aid_parent_class)->finalize (object);
}
static void
sysprof_aid_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofAid *self = SYSPROF_AID (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
g_value_set_string (value, sysprof_aid_get_display_name (self));
break;
case PROP_ICON:
g_value_set_object (value, sysprof_aid_get_icon (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_aid_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofAid *self = SYSPROF_AID (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
sysprof_aid_set_display_name (self, g_value_get_string (value));
break;
case PROP_ICON:
sysprof_aid_set_icon (self, g_value_get_object (value));
break;
case PROP_ICON_NAME:
sysprof_aid_set_icon_name (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_aid_class_init (SysprofAidClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_aid_finalize;
object_class->get_property = sysprof_aid_get_property;
object_class->set_property = sysprof_aid_set_property;
klass->present_async = sysprof_aid_real_present_async;
klass->present_finish = sysprof_aid_real_present_finish;
properties [PROP_DISPLAY_NAME] =
g_param_spec_string ("display-name",
"Display Name",
"Display Name",
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"Icon Name",
"Icon Name",
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_ICON] =
g_param_spec_object ("icon",
"Icon",
"The icon to display",
G_TYPE_ICON,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_aid_init (SysprofAid *self G_GNUC_UNUSED)
{
}
/**
* sysprof_aid_get_display_name:
* @self: a #SysprofAid
*
* Gets the display name as it should be shown to the user.
*
* Returns: a string containing the display name
*
* Since: 3.34
*/
const gchar *
sysprof_aid_get_display_name (SysprofAid *self)
{
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_return_val_if_fail (SYSPROF_IS_AID (self), NULL);
return priv->display_name;
}
/**
* sysprof_aid_get_icon:
*
* Gets the icon for the aid.
*
* Returns: (transfer none) (nullable): a #GIcon or %NULL
*
* Since: 3.34
*/
GIcon *
sysprof_aid_get_icon (SysprofAid *self)
{
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_return_val_if_fail (SYSPROF_IS_AID (self), NULL);
return priv->icon;
}
void
sysprof_aid_set_icon (SysprofAid *self,
GIcon *icon)
{
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_AID (self));
if (g_set_object (&priv->icon, icon))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON]);
}
void
sysprof_aid_set_icon_name (SysprofAid *self,
const gchar *icon_name)
{
g_autoptr(GIcon) icon = NULL;
g_return_if_fail (SYSPROF_IS_AID (self));
if (icon_name != NULL)
icon = g_themed_icon_new (icon_name);
sysprof_aid_set_icon (self, icon);
}
void
sysprof_aid_set_display_name (SysprofAid *self,
const gchar *display_name)
{
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_AID (self));
if (g_strcmp0 (display_name, priv->display_name) != 0)
{
g_free (priv->display_name);
priv->display_name = g_strdup (display_name);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
}
}
void
sysprof_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_AID (self));
g_return_if_fail (SYSPROF_IS_PROFILER (profiler));
if (priv->sources != NULL)
{
for (guint i = 0; i < priv->sources->len; i++)
{
SysprofSource *source = g_ptr_array_index (priv->sources, i);
sysprof_profiler_add_source (profiler, source);
}
if (priv->sources->len > 0)
g_ptr_array_remove_range (priv->sources, 0, priv->sources->len);
}
if (SYSPROF_AID_GET_CLASS (self)->prepare)
SYSPROF_AID_GET_CLASS (self)->prepare (self, profiler);
}
static void
sysprof_aid_add_child (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *object,
const gchar *type G_GNUC_UNUSED)
{
SysprofAid *self = (SysprofAid *)buildable;
SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self);
g_assert (SYSPROF_IS_AID (self));
g_assert (GTK_IS_BUILDER (builder));
g_assert (G_IS_OBJECT (object));
if (SYSPROF_IS_SOURCE (object))
{
if (priv->sources == NULL)
priv->sources = g_ptr_array_new_with_free_func (g_object_unref);
g_ptr_array_add (priv->sources, g_object_ref (object));
return;
}
g_warning ("Unsupported child type of %s: %s",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (object));
}
static void
buildable_iface_init (GtkBuildableIface *iface)
{
iface->add_child = sysprof_aid_add_child;
}
SysprofAid *
sysprof_aid_new (const gchar *display_name,
const gchar *icon_name)
{
return g_object_new (SYSPROF_TYPE_AID,
"display-aid", display_name,
"icon-name", icon_name,
NULL);
}
void
sysprof_aid_present_async (SysprofAid *self,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (SYSPROF_IS_AID (self));
g_return_if_fail (reader != NULL);
g_return_if_fail (SYSPROF_IS_DISPLAY (display));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
SYSPROF_AID_GET_CLASS (self)->present_async (self, reader, display, cancellable, callback, user_data);
}
gboolean
sysprof_aid_present_finish (SysprofAid *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_AID (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return SYSPROF_AID_GET_CLASS (self)->present_finish (self, result, error);
}

View File

@ -1,76 +0,0 @@
/* sysprof-aid.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
#include <sysprof.h>
#include "sysprof-display.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_AID (sysprof_aid_get_type())
G_DECLARE_DERIVABLE_TYPE (SysprofAid, sysprof_aid, SYSPROF, AID, GObject)
struct _SysprofAidClass
{
GObjectClass parent_class;
void (*prepare) (SysprofAid *self,
SysprofProfiler *profiler);
void (*present_async) (SysprofAid *self,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (*present_finish) (SysprofAid *self,
GAsyncResult *result,
GError **error);
/*< private >*/
gpointer _reserved[16];
};
SysprofAid *sysprof_aid_new (const gchar *display_name,
const gchar *icon_name);
const gchar *sysprof_aid_get_display_name (SysprofAid *self);
void sysprof_aid_set_display_name (SysprofAid *self,
const gchar *display_name);
GIcon *sysprof_aid_get_icon (SysprofAid *self);
void sysprof_aid_set_icon (SysprofAid *self,
GIcon *icon);
void sysprof_aid_set_icon_name (SysprofAid *self,
const gchar *icon_name);
void sysprof_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler);
void sysprof_aid_present_async (SysprofAid *self,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_aid_present_finish (SysprofAid *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -1,242 +0,0 @@
/* sysprof-battery-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-battery-aid"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-color-cycle.h"
#include "sysprof-battery-aid.h"
#include "sysprof-line-visualizer.h"
struct _SysprofBatteryAid
{
SysprofAid parent_instance;
};
typedef struct
{
SysprofCaptureCursor *cursor;
SysprofDisplay *display;
} Present;
G_DEFINE_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF_TYPE_AID)
static void
present_free (gpointer data)
{
Present *p = data;
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
g_clear_object (&p->display);
g_slice_free (Present, p);
}
/**
* sysprof_battery_aid_new:
*
* Create a new #SysprofBatteryAid.
*
* Returns: (transfer full): a newly created #SysprofBatteryAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_battery_aid_new (void)
{
return g_object_new (SYSPROF_TYPE_BATTERY_AID, NULL);
}
static void
sysprof_battery_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
#ifdef __linux__
g_autoptr(SysprofSource) source = NULL;
g_assert (SYSPROF_IS_BATTERY_AID (self));
g_assert (SYSPROF_IS_PROFILER (profiler));
source = sysprof_battery_source_new ();
sysprof_profiler_add_source (profiler, source);
#endif
}
static bool
collect_battery_counters (const SysprofCaptureFrame *frame,
gpointer user_data)
{
SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
GArray *counters = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
g_assert (counters != NULL);
for (guint i = 0; i < def->n_counters; i++)
{
const SysprofCaptureCounter *counter = &def->counters[i];
if (g_strcmp0 (counter->category, "Battery Charge") == 0)
g_array_append_vals (counters, counter, 1);
}
return TRUE;
}
static void
sysprof_battery_aid_present_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Present *present = task_data;
g_autoptr(GArray) counters = NULL;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_BATTERY_AID (source_object));
g_assert (present != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
sysprof_capture_cursor_foreach (present->cursor, collect_battery_counters, counters);
g_task_return_pointer (task,
g_steal_pointer (&counters),
(GDestroyNotify) g_array_unref);
}
static void
sysprof_battery_aid_present_async (SysprofAid *aid,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
g_autoptr(SysprofCaptureCondition) condition = NULL;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
Present present;
g_assert (SYSPROF_IS_BATTERY_AID (aid));
g_assert (reader != NULL);
g_assert (SYSPROF_IS_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
condition = sysprof_capture_condition_new_where_type_in (1, types);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
present.cursor = g_steal_pointer (&cursor);
present.display = g_object_ref (display);
task = g_task_new (aid, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_battery_aid_present_async);
g_task_set_task_data (task,
g_slice_dup (Present, &present),
present_free);
g_task_run_in_thread (task, sysprof_battery_aid_present_worker);
}
static gboolean
sysprof_battery_aid_present_finish (SysprofAid *aid,
GAsyncResult *result,
GError **error)
{
g_autoptr(GArray) counters = NULL;
Present *present;
g_assert (SYSPROF_IS_AID (aid));
g_assert (G_IS_TASK (result));
present = g_task_get_task_data (G_TASK (result));
if ((counters = g_task_propagate_pointer (G_TASK (result), error)))
{
g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
SysprofVisualizerGroup *group;
guint found = 0;
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"title", _("Battery Charge"),
"visible", TRUE,
NULL);
for (guint i = 0; i < counters->len; i++)
{
const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
if (g_strcmp0 (ctr->category, "Battery Charge") == 0)
{
g_autofree gchar *title = NULL;
gboolean is_combined = g_str_equal (ctr->name, "Combined");
GtkWidget *row;
GdkRGBA rgba;
if (is_combined)
title = g_strdup (_("Battery Charge (All)"));
else
title = g_strdup_printf ("Battery Charge (%s)", ctr->name);
row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", title,
"height-request", 35,
"visible", is_combined,
NULL);
sysprof_color_cycle_next (cycle, &rgba);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_visualizer_group_insert (group,
SYSPROF_VISUALIZER (row),
is_combined ? 0 : -1,
!is_combined);
found++;
}
}
if (found > 0)
sysprof_display_add_group (present->display, group);
else
g_object_unref (g_object_ref_sink (group));
}
return counters != NULL;
}
static void
sysprof_battery_aid_class_init (SysprofBatteryAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_battery_aid_prepare;
aid_class->present_async = sysprof_battery_aid_present_async;
aid_class->present_finish = sysprof_battery_aid_present_finish;
}
static void
sysprof_battery_aid_init (SysprofBatteryAid *self)
{
sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery"));
sysprof_aid_set_icon_name (SYSPROF_AID (self), "battery-low-charging-symbolic");
}

View File

@ -1,71 +0,0 @@
/* sysprof-cairo.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "sysprof-ui-private.h"
void
_sysprof_rounded_rectangle (cairo_t *cr,
const GdkRectangle *rect,
gint x_radius,
gint y_radius)
{
gint x;
gint y;
gint width;
gint height;
gint x1, x2;
gint y1, y2;
gint xr1, xr2;
gint yr1, yr2;
g_return_if_fail (cr);
g_return_if_fail (rect);
x = rect->x;
y = rect->y;
width = rect->width;
height = rect->height;
x1 = x;
x2 = x1 + width;
y1 = y;
y2 = y1 + height;
x_radius = MIN (x_radius, width / 2.0);
y_radius = MIN (y_radius, width / 2.0);
xr1 = x_radius;
xr2 = x_radius / 2.0;
yr1 = y_radius;
yr2 = y_radius / 2.0;
cairo_move_to (cr, x1 + xr1, y1);
cairo_line_to (cr, x2 - xr1, y1);
cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1);
cairo_line_to (cr, x2, y2 - yr1);
cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2);
cairo_line_to (cr, x1 + xr1, y2);
cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1);
cairo_line_to (cr, x1, y1 + yr1);
cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1);
cairo_close_path (cr);
}

View File

@ -1,275 +0,0 @@
/* sysprof-callgraph-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-callgraph-aid"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-callgraph-aid.h"
#include "sysprof-callgraph-page.h"
#include "sysprof-depth-visualizer.h"
struct _SysprofCallgraphAid
{
SysprofAid parent_instance;
};
typedef struct
{
SysprofCaptureCursor *cursor;
SysprofDisplay *display;
guint has_samples : 1;
} Present;
G_DEFINE_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF_TYPE_AID)
static void
present_free (gpointer data)
{
Present *p = data;
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
g_clear_object (&p->display);
g_slice_free (Present, p);
}
static void
on_group_activated_cb (SysprofVisualizerGroup *group,
SysprofPage *page)
{
SysprofDisplay *display;
g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
g_assert (SYSPROF_IS_PAGE (page));
display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
sysprof_display_set_visible_page (display, page);
}
/**
* sysprof_callgraph_aid_new:
*
* Create a new #SysprofCallgraphAid.
*
* Returns: (transfer full): a newly created #SysprofCallgraphAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_callgraph_aid_new (void)
{
return g_object_new (SYSPROF_TYPE_CALLGRAPH_AID, NULL);
}
static void
sysprof_callgraph_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
g_assert (SYSPROF_IS_CALLGRAPH_AID (self));
g_assert (SYSPROF_IS_PROFILER (profiler));
#ifdef __linux__
{
const GPid *pids;
guint n_pids;
if ((pids = sysprof_profiler_get_pids (profiler, &n_pids)))
{
for (guint i = 0; i < n_pids; i++)
{
g_autoptr(SysprofSource) source = NULL;
source = sysprof_perf_source_new ();
sysprof_perf_source_set_target_pid (SYSPROF_PERF_SOURCE (source), pids[i]);
sysprof_profiler_add_source (profiler, source);
}
}
else
{
g_autoptr(SysprofSource) source = NULL;
source = sysprof_perf_source_new ();
sysprof_profiler_add_source (profiler, source);
}
}
#endif
}
static bool
discover_samples_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
Present *p = user_data;
g_assert (frame != NULL);
g_assert (p != NULL);
if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE)
{
p->has_samples = TRUE;
return FALSE;
}
return TRUE;
}
static void
sysprof_callgraph_aid_present_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Present *p = task_data;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CALLGRAPH_AID (source_object));
g_assert (p != NULL);
g_assert (p->cursor != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
/* If we find a sample frame, then we should enable the callgraph
* and stack visualizers.
*/
sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p);
g_task_return_boolean (task, TRUE);
}
static void
sysprof_callgraph_aid_present_async (SysprofAid *aid,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE };
g_autoptr(SysprofCaptureCondition) condition = NULL;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
Present present;
g_assert (SYSPROF_IS_CALLGRAPH_AID (aid));
g_assert (reader != NULL);
g_assert (SYSPROF_IS_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
condition = sysprof_capture_condition_new_where_type_in (1, types);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
present.cursor = g_steal_pointer (&cursor);
present.display = g_object_ref (display);
task = g_task_new (aid, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_callgraph_aid_present_async);
g_task_set_task_data (task,
g_slice_dup (Present, &present),
present_free);
g_task_run_in_thread (task, sysprof_callgraph_aid_present_worker);
}
static gboolean
sysprof_callgraph_aid_present_finish (SysprofAid *aid,
GAsyncResult *result,
GError **error)
{
Present *p;
g_assert (SYSPROF_IS_CALLGRAPH_AID (aid));
g_assert (G_IS_TASK (result));
p = g_task_get_task_data (G_TASK (result));
if (p->has_samples)
{
SysprofVisualizerGroup *group;
SysprofVisualizer *depth;
SysprofPage *page;
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"has-page", TRUE,
"priority", -500,
"title", _("Stack Traces"),
"visible", TRUE,
NULL);
depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_COMBINED);
g_object_set (depth,
"title", _("Stack Traces"),
"height-request", 35,
"visible", TRUE,
NULL);
sysprof_visualizer_group_insert (group, depth, 0, FALSE);
depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY);
g_object_set (depth,
"title", _("Stack Traces (In Kernel)"),
"height-request", 35,
"visible", FALSE,
NULL);
sysprof_visualizer_group_insert (group, depth, 1, TRUE);
depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_USER_ONLY);
g_object_set (depth,
"title", _("Stack Traces (In User)"),
"height-request", 35,
"visible", FALSE,
NULL);
sysprof_visualizer_group_insert (group, depth, 2, TRUE);
sysprof_display_add_group (p->display, group);
page = g_object_new (SYSPROF_TYPE_CALLGRAPH_PAGE,
"title", _("Callgraph"),
"vexpand", TRUE,
"visible", TRUE,
NULL);
sysprof_display_add_page (p->display, page);
sysprof_display_set_visible_page (p->display, page);
g_signal_connect_object (group,
"group-activated",
G_CALLBACK (on_group_activated_cb),
page,
0);
}
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
sysprof_callgraph_aid_class_init (SysprofCallgraphAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_callgraph_aid_prepare;
aid_class->present_async = sysprof_callgraph_aid_present_async;
aid_class->present_finish = sysprof_callgraph_aid_present_finish;
}
static void
sysprof_callgraph_aid_init (SysprofCallgraphAid *self)
{
sysprof_aid_set_display_name (SYSPROF_AID (self), _("Callgraph"));
sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic");
}

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/* sysprof-callgraph-page.h
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include <sysprof.h>
#include "sysprof-page.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_PAGE (sysprof_callgraph_page_get_type())
G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF, CALLGRAPH_PAGE, SysprofPage)
struct _SysprofCallgraphPageClass
{
SysprofPageClass parent_class;
void (*go_previous) (SysprofCallgraphPage *self);
/*< private >*/
gpointer _reserved[16];
};
GtkWidget *sysprof_callgraph_page_new (void);
SysprofCallgraphProfile *sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self);
void sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self,
SysprofCallgraphProfile *profile);
gchar *sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self);
guint sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self);
G_END_DECLS

View File

@ -1,212 +0,0 @@
<interface>
<template class="SysprofCallgraphPage" parent="SysprofPage">
<child>
<object class="GtkStack" id="stack">
<child>
<object class="EggPaned" id="callgraph">
<property name="orientation">horizontal</property>
<child>
<object class="EggPaned">
<property name="orientation">vertical</property>
<property name="width-request">400</property>
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">true</property>
<child>
<object class="GtkTreeView" id="functions_view">
<property name="fixed-height-mode">true</property>
<child>
<object class="GtkTreeViewColumn" id="function_name_column">
<property name="expand">true</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">0</property>
<property name="title" translatable="yes">Functions</property>
<child>
<object class="GtkCellRendererText">
<property name="ellipsize">middle</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="function_self_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">1</property>
<property name="title" translatable="yes">Self</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="function_total_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">2</property>
<property name="title" translatable="yes">Total</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">2</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">true</property>
<child>
<object class="GtkTreeView" id="callers_view">
<child>
<object class="GtkTreeViewColumn" id="callers_name_column">
<property name="expand">true</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">0</property>
<property name="title" translatable="yes">Callers</property>
<child>
<object class="GtkCellRendererText">
<property name="ellipsize">middle</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="callers_self_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">1</property>
<property name="title" translatable="yes">Self</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="callers_total_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">2</property>
<property name="title" translatable="yes">Total</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">2</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="hexpand">true</property>
<child>
<object class="GtkTreeView" id="descendants_view">
<child>
<object class="GtkTreeViewColumn" id="descendants_name_column">
<property name="expand">true</property>
<property name="sizing">autosize</property>
<property name="sort-column-id">0</property>
<property name="title" translatable="yes">Descendants</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="descendants_self_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">1</property>
<property name="title" translatable="yes">Self</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="descendants_total_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="sort-column-id">2</property>
<property name="title" translatable="yes">Total</property>
<child>
<object class="SysprofCellRendererPercent">
<property name="width">65</property>
</object>
<attributes>
<attribute name="percent">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="function_hits_column">
<property name="expand">false</property>
<property name="sizing">fixed</property>
<property name="title" translatable="yes">Hits</property>
<child>
<object class="GtkCellRendererText">
<property name="xalign">1.0</property>
</object>
<attributes>
<attribute name="text">4</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwStatusPage" id="loading_state">
<property name="icon-name">content-loading-symbolic</property>
<property name="title" translatable="yes">Generating Callgraph</property>
<property name="description" translatable="yes">Sysprof is busy creating the selected callgraph.</property>
</object>
</child>
<child>
<object class="AdwStatusPage" id="empty_state">
<property name="icon-name">computer-fail-symbolic</property>
<property name="title" translatable="yes">Not Enough Samples</property>
<property name="description" translatable="yes">More samples are necessary to display a callgraph.</property>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -1,455 +0,0 @@
/* sysprof-cell-renderer-duration.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-cell-renderer-duration"
#include "config.h"
#include "sysprof-cell-renderer-duration.h"
#include "sysprof-ui-private.h"
#include "sysprof-zoom-manager.h"
typedef struct
{
gint64 capture_begin_time;
gint64 capture_end_time;
gint64 capture_duration;
gint64 begin_time;
gint64 end_time;
gchar *text;
SysprofZoomManager *zoom_manager;
GdkRGBA color;
guint color_set : 1;
} SysprofCellRendererDurationPrivate;
enum {
PROP_0,
PROP_BEGIN_TIME,
PROP_CAPTURE_BEGIN_TIME,
PROP_CAPTURE_END_TIME,
PROP_COLOR,
PROP_END_TIME,
PROP_TEXT,
PROP_ZOOM_MANAGER,
N_PROPS
};
G_DEFINE_TYPE_WITH_PRIVATE (SysprofCellRendererDuration, sysprof_cell_renderer_duration, GTK_TYPE_CELL_RENDERER)
static GParamSpec *properties [N_PROPS];
static inline void
rounded_rectangle (cairo_t *cr,
const GdkRectangle *rect,
int x_radius,
int y_radius)
{
int x;
int y;
int width;
int height;
int x1, x2;
int y1, y2;
int xr1, xr2;
int yr1, yr2;
g_assert (cr);
g_assert (rect);
x = rect->x;
y = rect->y;
width = rect->width;
height = rect->height;
x1 = x;
x2 = x1 + width;
y1 = y;
y2 = y1 + height;
x_radius = MIN (x_radius, width / 2.0);
y_radius = MIN (y_radius, width / 2.0);
xr1 = x_radius;
xr2 = x_radius / 2.0;
yr1 = y_radius;
yr2 = y_radius / 2.0;
cairo_move_to (cr, x1 + xr1, y1);
cairo_line_to (cr, x2 - xr1, y1);
cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1);
cairo_line_to (cr, x2, y2 - yr1);
cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2);
cairo_line_to (cr, x1 + xr1, y2);
cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1);
cairo_line_to (cr, x1, y1 + yr1);
cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1);
cairo_close_path (cr);
}
static void
sysprof_cell_renderer_duration_snapshot (GtkCellRenderer *renderer,
GtkSnapshot *snapshot,
GtkWidget *widget,
const GdkRectangle *bg_area,
const GdkRectangle *cell_area,
GtkCellRendererState state)
{
SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)renderer;
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
g_autoptr(GString) str = NULL;
GtkStyleContext *style_context;
cairo_t *cr;
gdouble x1, x2;
GdkRGBA rgba;
GdkRectangle r;
gint64 duration;
g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (self));
g_assert (snapshot != NULL);
g_assert (GTK_IS_WIDGET (widget));
if (priv->zoom_manager == NULL)
return;
cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (cell_area->x, cell_area->y, cell_area->width, cell_area->height));
style_context = gtk_widget_get_style_context (widget);
if (priv->color_set)
rgba = priv->color;
else
gtk_style_context_get_color (style_context, &rgba);
duration = sysprof_zoom_manager_get_duration_for_width (priv->zoom_manager, bg_area->width);
x1 = (priv->begin_time - priv->capture_begin_time) / (gdouble)duration * cell_area->width;
x2 = (priv->end_time - priv->capture_begin_time) / (gdouble)duration * cell_area->width;
if (x2 < x1)
x2 = x1;
r.x = cell_area->x + x1;
r.height = 12;
r.y = cell_area->y + (cell_area->height - r.height) / 2;
r.width = MAX (1.0, x2 - x1);
if ((cell_area->height - r.height) % 2 == 1)
r.height++;
gdk_cairo_set_source_rgba (cr, &rgba);
if (r.width > 3)
{
rounded_rectangle (cr, &r, 2, 2);
cairo_fill (cr);
}
else if (r.width > 1)
{
gdk_cairo_rectangle (cr, &r);
cairo_fill (cr);
}
else
{
cairo_set_line_width (cr, 1);
cairo_move_to (cr, r.x + .5, r.y);
cairo_line_to (cr, r.x + .5, r.y + r.height);
cairo_stroke (cr);
}
str = g_string_new (NULL);
if (priv->begin_time != priv->end_time)
{
g_autofree gchar *fmt = _sysprof_format_duration (priv->end_time - priv->begin_time);
g_string_append_printf (str, "%s — ", fmt);
}
if (priv->text != NULL)
g_string_append (str, priv->text);
if (str->len)
{
PangoLayout *layout;
gint w, h;
/* Add some spacing before/after */
r.x -= 24;
r.width += 48;
layout = gtk_widget_create_pango_layout (widget, NULL);
pango_layout_set_text (layout, str->str, str->len);
pango_layout_get_pixel_size (layout, &w, &h);
if ((r.x + r.width + w) < (cell_area->x + cell_area->width) ||
((cell_area->x + w) > r.x))
cairo_move_to (cr, r.x + r.width, r.y + ((r.height - h) / 2));
else
cairo_move_to (cr, r.x - w, r.y + ((r.height - h) / 2));
if (priv->end_time < priv->begin_time)
{
gdk_rgba_parse (&rgba, "#f00");
if (state & GTK_CELL_RENDERER_SELECTED)
rgba.alpha = 0.6;
}
gdk_cairo_set_source_rgba (cr, &rgba);
pango_cairo_show_layout (cr, layout);
g_object_unref (layout);
}
cairo_destroy (cr);
}
static GtkSizeRequestMode
sysprof_cell_renderer_duration_get_request_mode (GtkCellRenderer *renderer)
{
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
static void
sysprof_cell_renderer_duration_get_preferred_width (GtkCellRenderer *cell,
GtkWidget *widget,
gint *min_width,
gint *nat_width)
{
SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)cell;
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
gint width = 1;
g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (self));
g_assert (GTK_IS_WIDGET (widget));
GTK_CELL_RENDERER_CLASS (sysprof_cell_renderer_duration_parent_class)->get_preferred_width (cell, widget, min_width, nat_width);
if (priv->zoom_manager && priv->capture_begin_time && priv->capture_end_time)
width = sysprof_zoom_manager_get_width_for_duration (priv->zoom_manager,
priv->capture_end_time - priv->capture_begin_time);
if (min_width)
*min_width = width;
if (nat_width)
*nat_width = width;
}
static void
sysprof_cell_renderer_duration_get_preferred_height_for_width (GtkCellRenderer *cell,
GtkWidget *widget,
gint width,
gint *min_height,
gint *nat_height)
{
PangoLayout *layout;
gint w, h;
gint ypad;
g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (cell));
gtk_cell_renderer_get_padding (cell, NULL, &ypad);
layout = gtk_widget_create_pango_layout (widget, "XMZ09");
pango_layout_get_pixel_size (layout, &w, &h);
g_clear_object (&layout);
if (min_height)
*min_height = h + (ypad * 2);
if (nat_height)
*nat_height = h + (ypad * 2);
}
static void
sysprof_cell_renderer_duration_finalize (GObject *object)
{
SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)object;
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
g_clear_object (&priv->zoom_manager);
g_clear_pointer (&priv->text, g_free);
G_OBJECT_CLASS (sysprof_cell_renderer_duration_parent_class)->finalize (object);
}
static void
sysprof_cell_renderer_duration_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererDuration *self = SYSPROF_CELL_RENDERER_DURATION (object);
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
switch (prop_id)
{
case PROP_BEGIN_TIME:
g_value_set_int64 (value, priv->begin_time);
break;
case PROP_CAPTURE_BEGIN_TIME:
g_value_set_int64 (value, priv->capture_begin_time);
break;
case PROP_CAPTURE_END_TIME:
g_value_set_int64 (value, priv->capture_end_time);
break;
case PROP_END_TIME:
g_value_set_int64 (value, priv->end_time);
break;
case PROP_TEXT:
g_value_set_string (value, priv->text);
break;
case PROP_ZOOM_MANAGER:
g_value_set_object (value, priv->zoom_manager);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_cell_renderer_duration_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererDuration *self = SYSPROF_CELL_RENDERER_DURATION (object);
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
switch (prop_id)
{
case PROP_BEGIN_TIME:
priv->begin_time = g_value_get_int64 (value);
break;
case PROP_CAPTURE_BEGIN_TIME:
priv->capture_begin_time = g_value_get_int64 (value);
priv->capture_duration = priv->capture_end_time - priv->capture_begin_time;
break;
case PROP_CAPTURE_END_TIME:
priv->capture_end_time = g_value_get_int64 (value);
priv->capture_duration = priv->capture_end_time - priv->capture_begin_time;
break;
case PROP_COLOR:
if (g_value_get_boxed (value))
priv->color = *(GdkRGBA *)g_value_get_boxed (value);
else
gdk_rgba_parse (&priv->color, "#000");
priv->color_set = !!g_value_get_boolean (value);
break;
case PROP_END_TIME:
priv->end_time = g_value_get_int64 (value);
break;
case PROP_TEXT:
g_free (priv->text);
priv->text = g_value_dup_string (value);
break;
case PROP_ZOOM_MANAGER:
g_set_object (&priv->zoom_manager, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_cell_renderer_duration_class_init (SysprofCellRendererDurationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
object_class->finalize = sysprof_cell_renderer_duration_finalize;
object_class->get_property = sysprof_cell_renderer_duration_get_property;
object_class->set_property = sysprof_cell_renderer_duration_set_property;
cell_class->get_preferred_height_for_width = sysprof_cell_renderer_duration_get_preferred_height_for_width;
cell_class->get_preferred_width = sysprof_cell_renderer_duration_get_preferred_width;
cell_class->get_request_mode = sysprof_cell_renderer_duration_get_request_mode;
cell_class->snapshot = sysprof_cell_renderer_duration_snapshot;
/* Note we do not emit ::notify() for these properties */
properties [PROP_BEGIN_TIME] =
g_param_spec_int64 ("begin-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_CAPTURE_BEGIN_TIME] =
g_param_spec_int64 ("capture-begin-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_CAPTURE_END_TIME] =
g_param_spec_int64 ("capture-end-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_COLOR] =
g_param_spec_boxed ("color", NULL, NULL,
GDK_TYPE_RGBA,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_END_TIME] =
g_param_spec_int64 ("end-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_END_TIME] =
g_param_spec_int64 ("end-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_TEXT] =
g_param_spec_string ("text", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_ZOOM_MANAGER] =
g_param_spec_object ("zoom-manager", NULL, NULL,
SYSPROF_TYPE_ZOOM_MANAGER,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_cell_renderer_duration_init (SysprofCellRendererDuration *self)
{
SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self);
priv->color.alpha = 1.0;
}
GtkCellRenderer *
sysprof_cell_renderer_duration_new (void)
{
return g_object_new (SYSPROF_TYPE_CELL_RENDERER_DURATION, NULL);
}

View File

@ -1,139 +0,0 @@
/* sysprof-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
*/
#define G_LOG_DOMAIN "sysprof-cell-renderer-percent"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-cell-renderer-percent.h"
typedef struct
{
gdouble percent;
} SysprofCellRendererPercentPrivate;
enum {
PROP_0,
PROP_PERCENT,
N_PROPS
};
G_DEFINE_TYPE_WITH_PRIVATE (SysprofCellRendererPercent, sysprof_cell_renderer_percent, SYSPROF_TYPE_CELL_RENDERER_PROGRESS)
static GParamSpec *properties [N_PROPS];
static void
sysprof_cell_renderer_percent_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererPercent *self = SYSPROF_CELL_RENDERER_PERCENT (object);
switch (prop_id)
{
case PROP_PERCENT:
g_value_set_double (value, sysprof_cell_renderer_percent_get_percent (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_cell_renderer_percent_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererPercent *self = SYSPROF_CELL_RENDERER_PERCENT (object);
switch (prop_id)
{
case PROP_PERCENT:
sysprof_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
sysprof_cell_renderer_percent_class_init (SysprofCellRendererPercentClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_cell_renderer_percent_get_property;
object_class->set_property = sysprof_cell_renderer_percent_set_property;
properties [PROP_PERCENT] =
g_param_spec_double ("percent",
"Percent",
"Percent",
0.0,
100.0,
0.0,
/* Doesn't notify to avoid signal emission */
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_cell_renderer_percent_init (SysprofCellRendererPercent *self)
{
g_object_set (self, "text-xalign", 1.0f, NULL);
}
gdouble
sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self)
{
SysprofCellRendererPercentPrivate *priv = sysprof_cell_renderer_percent_get_instance_private (self);
g_return_val_if_fail (SYSPROF_IS_CELL_RENDERER_PERCENT (self), 0.0);
return priv->percent;
}
void
sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self,
gdouble percent)
{
SysprofCellRendererPercentPrivate *priv = sysprof_cell_renderer_percent_get_instance_private (self);
gchar text[8];
g_return_if_fail (SYSPROF_IS_CELL_RENDERER_PERCENT (self));
g_return_if_fail (percent >= 0.0);
g_return_if_fail (percent <= 100.0);
priv->percent = percent;
g_snprintf (text, sizeof text, "%.2lf%%", percent);
text [sizeof text - 1] = '\0';
g_object_set (self,
"value", (guint)percent,
"text", text,
NULL);
}

View File

@ -1,57 +0,0 @@
/* sysprof-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
*/
#pragma once
#include "sysprof-cell-renderer-progress.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CELL_RENDERER_PERCENT (sysprof_cell_renderer_percent_get_type())
#define SYSPROF_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercent))
#define SYSPROF_CELL_RENDERER_PERCENT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercent const))
#define SYSPROF_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercentClass))
#define SYSPROF_IS_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT))
#define SYSPROF_IS_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SYSPROF_TYPE_CELL_RENDERER_PERCENT))
#define SYSPROF_CELL_RENDERER_PERCENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercentClass))
typedef struct _SysprofCellRendererPercent SysprofCellRendererPercent;
typedef struct _SysprofCellRendererPercentClass SysprofCellRendererPercentClass;
struct _SysprofCellRendererPercent
{
SysprofCellRendererProgress parent;
};
struct _SysprofCellRendererPercentClass
{
SysprofCellRendererProgressClass parent_class;
/*< private >*/
gpointer _reserved[4];
};
GType sysprof_cell_renderer_percent_get_type (void);
GtkCellRenderer *sysprof_cell_renderer_percent_new (void);
gdouble sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self);
void sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self,
gdouble percent);
G_END_DECLS

View File

@ -1,712 +0,0 @@
/* gtkcellrendererprogress.c
* Copyright (C) 2002 Naba Kumar <kh_naba@users.sourceforge.net>
* heavily modified by Jörgen Scheibengruber <mfcn@gmx.de>
* heavily modified by Marco Pesenti Gritti <marco@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2007. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include <glib/gi18n.h>
#include <stdlib.h>
#include "sysprof-cell-renderer-progress.h"
enum
{
PROP_0,
PROP_VALUE,
PROP_TEXT,
PROP_PULSE,
PROP_TEXT_XALIGN,
PROP_TEXT_YALIGN,
PROP_ORIENTATION,
PROP_INVERTED
};
struct _SysprofCellRendererProgressPrivate
{
int value;
char *text;
char *label;
int min_h;
int min_w;
int pulse;
int offset;
float text_xalign;
float text_yalign;
GtkOrientation orientation;
gboolean inverted;
};
static void sysprof_cell_renderer_progress_finalize (GObject *object);
static void sysprof_cell_renderer_progress_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void sysprof_cell_renderer_progress_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void sysprof_cell_renderer_progress_set_value (SysprofCellRendererProgress *cellprogress,
int value);
static void sysprof_cell_renderer_progress_set_text (SysprofCellRendererProgress *cellprogress,
const char *text);
static void sysprof_cell_renderer_progress_set_pulse (SysprofCellRendererProgress *cellprogress,
int pulse);
static void compute_dimensions (GtkCellRenderer *cell,
GtkWidget *widget,
const char *text,
int *width,
int *height);
static void sysprof_cell_renderer_progress_snapshot (GtkCellRenderer *cell,
GtkSnapshot *snapshot,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
G_DEFINE_TYPE_WITH_CODE (SysprofCellRendererProgress, sysprof_cell_renderer_progress, GTK_TYPE_CELL_RENDERER,
G_ADD_PRIVATE (SysprofCellRendererProgress)
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
static void
recompute_label (SysprofCellRendererProgress *cellprogress)
{
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
char *label;
if (priv->text)
label = g_strdup (priv->text);
else if (priv->pulse < 0)
label = g_strdup_printf (C_("progress bar label", "%d%%"), priv->value);
else
label = NULL;
g_free (priv->label);
priv->label = label;
}
static void
sysprof_cell_renderer_progress_set_value (SysprofCellRendererProgress *cellprogress,
int value)
{
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
if (priv->value != value)
{
priv->value = value;
recompute_label (cellprogress);
g_object_notify (G_OBJECT (cellprogress), "value");
}
}
static void
sysprof_cell_renderer_progress_set_text (SysprofCellRendererProgress *cellprogress,
const char *text)
{
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
char *new_text;
new_text = g_strdup (text);
g_free (priv->text);
priv->text = new_text;
recompute_label (cellprogress);
g_object_notify (G_OBJECT (cellprogress), "text");
}
static void
sysprof_cell_renderer_progress_set_pulse (SysprofCellRendererProgress *cellprogress,
int pulse)
{
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
if (pulse != priv->pulse)
{
if (pulse <= 0)
priv->offset = 0;
else
priv->offset = pulse;
g_object_notify (G_OBJECT (cellprogress), "pulse");
}
priv->pulse = pulse;
recompute_label (cellprogress);
}
static void
sysprof_cell_renderer_progress_finalize (GObject *object)
{
SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
g_free (priv->text);
g_free (priv->label);
G_OBJECT_CLASS (sysprof_cell_renderer_progress_parent_class)->finalize (object);
}
static void
sysprof_cell_renderer_progress_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
switch (param_id)
{
case PROP_VALUE:
g_value_set_int (value, priv->value);
break;
case PROP_TEXT:
g_value_set_string (value, priv->text);
break;
case PROP_PULSE:
g_value_set_int (value, priv->pulse);
break;
case PROP_TEXT_XALIGN:
g_value_set_float (value, priv->text_xalign);
break;
case PROP_TEXT_YALIGN:
g_value_set_float (value, priv->text_yalign);
break;
case PROP_ORIENTATION:
g_value_set_enum (value, priv->orientation);
break;
case PROP_INVERTED:
g_value_set_boolean (value, priv->inverted);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
}
}
static void
sysprof_cell_renderer_progress_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
switch (param_id)
{
case PROP_VALUE:
sysprof_cell_renderer_progress_set_value (cellprogress,
g_value_get_int (value));
break;
case PROP_TEXT:
sysprof_cell_renderer_progress_set_text (cellprogress,
g_value_get_string (value));
break;
case PROP_PULSE:
sysprof_cell_renderer_progress_set_pulse (cellprogress,
g_value_get_int (value));
break;
case PROP_TEXT_XALIGN:
priv->text_xalign = g_value_get_float (value);
break;
case PROP_TEXT_YALIGN:
priv->text_yalign = g_value_get_float (value);
break;
case PROP_ORIENTATION:
if (priv->orientation != g_value_get_enum (value))
{
priv->orientation = g_value_get_enum (value);
g_object_notify_by_pspec (object, pspec);
}
break;
case PROP_INVERTED:
if (priv->inverted != g_value_get_boolean (value))
{
priv->inverted = g_value_get_boolean (value);
g_object_notify_by_pspec (object, pspec);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
}
}
static void
compute_dimensions (GtkCellRenderer *cell,
GtkWidget *widget,
const char *text,
int *width,
int *height)
{
PangoRectangle logical_rect;
PangoLayout *layout;
int xpad, ypad;
layout = gtk_widget_create_pango_layout (widget, text);
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
if (width)
*width = logical_rect.width + xpad * 2;
if (height)
*height = logical_rect.height + ypad * 2;
g_object_unref (layout);
}
static void
sysprof_cell_renderer_progress_get_preferred_width (GtkCellRenderer *cell,
GtkWidget *widget,
int *minimum,
int *natural)
{
SysprofCellRendererProgress *self = SYSPROF_CELL_RENDERER_PROGRESS (cell);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (self);
int w, h;
int size;
if (priv->min_w < 0)
{
char *text = g_strdup_printf (C_("progress bar label", "%d%%"), 100);
compute_dimensions (cell, widget, text,
&priv->min_w,
&priv->min_h);
g_free (text);
}
compute_dimensions (cell, widget, priv->label, &w, &h);
size = MAX (priv->min_w, w);
if (minimum != NULL)
*minimum = size;
if (natural != NULL)
*natural = size;
}
static void
sysprof_cell_renderer_progress_get_preferred_height (GtkCellRenderer *cell,
GtkWidget *widget,
int *minimum,
int *natural)
{
SysprofCellRendererProgress *self = SYSPROF_CELL_RENDERER_PROGRESS (cell);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (self);
int w, h;
int size;
if (priv->min_w < 0)
{
char *text = g_strdup_printf (C_("progress bar label", "%d%%"), 100);
compute_dimensions (cell, widget, text,
&priv->min_w,
&priv->min_h);
g_free (text);
}
compute_dimensions (cell, widget, priv->label, &w, &h);
size = MIN (priv->min_h, h);
if (minimum != NULL)
*minimum = size;
if (natural != NULL)
*natural = size;
}
static inline int
get_bar_size (int pulse,
int value,
int full_size)
{
int bar_size;
if (pulse < 0)
bar_size = full_size * MAX (0, value) / 100;
else if (pulse == 0)
bar_size = 0;
else if (pulse == G_MAXINT)
bar_size = full_size;
else
bar_size = MAX (2, full_size / 5);
return bar_size;
}
static inline int
get_bar_position (int start,
int full_size,
int bar_size,
int pulse,
int offset,
gboolean is_rtl)
{
int position;
if (pulse < 0 || pulse == 0 || pulse == G_MAXINT)
{
position = is_rtl ? (start + full_size - bar_size) : start;
}
else
{
position = (is_rtl ? offset + 12 : offset) % 24;
if (position > 12)
position = 24 - position;
position = start + full_size * position / 15;
}
return position;
}
static void
sysprof_cell_renderer_progress_snapshot (GtkCellRenderer *cell,
GtkSnapshot *snapshot,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (cell);
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
GtkStyleContext *context;
GtkBorder padding;
PangoLayout *layout;
PangoRectangle logical_rect;
int x, y, w, h, x_pos, y_pos, bar_position, bar_size, start, full_size;
int xpad, ypad;
GdkRectangle clip;
gboolean is_rtl;
context = gtk_widget_get_style_context (widget);
is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
x = cell_area->x + xpad;
y = cell_area->y + ypad;
w = cell_area->width - xpad * 2;
h = cell_area->height - ypad * 2;
gtk_style_context_save (context);
gtk_style_context_add_class (context, "trough");
gtk_snapshot_render_background (snapshot, context, x, y, w, h);
gtk_snapshot_render_frame (snapshot, context, x, y, w, h);
gtk_style_context_get_padding (context, &padding);
x += padding.left;
y += padding.top;
w -= padding.left + padding.right;
h -= padding.top + padding.bottom;
gtk_style_context_restore (context);
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
clip.y = y;
clip.height = h;
start = x;
full_size = w;
bar_size = get_bar_size (priv->pulse, priv->value, full_size);
if (!priv->inverted)
bar_position = get_bar_position (start, full_size, bar_size,
priv->pulse, priv->offset, is_rtl);
else
bar_position = get_bar_position (start, full_size, bar_size,
priv->pulse, priv->offset, !is_rtl);
clip.width = bar_size;
clip.x = bar_position;
}
else
{
clip.x = x;
clip.width = w;
start = y;
full_size = h;
bar_size = get_bar_size (priv->pulse, priv->value, full_size);
if (priv->inverted)
bar_position = get_bar_position (start, full_size, bar_size,
priv->pulse, priv->offset, TRUE);
else
bar_position = get_bar_position (start, full_size, bar_size,
priv->pulse, priv->offset, FALSE);
clip.height = bar_size;
clip.y = bar_position;
}
if (bar_size > 0)
{
gtk_style_context_save (context);
gtk_style_context_add_class (context, "progressbar");
gtk_snapshot_render_background (snapshot, context, clip.x, clip.y, clip.width, clip.height);
gtk_snapshot_render_frame (snapshot, context, clip.x, clip.y, clip.width, clip.height);
gtk_style_context_restore (context);
}
if (priv->label)
{
float text_xalign;
layout = gtk_widget_create_pango_layout (widget, priv->label);
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
text_xalign = 1.0 - priv->text_xalign;
else
text_xalign = priv->text_xalign;
x_pos = x + padding.left + text_xalign *
(w - padding.left - padding.right - logical_rect.width);
y_pos = y + padding.top + priv->text_yalign *
(h - padding.top - padding.bottom - logical_rect.height);
gtk_snapshot_push_clip (snapshot,
&GRAPHENE_RECT_INIT(
clip.x, clip.y,
clip.width, clip.height
));
gtk_style_context_save (context);
gtk_style_context_add_class (context, "progressbar");
gtk_snapshot_render_layout (snapshot, context,
x_pos, y_pos,
layout);
gtk_style_context_restore (context);
gtk_snapshot_pop (snapshot);
gtk_style_context_save (context);
gtk_style_context_add_class (context, "trough");
if (bar_position > start)
{
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
clip.x = x;
clip.width = bar_position - x;
}
else
{
clip.y = y;
clip.height = bar_position - y;
}
gtk_snapshot_push_clip (snapshot,
&GRAPHENE_RECT_INIT(
clip.x, clip.y,
clip.width, clip.height
));
gtk_snapshot_render_layout (snapshot, context,
x_pos, y_pos,
layout);
gtk_snapshot_pop (snapshot);
}
if (bar_position + bar_size < start + full_size)
{
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
clip.x = bar_position + bar_size;
clip.width = x + w - (bar_position + bar_size);
}
else
{
clip.y = bar_position + bar_size;
clip.height = y + h - (bar_position + bar_size);
}
gtk_snapshot_push_clip (snapshot,
&GRAPHENE_RECT_INIT(
clip.x, clip.y,
clip.width, clip.height
));
gtk_snapshot_render_layout (snapshot, context,
x_pos, y_pos,
layout);
gtk_snapshot_pop (snapshot);
}
gtk_style_context_restore (context);
g_object_unref (layout);
}
}
static void
sysprof_cell_renderer_progress_class_init (SysprofCellRendererProgressClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
object_class->finalize = sysprof_cell_renderer_progress_finalize;
object_class->get_property = sysprof_cell_renderer_progress_get_property;
object_class->set_property = sysprof_cell_renderer_progress_set_property;
cell_class->get_preferred_width = sysprof_cell_renderer_progress_get_preferred_width;
cell_class->get_preferred_height = sysprof_cell_renderer_progress_get_preferred_height;
cell_class->snapshot = sysprof_cell_renderer_progress_snapshot;
/**
* SysprofCellRendererProgress:value:
*
* The "value" property determines the percentage to which the
* progress bar will be "filled in".
**/
g_object_class_install_property (object_class,
PROP_VALUE,
g_param_spec_int ("value",
"Value",
"Value of the progress bar",
0, 100, 0,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/**
* SysprofCellRendererProgress:text:
*
* The "text" property determines the label which will be drawn
* over the progress bar. Setting this property to %NULL causes the default
* label to be displayed. Setting this property to an empty string causes
* no label to be displayed.
**/
g_object_class_install_property (object_class,
PROP_TEXT,
g_param_spec_string ("text",
"Text",
"Text on the progress bar",
NULL,
G_PARAM_READWRITE));
/**
* SysprofCellRendererProgress:pulse:
*
* Setting this to a non-negative value causes the cell renderer to
* enter "activity mode", where a block bounces back and forth to
* indicate that some progress is made, without specifying exactly how
* much.
*
* Each increment of the property causes the block to move by a little
* bit.
*
* To indicate that the activity has not started yet, set the property
* to zero. To indicate completion, set the property to %G_MAXINT.
*/
g_object_class_install_property (object_class,
PROP_PULSE,
g_param_spec_int ("pulse",
"Pulse",
"Set this to positive values to indicate that some progress is made, but you dont know how much.",
-1, G_MAXINT, -1,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/**
* SysprofCellRendererProgress:text-xalign:
*
* The "text-xalign" property controls the horizontal alignment of the
* text in the progress bar. Valid values range from 0 (left) to 1
* (right). Reserved for RTL layouts.
*/
g_object_class_install_property (object_class,
PROP_TEXT_XALIGN,
g_param_spec_float ("text-xalign",
"Text x alignment",
"The horizontal text alignment, from 0 (left) to 1 (right). Reversed for RTL layouts.",
0.0, 1.0, 0.5,
G_PARAM_READWRITE));
/**
* SysprofCellRendererProgress:text-yalign:
*
* The "text-yalign" property controls the vertical alignment of the
* text in the progress bar. Valid values range from 0 (top) to 1
* (bottom).
*/
g_object_class_install_property (object_class,
PROP_TEXT_YALIGN,
g_param_spec_float ("text-yalign",
"Text y alignment",
"The vertical text alignment, from 0 (top) to 1 (bottom).",
0.0, 1.0, 0.5,
G_PARAM_READWRITE));
g_object_class_override_property (object_class,
PROP_ORIENTATION,
"orientation");
g_object_class_install_property (object_class,
PROP_INVERTED,
g_param_spec_boolean ("inverted",
"Inverted",
"Invert the direction in which the progress bar grows",
FALSE,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
}
static void
sysprof_cell_renderer_progress_init (SysprofCellRendererProgress *cellprogress)
{
SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress);
priv->value = 0;
priv->text = NULL;
priv->label = NULL;
priv->min_w = -1;
priv->min_h = -1;
priv->pulse = -1;
priv->offset = 0;
priv->text_xalign = 0.5;
priv->text_yalign = 0.5;
priv->orientation = GTK_ORIENTATION_HORIZONTAL,
priv->inverted = FALSE;
}
/**
* sysprof_cell_renderer_progress_new:
*
* Creates a new `SysprofCellRendererProgress`.
*
* Returns: the new cell renderer
**/
GtkCellRenderer*
sysprof_cell_renderer_progress_new (void)
{
return g_object_new (SYSPROF_TYPE_CELL_RENDERER_PROGRESS, NULL);
}

View File

@ -1,53 +0,0 @@
/* gtkcellrendererprogress.h
* Copyright (C) 2002 Naba Kumar <kh_naba@users.sourceforge.net>
* modified by Jörgen Scheibengruber <mfcn@gmx.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2004. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_CELL_RENDERER_PROGRESS (sysprof_cell_renderer_progress_get_type ())
#define SYSPROF_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PROGRESS, SysprofCellRendererProgress))
#define SYSPROF_IS_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SYSPROF_TYPE_CELL_RENDERER_PROGRESS))
typedef struct _SysprofCellRendererProgress SysprofCellRendererProgress;
typedef struct _SysprofCellRendererProgressClass SysprofCellRendererProgressClass;
typedef struct _SysprofCellRendererProgressPrivate SysprofCellRendererProgressPrivate;
struct _SysprofCellRendererProgress
{
GtkCellRenderer parent_instance;
};
struct _SysprofCellRendererProgressClass
{
GtkCellRendererClass parent_class;
};
GType sysprof_cell_renderer_progress_get_type (void) G_GNUC_CONST;
GtkCellRenderer *sysprof_cell_renderer_progress_new (void);
G_END_DECLS

View File

@ -1,106 +0,0 @@
/* sysprof-check.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-check"
#include "config.h"
#include "sysprof-check.h"
static void
sysprof_check_supported_ping_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *bus = (GDBusConnection *)object;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!(reply = g_dbus_connection_call_finish (bus, result, &error)))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_boolean (task, TRUE);
}
static void
sysprof_check_supported_bus_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GDBusConnection) bus = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!(bus = g_bus_get_finish (result, &error)))
g_task_return_error (task, g_steal_pointer (&error));
else
g_dbus_connection_call (bus,
"org.gnome.Sysprof3",
"/org/gnome/Sysprof3",
"org.freedesktop.DBus.Peer",
"Ping",
g_variant_new ("()"),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
g_task_get_cancellable (task),
sysprof_check_supported_ping_cb,
g_object_ref (task));
}
void
sysprof_check_supported_async (GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_check_supported_async);
/* Get access to the System D-Bus and check to see if we can ping the
* service that is found at org.gnome.Sysprof3.
*/
g_bus_get (G_BUS_TYPE_SYSTEM,
cancellable,
sysprof_check_supported_bus_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_check_supported_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}

View File

@ -1,37 +0,0 @@
/* sysprof-check.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
SYSPROF_AVAILABLE_IN_ALL
void sysprof_check_supported_async (GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_check_supported_finish (GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -1,157 +0,0 @@
/* sysprof-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 "sysprof-color-cycle"
#include "config.h"
#include "sysprof-color-cycle.h"
G_DEFINE_BOXED_TYPE (SysprofColorCycle, sysprof_color_cycle, sysprof_color_cycle_ref, sysprof_color_cycle_unref)
static const gchar *default_colors[] = {
"#1a5fb4", /* Blue 5 */
"#26a269", /* Green 5 */
"#e5a50a", /* Yellow 5 */
"#c64600", /* Orange 5 */
"#a51d2d", /* Red 5 */
"#613583", /* Purple 5 */
"#63452c", /* Brown 5 */
"#1c71d8", /* Blue 4 */
"#2ec27e", /* Green 4 */
"#f5c211", /* Yellow 4 */
"#e66100", /* Orange 4 */
"#c01c28", /* Red 4 */
"#813d9c", /* Purple 4 */
"#865e3c", /* Brown 4 */
"#3584e4", /* Blue 3 */
"#33d17a", /* Green 3 */
"#f6d32d", /* Yellow 3 */
"#ff7800", /* Orange 3 */
"#e01b24", /* Red 3 */
"#9141ac", /* Purple 3 */
"#986a44", /* Brown 3 */
"#62a0ea", /* Blue 2 */
"#57e389", /* Green 2 */
"#f8e45c", /* Yellow 2 */
"#ffa348", /* Orange 2 */
"#ed333b", /* Red 2 */
"#c061cb", /* Purple 2 */
"#b5835a", /* Brown 2 */
"#99c1f1", /* Blue 1 */
"#8ff0a4", /* Green 1 */
"#f9f06b", /* Yellow 1 */
"#ffbe6f", /* Orange 1 */
"#f66151", /* Red 1 */
"#dc8add", /* Purple 1 */
"#cdab8f", /* Brown 1 */
NULL
};
struct _SysprofColorCycle
{
volatile gint ref_count;
GdkRGBA *colors;
gsize n_colors;
guint position;
};
static void
sysprof_color_cycle_destroy (SysprofColorCycle *self)
{
g_free (self->colors);
g_slice_free (SysprofColorCycle, self);
}
SysprofColorCycle *
sysprof_color_cycle_new (void)
{
SysprofColorCycle *self;
self = g_slice_new0 (SysprofColorCycle);
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;
}
SysprofColorCycle *
sysprof_color_cycle_ref (SysprofColorCycle *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
sysprof_color_cycle_unref (SysprofColorCycle *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))
sysprof_color_cycle_destroy (self);
}
void
sysprof_color_cycle_next (SysprofColorCycle *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
sysprof_color_cycle_reset (SysprofColorCycle *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;
}

View File

@ -1,41 +0,0 @@
/* sysprof-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
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_COLOR_CYCLE (sysprof_color_cycle_get_type())
typedef struct _SysprofColorCycle SysprofColorCycle;
GType sysprof_color_cycle_get_type (void);
SysprofColorCycle *sysprof_color_cycle_ref (SysprofColorCycle *self);
void sysprof_color_cycle_unref (SysprofColorCycle *self);
SysprofColorCycle *sysprof_color_cycle_new (void);
void sysprof_color_cycle_reset (SysprofColorCycle *self);
void sysprof_color_cycle_next (SysprofColorCycle *self,
GdkRGBA *rgba);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofColorCycle, sysprof_color_cycle_unref)
G_END_DECLS

View File

@ -1,284 +0,0 @@
/* sysprof-counters-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-counters-aid"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-color-cycle.h"
#include "sysprof-counters-aid.h"
#include "sysprof-line-visualizer.h"
#include "sysprof-marks-page.h"
#include "sysprof-time-visualizer.h"
struct _SysprofCountersAid
{
SysprofAid parent_instance;
};
typedef struct
{
SysprofCaptureCursor *cursor;
SysprofDisplay *display;
} Present;
G_DEFINE_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF_TYPE_AID)
static void
present_free (gpointer data)
{
Present *p = data;
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
g_clear_object (&p->display);
g_slice_free (Present, p);
}
static void
on_group_activated_cb (SysprofVisualizerGroup *group,
SysprofPage *page)
{
SysprofDisplay *display;
g_assert (SYSPROF_IS_VISUALIZER_GROUP (group));
g_assert (SYSPROF_IS_PAGE (page));
display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY));
sysprof_display_set_visible_page (display, page);
}
/**
* sysprof_counters_aid_new:
*
* Create a new #SysprofCountersAid.
*
* Returns: (transfer full): a newly created #SysprofCountersAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_counters_aid_new (void)
{
return g_object_new (SYSPROF_TYPE_COUNTERS_AID, NULL);
}
static void
sysprof_counters_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
}
static gchar *
build_title (const SysprofCaptureCounter *ctr)
{
GString *str;
str = g_string_new (NULL);
if (ctr->category[0] != 0)
{
if (str->len)
g_string_append_c (str, ' ');
g_string_append (str, ctr->category);
}
if (ctr->name[0] != 0)
{
if (str->len)
g_string_append (str, "");
g_string_append (str, ctr->name);
}
if (ctr->description[0] != 0)
{
if (str->len)
g_string_append_printf (str, " (%s)", ctr->description);
else
g_string_append (str, ctr->description);
}
if (str->len == 0)
/* this is untranslated on purpose */
g_string_append_printf (str, "Counter %d", ctr->id);
return g_string_free (str, FALSE);
}
static bool
collect_counters (const SysprofCaptureFrame *frame,
gpointer user_data)
{
SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
GArray *counters = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
g_assert (counters != NULL);
if (def->n_counters > 0)
g_array_append_vals (counters, def->counters, def->n_counters);
return TRUE;
}
static void
sysprof_counters_aid_present_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Present *present = task_data;
g_autoptr(GArray) counters = NULL;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_COUNTERS_AID (source_object));
g_assert (present != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
sysprof_capture_cursor_foreach (present->cursor, collect_counters, counters);
g_task_return_pointer (task,
g_steal_pointer (&counters),
(GDestroyNotify) g_array_unref);
}
static void
sysprof_counters_aid_present_async (SysprofAid *aid,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
g_autoptr(SysprofCaptureCondition) condition = NULL;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
Present present;
g_assert (SYSPROF_IS_COUNTERS_AID (aid));
g_assert (reader != NULL);
g_assert (SYSPROF_IS_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
condition = sysprof_capture_condition_new_where_type_in (1, types);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
present.cursor = g_steal_pointer (&cursor);
present.display = g_object_ref (display);
task = g_task_new (aid, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_counters_aid_present_async);
g_task_set_task_data (task,
g_slice_dup (Present, &present),
present_free);
g_task_run_in_thread (task, sysprof_counters_aid_present_worker);
}
static gboolean
sysprof_counters_aid_present_finish (SysprofAid *aid,
GAsyncResult *result,
GError **error)
{
g_autoptr(GArray) counters = NULL;
Present *present;
g_assert (SYSPROF_IS_AID (aid));
g_assert (G_IS_TASK (result));
present = g_task_get_task_data (G_TASK (result));
if ((counters = g_task_propagate_pointer (G_TASK (result), error)) && counters->len > 0)
{
g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
SysprofVisualizerGroup *group;
SysprofVisualizer *combined;
GtkWidget *page;
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"has-page", TRUE,
"title", _("Counters"),
"visible", TRUE,
NULL);
combined = g_object_new (SYSPROF_TYPE_TIME_VISUALIZER,
"title", _("Counters"),
"height-request", 35,
"visible", TRUE,
NULL);
sysprof_visualizer_group_insert (group, combined, -1, TRUE);
for (guint i = 0; i < counters->len; i++)
{
const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
g_autofree gchar *title = build_title (ctr);
GtkWidget *row;
GdkRGBA rgba;
row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", title,
"height-request", 35,
"visible", FALSE,
NULL);
sysprof_color_cycle_next (cycle, &rgba);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
rgba.alpha = .5;
sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_time_visualizer_add_counter (SYSPROF_TIME_VISUALIZER (combined), ctr->id, &rgba);
sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), -1, TRUE);
}
sysprof_display_add_group (present->display, group);
page = sysprof_marks_page_new (sysprof_display_get_zoom_manager (present->display),
SYSPROF_MARKS_MODEL_COUNTERS);
gtk_widget_show (page);
g_signal_connect_object (group,
"group-activated",
G_CALLBACK (on_group_activated_cb),
page,
0);
sysprof_display_add_page (present->display, SYSPROF_PAGE (page));
}
return counters != NULL;
}
static void
sysprof_counters_aid_class_init (SysprofCountersAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_counters_aid_prepare;
aid_class->present_async = sysprof_counters_aid_present_async;
aid_class->present_finish = sysprof_counters_aid_present_finish;
}
static void
sysprof_counters_aid_init (SysprofCountersAid *self)
{
sysprof_aid_set_display_name (SYSPROF_AID (self), _("Counters"));
sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic");
}

View File

@ -1,357 +0,0 @@
/* sysprof-cpu-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-cpu-aid"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-color-cycle.h"
#include "sysprof-cpu-aid.h"
#include "sysprof-line-visualizer.h"
#include "sysprof-procs-visualizer.h"
struct _SysprofCpuAid
{
SysprofAid parent_instance;
};
typedef struct
{
SysprofCaptureCursor *cursor;
SysprofDisplay *display;
GArray *counters;
guint has_processes : 1;
} Present;
G_DEFINE_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF_TYPE_AID)
static void
present_free (gpointer data)
{
Present *p = data;
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
g_clear_pointer (&p->counters, g_array_unref);
g_clear_object (&p->display);
g_slice_free (Present, p);
}
/**
* sysprof_cpu_aid_new:
*
* Create a new #SysprofCpuAid.
*
* Returns: (transfer full): a newly created #SysprofCpuAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_cpu_aid_new (void)
{
return g_object_new (SYSPROF_TYPE_CPU_AID, NULL);
}
static void
sysprof_cpu_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
#ifdef __linux__
g_autoptr(SysprofSource) source = NULL;
g_assert (SYSPROF_IS_CPU_AID (self));
g_assert (SYSPROF_IS_PROFILER (profiler));
source = sysprof_hostinfo_source_new ();
sysprof_profiler_add_source (profiler, source);
#endif
}
static bool
collect_info (const SysprofCaptureFrame *frame,
gpointer user_data)
{
SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
Present *p = user_data;
g_assert (frame != NULL);
g_assert (p != NULL);
g_assert (p->counters != NULL);
if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF)
{
for (guint i = 0; i < def->n_counters; i++)
{
const SysprofCaptureCounter *counter = &def->counters[i];
if (g_strcmp0 (counter->category, "CPU Percent") == 0 ||
g_strcmp0 (counter->category, "CPU Frequency") == 0)
g_array_append_vals (p->counters, counter, 1);
}
}
else if (!p->has_processes &&
(frame->type == SYSPROF_CAPTURE_FRAME_PROCESS ||
frame->type == SYSPROF_CAPTURE_FRAME_EXIT))
{
p->has_processes = TRUE;
}
return TRUE;
}
static void
sysprof_cpu_aid_present_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Present *present = task_data;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CPU_AID (source_object));
g_assert (present != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
sysprof_capture_cursor_foreach (present->cursor, collect_info, present);
g_task_return_pointer (task,
g_steal_pointer (&present->counters),
(GDestroyNotify) g_array_unref);
}
static void
sysprof_cpu_aid_present_async (SysprofAid *aid,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = {
SYSPROF_CAPTURE_FRAME_CTRDEF,
SYSPROF_CAPTURE_FRAME_PROCESS,
SYSPROF_CAPTURE_FRAME_EXIT,
};
g_autoptr(SysprofCaptureCondition) condition = NULL;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
Present present;
g_assert (SYSPROF_IS_CPU_AID (aid));
g_assert (reader != NULL);
g_assert (SYSPROF_IS_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
present.cursor = g_steal_pointer (&cursor);
present.display = g_object_ref (display);
present.counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
present.has_processes = FALSE;
task = g_task_new (aid, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_cpu_aid_present_async);
g_task_set_task_data (task,
g_slice_dup (Present, &present),
present_free);
g_task_run_in_thread (task, sysprof_cpu_aid_present_worker);
}
static gboolean
sysprof_cpu_aid_present_finish (SysprofAid *aid,
GAsyncResult *result,
GError **error)
{
g_autoptr(GArray) counters = NULL;
Present *present;
g_assert (SYSPROF_IS_AID (aid));
g_assert (G_IS_TASK (result));
present = g_task_get_task_data (G_TASK (result));
if ((counters = g_task_propagate_pointer (G_TASK (result), error)))
{
g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
g_autoptr(SysprofColorCycle) freq_cycle = sysprof_color_cycle_new ();
SysprofVisualizerGroup *usage;
SysprofVisualizerGroup *freq;
SysprofVisualizer *freq_row = NULL;
SysprofVisualizer *over_row = NULL;
gboolean found_combined = FALSE;
gboolean has_usage = FALSE;
gboolean has_freq = FALSE;
usage = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"priority", -1000,
"title", _("CPU Usage"),
"visible", TRUE,
NULL);
freq = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"priority", -999,
"title", _("CPU Frequency"),
"visible", TRUE,
NULL);
freq_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", _("CPU Frequency (All)"),
"height-request", 35,
"visible", TRUE,
"y-lower", 0.0,
"y-upper", 100.0,
NULL);
sysprof_visualizer_group_insert (freq, freq_row, -1, FALSE);
over_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", _("CPU Usage (All)"),
"height-request", 35,
"visible", TRUE,
"y-lower", 0.0,
"y-upper", 100.0,
NULL);
for (guint i = 0; i < counters->len; i++)
{
const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
if (g_strcmp0 (ctr->category, "CPU Percent") == 0)
{
if (strstr (ctr->name, "Combined") != NULL)
{
GtkWidget *row;
GdkRGBA rgba;
found_combined = TRUE;
gdk_rgba_parse (&rgba, "#1a5fb4");
row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
/* Translators: CPU is the processor. */
"title", _("CPU Usage (All)"),
"height-request", 35,
"visible", TRUE,
"y-lower", 0.0,
"y-upper", 100.0,
NULL);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
rgba.alpha = 0.5;
sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), 0, FALSE);
has_usage = TRUE;
}
else if (g_str_has_prefix (ctr->name, "Total CPU "))
{
GtkWidget *row;
GdkRGBA rgba;
sysprof_color_cycle_next (cycle, &rgba);
row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", ctr->name,
"height-request", 35,
"visible", FALSE,
"y-lower", 0.0,
"y-upper", 100.0,
NULL);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (over_row), ctr->id, &rgba);
rgba.alpha = 0.5;
sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE);
has_usage = TRUE;
}
}
else if (g_strcmp0 (ctr->category, "CPU Frequency") == 0)
{
if (g_str_has_prefix (ctr->name, "CPU "))
{
g_autofree gchar *title = g_strdup_printf ("%s Frequency", ctr->name);
GtkWidget *row;
GdkRGBA rgba;
sysprof_color_cycle_next (freq_cycle, &rgba);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, &rgba);
sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, TRUE);
row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER,
"title", title,
"height-request", 35,
"visible", FALSE,
"y-lower", 0.0,
"y-upper", 100.0,
NULL);
sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba);
sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (row), ctr->id, TRUE);
sysprof_visualizer_group_insert (freq, SYSPROF_VISUALIZER (row), -1, TRUE);
has_freq = TRUE;
}
}
}
if (present->has_processes)
{
GtkWidget *row;
row = g_object_new (SYSPROF_TYPE_PROCS_VISUALIZER,
"title", _("Processes"),
"height-request", 35,
"visible", FALSE,
NULL);
sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE);
}
if (has_usage && !found_combined)
sysprof_visualizer_group_insert (usage, over_row, 0, FALSE);
else
g_object_unref (g_object_ref_sink (over_row));
if (has_usage)
sysprof_display_add_group (present->display, usage);
else
g_object_unref (g_object_ref_sink (usage));
if (has_freq)
sysprof_display_add_group (present->display, freq);
else
g_object_unref (g_object_ref_sink (freq));
}
return counters != NULL;
}
static void
sysprof_cpu_aid_class_init (SysprofCpuAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_cpu_aid_prepare;
aid_class->present_async = sysprof_cpu_aid_present_async;
aid_class->present_finish = sysprof_cpu_aid_present_finish;
}
static void
sysprof_cpu_aid_init (SysprofCpuAid *self)
{
sysprof_aid_set_display_name (SYSPROF_AID (self), _("CPU Usage"));
sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic");
}

View File

@ -1,453 +0,0 @@
/* sysprof-depth-visualizer.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-depth-visualizer"
#include "config.h"
#include <glib/gi18n.h>
#include "pointcache.h"
#include "sysprof-depth-visualizer.h"
struct _SysprofDepthVisualizer
{
SysprofVisualizer parent_instance;
SysprofCaptureReader *reader;
PointCache *points;
guint reload_source;
guint mode;
int last_width;
int last_height;
guint reloading : 1;
guint needs_reload : 1;
};
typedef struct
{
SysprofCaptureReader *reader;
PointCache *pc;
gint64 begin_time;
gint64 end_time;
gint64 duration;
guint max_n_addrs;
guint mode;
} State;
static void sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self);
G_DEFINE_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF_TYPE_VISUALIZER)
static void
state_free (State *st)
{
g_clear_pointer (&st->reader, sysprof_capture_reader_unref);
g_clear_pointer (&st->pc, point_cache_unref);
g_slice_free (State, st);
}
static bool
discover_max_n_addr (const SysprofCaptureFrame *frame,
gpointer user_data)
{
const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
State *st = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
g_assert (st != NULL);
st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs);
return TRUE;
}
static bool
build_point_cache_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
State *st = user_data;
gdouble x, y;
gboolean has_kernel = FALSE;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
g_assert (st != NULL);
x = (frame->time - st->begin_time) / (gdouble)st->duration;
y = sample->n_addrs / (gdouble)st->max_n_addrs;
/* If this contains a context-switch (meaning we're going into the kernel
* to do some work, use a negative value for Y so that we know later on
* that we should draw it with a different color (after removing the negation
* on the value.
*
* We skip past the first index, which is always a context switch as it is
* our perf handler.
*/
for (guint i = 1; i < sample->n_addrs; i++)
{
SysprofAddressContext kind;
if (sysprof_address_is_context_switch (sample->addrs[i], &kind))
{
has_kernel = TRUE;
y = -y;
break;
}
}
if (!has_kernel)
point_cache_add_point_to_set (st->pc, 1, x, y);
else
point_cache_add_point_to_set (st->pc, 2, x, y);
return TRUE;
}
static void
sysprof_depth_visualizer_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, };
g_autoptr(SysprofCaptureCursor) cursor = NULL;
SysprofCaptureCondition *condition;
State *st = task_data;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (source_object));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
if (st->duration != 0)
{
cursor = sysprof_capture_cursor_new (st->reader);
condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st);
sysprof_capture_cursor_reset (cursor);
sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st);
}
g_task_return_pointer (task,
g_steal_pointer (&st->pc),
(GDestroyNotify) point_cache_unref);
}
static void
apply_point_cache_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
PointCache *pc;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
g_assert (G_IS_TASK (result));
self->reloading = FALSE;
if ((pc = g_task_propagate_pointer (G_TASK (result), NULL)))
{
g_clear_pointer (&self->points, point_cache_unref);
self->points = g_steal_pointer (&pc);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
if (self->needs_reload)
sysprof_depth_visualizer_reload (self);
}
static void
sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self)
{
g_autoptr(GTask) task = NULL;
GtkAllocation alloc;
State *st;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
self->needs_reload = TRUE;
if (self->reloading)
return;
self->reloading = TRUE;
self->needs_reload = FALSE;
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
st = g_slice_new0 (State);
st->reader = sysprof_capture_reader_ref (self->reader);
st->pc = point_cache_new ();
st->max_n_addrs = 0;
st->begin_time = sysprof_capture_reader_get_start_time (self->reader);
st->end_time = sysprof_capture_reader_get_end_time (self->reader);
st->duration = st->end_time - st->begin_time;
st->mode = self->mode;
point_cache_add_set (st->pc, 1);
point_cache_add_set (st->pc, 2);
task = g_task_new (self, NULL, apply_point_cache_cb, NULL);
g_task_set_source_tag (task, sysprof_depth_visualizer_reload);
g_task_set_task_data (task, st, (GDestroyNotify) state_free);
g_task_run_in_thread (task, sysprof_depth_visualizer_worker);
}
static void
sysprof_depth_visualizer_set_reader (SysprofVisualizer *row,
SysprofCaptureReader *reader)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)row;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
if (self->reader != reader)
{
if (self->reader != NULL)
{
sysprof_capture_reader_unref (self->reader);
self->reader = NULL;
}
if (reader != NULL)
{
self->reader = sysprof_capture_reader_ref (reader);
sysprof_depth_visualizer_reload (self);
}
}
}
static void
sysprof_depth_visualizer_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
GtkAllocation alloc;
GdkRectangle clip;
const Point *points;
cairo_t *cr;
guint n_points = 0;
GdkRGBA user;
GdkRGBA system;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
g_assert (snapshot != NULL);
GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->snapshot (widget, snapshot);
if (self->points == NULL)
return;
gdk_rgba_parse (&user, "#1a5fb4");
gdk_rgba_parse (&system, "#3584e4");
gtk_widget_get_allocation (widget, &alloc);
cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height));
/* FIXME: we should abstract visualizer drawing into regions so that we
* can still know the region we're drawing.
*/
#if 0
if (!gdk_cairo_get_clip_rectangle (cr, &clip))
return;
#else
clip.x = alloc.x = 0;
clip.y = alloc.y = 0;
clip.width = alloc.width;
clip.height = alloc.height;
#endif
/* Draw user-space stacks */
if (self->mode != SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY &&
(points = point_cache_get_points (self->points, 1, &n_points)))
{
g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
(const SysprofVisualizerRelativePoint *)points,
n_points, out_points, n_points);
cairo_set_line_width (cr, 1.0);
gdk_cairo_set_source_rgba (cr, &user);
for (guint i = 0; i < n_points; i++)
{
gdouble x, y;
x = out_points[i].x;
y = out_points[i].y;
if (x < clip.x)
continue;
if (x > clip.x + clip.width)
break;
for (guint j = i + 1; j < n_points; j++)
{
if (out_points[j].x != x)
break;
y = MIN (y, out_points[j].y);
}
x += alloc.x;
cairo_move_to (cr, (guint)x + .5, alloc.height);
cairo_line_to (cr, (guint)x + .5, y);
}
cairo_stroke (cr);
}
/* Draw kernel-space stacks */
if (self->mode != SYSPROF_DEPTH_VISUALIZER_USER_ONLY &&
(points = point_cache_get_points (self->points, 2, &n_points)))
{
g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
(const SysprofVisualizerRelativePoint *)points,
n_points, out_points, n_points);
cairo_set_line_width (cr, 1.0);
gdk_cairo_set_source_rgba (cr, &system);
for (guint i = 0; i < n_points; i++)
{
gdouble x, y;
x = out_points[i].x;
y = out_points[i].y;
if (x < clip.x)
continue;
if (x > clip.x + clip.width)
break;
for (guint j = i + 1; j < n_points; j++)
{
if (out_points[j].x != x)
break;
y = MIN (y, out_points[j].y);
}
x += alloc.x;
cairo_move_to (cr, (guint)x + .5, alloc.height);
cairo_line_to (cr, (guint)x + .5, y);
}
cairo_stroke (cr);
}
cairo_destroy (cr);
}
static gboolean
sysprof_depth_visualizer_do_reload (gpointer data)
{
SysprofDepthVisualizer *self = data;
self->reload_source = 0;
sysprof_depth_visualizer_reload (self);
return G_SOURCE_REMOVE;
}
static void
sysprof_depth_visualizer_queue_reload (SysprofDepthVisualizer *self)
{
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
g_clear_handle_id (&self->reload_source, g_source_remove);
self->reload_source = g_idle_add (sysprof_depth_visualizer_do_reload, self);
}
static void
sysprof_depth_visualizer_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
if (width != self->last_width || height != self->last_height)
{
sysprof_depth_visualizer_queue_reload (SYSPROF_DEPTH_VISUALIZER (widget));
self->last_width = width;
self->last_height = height;
}
}
static void
sysprof_depth_visualizer_finalize (GObject *object)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
g_clear_handle_id (&self->reload_source, g_source_remove);
G_OBJECT_CLASS (sysprof_depth_visualizer_parent_class)->finalize (object);
}
static void
sysprof_depth_visualizer_class_init (SysprofDepthVisualizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofVisualizerClass *row_class = SYSPROF_VISUALIZER_CLASS (klass);
object_class->finalize = sysprof_depth_visualizer_finalize;
widget_class->snapshot = sysprof_depth_visualizer_snapshot;
widget_class->size_allocate = sysprof_depth_visualizer_size_allocate;
row_class->set_reader = sysprof_depth_visualizer_set_reader;
}
static void
sysprof_depth_visualizer_init (SysprofDepthVisualizer *self)
{
}
SysprofVisualizer *
sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode)
{
SysprofDepthVisualizer *self;
g_return_val_if_fail (mode == SYSPROF_DEPTH_VISUALIZER_COMBINED ||
mode == SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY ||
mode == SYSPROF_DEPTH_VISUALIZER_USER_ONLY,
NULL);
self = g_object_new (SYSPROF_TYPE_DEPTH_VISUALIZER, NULL);
self->mode = mode;
return SYSPROF_VISUALIZER (g_steal_pointer (&self));
}

View File

@ -1,325 +0,0 @@
/* sysprof-details-page.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-details-page"
#include "config.h"
#include <glib/gi18n.h>
#include <string.h>
#include "sysprof-mark-detail.h"
#include "sysprof-details-page.h"
#include "sysprof-ui-private.h"
struct _SysprofDetailsPage
{
GtkWidget parent_instance ;
/* Template Objects */
GListStore *marks_store;
GtkSortListModel *mark_sort_model;
GtkLabel *counters;
GtkLabel *duration;
GtkLabel *filename;
GtkLabel *allocations;
GtkLabel *forks;
GtkLabel *marks;
GtkLabel *processes;
GtkLabel *samples;
GtkLabel *start_time;
GtkLabel *cpu_label;
guint next_row;
};
G_DEFINE_TYPE (SysprofDetailsPage, sysprof_details_page, GTK_TYPE_WIDGET)
#if GLIB_CHECK_VERSION(2, 56, 0)
# define _g_date_time_new_from_iso8601 g_date_time_new_from_iso8601
#else
static GDateTime *
_g_date_time_new_from_iso8601 (const gchar *str,
GTimeZone *default_tz)
{
GTimeVal tv;
if (g_time_val_from_iso8601 (str, &tv))
{
g_autoptr(GDateTime) dt = g_date_time_new_from_timeval_utc (&tv);
if (default_tz)
return g_date_time_to_timezone (dt, default_tz);
else
return g_steal_pointer (&dt);
}
return NULL;
}
#endif
char *
format_time (GObject *unused,
gint64 time)
{
return time ? _sysprof_format_duration (time) : g_strdup("");
}
static void
sysprof_details_page_dispose (GObject *object)
{
SysprofDetailsPage *self = (SysprofDetailsPage *)object;
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
gtk_widget_unparent (child);
G_OBJECT_CLASS (sysprof_details_page_parent_class)->dispose (object);
}
static void
sysprof_details_page_class_init (SysprofDetailsPageClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = sysprof_details_page_dispose;
g_type_ensure (SYSPROF_TYPE_MARK_DETAIL);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-details-page.ui");
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, allocations);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, counters);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, cpu_label);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, duration);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, filename);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, forks);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_store);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, mark_sort_model);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, processes);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, samples);
gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, start_time);
gtk_widget_class_bind_template_callback (widget_class, format_time);
}
static void
sysprof_details_page_init (SysprofDetailsPage *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->next_row = 8;
}
GtkWidget *
sysprof_details_page_new (void)
{
return g_object_new (SYSPROF_TYPE_DETAILS_PAGE, NULL);
}
static void
update_cpu_info_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(SysprofDetailsPage) self = user_data;
g_autofree gchar *str = NULL;
g_assert (SYSPROF_IS_DETAILS_PAGE (self));
g_assert (G_IS_TASK (result));
if ((str = g_task_propagate_pointer (G_TASK (result), NULL)))
gtk_label_set_label (self->cpu_label, str);
}
static bool
cpu_info_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
const SysprofCaptureFileChunk *fc = (gpointer)frame;
const gchar *endptr;
const gchar *line;
gchar **str = user_data;
line = memmem ((gchar *)fc->data, fc->len, "model name", 10);
if (!line)
return FALSE;
endptr = (gchar *)fc->data + fc->len;
endptr = memchr (line, '\n', endptr - line);
if (endptr)
{
gchar *tmp = *str = g_strndup (line, endptr - line);
for (; *tmp && *tmp != ':'; tmp++)
*tmp = ' ';
if (*tmp == ':')
*tmp = ' ';
g_strstrip (*str);
return FALSE;
}
return TRUE;
}
static void
sysprof_details_page_update_cpu_info_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofCaptureCursor *cursor = task_data;
gchar *str = NULL;
g_assert (G_IS_TASK (task));
g_assert (cursor != NULL);
sysprof_capture_cursor_foreach (cursor, cpu_info_cb, &str);
g_task_return_pointer (task, g_steal_pointer (&str), g_free);
}
static void
sysprof_details_page_update_cpu_info (SysprofDetailsPage *self,
SysprofCaptureReader *reader)
{
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
g_assert (SYSPROF_IS_DETAILS_PAGE (self));
g_assert (reader != NULL);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor,
sysprof_capture_condition_new_where_file ("/proc/cpuinfo"));
task = g_task_new (NULL, NULL, update_cpu_info_cb, g_object_ref (self));
g_task_set_task_data (task,
g_steal_pointer (&cursor),
(GDestroyNotify) sysprof_capture_cursor_unref);
g_task_run_in_thread (task, sysprof_details_page_update_cpu_info_worker);
}
void
sysprof_details_page_set_reader (SysprofDetailsPage *self,
SysprofCaptureReader *reader)
{
g_autoptr(GDateTime) dt = NULL;
g_autoptr(GDateTime) local = NULL;
g_autofree gchar *duration_str = NULL;
const gchar *filename;
const gchar *capture_at;
SysprofCaptureStat st_buf;
gint64 duration;
g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
g_return_if_fail (reader != NULL);
sysprof_details_page_update_cpu_info (self, reader);
if (!(filename = sysprof_capture_reader_get_filename (reader)))
filename = _("Memory Capture");
gtk_label_set_label (self->filename, filename);
if ((capture_at = sysprof_capture_reader_get_time (reader)) &&
(dt = _g_date_time_new_from_iso8601 (capture_at, NULL)) &&
(local = g_date_time_to_local (dt)))
{
g_autofree gchar *str = g_date_time_format (local, "%x %X");
gtk_label_set_label (self->start_time, str);
}
duration = sysprof_capture_reader_get_end_time (reader) -
sysprof_capture_reader_get_start_time (reader);
duration_str = g_strdup_printf (_("%0.4lf seconds"), duration / (gdouble)SYSPROF_NSEC_PER_SEC);
gtk_label_set_label (self->duration, duration_str);
if (sysprof_capture_reader_get_stat (reader, &st_buf))
{
#define SET_FRAME_COUNT(field, TYPE) \
G_STMT_START { \
g_autofree gchar *str = NULL; \
str = g_strdup_printf ("%"G_GSIZE_FORMAT, st_buf.frame_count[TYPE]); \
gtk_label_set_label (self->field, str); \
} G_STMT_END
SET_FRAME_COUNT (samples, SYSPROF_CAPTURE_FRAME_SAMPLE);
SET_FRAME_COUNT (marks, SYSPROF_CAPTURE_FRAME_MARK);
SET_FRAME_COUNT (processes, SYSPROF_CAPTURE_FRAME_PROCESS);
SET_FRAME_COUNT (forks, SYSPROF_CAPTURE_FRAME_FORK);
SET_FRAME_COUNT (counters, SYSPROF_CAPTURE_FRAME_CTRSET);
SET_FRAME_COUNT (allocations, SYSPROF_CAPTURE_FRAME_ALLOCATION);
#undef SET_FRAME_COUNT
}
}
void
sysprof_details_page_add_mark (SysprofDetailsPage *self,
const gchar *mark,
gint64 min,
gint64 max,
gint64 avg,
gint64 hits)
{
SysprofMarkDetail *detail;
g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
detail = sysprof_mark_detail_new (mark, min, max, avg, hits);
g_list_store_append (self->marks_store, detail);
/*gtk_list_store_append (self->marks_store, &iter);
gtk_list_store_set (self->marks_store, &iter,
0, mark,
1, min ? _sysprof_format_duration (min) : "—",
2, max ? _sysprof_format_duration (max) : "—",
3, avg ? _sysprof_format_duration (avg) : "—",
4, hits,
5, detail,
-1);*/
g_object_unref (detail);
}
void
sysprof_details_page_add_marks (SysprofDetailsPage *self,
const SysprofMarkStat *marks,
guint n_marks)
{
g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self));
g_return_if_fail (marks != NULL || n_marks == 0);
if (marks == NULL || n_marks == 0)
return;
/* Be reasonable */
if (n_marks > 100)
n_marks = 100;
for (guint i = 0; i < n_marks; i++)
sysprof_details_page_add_mark (self,
marks[i].name,
marks[i].min,
marks[i].max,
marks[i].avg,
marks[i].count);
}

View File

@ -1,57 +0,0 @@
/* sysprof-details-page.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
SYSPROF_ALIGNED_BEGIN (8)
typedef struct
{
gchar name[152];
guint64 count;
gint64 max;
gint64 min;
gint64 avg;
guint64 avg_count;
} SysprofMarkStat
SYSPROF_ALIGNED_END (8);
#define SYSPROF_TYPE_DETAILS_PAGE (sysprof_details_page_get_type())
G_DECLARE_FINAL_TYPE (SysprofDetailsPage, sysprof_details_page, SYSPROF, DETAILS_PAGE, GtkWidget)
GtkWidget *sysprof_details_page_new (void);
void sysprof_details_page_set_reader (SysprofDetailsPage *self,
SysprofCaptureReader *reader);
void sysprof_details_page_add_marks (SysprofDetailsPage *self,
const SysprofMarkStat *marks,
guint n_marks);
void sysprof_details_page_add_mark (SysprofDetailsPage *self,
const gchar *mark,
gint64 min,
gint64 max,
gint64 avg,
gint64 hits);
G_END_DECLS

View File

@ -1,361 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SysprofDetailsPage" parent="GtkWidget">
<child>
<object class="AdwPreferencesPage">
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Capture</property>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Location</property>
<child type="suffix">
<object class="GtkLabel" id="filename">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Recorded At</property>
<child type="suffix">
<object class="GtkLabel" id="start_time">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Duration</property>
<child type="suffix">
<object class="GtkLabel" id="duration">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">CPU Model</property>
<child type="suffix">
<object class="GtkLabel" id="cpu_label">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Statistics</property>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Stack Traces</property>
<property name="subtitle" translatable="yes">Number of stack traces sampled for performance callgraphs</property>
<child type="suffix">
<object class="GtkLabel" id="samples">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Marks</property>
<property name="subtitle" translatable="yes">Number of marks seen</property>
<child type="suffix">
<object class="GtkLabel" id="marks">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Processes</property>
<property name="subtitle" translatable="yes">Number of processes seen</property>
<child type="suffix">
<object class="GtkLabel" id="processes">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Forks</property>
<property name="subtitle" translatable="yes">Number of times a process forked</property>
<child type="suffix">
<object class="GtkLabel" id="forks">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Memory Allocations</property>
<property name="subtitle" translatable="yes">Number of stack traces recorded for tracing memory allocations</property>
<child type="suffix">
<object class="GtkLabel" id="allocations">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="activatable">false</property>
<property name="title" translatable="yes">Counters</property>
<property name="subtitle" translatable="yes">Number of recorded counter values</property>
<child type="suffix">
<object class="GtkLabel" id="counters">
<property name="hexpand">true</property>
<property name="xalign">1</property>
<property name="ellipsize">start</property>
<property name="selectable">true</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Statistics</property>
<child>
<object class="GtkColumnView" id="marks_view">
<property name="width-request">500</property>
<property name="height-request">100</property>
<property name="model">
<object class="GtkNoSelection">
<property name="model">mark_sort_model</property>
</object>
</property>
<style>
<class name="data-table"/>
<class name="card"/>
</style>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Mark</property>
<property name="expand">true</property>
<property name="sorter">
<object class="GtkStringSorter">
<property name="expression">
<lookup name="label" type="SysprofMarkDetail"/>
</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<property name="ellipsize">end</property>
<binding name="label">
<lookup name="label" type="SysprofMarkDetail">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Hits</property>
<property name="sorter">
<object class="GtkNumericSorter">
<property name="expression">
<lookup name="hits" type="SysprofMarkDetail"/>
</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="hits" type="SysprofMarkDetail">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Min</property>
<property name="sorter">
<object class="GtkNumericSorter">
<property name="expression">
<lookup name="min" type="SysprofMarkDetail"/>
</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<closure type="gchararray" function="format_time">
<lookup name='min' type='SysprofMarkDetail'><lookup name='item'>GtkListItem</lookup></lookup>
</closure>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Max</property>
<property name="sorter">
<object class="GtkNumericSorter">
<property name="expression">
<lookup name="max" type="SysprofMarkDetail"/>
</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<closure type="gchararray" function="format_time">
<lookup name='max' type='SysprofMarkDetail'><lookup name='item'>GtkListItem</lookup></lookup>
</closure>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Avg</property>
<property name="sorter">
<object class="GtkNumericSorter">
<property name="expression">
<lookup name="average" type="SysprofMarkDetail"/>
</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<closure type="gchararray" function="format_time">
<lookup name='average' type='SysprofMarkDetail'><lookup name='item'>GtkListItem</lookup></lookup>
</closure>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkSortListModel" id="mark_sort_model">
<property name="model">marks_store</property>
<property name="incremental">true</property>
<binding name="sorter">
<lookup name="sorter">marks_view</lookup>
</binding>
</object>
<object class="GListStore" id="marks_store">
</object>
</interface>

View File

@ -1,269 +0,0 @@
/* sysprof-diskstat-aid.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-diskstat-aid"
#include "config.h"
#include <glib/gi18n.h>
#include <string.h>
#include "sysprof-color-cycle.h"
#include "sysprof-duplex-visualizer.h"
#include "sysprof-diskstat-aid.h"
struct _SysprofDiskstatAid
{
SysprofAid parent_instance;
};
typedef struct
{
SysprofCaptureCursor *cursor;
SysprofDisplay *display;
} Present;
G_DEFINE_TYPE (SysprofDiskstatAid, sysprof_diskstat_aid, SYSPROF_TYPE_AID)
static void
present_free (gpointer data)
{
Present *p = data;
g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref);
g_clear_object (&p->display);
g_slice_free (Present, p);
}
/**
* sysprof_diskstat_aid_new:
*
* Create a new #SysprofDiskstatAid.
*
* Returns: (transfer full): a newly created #SysprofDiskstatAid
*
* Since: 3.34
*/
SysprofAid *
sysprof_diskstat_aid_new (void)
{
return g_object_new (SYSPROF_TYPE_DISKSTAT_AID, NULL);
}
static void
sysprof_diskstat_aid_prepare (SysprofAid *self,
SysprofProfiler *profiler)
{
g_autoptr(SysprofSource) source = NULL;
g_assert (SYSPROF_IS_DISKSTAT_AID (self));
g_assert (SYSPROF_IS_PROFILER (profiler));
source = sysprof_diskstat_source_new ();
sysprof_profiler_add_source (profiler, source);
}
static bool
collect_diskstat_counters (const SysprofCaptureFrame *frame,
gpointer user_data)
{
SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
GArray *counters = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
g_assert (counters != NULL);
for (guint i = 0; i < def->n_counters; i++)
{
const SysprofCaptureCounter *counter = &def->counters[i];
if (strcmp (counter->category, "Disk") == 0 &&
(g_str_has_prefix (counter->name, "Total Reads") ||
g_str_has_prefix (counter->name, "Total Writes")))
g_array_append_vals (counters, counter, 1);
}
return TRUE;
}
static void
sysprof_diskstat_aid_present_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
Present *present = task_data;
g_autoptr(GArray) counters = NULL;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_DISKSTAT_AID (source_object));
g_assert (present != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
sysprof_capture_cursor_foreach (present->cursor, collect_diskstat_counters, counters);
g_task_return_pointer (task,
g_steal_pointer (&counters),
(GDestroyNotify) g_array_unref);
}
static guint
find_other_id (GArray *counters,
const gchar *name)
{
g_autofree gchar *other = NULL;
g_assert (counters);
g_assert (name != NULL);
g_assert (g_str_has_prefix (name, "Total Reads"));
other = g_strdup_printf ("Total Writes%s", name + strlen ("Total Reads"));
for (guint i = 0; i < counters->len; i++)
{
const SysprofCaptureCounter *c = &g_array_index (counters, SysprofCaptureCounter, i);
if (g_str_equal (c->name, other))
return c->id;
}
return 0;
}
static void
sysprof_diskstat_aid_present_async (SysprofAid *aid,
SysprofCaptureReader *reader,
SysprofDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF };
g_autoptr(SysprofCaptureCondition) condition = NULL;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
Present present;
g_assert (SYSPROF_IS_DISKSTAT_AID (aid));
g_assert (reader != NULL);
g_assert (SYSPROF_IS_DISPLAY (display));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
condition = sysprof_capture_condition_new_where_type_in (1, types);
cursor = sysprof_capture_cursor_new (reader);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
present.cursor = g_steal_pointer (&cursor);
present.display = g_object_ref (display);
task = g_task_new (aid, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_diskstat_aid_present_async);
g_task_set_task_data (task,
g_slice_dup (Present, &present),
present_free);
g_task_run_in_thread (task, sysprof_diskstat_aid_present_worker);
}
static gboolean
sysprof_diskstat_aid_present_finish (SysprofAid *aid,
GAsyncResult *result,
GError **error)
{
g_autoptr(GArray) counters = NULL;
Present *present;
g_assert (SYSPROF_IS_AID (aid));
g_assert (G_IS_TASK (result));
present = g_task_get_task_data (G_TASK (result));
if ((counters = g_task_propagate_pointer (G_TASK (result), error)))
{
g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new ();
SysprofVisualizerGroup *group;
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
"can-focus", TRUE,
"title", _("Disk"),
"visible", TRUE,
NULL);
for (guint i = 0; i < counters->len; i++)
{
const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i);
if (g_str_has_prefix (ctr->name, "Total Reads"))
{
g_autofree gchar *title = NULL;
gboolean is_combined;
GtkWidget *row;
GdkRGBA rgba;
guint other_id;
if (!(other_id = find_other_id (counters, ctr->name)))
continue;
is_combined = g_str_equal (ctr->description, "Combined");
if (is_combined)
title = g_strdup ("Disk Reads/Writes (All)");
else
title = g_strdup_printf ("Disk Reads/Writes%s", ctr->name + strlen ("Total Reads"));
row = g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER,
"title", title,
"height-request", 35,
"visible", is_combined,
NULL);
sysprof_color_cycle_next (cycle, &rgba);
sysprof_duplex_visualizer_set_counters (SYSPROF_DUPLEX_VISUALIZER (row), ctr->id, other_id);
sysprof_duplex_visualizer_set_colors (SYSPROF_DUPLEX_VISUALIZER (row), &rgba, &rgba);
sysprof_duplex_visualizer_set_labels (SYSPROF_DUPLEX_VISUALIZER (row), _("Reads"), _("Writes"));
sysprof_duplex_visualizer_set_use_diff (SYSPROF_DUPLEX_VISUALIZER (row), FALSE);
sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), is_combined ? 0 : -1, !is_combined);
}
}
if (counters->len > 0)
sysprof_display_add_group (present->display, group);
else
g_object_unref (g_object_ref_sink (group));
}
return counters != NULL;
}
static void
sysprof_diskstat_aid_class_init (SysprofDiskstatAidClass *klass)
{
SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass);
aid_class->prepare = sysprof_diskstat_aid_prepare;
aid_class->present_async = sysprof_diskstat_aid_present_async;
aid_class->present_finish = sysprof_diskstat_aid_present_finish;
}
static void
sysprof_diskstat_aid_init (SysprofDiskstatAid *self)
{
sysprof_aid_set_display_name (SYSPROF_AID (self), _("Disk"));
sysprof_aid_set_icon_name (SYSPROF_AID (self), "drive-harddisk-system-symbolic");
}

View File

@ -1,33 +0,0 @@
/* sysprof-diskstat-aid.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "sysprof-aid.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DISKSTAT_AID (sysprof_diskstat_aid_get_type())
G_DECLARE_FINAL_TYPE (SysprofDiskstatAid, sysprof_diskstat_aid, SYSPROF, DISKSTAT_AID, SysprofAid)
SysprofAid *sysprof_diskstat_aid_new (void);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
/* sysprof-display.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include <sysprof.h>
#include "sysprof-page.h"
#include "sysprof-visualizer-group.h"
#include "sysprof-zoom-manager.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DISPLAY (sysprof_display_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofDisplay, sysprof_display, SYSPROF, DISPLAY, GtkWidget)
struct _SysprofDisplayClass
{
GtkWidgetClass parent_class;
/*< private >*/
gpointer _reserved[16];
};
SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_display_new (void);
SYSPROF_AVAILABLE_IN_ALL
GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler);
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_display_dup_title (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_add_group (SysprofDisplay *self,
SysprofVisualizerGroup *group);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_add_page (SysprofDisplay *self,
SysprofPage *page);
SYSPROF_AVAILABLE_IN_ALL
SysprofPage *sysprof_display_get_visible_page (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_set_visible_page (SysprofDisplay *self,
SysprofPage *page);
SYSPROF_AVAILABLE_IN_ALL
SysprofZoomManager *sysprof_display_get_zoom_manager (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_load_async (SysprofDisplay *self,
SysprofCaptureReader *reader,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_display_load_finish (SysprofDisplay *self,
GAsyncResult *result,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_display_is_empty (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_open (SysprofDisplay *self,
GFile *file);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_save (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_display_get_can_save (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_stop_recording (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_display_get_can_replay (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofDisplay *sysprof_display_replay (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_3_38
void sysprof_display_add_to_selection (SysprofDisplay *self,
gint64 begin_time,
gint64 end_time);
G_END_DECLS

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="SysprofDisplay" parent="GtkWidget">
<child>
<object class="GtkStack" id="stack">
<property name="hhomogeneous">0</property>
<property name="vhomogeneous">0</property>
<child>
<object class="GtkStackPage">
<property name="name">assistant</property>
<property name="child">
<object class="SysprofProfilerAssistant" id="assistant">
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">view</property>
<property name="child">
<object class="EggPaned">
<property name="orientation">vertical</property>
<child>
<object class="SysprofVisualizersFrame" id="visualizers">
<property name="vexpand">false</property>
</object>
</child>
<child>
<object class="GtkStack" id="pages">
<property name="hhomogeneous">0</property>
<property name="vhomogeneous">0</property>
<property name="vexpand">true</property>
<child>
<object class="GtkStackPage">
<property name="title" translatable="yes">Details</property>
<property name="name">details</property>
<property name="child">
<object class="SysprofDetailsPage" id="details">
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">record</property>
<property name="child">
<object class="SysprofRecordingStateView" id="recording_view">
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">failed</property>
<property name="child">
<object class="SysprofFailedStateView" id="failed_view">
</object>
</property>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -1,633 +0,0 @@
/* sysprof-duplex-visualizer.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-duplex-visualizer"
#include "config.h"
#include "pointcache.h"
#include "sysprof-duplex-visualizer.h"
#define LABEL_HEIGHT_PX 10
struct _SysprofDuplexVisualizer
{
SysprofVisualizer parent_instance;
gint64 begin_time;
gint64 duration;
guint rx_counter;
guint tx_counter;
GdkRGBA rx_rgba;
GdkRGBA tx_rgba;
gchar *rx_label;
gchar *tx_label;
PointCache *cache;
guint rx_rgba_set : 1;
guint tx_rgba_set : 1;
guint use_diff : 1;
};
typedef struct
{
PointCache *cache;
gint64 begin_time;
gint64 duration;
gint64 max_change;
/* Last value to convert to rate of change */
gint64 last_rx_val;
gint64 last_tx_val;
/* Counter IDs */
guint rx;
guint tx;
/* Do we need to subtract previous value */
guint use_diff : 1;
} Collect;
G_DEFINE_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF_TYPE_VISUALIZER)
static bool
collect_ranges_cb (const SysprofCaptureFrame *frame,
gpointer data)
{
Collect *state = data;
g_assert (frame != NULL);
g_assert (state != NULL);
g_assert (state->cache != NULL);
if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET)
{
const SysprofCaptureCounterSet *set = (gconstpointer)frame;
for (guint i = 0; i < set->n_values; i++)
{
const SysprofCaptureCounterValues *values = &set->values[i];
for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++)
{
gint64 v64 = values->values[j].v64;
guint id = values->ids[j];
gint64 max_change = 0;
if (id == 0)
break;
if (id == state->rx)
{
if (state->last_rx_val != G_MININT64)
max_change = v64 - state->last_rx_val;
state->last_rx_val = v64;
}
else if (id == state->tx)
{
if (state->last_tx_val != G_MININT64)
max_change = v64 - state->last_tx_val;
state->last_tx_val = v64;
}
else
{
continue;
}
if (max_change > state->max_change)
state->max_change = max_change;
}
}
}
return TRUE;
}
static bool
collect_values_cb (const SysprofCaptureFrame *frame,
gpointer data)
{
Collect *state = data;
g_assert (frame != NULL);
g_assert (state != NULL);
g_assert (state->cache != NULL);
if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET)
{
const SysprofCaptureCounterSet *set = (gconstpointer)frame;
gdouble x = (frame->time - state->begin_time) / (gdouble)state->duration;
for (guint i = 0; i < set->n_values; i++)
{
const SysprofCaptureCounterValues *values = &set->values[i];
for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++)
{
gint64 v64 = values->values[j].v64;
guint id = values->ids[j];
gint64 val = v64;
gdouble y = 0.5;
if (id == 0)
break;
if (id == state->rx)
{
if (state->use_diff)
{
if (state->last_rx_val == G_MININT64)
val = 0;
else
val -= state->last_rx_val;
}
/* RX goes upward from half point */
if (state->max_change != 0)
y += (gdouble)val / (gdouble)state->max_change / 2.0;
state->last_rx_val = v64;
}
else if (id == state->tx)
{
if (state->use_diff)
{
if (state->last_tx_val == G_MININT64)
val = 0;
else
val -= state->last_tx_val;
}
/* TX goes downward from half point */
if (state->max_change != 0)
y -= (gdouble)val / (gdouble)state->max_change / 2.0;
state->last_tx_val = v64;
}
else
{
continue;
}
point_cache_add_point_to_set (state->cache, id, x, y);
}
}
}
return TRUE;
}
static void
sysprof_duplex_visualizer_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofCaptureCursor *cursor = task_data;
SysprofDuplexVisualizer *self = source_object;
Collect state = {0};
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self));
g_assert (cursor != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
state.cache = point_cache_new ();
state.begin_time = self->begin_time;
state.duration = self->duration;
state.rx = g_atomic_int_get (&self->rx_counter);
state.tx = g_atomic_int_get (&self->tx_counter);
state.last_rx_val = G_MININT64;
state.last_tx_val = G_MININT64;
state.max_change = 0;
state.use_diff = self->use_diff;
point_cache_add_set (state.cache, state.rx);
point_cache_add_set (state.cache, state.tx);
sysprof_capture_cursor_foreach (cursor, collect_ranges_cb, &state);
sysprof_capture_cursor_reset (cursor);
/* Give just a bit of overhead */
state.max_change *= 1.1;
/* Reset for calculations */
state.last_rx_val = G_MININT64;
state.last_tx_val = G_MININT64;
sysprof_capture_cursor_foreach (cursor, collect_values_cb, &state);
g_task_return_pointer (task,
g_steal_pointer (&state.cache),
(GDestroyNotify) point_cache_unref);
}
static void
load_data_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object;
g_autoptr(PointCache) pc = NULL;
g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self));
g_assert (G_IS_TASK (result));
if ((pc = g_task_propagate_pointer (G_TASK (result), NULL)))
{
g_clear_pointer (&self->cache, point_cache_unref);
self->cache = g_steal_pointer (&pc);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
sysprof_duplex_visualizer_set_reader (SysprofVisualizer *visualizer,
SysprofCaptureReader *reader)
{
SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)visualizer;
g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
SysprofCaptureCondition *c;
guint counters[2];
g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self));
g_assert (reader != NULL);
self->begin_time = sysprof_capture_reader_get_start_time (reader);
self->duration = sysprof_capture_reader_get_end_time (reader)
- sysprof_capture_reader_get_start_time (reader);
counters[0] = self->rx_counter;
counters[1] = self->tx_counter;
cursor = sysprof_capture_cursor_new (reader);
c = sysprof_capture_condition_new_where_counter_in (G_N_ELEMENTS (counters), counters);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c));
task = g_task_new (self, NULL, load_data_cb, NULL);
g_task_set_source_tag (task, sysprof_duplex_visualizer_set_reader);
g_task_set_task_data (task,
g_steal_pointer (&cursor),
(GDestroyNotify)sysprof_capture_cursor_unref);
g_task_run_in_thread (task, sysprof_duplex_visualizer_worker);
}
static void
sysprof_duplex_visualizer_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
static const gdouble dashes[] = { 1.0, 2.0 };
SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)widget;
PangoFontDescription *font_desc;
GtkStyleContext *style_context;
PangoLayout *layout;
cairo_t *cr;
GtkAllocation alloc;
GdkRGBA fg;
guint mid;
g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self));
g_assert (snapshot != NULL);
/* FIXME: This should all be drawn offscreen and then drawn to the snapshot
* using GdkMemoryTexture so that we can avoid extra GPU uploads.
*/
gtk_widget_get_allocation (widget, &alloc);
mid = alloc.height / 2;
GTK_WIDGET_CLASS (sysprof_duplex_visualizer_parent_class)->snapshot (widget, snapshot);
cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height));
style_context = gtk_widget_get_style_context (widget);
gtk_style_context_get_color (style_context, &fg);
fg.alpha *= 0.4;
/* Draw our center line */
cairo_save (cr);
cairo_set_line_width (cr, 1.0);
cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0);
cairo_move_to (cr, 0, mid);
cairo_line_to (cr, alloc.width, mid);
gdk_cairo_set_source_rgba (cr, &fg);
cairo_stroke (cr);
cairo_restore (cr);
if (self->cache != NULL)
{
g_autofree SysprofVisualizerAbsolutePoint *points = NULL;
const Point *fpoints;
guint n_fpoints = 0;
cairo_save (cr);
cairo_set_line_width (cr, 1.0);
if (self->rx_rgba_set)
gdk_cairo_set_source_rgba (cr, &self->rx_rgba);
fpoints = point_cache_get_points (self->cache, self->rx_counter, &n_fpoints);
if (n_fpoints > 0)
{
GdkRGBA rgba = self->rx_rgba;
gdouble last_x;
gdouble last_y;
guint p;
points = g_realloc_n (points, n_fpoints, sizeof *points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self),
(const SysprofVisualizerRelativePoint *)fpoints,
n_fpoints,
points,
n_fpoints);
/* Skip past data that we won't see anyway */
#if 0
for (p = 0; p < n_fpoints; p++)
{
if (points[p].x >= clip.x)
break;
}
if (p >= n_fpoints)
return;
#endif
/* But get at least one data point to anchor out of view */
if (p > 0)
p--;
last_x = points[p].x;
last_y = points[p].y;
cairo_move_to (cr, last_x, mid);
cairo_line_to (cr, last_x, last_y);
for (guint i = p + 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 0
if (points[i].x > clip.x + clip.width)
break;
#endif
}
cairo_line_to (cr, last_x, mid);
cairo_close_path (cr);
cairo_stroke_preserve (cr);
rgba.alpha *= 0.5;
gdk_cairo_set_source_rgba (cr, &rgba);
cairo_fill (cr);
}
cairo_restore (cr);
/* AND NOW Tx */
cairo_save (cr);
cairo_set_line_width (cr, 1.0);
if (self->tx_rgba_set)
gdk_cairo_set_source_rgba (cr, &self->tx_rgba);
fpoints = point_cache_get_points (self->cache, self->tx_counter, &n_fpoints);
if (n_fpoints > 0)
{
GdkRGBA rgba = self->tx_rgba;
gdouble last_x;
gdouble last_y;
guint p;
points = g_realloc_n (points, n_fpoints, sizeof *points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self),
(const SysprofVisualizerRelativePoint *)fpoints,
n_fpoints,
points,
n_fpoints);
#if 0
/* Skip past data that we won't see anyway */
for (p = 0; p < n_fpoints; p++)
{
if (points[p].x >= clip.x)
break;
}
if (p >= n_fpoints)
return ret;
#endif
/* But get at least one data point to anchor out of view */
if (p > 0)
p--;
last_x = points[p].x;
last_y = points[p].y;
cairo_move_to (cr, last_x, mid);
cairo_line_to (cr, last_x, last_y);
for (guint i = p + 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 0
if (points[i].x > clip.x + clip.width)
break;
#endif
}
cairo_line_to (cr, last_x, mid);
cairo_close_path (cr);
cairo_stroke_preserve (cr);
rgba.alpha *= 0.5;
gdk_cairo_set_source_rgba (cr, &rgba);
cairo_fill (cr);
}
cairo_restore (cr);
}
layout = gtk_widget_create_pango_layout (widget, "");
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);
gdk_cairo_set_source_rgba (cr, &fg);
cairo_move_to (cr, 2, 2);
if (self->rx_label != NULL)
pango_layout_set_text (layout, self->rx_label, -1);
else
pango_layout_set_text (layout, "RX", 2);
pango_cairo_show_layout (cr, layout);
cairo_move_to (cr, 2, mid + 2);
if (self->tx_label != NULL)
pango_layout_set_text (layout, self->tx_label, -1);
else
pango_layout_set_text (layout, "TX", 2);
pango_cairo_show_layout (cr, layout);
pango_font_description_free (font_desc);
g_object_unref (layout);
cairo_destroy (cr);
}
static void
sysprof_duplex_visualizer_finalize (GObject *object)
{
SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object;
g_clear_pointer (&self->cache, point_cache_unref);
g_clear_pointer (&self->rx_label, g_free);
g_clear_pointer (&self->tx_label, g_free);
G_OBJECT_CLASS (sysprof_duplex_visualizer_parent_class)->finalize (object);
}
static void
sysprof_duplex_visualizer_class_init (SysprofDuplexVisualizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
object_class->finalize = sysprof_duplex_visualizer_finalize;
widget_class->snapshot = sysprof_duplex_visualizer_snapshot;
visualizer_class->set_reader = sysprof_duplex_visualizer_set_reader;
}
static void
sysprof_duplex_visualizer_init (SysprofDuplexVisualizer *self)
{
self->use_diff = TRUE;
}
GtkWidget *
sysprof_duplex_visualizer_new (void)
{
return g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, NULL);
}
void
sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self,
guint rx_counter,
guint tx_counter)
{
g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self));
g_return_if_fail (rx_counter != 0);
g_return_if_fail (tx_counter != 0);
self->rx_counter = rx_counter;
self->tx_counter = tx_counter;
}
void
sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self,
const GdkRGBA *rx_rgba,
const GdkRGBA *tx_rgba)
{
g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self));
if (rx_rgba)
self->rx_rgba = *rx_rgba;
self->rx_rgba_set = !!rx_rgba;
if (tx_rgba)
self->tx_rgba = *tx_rgba;
self->tx_rgba_set = !!tx_rgba;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
gboolean
sysprof_duplex_visualizer_get_use_diff (SysprofDuplexVisualizer *self)
{
g_return_val_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self), FALSE);
return self->use_diff;
}
void
sysprof_duplex_visualizer_set_use_diff (SysprofDuplexVisualizer *self,
gboolean use_diff)
{
g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self));
self->use_diff = !!use_diff;
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
void
sysprof_duplex_visualizer_set_labels (SysprofDuplexVisualizer *self,
const gchar *rx_label,
const gchar *tx_label)
{
g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self));
if (g_strcmp0 (rx_label, self->rx_label) != 0)
{
g_free (self->rx_label);
self->rx_label = g_strdup (rx_label);
}
if (g_strcmp0 (tx_label, self->tx_label) != 0)
{
g_free (self->tx_label);
self->tx_label = g_strdup (tx_label);
}
gtk_widget_queue_draw (GTK_WIDGET (self));
}

View File

@ -1,45 +0,0 @@
/* sysprof-duplex-visualizer.h
*
* Copyright 2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "sysprof-visualizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DUPLEX_VISUALIZER (sysprof_duplex_visualizer_get_type())
G_DECLARE_FINAL_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF, DUPLEX_VISUALIZER, SysprofVisualizer)
GtkWidget *sysprof_duplex_visualizer_new (void);
gboolean sysprof_duplex_visualizer_get_use_diff (SysprofDuplexVisualizer *self);
void sysprof_duplex_visualizer_set_use_diff (SysprofDuplexVisualizer *self,
gboolean use_diff);
void sysprof_duplex_visualizer_set_labels (SysprofDuplexVisualizer *self,
const gchar *rx_label,
const gchar *tx_label);
void sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self,
guint rx_counter,
guint tx_counter);
void sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self,
const GdkRGBA *rx_rgba,
const GdkRGBA *tx_rgba);
G_END_DECLS

View File

@ -1,277 +0,0 @@
/* sysprof-environ-editor-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 "sysprof-environ-editor-row"
#include "config.h"
#include "sysprof-environ-editor-row.h"
struct _SysprofEnvironEditorRow
{
GtkListBoxRow parent_instance;
SysprofEnvironVariable *variable;
GtkEntry *key_entry;
GtkEntry *value_entry;
GtkButton *delete_button;
GBinding *key_binding;
GBinding *value_binding;
};
enum {
PROP_0,
PROP_VARIABLE,
LAST_PROP
};
enum {
DELETE,
LAST_SIGNAL
};
G_DEFINE_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, GTK_TYPE_LIST_BOX_ROW)
static GParamSpec *properties [LAST_PROP];
static guint signals [LAST_SIGNAL];
static gboolean
null_safe_mapping (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data)
{
const gchar *str = g_value_get_string (from_value);
g_value_set_string (to_value, str ?: "");
return TRUE;
}
static void
sysprof_environ_editor_row_connect (SysprofEnvironEditorRow *self)
{
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable));
self->key_binding =
g_object_bind_property_full (self->variable, "key", self->key_entry, "text",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
null_safe_mapping, NULL, NULL, NULL);
self->value_binding =
g_object_bind_property_full (self->variable, "value", self->value_entry, "text",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
null_safe_mapping, NULL, NULL, NULL);
}
static void
sysprof_environ_editor_row_disconnect (SysprofEnvironEditorRow *self)
{
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable));
g_clear_pointer (&self->key_binding, g_binding_unbind);
g_clear_pointer (&self->value_binding, g_binding_unbind);
}
static void
delete_button_clicked (GtkButton *button,
SysprofEnvironEditorRow *self)
{
g_assert (GTK_IS_BUTTON (button));
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
g_signal_emit (self, signals [DELETE], 0);
}
static void
key_entry_activate (GtkWidget *entry,
SysprofEnvironEditorRow *self)
{
g_assert (GTK_IS_ENTRY (entry));
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
gtk_widget_grab_focus (GTK_WIDGET (self->value_entry));
}
static void
value_entry_activate (GtkWidget *entry,
SysprofEnvironEditorRow *self)
{
GtkWidget *parent;
g_assert (GTK_IS_ENTRY (entry));
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
gtk_widget_grab_focus (GTK_WIDGET (self));
parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_LIST_BOX);
g_signal_emit_by_name (parent, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, 1);
}
static void
sysprof_environ_editor_row_dispose (GObject *object)
{
SysprofEnvironEditorRow *self = (SysprofEnvironEditorRow *)object;
if (self->variable != NULL)
{
sysprof_environ_editor_row_disconnect (self);
g_clear_object (&self->variable);
}
G_OBJECT_CLASS (sysprof_environ_editor_row_parent_class)->dispose (object);
}
static void
sysprof_environ_editor_row_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object);
switch (prop_id)
{
case PROP_VARIABLE:
g_value_set_object (value, sysprof_environ_editor_row_get_variable (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_environ_editor_row_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object);
switch (prop_id)
{
case PROP_VARIABLE:
sysprof_environ_editor_row_set_variable (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_environ_editor_row_class_init (SysprofEnvironEditorRowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = sysprof_environ_editor_row_dispose;
object_class->get_property = sysprof_environ_editor_row_get_property;
object_class->set_property = sysprof_environ_editor_row_set_property;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-environ-editor-row.ui");
gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, delete_button);
gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, key_entry);
gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, value_entry);
properties [PROP_VARIABLE] =
g_param_spec_object ("variable",
"Variable",
"Variable",
SYSPROF_TYPE_ENVIRON_VARIABLE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, LAST_PROP, properties);
signals [DELETE] =
g_signal_new ("delete",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
sysprof_environ_editor_row_init (SysprofEnvironEditorRow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
g_signal_connect (self->delete_button,
"clicked",
G_CALLBACK (delete_button_clicked),
self);
g_signal_connect (self->key_entry,
"activate",
G_CALLBACK (key_entry_activate),
self);
g_signal_connect (self->value_entry,
"activate",
G_CALLBACK (value_entry_activate),
self);
}
/**
* sysprof_environ_editor_row_get_variable:
*
* Returns: (transfer none) (nullable): An #SysprofEnvironVariable.
*/
SysprofEnvironVariable *
sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self)
{
g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self), NULL);
return self->variable;
}
void
sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self,
SysprofEnvironVariable *variable)
{
g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
g_return_if_fail (!variable || SYSPROF_IS_ENVIRON_VARIABLE (variable));
if (variable != self->variable)
{
if (self->variable != NULL)
{
sysprof_environ_editor_row_disconnect (self);
g_clear_object (&self->variable);
}
if (variable != NULL)
{
self->variable = g_object_ref (variable);
sysprof_environ_editor_row_connect (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VARIABLE]);
}
}
void
sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self)
{
g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self));
gtk_widget_grab_focus (GTK_WIDGET (self->key_entry));
}

View File

@ -1,38 +0,0 @@
/* ide-environ-editor-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
*/
#pragma once
#include <gtk/gtk.h>
#include "sysprof-environ-variable.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ENVIRON_EDITOR_ROW (sysprof_environ_editor_row_get_type())
G_DECLARE_FINAL_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, SYSPROF, ENVIRON_EDITOR_ROW, GtkListBoxRow)
SysprofEnvironVariable *sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self);
void sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self,
SysprofEnvironVariable *variable);
void sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self);
G_END_DECLS

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SysprofEnvironEditorRow" parent="GtkListBoxRow">
<child>
<object class="GtkBox">
<property name="spacing">12</property>
<child>
<object class="GtkEntry" id="key_entry">
<property name="margin-start">3</property>
<property name="has-frame">False</property>
</object>
</child>
<child>
<object class="GtkLabel" id="eq_label">
<property name="label">=</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkEntry" id="value_entry">
<property name="hexpand">True</property>
<property name="has-frame">False</property>
</object>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="tooltip-text" translatable="yes">Remove environment variable</property>
<property name="icon-name">list-remove-symbolic</property>
<style>
<class name="image-button"/>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -1,343 +0,0 @@
/* sysprof-environ-editor.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-environ-editor"
#include "config.h"
#include <glib/gi18n.h>
#include "sysprof-environ-editor.h"
#include "sysprof-environ-editor-row.h"
#include "sysprof-theme-manager.h"
struct _SysprofEnvironEditor
{
GtkWidget parent_instance;
GtkListBox *list_box;
SysprofEnviron *environ;
GtkWidget *dummy_row;
SysprofEnvironVariable *dummy;
};
G_DEFINE_TYPE (SysprofEnvironEditor, sysprof_environ_editor, GTK_TYPE_WIDGET)
enum {
PROP_0,
PROP_ENVIRON,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
sysprof_environ_editor_delete_row (SysprofEnvironEditor *self,
SysprofEnvironEditorRow *row)
{
SysprofEnvironVariable *variable;
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (row));
variable = sysprof_environ_editor_row_get_variable (row);
sysprof_environ_remove (self->environ, variable);
}
static GtkWidget *
sysprof_environ_editor_create_dummy_row (SysprofEnvironEditor *self)
{
GtkWidget *row;
GtkWidget *label;
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
label = g_object_new (GTK_TYPE_LABEL,
"label", _("New environment variable…"),
"margin-start", 6,
"margin-end", 6,
"margin-top", 6,
"margin-bottom", 6,
"visible", TRUE,
"xalign", 0.0f,
NULL);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
"child", label,
"visible", TRUE,
NULL);
return row;
}
static GtkWidget *
sysprof_environ_editor_create_row (gpointer item,
gpointer user_data)
{
SysprofEnvironVariable *variable = item;
SysprofEnvironEditor *self = user_data;
SysprofEnvironEditorRow *row;
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable));
row = g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR_ROW,
"variable", variable,
"visible", TRUE,
NULL);
g_signal_connect_object (row,
"delete",
G_CALLBACK (sysprof_environ_editor_delete_row),
self,
G_CONNECT_SWAPPED);
return GTK_WIDGET (row);
}
static void
sysprof_environ_editor_disconnect (SysprofEnvironEditor *self)
{
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
g_assert (SYSPROF_IS_ENVIRON (self->environ));
gtk_list_box_bind_model (self->list_box, NULL, NULL, NULL, NULL);
g_clear_object (&self->dummy);
}
static void
sysprof_environ_editor_connect (SysprofEnvironEditor *self)
{
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
g_assert (SYSPROF_IS_ENVIRON (self->environ));
gtk_list_box_bind_model (self->list_box,
G_LIST_MODEL (self->environ),
sysprof_environ_editor_create_row, self, NULL);
self->dummy_row = sysprof_environ_editor_create_dummy_row (self);
gtk_list_box_append (self->list_box, self->dummy_row);
}
static void
find_row_cb (GtkWidget *widget,
gpointer data)
{
struct {
SysprofEnvironVariable *variable;
SysprofEnvironEditorRow *row;
} *lookup = data;
g_assert (lookup != NULL);
g_assert (GTK_IS_LIST_BOX_ROW (widget));
if (SYSPROF_IS_ENVIRON_EDITOR_ROW (widget))
{
SysprofEnvironVariable *variable;
variable = sysprof_environ_editor_row_get_variable (SYSPROF_ENVIRON_EDITOR_ROW (widget));
if (variable == lookup->variable)
lookup->row = SYSPROF_ENVIRON_EDITOR_ROW (widget);
}
}
static SysprofEnvironEditorRow *
find_row (SysprofEnvironEditor *self,
SysprofEnvironVariable *variable)
{
struct {
SysprofEnvironVariable *variable;
SysprofEnvironEditorRow *row;
} lookup = { variable, NULL };
g_assert (SYSPROF_IS_ENVIRON_EDITOR (self));
g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable));
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->list_box));
child;
child = gtk_widget_get_next_sibling (child))
find_row_cb (child, &lookup);
return lookup.row;
}
static void
sysprof_environ_editor_row_activated (SysprofEnvironEditor *self,
GtkListBoxRow *row,
GtkListBox *list_box)
{
g_assert (GTK_IS_LIST_BOX (list_box));
g_assert (GTK_IS_LIST_BOX_ROW (row));
if (self->environ == NULL)
return;
if (self->dummy_row == GTK_WIDGET (row))
{
g_autoptr(SysprofEnvironVariable) variable = NULL;
variable = sysprof_environ_variable_new (NULL, NULL);
sysprof_environ_append (self->environ, variable);
sysprof_environ_editor_row_start_editing (find_row (self, variable));
}
}
static void
sysprof_environ_editor_dispose (GObject *object)
{
SysprofEnvironEditor *self = (SysprofEnvironEditor *)object;
if (self->list_box)
{
gtk_widget_unparent (GTK_WIDGET (self->list_box));
self->list_box = NULL;
}
g_clear_object (&self->environ);
G_OBJECT_CLASS (sysprof_environ_editor_parent_class)->dispose (object);
}
static void
sysprof_environ_editor_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object);
switch (prop_id)
{
case PROP_ENVIRON:
g_value_set_object (value, sysprof_environ_editor_get_environ (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void
sysprof_environ_editor_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object);
switch (prop_id)
{
case PROP_ENVIRON:
sysprof_environ_editor_set_environ (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void
sysprof_environ_editor_class_init (SysprofEnvironEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofThemeManager *theme_manager = sysprof_theme_manager_get_default ();
object_class->dispose = sysprof_environ_editor_dispose;
object_class->get_property = sysprof_environ_editor_get_property;
object_class->set_property = sysprof_environ_editor_set_property;
properties [PROP_ENVIRON] =
g_param_spec_object ("environ",
"Environment",
"Environment",
SYSPROF_TYPE_ENVIRON,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
sysprof_theme_manager_register_resource (theme_manager, NULL, NULL, "/org/gnome/sysprof/css/SysprofEnvironEditor-shared.css");
}
static void
sysprof_environ_editor_init (SysprofEnvironEditor *self)
{
self->list_box = GTK_LIST_BOX (gtk_list_box_new ());
gtk_widget_set_parent (GTK_WIDGET (self->list_box), GTK_WIDGET (self));
gtk_list_box_set_selection_mode (self->list_box, GTK_SELECTION_NONE);
gtk_widget_add_css_class (GTK_WIDGET (self), "environ-editor");
g_signal_connect_object (self->list_box,
"row-activated",
G_CALLBACK (sysprof_environ_editor_row_activated),
self,
G_CONNECT_SWAPPED);
}
GtkWidget *
sysprof_environ_editor_new (void)
{
return g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR, NULL);
}
void
sysprof_environ_editor_set_environ (SysprofEnvironEditor *self,
SysprofEnviron *environ_)
{
g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self));
g_return_if_fail (SYSPROF_IS_ENVIRON (environ_));
if (self->environ != environ_)
{
if (self->environ != NULL)
{
sysprof_environ_editor_disconnect (self);
g_clear_object (&self->environ);
}
if (environ_ != NULL)
{
self->environ = g_object_ref (environ_);
sysprof_environ_editor_connect (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRON]);
}
}
/**
* sysprof_environ_editor_get_environ:
*
* Returns: (nullable) (transfer none): An #SysprofEnviron or %NULL.
*
* Since: 3.34
*/
SysprofEnviron *
sysprof_environ_editor_get_environ (SysprofEnvironEditor *self)
{
g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self), NULL);
return self->environ;
}

View File

@ -1,185 +0,0 @@
/* sysprof-environ-variable.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-environ-variable"
#include "config.h"
#include "sysprof-environ-variable.h"
struct _SysprofEnvironVariable
{
GObject parent_instance;
gchar *key;
gchar *value;
};
G_DEFINE_TYPE (SysprofEnvironVariable, sysprof_environ_variable, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_KEY,
PROP_VALUE,
LAST_PROP
};
static GParamSpec *properties [LAST_PROP];
static void
sysprof_environ_variable_finalize (GObject *object)
{
SysprofEnvironVariable *self = (SysprofEnvironVariable *)object;
g_clear_pointer (&self->key, g_free);
g_clear_pointer (&self->value, g_free);
G_OBJECT_CLASS (sysprof_environ_variable_parent_class)->finalize (object);
}
static void
sysprof_environ_variable_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object);
switch (prop_id)
{
case PROP_KEY:
g_value_set_string (value, self->key);
break;
case PROP_VALUE:
g_value_set_string (value, self->value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void
sysprof_environ_variable_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object);
switch (prop_id)
{
case PROP_KEY:
sysprof_environ_variable_set_key (self, g_value_get_string (value));
break;
case PROP_VALUE:
sysprof_environ_variable_set_value (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void
sysprof_environ_variable_class_init (SysprofEnvironVariableClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_environ_variable_finalize;
object_class->get_property = sysprof_environ_variable_get_property;
object_class->set_property = sysprof_environ_variable_set_property;
properties [PROP_KEY] =
g_param_spec_string ("key",
"Key",
"The key for the environment variable",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties [PROP_VALUE] =
g_param_spec_string ("value",
"Value",
"The value for the environment variable",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, LAST_PROP, properties);
}
static void
sysprof_environ_variable_init (SysprofEnvironVariable *self)
{
}
const gchar *
sysprof_environ_variable_get_key (SysprofEnvironVariable *self)
{
g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL);
return self->key;
}
void
sysprof_environ_variable_set_key (SysprofEnvironVariable *self,
const gchar *key)
{
g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self));
if (g_strcmp0 (key, self->key) != 0)
{
g_free (self->key);
self->key = g_strdup (key);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KEY]);
}
}
const gchar *
sysprof_environ_variable_get_value (SysprofEnvironVariable *self)
{
g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL);
return self->value;
}
void
sysprof_environ_variable_set_value (SysprofEnvironVariable *self,
const gchar *value)
{
g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self));
if (g_strcmp0 (value, self->value) != 0)
{
g_free (self->value);
self->value = g_strdup (value);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VALUE]);
}
}
SysprofEnvironVariable *
sysprof_environ_variable_new (const gchar *key,
const gchar *value)
{
return g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE,
"key", key,
"value", value,
NULL);
}

View File

@ -1,40 +0,0 @@
/* sysprof-environ-variable.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_ENVIRON_VARIABLE (sysprof_environ_variable_get_type())
G_DECLARE_FINAL_TYPE (SysprofEnvironVariable, sysprof_environ_variable, SYSPROF, ENVIRON_VARIABLE, GObject)
SysprofEnvironVariable *sysprof_environ_variable_new (const gchar *key,
const gchar *value);
const gchar *sysprof_environ_variable_get_key (SysprofEnvironVariable *self);
void sysprof_environ_variable_set_key (SysprofEnvironVariable *self,
const gchar *key);
const gchar *sysprof_environ_variable_get_value (SysprofEnvironVariable *self);
void sysprof_environ_variable_set_value (SysprofEnvironVariable *self,
const gchar *value);
G_END_DECLS

View File

@ -1,379 +0,0 @@
/* sysprof-environ.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-environ"
#include "config.h"
#include <string.h>
#include "sysprof-environ.h"
#include "sysprof-environ-variable.h"
struct _SysprofEnviron
{
GObject parent_instance;
GPtrArray *variables;
};
static void list_model_iface_init (GListModelInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofEnviron, sysprof_environ, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
enum {
CHANGED,
LAST_SIGNAL
};
static guint signals [LAST_SIGNAL];
static void
sysprof_environ_finalize (GObject *object)
{
SysprofEnviron *self = (SysprofEnviron *)object;
g_clear_pointer (&self->variables, g_ptr_array_unref);
G_OBJECT_CLASS (sysprof_environ_parent_class)->finalize (object);
}
static void
sysprof_environ_class_init (SysprofEnvironClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_environ_finalize;
signals [CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_signal_set_va_marshaller (signals [CHANGED],
G_TYPE_FROM_CLASS (klass),
g_cclosure_marshal_VOID__VOIDv);
}
static void
sysprof_environ_items_changed (SysprofEnviron *self)
{
g_assert (SYSPROF_IS_ENVIRON (self));
g_signal_emit (self, signals [CHANGED], 0);
}
static void
sysprof_environ_init (SysprofEnviron *self)
{
self->variables = g_ptr_array_new_with_free_func (g_object_unref);
g_signal_connect (self,
"items-changed",
G_CALLBACK (sysprof_environ_items_changed),
NULL);
}
static GType
sysprof_environ_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_ENVIRON_VARIABLE;
}
static gpointer
sysprof_environ_get_item (GListModel *model,
guint position)
{
SysprofEnviron *self = (SysprofEnviron *)model;
g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL);
g_return_val_if_fail (position < self->variables->len, NULL);
return g_object_ref (g_ptr_array_index (self->variables, position));
}
static guint
sysprof_environ_get_n_items (GListModel *model)
{
SysprofEnviron *self = (SysprofEnviron *)model;
g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), 0);
return self->variables->len;
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_n_items = sysprof_environ_get_n_items;
iface->get_item = sysprof_environ_get_item;
iface->get_item_type = sysprof_environ_get_item_type;
}
static void
sysprof_environ_variable_notify (SysprofEnviron *self,
GParamSpec *pspec,
SysprofEnvironVariable *variable)
{
g_assert (SYSPROF_IS_ENVIRON (self));
g_signal_emit (self, signals [CHANGED], 0);
}
void
sysprof_environ_setenv (SysprofEnviron *self,
const gchar *key,
const gchar *value)
{
guint i;
g_return_if_fail (SYSPROF_IS_ENVIRON (self));
g_return_if_fail (key != NULL);
for (i = 0; i < self->variables->len; i++)
{
SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i);
const gchar *var_key = sysprof_environ_variable_get_key (var);
if (g_strcmp0 (key, var_key) == 0)
{
if (value == NULL)
{
g_ptr_array_remove_index (self->variables, i);
g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
return;
}
sysprof_environ_variable_set_value (var, value);
return;
}
}
if (value != NULL)
{
SysprofEnvironVariable *var;
guint position = self->variables->len;
var = g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE,
"key", key,
"value", value,
NULL);
g_signal_connect_object (var,
"notify",
G_CALLBACK (sysprof_environ_variable_notify),
self,
G_CONNECT_SWAPPED);
g_ptr_array_add (self->variables, var);
g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
}
}
const gchar *
sysprof_environ_getenv (SysprofEnviron *self,
const gchar *key)
{
guint i;
g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL);
g_return_val_if_fail (key != NULL, NULL);
for (i = 0; i < self->variables->len; i++)
{
SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i);
const gchar *var_key = sysprof_environ_variable_get_key (var);
if (g_strcmp0 (key, var_key) == 0)
return sysprof_environ_variable_get_value (var);
}
return NULL;
}
/**
* sysprof_environ_get_environ:
* @self: An #SysprofEnviron
*
* Gets the environment as a set of key=value pairs, suitable for use
* in various GLib process functions.
*
* Returns: (transfer full): A newly allocated string array.
*
* Since: 3.32
*/
gchar **
sysprof_environ_get_environ (SysprofEnviron *self)
{
GPtrArray *ar;
guint i;
g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL);
ar = g_ptr_array_new ();
for (i = 0; i < self->variables->len; i++)
{
SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i);
const gchar *key = sysprof_environ_variable_get_key (var);
const gchar *value = sysprof_environ_variable_get_value (var);
if (value == NULL)
value = "";
if (key != NULL)
g_ptr_array_add (ar, g_strdup_printf ("%s=%s", key, value));
}
g_ptr_array_add (ar, NULL);
return (gchar **)g_ptr_array_free (ar, FALSE);
}
SysprofEnviron *
sysprof_environ_new (void)
{
return g_object_new (SYSPROF_TYPE_ENVIRON, NULL);
}
void
sysprof_environ_remove (SysprofEnviron *self,
SysprofEnvironVariable *variable)
{
guint i;
g_return_if_fail (SYSPROF_IS_ENVIRON (self));
g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable));
for (i = 0; i < self->variables->len; i++)
{
SysprofEnvironVariable *item = g_ptr_array_index (self->variables, i);
if (item == variable)
{
g_ptr_array_remove_index (self->variables, i);
g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
break;
}
}
}
void
sysprof_environ_append (SysprofEnviron *self,
SysprofEnvironVariable *variable)
{
guint position;
g_return_if_fail (SYSPROF_IS_ENVIRON (self));
g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable));
position = self->variables->len;
g_signal_connect_object (variable,
"notify",
G_CALLBACK (sysprof_environ_variable_notify),
self,
G_CONNECT_SWAPPED);
g_ptr_array_add (self->variables, g_object_ref (variable));
g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
}
/**
* sysprof_environ_copy:
* @self: An #SysprofEnviron
*
* Copies the contents of #SysprofEnviron into a newly allocated #SysprofEnviron.
*
* Returns: (transfer full): An #SysprofEnviron.
*
* Since: 3.32
*/
SysprofEnviron *
sysprof_environ_copy (SysprofEnviron *self)
{
g_autoptr(SysprofEnviron) copy = NULL;
g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL);
copy = sysprof_environ_new ();
sysprof_environ_copy_into (self, copy, TRUE);
return g_steal_pointer (&copy);
}
void
sysprof_environ_copy_into (SysprofEnviron *self,
SysprofEnviron *dest,
gboolean replace)
{
g_return_if_fail (SYSPROF_IS_ENVIRON (self));
g_return_if_fail (SYSPROF_IS_ENVIRON (dest));
for (guint i = 0; i < self->variables->len; i++)
{
SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i);
const gchar *key = sysprof_environ_variable_get_key (var);
const gchar *value = sysprof_environ_variable_get_value (var);
if (replace || sysprof_environ_getenv (dest, key) == NULL)
sysprof_environ_setenv (dest, key, value);
}
}
/**
* ide_environ_parse:
* @pair: the KEY=VALUE pair
* @key: (out) (optional): a location for a @key
* @value: (out) (optional): a location for a @value
*
* Parses a KEY=VALUE style key-pair into @key and @value.
*
* Returns: %TRUE if @pair was successfully parsed
*
* Since: 3.32
*/
gboolean
ide_environ_parse (const gchar *pair,
gchar **key,
gchar **value)
{
const gchar *eq;
g_return_val_if_fail (pair != NULL, FALSE);
if (key != NULL)
*key = NULL;
if (value != NULL)
*value = NULL;
if ((eq = strchr (pair, '=')))
{
if (key != NULL)
*key = g_strndup (pair, eq - pair);
if (value != NULL)
*value = g_strdup (eq + 1);
return TRUE;
}
return FALSE;
}

View File

@ -1,52 +0,0 @@
/* sysprof-environ.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-environ-variable.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ENVIRON (sysprof_environ_get_type())
G_DECLARE_FINAL_TYPE (SysprofEnviron, sysprof_environ, SYSPROF, ENVIRON, GObject)
gboolean ide_environ_parse (const gchar *pair,
gchar **key,
gchar **value);
SysprofEnviron *sysprof_environ_new (void);
void sysprof_environ_setenv (SysprofEnviron *self,
const gchar *key,
const gchar *value);
const gchar *sysprof_environ_getenv (SysprofEnviron *self,
const gchar *key);
gchar **sysprof_environ_get_environ (SysprofEnviron *self);
void sysprof_environ_append (SysprofEnviron *self,
SysprofEnvironVariable *variable);
void sysprof_environ_remove (SysprofEnviron *self,
SysprofEnvironVariable *variable);
SysprofEnviron *sysprof_environ_copy (SysprofEnviron *self);
void sysprof_environ_copy_into (SysprofEnviron *self,
SysprofEnviron *dest,
gboolean replace);
G_END_DECLS

View File

@ -1,62 +0,0 @@
/* sysprof-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 "config.h"
#include "sysprof-failed-state-view.h"
G_DEFINE_TYPE (SysprofFailedStateView, sysprof_failed_state_view, GTK_TYPE_WIDGET)
GtkWidget *
sysprof_failed_state_view_new (void)
{
return g_object_new (SYSPROF_TYPE_FAILED_STATE_VIEW, NULL);
}
static void
sysprof_failed_state_view_dispose (GObject *object)
{
SysprofFailedStateView *self = (SysprofFailedStateView *)object;
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
gtk_widget_unparent (child);
G_OBJECT_CLASS (sysprof_failed_state_view_parent_class)->dispose (object);
}
static void
sysprof_failed_state_view_class_init (SysprofFailedStateViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = sysprof_failed_state_view_dispose;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/sysprof/ui/sysprof-failed-state-view.ui");
}
static void
sysprof_failed_state_view_init (SysprofFailedStateView *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}

View File

@ -1,47 +0,0 @@
<?xml version="1.0"?>
<interface>
<template class="SysprofFailedStateView" parent="GtkWidget">
<property name="vexpand">true</property>
<property name="valign">center</property>
<child>
<object class="GtkBox">
<property name="margin-top">36</property>
<property name="margin-bottom">36</property>
<property name="margin-start">36</property>
<property name="margin-end">36</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child type="center">
<object class="GtkImage">
<property name="icon-name">computer-fail-symbolic</property>
<property name="pixel-size">256</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label" translatable="yes">Ouch, that hurt!</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="2"/>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
<child type="end">
<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>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -1,909 +0,0 @@
/* sysprof-line-visualizer.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-line-visualizer"
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <sysprof.h>
#include "pointcache.h"
#include "sysprof-line-visualizer.h"
typedef struct
{
/*
* Our reader as assigned by the visualizer system.
*/
SysprofCaptureReader *reader;
/*
* An array of LineInfo which contains information about the counters
* we need to render.
*/
GArray *lines;
/*
* This is our set of cached points to render. Once it is assigned here,
* it is immutable (and therefore may be shared with worker processes
* that are rendering the points).
*/
PointCache *cache;
/* The format for units (such as mHz, Watts, etc). */
gchar *units;
/*
* 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;
} SysprofLineVisualizerPrivate;
typedef struct
{
guint id;
guint type;
gdouble line_width;
GdkRGBA foreground;
GdkRGBA background;
guint use_default_style : 1;
guint fill : 1;
guint use_dash : 1;
} LineInfo;
typedef struct
{
SysprofCaptureCursor *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 (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF_TYPE_VISUALIZER)
static void sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
static PointCache *sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self,
GAsyncResult *result,
GError **error);
enum {
PROP_0,
PROP_Y_LOWER,
PROP_Y_UPPER,
PROP_UNITS,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static const gdouble dashes[] = { 1.0, 2.0 };
static void
load_data_free (gpointer data)
{
LoadData *load = data;
if (load != NULL)
{
g_clear_pointer (&load->lines, g_array_unref);
g_clear_pointer (&load->cursor, sysprof_capture_cursor_unref);
g_clear_pointer (&load->cache, point_cache_unref);
g_slice_free (LoadData, load);
}
}
static GArray *
copy_array (GArray *ar)
{
GArray *ret;
ret = g_array_sized_new (FALSE, FALSE, g_array_get_element_size (ar), ar->len);
g_array_set_size (ret, ar->len);
memcpy (ret->data, ar->data, ar->len * g_array_get_element_size (ret));
return ret;
}
static void
sysprof_line_visualizer_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
static PangoAttrList *attrs = NULL;
SysprofLineVisualizer *self = (SysprofLineVisualizer *)widget;
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_autofree gchar *upper = NULL;
GtkStyleContext *style_context;
PangoLayout *layout;
cairo_t *cr;
GtkAllocation alloc;
GdkRectangle clip;
GdkRGBA foreground;
g_assert (SYSPROF_IS_LINE_VISUALIZER (widget));
g_assert (snapshot != NULL);
gtk_widget_get_allocation (widget, &alloc);
GTK_WIDGET_CLASS (sysprof_line_visualizer_parent_class)->snapshot (widget, snapshot);
if (priv->cache == NULL)
return;
#if 0
if (!gdk_cairo_get_clip_rectangle (cr, &clip))
return ret;
#else
clip.x = 0;
clip.y = 0;
clip.width = alloc.width;
clip.height = alloc.height;
#endif
cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height));
style_context = gtk_widget_get_style_context (widget);
gtk_style_context_get_color (style_context, &foreground);
for (guint line = 0; line < priv->lines->len; line++)
{
g_autofree SysprofVisualizerAbsolutePoint *points = NULL;
const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line);
const Point *fpoints;
guint n_fpoints = 0;
GdkRGBA color;
fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints);
if (n_fpoints > 0)
{
gdouble last_x = 0;
gdouble last_y = 0;
guint p;
points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self),
(const SysprofVisualizerRelativePoint *)fpoints,
n_fpoints,
points,
n_fpoints);
for (p = 0; p < n_fpoints; p++)
{
if (points[p].x >= clip.x)
break;
}
if (p >= n_fpoints)
goto cleanup;
if (p > 0)
p--;
last_x = points[p].x;
last_y = points[p].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 = p + 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 (points[i].x > clip.x + clip.width)
break;
}
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->use_dash)
cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0);
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);
}
}
if (!attrs)
{
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_scale_new (0.666));
}
if (priv->y_upper != 100.0)
{
if (priv->units)
upper = g_strdup_printf ("%lg %s", priv->y_upper, priv->units);
else
upper = g_strdup_printf ("%lg", priv->y_upper);
layout = gtk_widget_create_pango_layout (widget, upper);
pango_layout_set_attributes (layout, attrs);
cairo_move_to (cr, 2, 2);
foreground.alpha *= 0.5;
gdk_cairo_set_source_rgba (cr, &foreground);
pango_cairo_show_layout (cr, layout);
g_clear_object (&layout);
}
cleanup:
cairo_destroy (cr);
}
static void
sysprof_line_visualizer_load_data_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofLineVisualizer *self = (SysprofLineVisualizer *)object;
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_autoptr(GError) error = NULL;
g_autoptr(PointCache) cache = NULL;
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
cache = sysprof_line_visualizer_load_data_finish (self, result, &error);
if (cache == NULL)
{
g_warning ("%s", error->message);
return;
}
g_clear_pointer (&priv->cache, point_cache_unref);
priv->cache = g_steal_pointer (&cache);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static gboolean
sysprof_line_visualizer_do_reload (gpointer data)
{
SysprofLineVisualizer *self = data;
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
priv->queued_load = 0;
if (priv->reader != NULL)
{
sysprof_line_visualizer_load_data_async (self,
NULL,
sysprof_line_visualizer_load_data_cb,
NULL);
}
return G_SOURCE_REMOVE;
}
static void
sysprof_line_visualizer_queue_reload (SysprofLineVisualizer *self)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->queued_load == 0)
priv->queued_load = g_idle_add_full (G_PRIORITY_LOW,
sysprof_line_visualizer_do_reload,
self,
NULL);
}
static void
sysprof_line_visualizer_set_reader (SysprofVisualizer *row,
SysprofCaptureReader *reader)
{
SysprofLineVisualizer *self = (SysprofLineVisualizer *)row;
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->reader != reader)
{
if (priv->reader != NULL)
{
sysprof_capture_reader_unref (priv->reader);
priv->reader = NULL;
}
if (reader != NULL)
priv->reader = sysprof_capture_reader_ref (reader);
sysprof_line_visualizer_queue_reload (self);
}
}
static void
sysprof_line_visualizer_finalize (GObject *object)
{
SysprofLineVisualizer *self = (SysprofLineVisualizer *)object;
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_clear_pointer (&priv->units, g_free);
g_clear_pointer (&priv->lines, g_array_unref);
g_clear_pointer (&priv->cache, point_cache_unref);
g_clear_pointer (&priv->reader, sysprof_capture_reader_unref);
g_clear_handle_id (&priv->queued_load, g_source_remove);
G_OBJECT_CLASS (sysprof_line_visualizer_parent_class)->finalize (object);
}
static void
sysprof_line_visualizer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object);
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
switch (prop_id)
{
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;
case PROP_UNITS:
g_value_set_string (value, priv->units);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_line_visualizer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object);
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
switch (prop_id)
{
case PROP_Y_LOWER:
priv->y_lower = g_value_get_double (value);
priv->y_lower_set = TRUE;
gtk_widget_queue_allocate (GTK_WIDGET (self));
break;
case PROP_Y_UPPER:
priv->y_upper = g_value_get_double (value);
priv->y_upper_set = TRUE;
gtk_widget_queue_allocate (GTK_WIDGET (self));
break;
case PROP_UNITS:
g_free (priv->units);
priv->units = g_value_dup_string (value);
gtk_widget_queue_allocate (GTK_WIDGET (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_line_visualizer_class_init (SysprofLineVisualizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
object_class->finalize = sysprof_line_visualizer_finalize;
object_class->get_property = sysprof_line_visualizer_get_property;
object_class->set_property = sysprof_line_visualizer_set_property;
widget_class->snapshot = sysprof_line_visualizer_snapshot;
visualizer_class->set_reader = sysprof_line_visualizer_set_reader;
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));
properties [PROP_UNITS] =
g_param_spec_string ("units",
"Units",
"The format for units (mHz, Watts, etc)",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_line_visualizer_init (SysprofLineVisualizer *self)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo));
}
void
sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self,
guint counter_id,
const GdkRGBA *color)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
LineInfo line_info = { 0 };
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
g_assert (priv->lines != NULL);
line_info.id = counter_id;
line_info.line_width = 1.0;
line_info.type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
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 (SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added)
SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id);
sysprof_line_visualizer_queue_reload (self);
}
void
sysprof_line_visualizer_clear (SysprofLineVisualizer *self)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
if (priv->lines->len > 0)
g_array_remove_range (priv->lines, 0, priv->lines->len);
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)
{
for (guint i = 0; i < load->lines->len; i++)
{
const LineInfo *info = &g_array_index (load->lines, LineInfo, i);
if (info->id == counter_id)
return info->type;
}
return SYSPROF_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 bool
sysprof_line_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
LoadData *load = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET ||
frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
g_assert (load != NULL);
if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET)
{
const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame;
gdouble x = calc_x (load->begin_time, load->end_time, frame->time);
for (guint i = 0; i < set->n_values; i++)
{
const SysprofCaptureCounterValues *group = &set->values[i];
for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++)
{
guint counter_id = group->ids[j];
if (counter_id != 0 && contains_id (load->lines, counter_id))
{
gdouble y;
if (counter_type (load, counter_id) == SYSPROF_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 bool
sysprof_line_visualizer_load_data_range_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
LoadData *load = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET ||
frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF);
g_assert (load != NULL);
g_assert (load->y_upper_set == FALSE ||
load->y_lower_set == FALSE);
if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF)
{
const SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame;
for (guint i = 0; i < def->n_counters; i++)
{
const SysprofCaptureCounter *ctr = &def->counters[i];
for (guint j = 0; j < load->lines->len; j++)
{
LineInfo *info = &g_array_index (load->lines, LineInfo, j);
if (info->id == ctr->id)
{
info->type = ctr->type;
break;
}
}
}
}
else if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET)
{
const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame;
for (guint i = 0; i < set->n_values; i++)
{
const SysprofCaptureCounterValues *group = &set->values[i];
for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++)
{
guint counter_id = group->ids[j];
if (counter_id != 0 && contains_id (load->lines, counter_id))
{
gdouble y;
if (counter_type (load, counter_id) == SYSPROF_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
sysprof_line_visualizer_load_data_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
LoadData *load = task_data;
g_autoptr(GArray) counter_ids = NULL;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_LINE_VISUALIZER (source_object));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
counter_ids = g_array_new (FALSE, FALSE, sizeof (guint));
for (guint i = 0; i < load->lines->len; i++)
{
const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i);
g_array_append_val (counter_ids, line_info->id);
}
sysprof_capture_cursor_add_condition (load->cursor,
sysprof_capture_condition_new_where_counter_in (counter_ids->len,
(guint *)(gpointer)counter_ids->data));
/* If y boundaries are not set, we need to discover them by scaning the data. */
if (!load->y_lower_set || !load->y_upper_set)
{
sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_range_cb, load);
sysprof_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 = load->y_upper + ((load->y_upper - load->y_lower) * .25);
}
sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_frame_cb, load);
g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref);
}
static void
sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_autoptr(GTask) task = NULL;
LoadData *load;
g_assert (SYSPROF_IS_LINE_VISUALIZER (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_priority (task, G_PRIORITY_LOW);
g_task_set_source_tag (task, sysprof_line_visualizer_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_set ? priv->y_lower : G_MAXDOUBLE;
load->y_upper = priv->y_upper_set ? priv->y_upper : -G_MAXDOUBLE;
load->y_lower_set = priv->y_lower_set;
load->y_upper_set = priv->y_upper_set;
load->begin_time = sysprof_capture_reader_get_start_time (priv->reader);
load->end_time = sysprof_capture_reader_get_end_time (priv->reader);
load->cursor = sysprof_capture_cursor_new (priv->reader);
load->lines = copy_array (priv->lines);
for (guint i = 0; i < load->lines->len; i++)
{
const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i);
point_cache_add_set (load->cache, line_info->id);
}
g_task_set_task_data (task, load, load_data_free);
g_task_run_in_thread (task, sysprof_line_visualizer_load_data_worker);
}
static PointCache *
sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self,
GAsyncResult *result,
GError **error)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
LoadData *state;
g_assert (SYSPROF_IS_LINE_VISUALIZER (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
sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self,
guint counter_id,
gdouble width)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
LineInfo *info = &g_array_index (priv->lines, LineInfo, i);
if (info->id == counter_id)
{
info->line_width = width;
sysprof_line_visualizer_queue_reload (self);
break;
}
}
}
void
sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self,
guint counter_id,
const GdkRGBA *color)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
LineInfo *info = &g_array_index (priv->lines, LineInfo, i);
if (info->id == counter_id)
{
info->fill = !!color;
if (color != NULL)
info->background = *color;
sysprof_line_visualizer_queue_reload (self);
break;
}
}
}
void
sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self,
guint counter_id,
gboolean use_dash)
{
SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self);
g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self));
for (guint i = 0; i < priv->lines->len; i++)
{
LineInfo *info = &g_array_index (priv->lines, LineInfo, i);
if (info->id == counter_id)
{
info->use_dash = !!use_dash;
sysprof_line_visualizer_queue_reload (self);
break;
}
}
}

View File

@ -1,57 +0,0 @@
/* sysprof-line-visualizer.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "sysprof-visualizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_LINE_VISUALIZER (sysprof_line_visualizer_get_type())
G_DECLARE_DERIVABLE_TYPE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF, LINE_VISUALIZER, SysprofVisualizer)
struct _SysprofLineVisualizerClass
{
SysprofVisualizerClass parent_class;
void (*counter_added) (SysprofLineVisualizer *self,
guint counter_id);
/*< private >*/
gpointer _reserved[16];
};
GtkWidget *sysprof_line_visualizer_new (void);
void sysprof_line_visualizer_clear (SysprofLineVisualizer *self);
void sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self,
guint counter_id,
const GdkRGBA *color);
void sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self,
guint counter_id,
gdouble width);
void sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self,
guint counter_id,
const GdkRGBA *color);
void sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self,
guint counter_id,
gboolean use_dash);
G_END_DECLS

View File

@ -1,422 +0,0 @@
/* sysprof-log-model.c
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "sysprof-log-model"
#include "config.h"
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <string.h>
#include "sysprof-log-model.h"
struct _SysprofLogModel
{
GObject parent_instance;
GStringChunk *chunks;
GArray *items;
gint64 begin_time;
};
typedef struct
{
gint64 time;
const gchar *domain;
const gchar *message;
guint16 severity;
} Item;
static gint
sysprof_log_model_get_n_columns (GtkTreeModel *model)
{
return SYSPROF_LOG_MODEL_COLUMN_LAST;
}
static GType
sysprof_log_model_get_column_type (GtkTreeModel *model,
gint column)
{
switch (column)
{
case SYSPROF_LOG_MODEL_COLUMN_TIME:
return G_TYPE_INT64;
case SYSPROF_LOG_MODEL_COLUMN_SEVERITY:
case SYSPROF_LOG_MODEL_COLUMN_DOMAIN:
case SYSPROF_LOG_MODEL_COLUMN_MESSAGE:
case SYSPROF_LOG_MODEL_COLUMN_TIME_STRING:
return G_TYPE_STRING;
default:
return 0;
}
}
static GtkTreePath *
sysprof_log_model_get_path (GtkTreeModel *model,
GtkTreeIter *iter)
{
gint off;
g_assert (SYSPROF_IS_LOG_MODEL (model));
g_assert (iter != NULL);
off = GPOINTER_TO_INT (iter->user_data);
return gtk_tree_path_new_from_indices (off, -1);
}
static gboolean
sysprof_log_model_get_iter (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path)
{
SysprofLogModel *self = (SysprofLogModel *)model;
gint off;
g_assert (SYSPROF_IS_LOG_MODEL (self));
g_assert (iter != NULL);
g_assert (path != NULL);
memset (iter, 0, sizeof *iter);
if (gtk_tree_path_get_depth (path) != 1)
return FALSE;
off = gtk_tree_path_get_indices (path)[0];
iter->user_data = GINT_TO_POINTER (off);
return off >= 0 && off < self->items->len;
}
static gboolean
sysprof_log_model_iter_next (GtkTreeModel *model,
GtkTreeIter *iter)
{
SysprofLogModel *self = (SysprofLogModel *)model;
gint off;
g_assert (SYSPROF_IS_LOG_MODEL (self));
g_assert (iter != NULL);
off = GPOINTER_TO_INT (iter->user_data);
off++;
iter->user_data = GINT_TO_POINTER (off);
return off < self->items->len;
}
static gboolean
sysprof_log_model_iter_nth_child (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
SysprofLogModel *self = (SysprofLogModel *)model;
g_assert (SYSPROF_IS_LOG_MODEL (self));
g_assert (iter != NULL);
if (parent != NULL)
return FALSE;
iter->user_data = GINT_TO_POINTER (n);
return n < self->items->len;
}
static gint
sysprof_log_model_iter_n_children (GtkTreeModel *model,
GtkTreeIter *iter)
{
SysprofLogModel *self = (SysprofLogModel *)model;
g_assert (SYSPROF_IS_LOG_MODEL (self));
return iter ? 0 : self->items->len;
}
static gboolean
sysprof_log_model_iter_has_child (GtkTreeModel *model,
GtkTreeIter *iter)
{
return FALSE;
}
static GtkTreeModelFlags
sysprof_log_model_get_flags (GtkTreeModel *model)
{
return GTK_TREE_MODEL_LIST_ONLY;
}
static void
sysprof_log_model_get_value (GtkTreeModel *model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
SysprofLogModel *self = (SysprofLogModel *)model;
const Item *item;
g_assert (SYSPROF_IS_LOG_MODEL (self));
g_assert (iter != NULL);
g_assert (column < SYSPROF_LOG_MODEL_COLUMN_LAST);
item = &g_array_index (self->items, Item, GPOINTER_TO_INT (iter->user_data));
switch (column)
{
case SYSPROF_LOG_MODEL_COLUMN_TIME_STRING:
{
gint64 offset = item->time - self->begin_time;
gint min = offset / SYSPROF_NSEC_PER_SEC / 60L;
gint seconds = ((offset - (min * SYSPROF_NSEC_PER_SEC)) / SYSPROF_NSEC_PER_SEC) % 60;
gint msec = (offset % SYSPROF_NSEC_PER_SEC) / (SYSPROF_NSEC_PER_SEC / 1000L);
g_value_init (value, G_TYPE_STRING);
g_value_take_string (value,
g_strdup_printf ("%02d:%02d.%03d", min, seconds, msec));
}
break;
case SYSPROF_LOG_MODEL_COLUMN_TIME:
g_value_init (value, G_TYPE_INT64);
g_value_set_int64 (value, item->time);
break;
case SYSPROF_LOG_MODEL_COLUMN_SEVERITY:
g_value_init (value, G_TYPE_STRING);
switch (item->severity)
{
case G_LOG_LEVEL_MESSAGE:
g_value_set_static_string (value, _("Message"));
break;
case G_LOG_LEVEL_INFO:
g_value_set_static_string (value, _("Info"));
break;
case G_LOG_LEVEL_CRITICAL:
g_value_set_static_string (value, _("Critical"));
break;
case G_LOG_LEVEL_ERROR:
g_value_set_static_string (value, _("Error"));
break;
case G_LOG_LEVEL_DEBUG:
g_value_set_static_string (value, _("Debug"));
break;
case G_LOG_LEVEL_WARNING:
g_value_set_static_string (value, _("Warning"));
break;
default:
g_value_set_static_string (value, "");
break;
}
break;
case SYSPROF_LOG_MODEL_COLUMN_DOMAIN:
g_value_init (value, G_TYPE_STRING);
g_value_set_string (value, item->domain);
break;
case SYSPROF_LOG_MODEL_COLUMN_MESSAGE:
g_value_init (value, G_TYPE_STRING);
g_value_set_string (value, item->message);
break;
default:
break;
}
}
static void
tree_model_iface_init (GtkTreeModelIface *iface)
{
iface->get_n_columns = sysprof_log_model_get_n_columns;
iface->get_column_type = sysprof_log_model_get_column_type;
iface->get_iter = sysprof_log_model_get_iter;
iface->get_path = sysprof_log_model_get_path;
iface->iter_next = sysprof_log_model_iter_next;
iface->iter_n_children = sysprof_log_model_iter_n_children;
iface->iter_nth_child = sysprof_log_model_iter_nth_child;
iface->iter_has_child = sysprof_log_model_iter_has_child;
iface->get_flags = sysprof_log_model_get_flags;
iface->get_value = sysprof_log_model_get_value;
}
G_DEFINE_TYPE_WITH_CODE (SysprofLogModel, sysprof_log_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, tree_model_iface_init))
static void
sysprof_log_model_finalize (GObject *object)
{
SysprofLogModel *self = (SysprofLogModel *)object;
g_clear_pointer (&self->items, g_array_unref);
g_clear_pointer (&self->chunks, g_string_chunk_free);
G_OBJECT_CLASS (sysprof_log_model_parent_class)->finalize (object);
}
static void
sysprof_log_model_class_init (SysprofLogModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_log_model_finalize;
}
static void
sysprof_log_model_init (SysprofLogModel *self)
{
self->chunks = g_string_chunk_new (4096*16);
self->items = g_array_new (FALSE, FALSE, sizeof (Item));
}
static bool
cursor_foreach_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
SysprofLogModel *self = user_data;
SysprofCaptureLog *log = (SysprofCaptureLog *)frame;
Item item;
g_assert (SYSPROF_IS_LOG_MODEL (self));
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_LOG);
item.time = frame->time;
item.severity = log->severity;
item.domain = g_string_chunk_insert_const (self->chunks, log->domain);
item.message = g_string_chunk_insert_const (self->chunks, log->message);
g_array_append_val (self->items, item);
return TRUE;
}
static gint
item_compare (gconstpointer a,
gconstpointer b)
{
const Item *ia = a;
const Item *ib = b;
if (ia->time < ib->time)
return -1;
else if (ia->time > ib->time)
return 1;
else
return 0;
}
static void
sysprof_log_model_new_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(SysprofLogModel) self = NULL;
SysprofCaptureCursor *cursor = task_data;
SysprofCaptureReader *reader;
g_assert (G_IS_TASK (task));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
self = g_object_new (SYSPROF_TYPE_LOG_MODEL, NULL);
reader = sysprof_capture_cursor_get_reader (cursor);
self->begin_time = sysprof_capture_reader_get_start_time (reader);
sysprof_capture_cursor_foreach (cursor, cursor_foreach_cb, self);
g_array_sort (self->items, item_compare);
g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref);
}
static void
sysprof_log_model_selection_foreach_cb (SysprofSelection *selection,
gint64 begin,
gint64 end,
gpointer user_data)
{
SysprofCaptureCondition **condition = user_data;
SysprofCaptureCondition *c;
g_assert (SYSPROF_IS_SELECTION (selection));
g_assert (condition != NULL);
c = sysprof_capture_condition_new_where_time_between (begin, end);
if (*condition != NULL)
*condition = sysprof_capture_condition_new_or (g_steal_pointer (&c),
g_steal_pointer (condition));
else
*condition = g_steal_pointer (&c);
}
void
sysprof_log_model_new_async (SysprofCaptureReader *reader,
SysprofSelection *selection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
static const SysprofCaptureFrameType types[] = {
SYSPROF_CAPTURE_FRAME_LOG,
};
g_autoptr(SysprofCaptureCursor) cursor = NULL;
SysprofCaptureCondition *c;
g_autoptr(GTask) task = NULL;
g_return_if_fail (reader != NULL);
g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
cursor = sysprof_capture_cursor_new (reader);
c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
if (selection)
{
SysprofCaptureCondition *condition = NULL;
sysprof_selection_foreach (selection,
sysprof_log_model_selection_foreach_cb,
&condition);
if (condition)
c = sysprof_capture_condition_new_and (c, g_steal_pointer (&condition));
}
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_log_model_new_async);
g_task_set_task_data (task,
g_steal_pointer (&cursor),
(GDestroyNotify) sysprof_capture_cursor_unref);
g_task_run_in_thread (task, sysprof_log_model_new_worker);
}
SysprofLogModel *
sysprof_log_model_new_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}

View File

@ -1,49 +0,0 @@
/* sysprof-log-model.h
*
* Copyright 2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <sysprof.h>
G_BEGIN_DECLS
typedef enum
{
SYSPROF_LOG_MODEL_COLUMN_TIME,
SYSPROF_LOG_MODEL_COLUMN_SEVERITY,
SYSPROF_LOG_MODEL_COLUMN_DOMAIN,
SYSPROF_LOG_MODEL_COLUMN_MESSAGE,
SYSPROF_LOG_MODEL_COLUMN_TIME_STRING,
SYSPROF_LOG_MODEL_COLUMN_LAST
} SysprofLogModelColumn;
#define SYSPROF_TYPE_LOG_MODEL (sysprof_log_model_get_type())
G_DECLARE_FINAL_TYPE (SysprofLogModel, sysprof_log_model, SYSPROF, LOG_MODEL, GObject)
void sysprof_log_model_new_async (SysprofCaptureReader *reader,
SysprofSelection *selection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SysprofLogModel *sysprof_log_model_new_finish (GAsyncResult *result,
GError **error);
G_END_DECLS

Some files were not shown because too many files have changed in this diff Show More