source tree cleanup

The lib/ directory was getting a bit out of hand, so this tries
to organize things a bit so it is easier going forward to locate
the code people want to patch.
This commit is contained in:
Christian Hergert
2017-09-28 16:17:56 -07:00
parent a71f05b885
commit c47822b26e
103 changed files with 304 additions and 328 deletions

View File

@ -35,10 +35,10 @@
#include <sys/stat.h>
#include <stdint.h>
#include "binfile.h"
#include "elfparser.h"
#include "util/binfile.h"
#include "util/elfparser.h"
#include "../sp-symbol-dirs.h"
#include "symbols/sp-symbol-dirs.h"
struct bin_file_t
{

View File

@ -20,8 +20,8 @@
#include <elf.h>
#include <sys/mman.h>
#include "demangle.h"
#include "elfparser.h"
#include "util/demangle.h"
#include "util/elfparser.h"
typedef struct Section Section;

View File

@ -18,7 +18,7 @@
#define G_LOG_DOMAIN "pointcache"
#include "pointcache.h"
#include "util/pointcache.h"
struct _PointCache
{

136
lib/util/sp-color-cycle.c Normal file
View File

@ -0,0 +1,136 @@
/* sp-color-cycle.c
*
* Copyright (C) 2016 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/>.
*/
#define G_LOG_DOMAIN "sp-color-cycle"
#include "util/sp-color-cycle.h"
G_DEFINE_BOXED_TYPE (SpColorCycle, sp_color_cycle, sp_color_cycle_ref, sp_color_cycle_unref)
static const gchar *default_colors[] = {
"#73d216",
"#f57900",
"#3465a4",
"#ef2929",
"#75507b",
"#ce5c00",
"#c17d11",
"#cc0000",
"#edd400",
"#555753",
"#4e9a06",
"#204a87",
"#5c3566",
"#a40000",
"#c4a000",
"#8f5902",
"#2e3436",
"#8ae234",
"#729fcf",
"#ad7fa8",
"#fce94f",
"#fcaf3e",
"#e9b96e",
"#888a85",
NULL
};
struct _SpColorCycle
{
volatile gint ref_count;
GdkRGBA *colors;
gsize n_colors;
guint position;
};
static void
sp_color_cycle_destroy (SpColorCycle *self)
{
g_free (self->colors);
g_slice_free (SpColorCycle, self);
}
SpColorCycle *
sp_color_cycle_new (void)
{
SpColorCycle *self;
self = g_slice_new0 (SpColorCycle);
self->ref_count = 1;
self->n_colors = g_strv_length ((gchar **)default_colors);
self->colors = g_new0 (GdkRGBA, self->n_colors);
for (guint i = 0; default_colors[i]; i++)
{
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
}
return self;
}
SpColorCycle *
sp_color_cycle_ref (SpColorCycle *self)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (self->ref_count > 0, NULL);
g_atomic_int_inc (&self->ref_count);
return self;
}
void
sp_color_cycle_unref (SpColorCycle *self)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
if (g_atomic_int_dec_and_test (&self->ref_count))
sp_color_cycle_destroy (self);
}
void
sp_color_cycle_next (SpColorCycle *self,
GdkRGBA *rgba)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->position < self->n_colors);
*rgba = self->colors[self->position];
/*
* TODO: Adjust color HSV/etc
*
* We could simply adjust the brightness/etc after we dispatch
* a color so that we get darker as we go.
*/
self->position = (self->position + 1) % self->n_colors;
}
void
sp_color_cycle_reset (SpColorCycle *self)
{
g_return_if_fail (self != NULL);
for (guint i = 0; default_colors[i]; i++)
{
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
}
self->position = 0;
}

40
lib/util/sp-color-cycle.h Normal file
View File

@ -0,0 +1,40 @@
/* sp-color-cycle.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_COLOR_CYCLE_H
#define SP_COLOR_CYCLE_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SP_TYPE_COLOR_CYCLE (sp_color_cycle_get_type())
typedef struct _SpColorCycle SpColorCycle;
GType sp_color_cycle_get_type (void);
SpColorCycle *sp_color_cycle_ref (SpColorCycle *self);
void sp_color_cycle_unref (SpColorCycle *self);
SpColorCycle *sp_color_cycle_new (void);
void sp_color_cycle_reset (SpColorCycle *self);
void sp_color_cycle_next (SpColorCycle *self,
GdkRGBA *rgba);
G_END_DECLS
#endif /* SP_COLOR_CYCLE_H */

118
lib/util/sp-line-reader.c Normal file
View File

@ -0,0 +1,118 @@
/* sp-line-reader.c
*
* Copyright (C) 2015 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/>.
*/
#include <string.h>
#include "util/sp-line-reader.h"
struct _SpLineReader
{
const gchar *contents;
gsize length;
gsize pos;
};
void
sp_line_reader_free (SpLineReader *self)
{
g_slice_free (SpLineReader, self);
}
/**
* sp_line_reader_new:
* @contents: The buffer to read lines from
* @length: the length of @buffer in bytes
*
* Creates a new #SpLineReader for the contents provided. @contents are not
* copied and therefore it is a programming error to free contents before
* freeing the #SpLineReader structure.
*
* Use sp_line_reader_next() to read through the lines of the buffer.
*
* Returns: (transfer full): A new #SpLineReader that should be freed with
* sp_line_reader_free() when no longer in use.
*/
SpLineReader *
sp_line_reader_new (const gchar *contents,
gssize length)
{
SpLineReader *self = g_slice_new (SpLineReader);
if (contents == NULL)
{
contents = "";
length = 0;
}
else if (length < 0)
{
length = strlen (contents);
}
self->contents = contents;
self->length = length;
self->pos = 0;
return self;
}
/**
* sp_line_reader_next:
* @self: the #SpLineReader
* @length: a location for the length of the line in bytes
*
* Moves forward to the beginning of the next line in the buffer. No changes to
* the buffer are made, and the result is a pointer within the string passed as
* @contents in sp_line_reader_init(). Since the line most likely will not be
* terminated with a NULL byte, you must provide @length to determine the
* length of the line.
*
* Using "line[length]" will place you on the \n that was found for the line.
* However, to perform this safely, you need to know that your string was
* either \0 terminated to begin with, or that your buffer provides enough space
* to guarantee you can dereference past the last "textual content" of the
* buffer.
*
* Returns: (nullable) (transfer none): The beginning of the line within the buffer
*/
const gchar *
sp_line_reader_next (SpLineReader *self,
gsize *length)
{
const gchar *ret;
const gchar *endptr;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (length != NULL, NULL);
if ((self->contents == NULL) || (self->pos >= self->length))
{
*length = 0;
return NULL;
}
ret = &self->contents [self->pos];
endptr = memchr (ret, '\n', self->length - self->pos);
if (G_UNLIKELY (endptr == NULL))
endptr = &self->contents [self->length];
*length = (endptr - ret);
self->pos += *length + 1;
return ret;
}

38
lib/util/sp-line-reader.h Normal file
View File

@ -0,0 +1,38 @@
/* sp-line-reader.h
*
* Copyright (C) 2015 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/>.
*/
#ifndef SP_LINE_READER_H
#define SP_LINE_READER_H
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SpLineReader SpLineReader;
SpLineReader *sp_line_reader_new (const gchar *contents,
gssize length);
void sp_line_reader_free (SpLineReader *self);
const gchar *sp_line_reader_next (SpLineReader *self,
gsize *length);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpLineReader, sp_line_reader_free)
G_END_DECLS
#endif /* SP_LINE_READER_H */

119
lib/util/sp-map-lookaside.c Normal file
View File

@ -0,0 +1,119 @@
/* sp-map-lookaside.c
*
* Copyright (C) 2016 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/>.
*/
#include "util/sp-map-lookaside.h"
struct _SpMapLookaside
{
GSequence *seq;
GStringChunk *chunk;
};
static gint
sp_map_compare (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const SpMap *map_a = a;
const SpMap *map_b = b;
return sp_capture_address_compare (map_a->start, map_b->start);
}
static gint
sp_map_compare_in_range (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const SpMap *map_a = a;
const SpMap *map_b = b;
/*
* map_b is the needle for the search.
* Only map_b->start is set.
*/
if ((map_b->start >= map_a->start) && (map_b->start < map_a->end))
return 0;
return sp_capture_address_compare (map_a->start, map_b->start);
}
static void
sp_map_free (gpointer data)
{
SpMap *map = data;
g_slice_free (SpMap, map);
}
SpMapLookaside *
sp_map_lookaside_new (void)
{
SpMapLookaside *ret;
ret = g_slice_new (SpMapLookaside);
ret->seq = g_sequence_new (sp_map_free);
ret->chunk = g_string_chunk_new (4096);
return ret;
}
void
sp_map_lookaside_free (SpMapLookaside *self)
{
g_sequence_free (self->seq);
g_string_chunk_free (self->chunk);
g_slice_free (SpMapLookaside, self);
}
void
sp_map_lookaside_insert (SpMapLookaside *self,
const SpMap *map)
{
SpMap *copy;
g_assert (self != NULL);
g_assert (map != NULL);
copy = g_slice_new (SpMap);
copy->start = map->start;
copy->end = map->end;
copy->offset = map->offset;
copy->inode = map->inode;
copy->filename = g_string_chunk_insert_const (self->chunk, map->filename);
g_sequence_insert_sorted (self->seq, copy, sp_map_compare, NULL);
}
const SpMap *
sp_map_lookaside_lookup (SpMapLookaside *self,
SpCaptureAddress address)
{
SpMap map = { address };
GSequenceIter *iter;
g_assert (self != NULL);
iter = g_sequence_lookup (self->seq, &map, sp_map_compare_in_range, NULL);
if (iter != NULL)
return g_sequence_get (iter);
return NULL;
}

View File

@ -0,0 +1,50 @@
/* sp-map-lookaside.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_MAP_LOOKASIDE_H
#define SP_MAP_LOOKASIDE_H
#include <glib.h>
#include "capture/sp-capture-types.h"
G_BEGIN_DECLS
typedef struct _SpMapLookaside SpMapLookaside;
typedef struct
{
SpCaptureAddress start;
SpCaptureAddress end;
off_t offset;
ino_t inode;
const gchar *filename;
} SpMap;
SpMapLookaside *sp_map_lookaside_new (void);
void sp_map_lookaside_insert (SpMapLookaside *self,
const SpMap *map);
const SpMap *sp_map_lookaside_lookup (SpMapLookaside *self,
SpCaptureAddress address);
void sp_map_lookaside_free (SpMapLookaside *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpMapLookaside, sp_map_lookaside_free)
G_END_DECLS
#endif /* SP_MAP_LOOKASIDE_H */

472
lib/util/sp-model-filter.c Normal file
View File

@ -0,0 +1,472 @@
/* sp-model-filter.c
*
* Copyright (C) 2016 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/>.
*/
#include "util/sp-model-filter.h"
/*
* This is a simple model filter for GListModel.
*
* Let me start by saying how it works, and then how I wish it worked.
*
* This uses 2 GSequence (Treaps) to track the filters. One matches the
* underlying listmodel. One matches the filtered set. We hold onto our
* own reference to the object in the child list model, and update things
* as necessary when ::items-changed is emitted.
*
* I'd rather see this solved in one of two ways.
*
* 1) Add filtering support to GListStore
*
* or
*
* 2) Create a multi-tree data-structure that contains two tree nodes
* in each element. One tree contains all items, one tree contains
* the visible items (a subset of the other tree). The nodes might
* look something like:
*
* Item {
* TreeNode all_tree;
* TreeNode visible_tree;
* GObject *item;
* }
*
* But either way, this gets the job done for now. I'd venture a guess
* that in many cases (small lists), this is actually slower than just
* rechecking a simple GPtrArray, but let's see how it goes.
*
* -- Christian
*/
typedef struct
{
GListModel *child_model;
GSequence *seq;
GSequence *visible_seq;
SpModelFilterFunc filter_func;
gpointer filter_func_data;
GDestroyNotify filter_func_data_destroy;
guint needs_rebuild : 1;
} SpModelFilterPrivate;
typedef struct
{
GSequenceIter *iter;
GObject *object;
} Element;
static void list_model_iface_init (GListModelInterface *iface);
G_DEFINE_TYPE_EXTENDED (SpModelFilter, sp_model_filter, G_TYPE_OBJECT, 0,
G_ADD_PRIVATE (SpModelFilter)
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
list_model_iface_init))
enum {
PROP_0,
PROP_CHILD_MODEL,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
element_free (gpointer data)
{
Element *e = data;
g_clear_object (&e->object);
g_slice_free (Element, e);
}
static gboolean
sp_model_filter_default_filter_func (GObject *item,
gpointer user_data)
{
return TRUE;
}
static void
sp_model_filter_child_model_items_changed (SpModelFilter *self,
guint position,
guint n_removed,
guint n_added,
GListModel *child_model)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
GSequenceIter *insert_before = NULL;
GSequenceIter *insert_iter;
GSequenceIter *lower;
GSequenceIter *upper;
guint i;
g_assert (SP_IS_MODEL_FILTER (self));
g_assert (G_IS_LIST_MODEL (child_model));
for (i = 0; i < n_removed; i++)
{
GSequenceIter *iter;
Element *ele;
iter = g_sequence_get_iter_at_pos (priv->seq, position);
ele = g_sequence_get (iter);
if (ele->iter)
{
guint visible_position = g_sequence_iter_get_position (ele->iter);
insert_before = g_sequence_iter_next (ele->iter);
g_sequence_remove (ele->iter);
g_list_model_items_changed (G_LIST_MODEL (self), visible_position, 1, 0);
}
g_sequence_remove (iter);
}
insert_iter = g_sequence_get_iter_at_pos (priv->seq, position + 1);
if (insert_before != NULL)
goto add_items;
#if GLIB_CHECK_VERSION(2, 48, 0)
if (g_sequence_is_empty (priv->visible_seq))
#else
if (g_sequence_get_begin_iter (priv->visible_seq) == g_sequence_get_end_iter (priv->visible_seq))
#endif
{
insert_before = g_sequence_get_end_iter (priv->visible_seq);
goto add_items;
}
lower = g_sequence_get_begin_iter (priv->visible_seq);
upper = g_sequence_get_end_iter (priv->visible_seq);
while (lower != upper)
{
GSequenceIter *mid;
GSequenceIter *iter;
guint mid_pos;
mid = g_sequence_range_get_midpoint (lower, upper);
iter = g_sequence_get (mid);
mid_pos = g_sequence_iter_get_position (iter);
if (mid_pos < position)
lower = g_sequence_iter_next (mid);
else if (mid_pos > position)
upper = g_sequence_iter_prev (mid);
else
upper = lower = mid;
}
if (upper == g_sequence_get_end_iter (priv->visible_seq))
insert_before = upper;
else
insert_before =
((guint)g_sequence_iter_get_position (g_sequence_get (upper)) <= position)
? upper : g_sequence_iter_next (upper);
add_items:
for (i = 0; i < n_added; i++)
{
GSequenceIter *iter;
Element *ele;
ele = g_slice_new (Element);
ele->object = g_list_model_get_item (priv->child_model, position + i);
ele->iter = NULL;
iter = g_sequence_insert_before (insert_iter, ele);
if (priv->filter_func (ele->object, priv->filter_func_data))
{
ele->iter = g_sequence_insert_before (insert_before, iter);
g_list_model_items_changed (G_LIST_MODEL (self),
g_sequence_iter_get_position (ele->iter),
0, 1);
}
}
}
static void
sp_model_filter_finalize (GObject *object)
{
SpModelFilter *self = (SpModelFilter *)object;
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_clear_pointer (&priv->seq, g_sequence_free);
g_clear_pointer (&priv->visible_seq, g_sequence_free);
if (priv->filter_func_data_destroy)
{
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
priv->filter_func_data_destroy = NULL;
}
g_clear_object (&priv->child_model);
G_OBJECT_CLASS (sp_model_filter_parent_class)->finalize (object);
}
static void
sp_model_filter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpModelFilter *self = SP_MODEL_FILTER (object);
switch (prop_id)
{
case PROP_CHILD_MODEL:
g_value_set_object (value, sp_model_filter_get_child_model (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_model_filter_class_init (SpModelFilterClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_model_filter_finalize;
object_class->get_property = sp_model_filter_get_property;
properties [PROP_CHILD_MODEL] =
g_param_spec_object ("child-model",
"Child Model",
"The child model being filtered.",
G_TYPE_LIST_MODEL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sp_model_filter_init (SpModelFilter *self)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
priv->filter_func = sp_model_filter_default_filter_func;
priv->seq = g_sequence_new (element_free);
priv->visible_seq = g_sequence_new (NULL);
priv->needs_rebuild = TRUE;
}
static void
sp_model_filter_rebuild (SpModelFilter *self,
gboolean no_emit)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
guint new_n_items = 0;
guint old_n_items;
guint n_items;
guint i;
g_assert (SP_IS_MODEL_FILTER (self));
g_assert (priv->needs_rebuild);
old_n_items = g_sequence_get_length (priv->visible_seq);
g_clear_pointer (&priv->seq, g_sequence_free);
g_clear_pointer (&priv->visible_seq, g_sequence_free);
priv->seq = g_sequence_new (element_free);
priv->visible_seq = g_sequence_new (NULL);
n_items = g_list_model_get_n_items (priv->child_model);
for (i = 0; i < n_items; i++)
{
GSequenceIter *iter;
Element *ele;
ele = g_slice_new (Element);
ele->object = g_list_model_get_item (priv->child_model, i);
ele->iter = NULL;
iter = g_sequence_append (priv->seq, ele);
if (priv->filter_func (ele->object, priv->filter_func_data))
{
ele->iter = g_sequence_append (priv->visible_seq, iter);
new_n_items++;
}
}
if (!no_emit)
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_n_items, new_n_items);
priv->needs_rebuild = FALSE;
}
static GType
sp_model_filter_get_item_type (GListModel *model)
{
SpModelFilter *self = (SpModelFilter *)model;
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_assert (SP_IS_MODEL_FILTER (self));
return g_list_model_get_item_type (priv->child_model);
}
static guint
sp_model_filter_get_n_items (GListModel *model)
{
SpModelFilter *self = (SpModelFilter *)model;
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_assert (SP_IS_MODEL_FILTER (self));
if (priv->needs_rebuild)
sp_model_filter_rebuild (self, TRUE);
return g_sequence_get_length (priv->visible_seq);
}
static gpointer
sp_model_filter_get_item (GListModel *model,
guint position)
{
SpModelFilter *self = (SpModelFilter *)model;
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
GSequenceIter *iter;
Element *ele;
g_assert (SP_IS_MODEL_FILTER (self));
if (priv->needs_rebuild)
sp_model_filter_rebuild (self, TRUE);
iter = g_sequence_get_iter_at_pos (priv->visible_seq, position);
if (!iter || g_sequence_iter_is_end (iter))
{
g_warning ("invalid position for filter, filter is corrupt");
return NULL;
}
iter = g_sequence_get (iter);
if (!iter || g_sequence_iter_is_end (iter))
{
g_warning ("invalid position for filter, filter is corrupt");
return NULL;
}
ele = g_sequence_get (iter);
return g_object_ref (ele->object);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sp_model_filter_get_item_type;
iface->get_n_items = sp_model_filter_get_n_items;
iface->get_item = sp_model_filter_get_item;
}
SpModelFilter *
sp_model_filter_new (GListModel *child_model)
{
SpModelFilter *ret;
SpModelFilterPrivate *priv;
g_return_val_if_fail (G_IS_LIST_MODEL (child_model), NULL);
ret = g_object_new (SP_TYPE_MODEL_FILTER, NULL);
priv = sp_model_filter_get_instance_private (ret);
priv->child_model = g_object_ref (child_model);
g_signal_connect_object (child_model,
"items-changed",
G_CALLBACK (sp_model_filter_child_model_items_changed),
ret,
G_CONNECT_SWAPPED);
return ret;
}
/**
* sp_model_filter_get_child_model:
* @self: A #SpModelFilter
*
* Gets the child model that is being filtered.
*
* Returns: (transfer none): A #GListModel.
*/
GListModel *
sp_model_filter_get_child_model (SpModelFilter *self)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_return_val_if_fail (SP_IS_MODEL_FILTER (self), NULL);
return priv->child_model;
}
void
sp_model_filter_invalidate (SpModelFilter *self)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_return_if_fail (SP_IS_MODEL_FILTER (self));
priv->needs_rebuild = TRUE;
sp_model_filter_rebuild (self, FALSE);
}
void
sp_model_filter_set_filter_func (SpModelFilter *self,
SpModelFilterFunc filter_func,
gpointer filter_func_data,
GDestroyNotify filter_func_data_destroy)
{
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
g_return_if_fail (SP_IS_MODEL_FILTER (self));
g_return_if_fail (filter_func || (!filter_func_data && !filter_func_data_destroy));
if (priv->filter_func_data_destroy)
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
if (filter_func != NULL)
{
priv->filter_func = filter_func;
priv->filter_func_data = filter_func_data;
priv->filter_func_data_destroy = filter_func_data_destroy;
}
else
{
priv->filter_func = sp_model_filter_default_filter_func;
priv->filter_func_data = NULL;
priv->filter_func_data_destroy = NULL;
}
sp_model_filter_invalidate (self);
}

View File

@ -0,0 +1,50 @@
/* sp-model-filter.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_MODEL_FILTER_H
#define SP_MODEL_FILTER_H
#include <gio/gio.h>
G_BEGIN_DECLS
#define SP_TYPE_MODEL_FILTER (sp_model_filter_get_type())
typedef gboolean (*SpModelFilterFunc) (GObject *object,
gpointer user_data);
G_DECLARE_DERIVABLE_TYPE (SpModelFilter, sp_model_filter, SP, MODEL_FILTER, GObject)
struct _SpModelFilterClass
{
GObjectClass parent_class;
gpointer padding[8];
};
SpModelFilter *sp_model_filter_new (GListModel *child_model);
GListModel *sp_model_filter_get_child_model (SpModelFilter *self);
void sp_model_filter_invalidate (SpModelFilter *self);
void sp_model_filter_set_filter_func (SpModelFilter *self,
SpModelFilterFunc filter_func,
gpointer filter_func_data,
GDestroyNotify filter_func_data_destroy);
G_END_DECLS
#endif /* SP_MODEL_FILTER_H */

65
lib/util/sp-platform.c Normal file
View File

@ -0,0 +1,65 @@
/* sp-platform.c
*
* Copyright (C) 2016 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/>.
*/
#include <glib.h>
#include <glib/gstdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "util/sp-platform.h"
/**
* sp_memfd_create:
* @name: (nullable): A descriptive name for the memfd or %NULL
*
* Creates a new memfd using the memfd_create syscall if available.
* Otherwise, a tmpfile is used. Currently, no attempt is made to
* ensure the tmpfile is on a tmpfs backed mount.
*
* Returns: An fd if successful; otherwise -1 and errno is set.
*/
int
sp_memfd_create (const gchar *name)
{
#ifdef __NR_memfd_create
if (name == NULL)
name = "[sysprof]";
return syscall (__NR_memfd_create, name, 0);
#else
gchar *name_used = NULL;
int fd;
/*
* TODO: It would be nice to ensure tmpfs
*
* It is not strictly required that the preferred temporary directory
* will be mounted as tmpfs. We should look through the mounts and ensure
* that the tmpfile we open is on tmpfs so that we get anonymous backed
* pages after unlinking.
*/
fd = g_file_open_tmp (NULL, &name_used, NULL);
if (name_used != NULL)
{
g_unlink (name_used);
g_free (name_used);
}
return fd;
#endif
}

30
lib/util/sp-platform.h Normal file
View File

@ -0,0 +1,30 @@
/* sp-platform.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_PLATFORM_H
#define SP_PLATFORM_H
#include <glib.h>
G_BEGIN_DECLS
int sp_memfd_create (const gchar *desc);
G_END_DECLS
#endif /* SP_PLATFORM_H */

View File

@ -0,0 +1,234 @@
/* sp-process-model-item.c
*
* Copyright (C) 2016 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/>.
*/
#include <string.h>
#include "util/sp-process-model-item.h"
#include "sources/sp-proc-source.h"
struct _SpProcessModelItem
{
GObject parent_instance;
GPid pid;
gchar *command_line; /* Short version (first field) */
gchar **argv; /* Long version (argv as a strv) */
guint is_kernel : 1;
};
G_DEFINE_TYPE (SpProcessModelItem, sp_process_model_item, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_COMMAND_LINE,
PROP_PID,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
sp_process_model_item_finalize (GObject *object)
{
SpProcessModelItem *self = (SpProcessModelItem *)object;
g_clear_pointer (&self->command_line, g_free);
g_clear_pointer (&self->argv, g_strfreev);
G_OBJECT_CLASS (sp_process_model_item_parent_class)->finalize (object);
}
static void
sp_process_model_item_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object);
switch (prop_id)
{
case PROP_COMMAND_LINE:
g_value_set_string (value, self->command_line);
break;
case PROP_PID:
g_value_set_int (value, self->pid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_process_model_item_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object);
switch (prop_id)
{
case PROP_COMMAND_LINE:
self->command_line = g_value_dup_string (value);
break;
case PROP_PID:
self->pid = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_process_model_item_class_init (SpProcessModelItemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_process_model_item_finalize;
object_class->get_property = sp_process_model_item_get_property;
object_class->set_property = sp_process_model_item_set_property;
properties [PROP_COMMAND_LINE] =
g_param_spec_string ("command-line",
"Command Line",
"Command Line",
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties [PROP_PID] =
g_param_spec_int ("pid",
"Pid",
"Pid",
-1,
G_MAXINT,
-1,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sp_process_model_item_init (SpProcessModelItem *self)
{
}
SpProcessModelItem *
sp_process_model_item_new (GPid pid)
{
SpProcessModelItem *ret;
gchar *cmdline;
gboolean is_kernel;
cmdline = sp_proc_source_get_command_line (pid, &is_kernel);
ret = g_object_new (SP_TYPE_PROCESS_MODEL_ITEM,
"command-line", cmdline,
"pid", (int)pid,
NULL);
ret->is_kernel = is_kernel;
g_free (cmdline);
return ret;
}
guint
sp_process_model_item_hash (SpProcessModelItem *self)
{
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0);
return self->pid;
}
gboolean
sp_process_model_item_equal (SpProcessModelItem *self,
SpProcessModelItem *other)
{
g_assert (SP_IS_PROCESS_MODEL_ITEM (self));
g_assert (SP_IS_PROCESS_MODEL_ITEM (other));
return ((self->pid == other->pid) &&
(g_strcmp0 (self->command_line, other->command_line) == 0));
}
GPid
sp_process_model_item_get_pid (SpProcessModelItem *self)
{
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0);
return self->pid;
}
const gchar *
sp_process_model_item_get_command_line (SpProcessModelItem *self)
{
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), NULL);
return self->command_line;
}
gboolean
sp_process_model_item_is_kernel (SpProcessModelItem *self)
{
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), FALSE);
return self->is_kernel;
}
const gchar * const *
sp_process_model_item_get_argv (SpProcessModelItem *self)
{
g_autofree gchar *contents = NULL;
g_autofree gchar *path = NULL;
const gchar *pos;
const gchar *endptr;
GPtrArray *ar;
gsize size = 0;
GPid pid;
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), NULL);
if (self->argv)
return (const gchar * const *)self->argv;
if ((pid = sp_process_model_item_get_pid (self)) < 0)
return NULL;
path = g_strdup_printf ("/proc/%u/cmdline", (guint)pid);
if (!g_file_get_contents (path, &contents, &size, NULL))
return NULL;
ar = g_ptr_array_new ();
/* Each parameter is followed by \0 */
for (pos = contents, endptr = contents + size;
pos < endptr;
pos += strlen (pos) + 1)
g_ptr_array_add (ar, g_strdup (pos));
g_ptr_array_add (ar, NULL);
g_clear_pointer (&self->argv, g_strfreev);
self->argv = (gchar **)g_ptr_array_free (ar, FALSE);
return (const gchar * const *)self->argv;
}

View File

@ -0,0 +1,41 @@
/* sp-process-model-item.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_PROCESS_MODEL_ITEM_H
#define SP_PROCESS_MODEL_ITEM_H
#include <gio/gio.h>
G_BEGIN_DECLS
#define SP_TYPE_PROCESS_MODEL_ITEM (sp_process_model_item_get_type())
G_DECLARE_FINAL_TYPE (SpProcessModelItem, sp_process_model_item, SP, PROCESS_MODEL_ITEM, GObject)
SpProcessModelItem *sp_process_model_item_new (GPid pid);
guint sp_process_model_item_hash (SpProcessModelItem *self);
gboolean sp_process_model_item_equal (SpProcessModelItem *self,
SpProcessModelItem *other);
GPid sp_process_model_item_get_pid (SpProcessModelItem *self);
const gchar *sp_process_model_item_get_command_line (SpProcessModelItem *self);
gboolean sp_process_model_item_is_kernel (SpProcessModelItem *self);
const gchar * const *sp_process_model_item_get_argv (SpProcessModelItem *self);
G_END_DECLS
#endif /* SP_PROCESS_MODEL_ITEM_H */

293
lib/util/sp-process-model.c Normal file
View File

@ -0,0 +1,293 @@
/* sp-process-model.c
*
* Copyright (C) 2016 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/>.
*/
#include <stdlib.h>
#include "util/sp-process-model.h"
#include "util/sp-process-model-item.h"
#define QUEUE_RELOAD_TIMEOUT_MSEC 100
struct _SpProcessModel
{
GObject parent_instance;
guint reload_source;
GPtrArray *items;
};
static void list_model_iface_init (GListModelInterface *iface);
G_DEFINE_TYPE_EXTENDED (SpProcessModel, sp_process_model, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static void
sp_process_model_finalize (GObject *object)
{
SpProcessModel *self = (SpProcessModel *)object;
if (self->reload_source)
{
g_source_remove (self->reload_source);
self->reload_source = 0;
}
g_clear_pointer (&self->items, g_ptr_array_unref);
G_OBJECT_CLASS (sp_process_model_parent_class)->finalize (object);
}
static void
sp_process_model_class_init (SpProcessModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_process_model_finalize;
}
static void
sp_process_model_init (SpProcessModel *self)
{
self->items = g_ptr_array_new_with_free_func (g_object_unref);
sp_process_model_queue_reload (self);
}
static guint
find_index (GPtrArray *ar,
GPid pid)
{
guint i;
g_assert (ar != NULL);
for (i = 0; i < ar->len; i++)
{
SpProcessModelItem *item = g_ptr_array_index (ar, i);
GPid item_pid = sp_process_model_item_get_pid (item);
g_assert (pid != item_pid);
if (item_pid > pid)
return i;
}
return ar->len;
}
static void
sp_process_model_merge_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SpProcessModel *self = (SpProcessModel *)object;
g_autoptr(GPtrArray) ret = NULL;
g_autoptr(GHashTable) old_hash = NULL;
g_autoptr(GHashTable) new_hash = NULL;
GError *error = NULL;
guint i;
g_assert (SP_IS_PROCESS_MODEL (self));
g_assert (G_IS_TASK (result));
ret = g_task_propagate_pointer (G_TASK (result), &error);
if (ret == NULL)
{
g_warning ("%s", error->message);
g_clear_error (&error);
return;
}
/*
* TODO: Clearly this could be optimized to walk both arrays at once
* and do a proper 2-way merge.
*/
old_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash,
(GEqualFunc)sp_process_model_item_equal);
new_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash,
(GEqualFunc)sp_process_model_item_equal);
for (i = 0; i < self->items->len; i++)
{
SpProcessModelItem *item = g_ptr_array_index (self->items, i);
g_hash_table_insert (old_hash, item, NULL);
}
for (i = 0; i < ret->len; i++)
{
SpProcessModelItem *item = g_ptr_array_index (ret, i);
g_hash_table_insert (new_hash, item, NULL);
}
for (i = self->items->len; i > 0; i--)
{
guint index = i - 1;
SpProcessModelItem *item = g_ptr_array_index (self->items, index);
if (!g_hash_table_contains (new_hash, item))
{
g_ptr_array_remove_index (self->items, index);
g_list_model_items_changed (G_LIST_MODEL (self), index, 1, 0);
}
}
for (i = 0; i < ret->len; i++)
{
SpProcessModelItem *item = g_ptr_array_index (ret, i);
GPid pid;
guint index;
if (g_hash_table_contains (old_hash, item))
continue;
pid = sp_process_model_item_get_pid (item);
index = find_index (self->items, pid);
g_ptr_array_insert (self->items, index, g_object_ref (item));
g_list_model_items_changed (G_LIST_MODEL (self), index, 0, 1);
}
}
static gint
compare_by_pid (gconstpointer a,
gconstpointer b)
{
SpProcessModelItem **aitem = (SpProcessModelItem **)a;
SpProcessModelItem **bitem = (SpProcessModelItem **)b;
return sp_process_model_item_get_pid (*aitem) - sp_process_model_item_get_pid (*bitem);
}
static void
sp_process_model_reload_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(GPtrArray) ret = NULL;
const gchar *name;
GError *error = NULL;
GDir *dir;
g_assert (SP_IS_PROCESS_MODEL (source_object));
g_assert (G_IS_TASK (task));
dir = g_dir_open ("/proc", 0, &error);
if (dir == NULL)
{
g_task_return_error (task, error);
return;
}
ret = g_ptr_array_new_with_free_func (g_object_unref);
while ((name = g_dir_read_name (dir)))
{
SpProcessModelItem *item;
GPid pid;
gchar *end;
pid = strtol (name, &end, 10);
if (pid <= 0 || *end != '\0')
continue;
item = sp_process_model_item_new (pid);
if (sp_process_model_item_is_kernel (item))
{
g_object_unref (item);
continue;
}
g_ptr_array_add (ret, item);
}
g_dir_close (dir);
g_ptr_array_sort (ret, compare_by_pid);
g_task_return_pointer (task, g_ptr_array_ref (ret), (GDestroyNotify)g_ptr_array_unref);
}
static gboolean
sp_process_model_do_reload (gpointer user_data)
{
SpProcessModel *self = user_data;
g_autoptr(GTask) task = NULL;
self->reload_source = 0;
task = g_task_new (self, NULL, sp_process_model_merge_cb, NULL);
g_task_run_in_thread (task, sp_process_model_reload_worker);
return G_SOURCE_REMOVE;
}
SpProcessModel *
sp_process_model_new (void)
{
return g_object_new (SP_TYPE_PROCESS_MODEL, NULL);
}
void
sp_process_model_queue_reload (SpProcessModel *self)
{
g_return_if_fail (SP_IS_PROCESS_MODEL (self));
if (self->reload_source == 0)
self->reload_source = g_timeout_add (QUEUE_RELOAD_TIMEOUT_MSEC,
sp_process_model_do_reload,
self);
}
static GType
sp_process_model_get_item_type (GListModel *model)
{
return SP_TYPE_PROCESS_MODEL_ITEM;
}
static guint
sp_process_model_get_n_items (GListModel *model)
{
SpProcessModel *self = (SpProcessModel *)model;
return self->items->len;
}
static gpointer
sp_process_model_get_item (GListModel *model,
guint position)
{
SpProcessModel *self = (SpProcessModel *)model;
g_return_val_if_fail (SP_IS_PROCESS_MODEL (self), NULL);
g_return_val_if_fail (position < self->items->len, NULL);
return g_object_ref (g_ptr_array_index (self->items, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sp_process_model_get_item_type;
iface->get_n_items = sp_process_model_get_n_items;
iface->get_item = sp_process_model_get_item;
}

View File

@ -0,0 +1,35 @@
/* sp-process-model.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_PROCESS_MODEL_H
#define SP_PROCESS_MODEL_H
#include <gio/gio.h>
G_BEGIN_DECLS
#define SP_TYPE_PROCESS_MODEL (sp_process_model_get_type())
G_DECLARE_FINAL_TYPE (SpProcessModel, sp_process_model, SP, PROCESS_MODEL, GObject)
SpProcessModel *sp_process_model_new (void);
void sp_process_model_queue_reload (SpProcessModel *self);
G_END_DECLS
#endif /* SP_PROCESS_MODEL_H */

251
lib/util/sp-selection.c Normal file
View File

@ -0,0 +1,251 @@
/* sp-selection.c
*
* Copyright (C) 2016 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/>.
*/
#define G_LOG_DOMAIN "sp-selection"
#include "util/sp-selection.h"
struct _SpSelection
{
GObject parent_instance;
GArray *ranges;
};
typedef struct
{
gint64 begin;
gint64 end;
} Range;
G_DEFINE_TYPE (SpSelection, sp_selection, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_HAS_SELECTION,
N_PROPS
};
enum {
CHANGED,
N_SIGNALS
};
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static inline void
int64_swap (gint64 *a,
gint64 *b)
{
if (*a > *b)
{
gint64 tmp = *a;
*a = *b;
*b = tmp;
}
}
static void
sp_selection_finalize (GObject *object)
{
SpSelection *self = (SpSelection *)object;
g_clear_pointer (&self->ranges, g_array_unref);
G_OBJECT_CLASS (sp_selection_parent_class)->finalize (object);
}
static void
sp_selection_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpSelection *self = SP_SELECTION (object);
switch (prop_id)
{
case PROP_HAS_SELECTION:
g_value_set_boolean (value, sp_selection_get_has_selection (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_selection_class_init (SpSelectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_selection_finalize;
object_class->get_property = sp_selection_get_property;
properties [PROP_HAS_SELECTION] =
g_param_spec_boolean ("has-selection",
"Has Selection",
"Has Selection",
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
* SpSelection::changed:
*
* This signal is emitted when the selection has changed.
*/
signals [CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
sp_selection_init (SpSelection *self)
{
self->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
}
gboolean
sp_selection_get_has_selection (SpSelection *self)
{
g_return_val_if_fail (SP_IS_SELECTION (self), FALSE);
return self->ranges->len > 0;
}
/**
* sp_selection_foreach:
* @self: A #SpSelection
* @foreach_func: (scope call): a callback for each range
* @user_data: user data for @foreach_func
*
* Calls @foreach_func for every selected range.
*/
void
sp_selection_foreach (SpSelection *self,
SpSelectionForeachFunc foreach_func,
gpointer user_data)
{
g_return_if_fail (SP_IS_SELECTION (self));
g_return_if_fail (foreach_func != NULL);
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
foreach_func (self, range->begin, range->end, user_data);
}
}
void
sp_selection_select_range (SpSelection *self,
gint64 begin_time,
gint64 end_time)
{
Range range = { 0 };
g_return_if_fail (SP_IS_SELECTION (self));
int64_swap (&begin_time, &end_time);
range.begin = begin_time;
range.end = end_time;
g_array_append_val (self->ranges, range);
if (self->ranges->len == 1)
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
}
void
sp_selection_unselect_range (SpSelection *self,
gint64 begin,
gint64 end)
{
g_return_if_fail (SP_IS_SELECTION (self));
int64_swap (&begin, &end);
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
if (range->begin == begin && range->end == end)
{
g_array_remove_index (self->ranges, i);
if (self->ranges->len == 0)
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
break;
}
}
}
void
sp_selection_unselect_all (SpSelection *self)
{
g_return_if_fail (SP_IS_SELECTION (self));
if (self->ranges->len > 0)
{
g_array_remove_range (self->ranges, 0, self->ranges->len);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
}
}
gboolean
sp_selection_contains (SpSelection *self,
gint64 time_at)
{
if (self == NULL || self->ranges->len == 0)
return TRUE;
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
if (time_at >= range->begin && time_at <= range->end)
return TRUE;
}
return FALSE;
}
SpSelection *
sp_selection_copy (const SpSelection *self)
{
SpSelection *copy;
if (self == NULL)
return NULL;
copy = g_object_new (SP_TYPE_SELECTION, NULL);
for (guint i = 0; i < self->ranges->len; i++)
{
Range range = g_array_index (self->ranges, Range, i);
g_array_append_val (copy->ranges, range);
}
return copy;
}

52
lib/util/sp-selection.h Normal file
View File

@ -0,0 +1,52 @@
/* sp-selection.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_SELECTION_H
#define SP_SELECTION_H
#include <glib-object.h>
G_BEGIN_DECLS
#define SP_TYPE_SELECTION (sp_selection_get_type())
G_DECLARE_FINAL_TYPE (SpSelection, sp_selection, SP, SELECTION, GObject)
typedef void (*SpSelectionForeachFunc) (SpSelection *self,
gint64 begin_time,
gint64 end_time,
gpointer user_data);
gboolean sp_selection_get_has_selection (SpSelection *self);
gboolean sp_selection_contains (SpSelection *self,
gint64 time_at);
void sp_selection_select_range (SpSelection *self,
gint64 begin_time,
gint64 end_time);
void sp_selection_unselect_range (SpSelection *self,
gint64 begin,
gint64 end);
void sp_selection_unselect_all (SpSelection *self);
void sp_selection_foreach (SpSelection *self,
SpSelectionForeachFunc foreach_func,
gpointer user_data);
SpSelection *sp_selection_copy (const SpSelection *self);
G_END_DECLS
#endif /* SP_SELECTION_H */

264
lib/util/sp-theme-manager.c Normal file
View File

@ -0,0 +1,264 @@
/* sp-theme-manager.c
*
* Copyright (C) 2016 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/>.
*/
#define G_LOG_DOMAIN "sp-theme-manager"
#include "util/sp-theme-manager.h"
struct _SpThemeManager
{
GObject parent_instance;
GHashTable *theme_resources;
guint reload_source;
guint registered_signals : 1;
};
typedef struct
{
guint id;
gchar *key;
gchar *theme_name;
gchar *variant;
gchar *resource;
GtkCssProvider *provider;
} ThemeResource;
G_DEFINE_TYPE (SpThemeManager, sp_theme_manager, G_TYPE_OBJECT)
static void
theme_resource_free (gpointer data)
{
ThemeResource *theme_resource = data;
if (theme_resource != NULL)
{
g_clear_pointer (&theme_resource->key, g_free);
g_clear_pointer (&theme_resource->theme_name, g_free);
g_clear_pointer (&theme_resource->variant, g_free);
g_clear_pointer (&theme_resource->resource, g_free);
if (theme_resource->provider != NULL)
{
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (theme_resource->provider));
g_clear_object (&theme_resource->provider);
}
g_slice_free (ThemeResource, theme_resource);
}
}
static gboolean
theme_resource_matches (ThemeResource *theme_resource,
GtkSettings *settings)
{
g_autofree gchar *theme_name = NULL;
gboolean dark_theme = FALSE;
g_assert (theme_resource != NULL);
g_assert (GTK_IS_SETTINGS (settings));
if (theme_resource->theme_name == NULL)
return TRUE;
g_object_get (settings,
"gtk-theme-name", &theme_name,
"gtk-application-prefer-dark-theme", &dark_theme,
NULL);
if (g_strcmp0 (theme_name, theme_resource->theme_name) == 0)
{
if (dark_theme && g_strcmp0 ("dark", theme_resource->variant) == 0)
return TRUE;
if (!dark_theme && (!theme_resource->variant || g_strcmp0 ("light", theme_resource->variant) == 0))
return TRUE;
}
return FALSE;
}
static gboolean
sp_theme_manager_do_reload (gpointer data)
{
SpThemeManager *self = data;
ThemeResource *theme_resource;
GHashTableIter iter;
GtkSettings *settings;
g_assert (SP_IS_THEME_MANAGER (self));
self->reload_source = 0;
settings = gtk_settings_get_default ();
g_hash_table_iter_init (&iter, self->theme_resources);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
{
if (theme_resource_matches (theme_resource, settings))
{
if (theme_resource->provider == NULL)
{
theme_resource->provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (theme_resource->provider, theme_resource->resource);
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (theme_resource->provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
}
}
else
{
if (theme_resource->provider != NULL)
{
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (theme_resource->provider));
g_clear_object (&theme_resource->provider);
}
}
}
return G_SOURCE_REMOVE;
}
static void
sp_theme_manager_queue_reload (SpThemeManager *self)
{
g_assert (SP_IS_THEME_MANAGER (self));
if (self->reload_source == 0)
self->reload_source = gdk_threads_add_idle_full (G_PRIORITY_LOW,
sp_theme_manager_do_reload,
self,
NULL);
}
static void
sp_theme_manager_finalize (GObject *object)
{
SpThemeManager *self = (SpThemeManager *)object;
if (self->reload_source != 0)
{
g_source_remove (self->reload_source);
self->reload_source = 0;
}
g_clear_pointer (&self->theme_resources, g_hash_table_unref);
G_OBJECT_CLASS (sp_theme_manager_parent_class)->finalize (object);
}
static void
sp_theme_manager_class_init (SpThemeManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_theme_manager_finalize;
}
static void
sp_theme_manager_init (SpThemeManager *self)
{
self->theme_resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, theme_resource_free);
gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/gnome/sysprof/icons");
}
/**
* sp_theme_manager_get_default:
*
* Returns: (transfer none): An #SpThemeManager
*/
SpThemeManager *
sp_theme_manager_get_default (void)
{
static SpThemeManager *instance;
if (instance == NULL)
instance = g_object_new (SP_TYPE_THEME_MANAGER, NULL);
return instance;
}
guint
sp_theme_manager_register_resource (SpThemeManager *self,
const gchar *theme_name,
const gchar *variant,
const gchar *resource)
{
ThemeResource *theme_resource;
static guint counter;
guint id;
g_return_val_if_fail (SP_IS_THEME_MANAGER (self), 0);
theme_resource = g_slice_new0 (ThemeResource);
theme_resource->id = id = ++counter;
theme_resource->key = g_strdup_printf ("%s-%s-%d",
theme_name ? theme_name : "shared",
variant ? variant : "light",
theme_resource->id);
theme_resource->theme_name = g_strdup (theme_name);
theme_resource->variant = g_strdup (variant);
theme_resource->resource = g_strdup (resource);
theme_resource->provider = NULL;
g_hash_table_insert (self->theme_resources, theme_resource->key, theme_resource);
if (!self->registered_signals)
{
self->registered_signals = TRUE;
g_signal_connect_object (gtk_settings_get_default (),
"notify::gtk-application-prefer-dark-theme",
G_CALLBACK (sp_theme_manager_queue_reload),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (gtk_settings_get_default (),
"notify::gtk-theme-name",
G_CALLBACK (sp_theme_manager_queue_reload),
self,
G_CONNECT_SWAPPED);
}
sp_theme_manager_queue_reload (self);
return id;
}
void
sp_theme_manager_unregister (SpThemeManager *self,
guint registration_id)
{
GHashTableIter iter;
ThemeResource *theme_resource;
g_return_if_fail (SP_IS_THEME_MANAGER (self));
g_hash_table_iter_init (&iter, self->theme_resources);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
{
if (theme_resource->id == registration_id)
{
/* Provider is unregistered during destroy */
g_hash_table_iter_remove (&iter);
break;
}
}
}

View File

@ -0,0 +1,40 @@
/* sp-theme-manager.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_THEME_MANAGER_H
#define SP_THEME_MANAGER_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SP_TYPE_THEME_MANAGER (sp_theme_manager_get_type())
G_DECLARE_FINAL_TYPE (SpThemeManager, sp_theme_manager, SP, THEME_MANAGER, GObject)
SpThemeManager *sp_theme_manager_get_default (void);
void sp_theme_manager_unregister (SpThemeManager *self,
guint registration_id);
guint sp_theme_manager_register_resource (SpThemeManager *self,
const gchar *theme_name,
const gchar *variant,
const gchar *resource);
G_END_DECLS
#endif /* SP_THEME_MANAGER_H */

474
lib/util/sp-zoom-manager.c Normal file
View File

@ -0,0 +1,474 @@
/* sp-zoom-manager.c
*
* Copyright (C) 2016 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/>.
*/
#define G_LOG_DOMAIN "sp-zoom-manager"
#include <glib/gi18n.h>
#include <gio/gio.h>
#include "util/sp-zoom-manager.h"
struct _SpZoomManager
{
GObject parent_instance;
GSimpleActionGroup *actions;
gdouble min_zoom;
gdouble max_zoom;
gdouble zoom;
} __attribute__((aligned(8)));
enum {
PROP_0,
PROP_CAN_ZOOM_IN,
PROP_CAN_ZOOM_OUT,
PROP_MIN_ZOOM,
PROP_MAX_ZOOM,
PROP_ZOOM,
N_PROPS
};
static void action_group_iface_init (GActionGroupInterface *iface);
G_DEFINE_TYPE_EXTENDED (SpZoomManager, sp_zoom_manager, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
static GParamSpec *properties [N_PROPS];
static gdouble zoom_levels[] = {
0.3,
0.5,
0.67,
0.80,
0.90,
1.0,
1.1,
1.2,
1.33,
1.5,
1.7,
2.0,
2.4,
3.0,
5.0,
10.0,
20.0,
30.0,
50.0,
};
static void
sp_zoom_manager_zoom_in_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
SpZoomManager *self = user_data;
g_assert (SP_IS_ZOOM_MANAGER (self));
sp_zoom_manager_zoom_in (self);
}
static void
sp_zoom_manager_zoom_out_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
SpZoomManager *self = user_data;
g_assert (SP_IS_ZOOM_MANAGER (self));
sp_zoom_manager_zoom_out (self);
}
static void
sp_zoom_manager_zoom_one_action (GSimpleAction *action,
GVariant *param,
gpointer user_data)
{
SpZoomManager *self = user_data;
g_assert (SP_IS_ZOOM_MANAGER (self));
sp_zoom_manager_reset (self);
}
static void
sp_zoom_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpZoomManager *self = SP_ZOOM_MANAGER (object);
switch (prop_id)
{
case PROP_MIN_ZOOM:
g_value_set_double (value, sp_zoom_manager_get_min_zoom (self));
break;
case PROP_MAX_ZOOM:
g_value_set_double (value, sp_zoom_manager_get_max_zoom (self));
break;
case PROP_ZOOM:
g_value_set_double (value, sp_zoom_manager_get_zoom (self));
break;
case PROP_CAN_ZOOM_IN:
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_in (self));
break;
case PROP_CAN_ZOOM_OUT:
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_out (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_zoom_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpZoomManager *self = SP_ZOOM_MANAGER (object);
switch (prop_id)
{
case PROP_MIN_ZOOM:
sp_zoom_manager_set_min_zoom (self, g_value_get_double (value));
break;
case PROP_MAX_ZOOM:
sp_zoom_manager_set_max_zoom (self, g_value_get_double (value));
break;
case PROP_ZOOM:
sp_zoom_manager_set_zoom (self, g_value_get_double (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sp_zoom_manager_class_init (SpZoomManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sp_zoom_manager_get_property;
object_class->set_property = sp_zoom_manager_set_property;
properties [PROP_CAN_ZOOM_IN] =
g_param_spec_boolean ("can-zoom-in",
"Can Zoom In",
"Can Zoom In",
TRUE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_CAN_ZOOM_OUT] =
g_param_spec_boolean ("can-zoom-out",
"Can Zoom Out",
"Can Zoom Out",
TRUE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MIN_ZOOM] =
g_param_spec_double ("min-zoom",
"Min Zoom",
"The minimum zoom to apply",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties [PROP_MAX_ZOOM] =
g_param_spec_double ("max-zoom",
"Max Zoom",
"The maximum zoom to apply",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties [PROP_ZOOM] =
g_param_spec_double ("zoom",
"Zoom",
"The current zoom level",
-G_MAXDOUBLE,
G_MAXDOUBLE,
1.0,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sp_zoom_manager_init (SpZoomManager *self)
{
static const GActionEntry entries[] = {
{ "zoom-in", sp_zoom_manager_zoom_in_action },
{ "zoom-out", sp_zoom_manager_zoom_out_action },
{ "zoom-one", sp_zoom_manager_zoom_one_action },
};
GAction *action;
self->min_zoom = 0.0;
self->max_zoom = 0.0;
self->zoom = 1.0;
self->actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
entries,
G_N_ELEMENTS (entries),
self);
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-in");
g_object_bind_property (self, "can-zoom-in", action, "enabled", G_BINDING_SYNC_CREATE);
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-out");
g_object_bind_property (self, "can-zoom-out", action, "enabled", G_BINDING_SYNC_CREATE);
}
SpZoomManager *
sp_zoom_manager_new (void)
{
return g_object_new (SP_TYPE_ZOOM_MANAGER, NULL);
}
gboolean
sp_zoom_manager_get_can_zoom_in (SpZoomManager *self)
{
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
return self->max_zoom == 0.0 || self->zoom < self->max_zoom;
}
gboolean
sp_zoom_manager_get_can_zoom_out (SpZoomManager *self)
{
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
return self->min_zoom == 0.0 || self->zoom > self->min_zoom;
}
gboolean
sp_zoom_manager_get_min_zoom (SpZoomManager *self)
{
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
return self->min_zoom;
}
gboolean
sp_zoom_manager_get_max_zoom (SpZoomManager *self)
{
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
return self->max_zoom;
}
void
sp_zoom_manager_set_min_zoom (SpZoomManager *self,
gdouble min_zoom)
{
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
if (min_zoom != self->min_zoom)
{
self->min_zoom = min_zoom;
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MIN_ZOOM]);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
}
}
void
sp_zoom_manager_set_max_zoom (SpZoomManager *self,
gdouble max_zoom)
{
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
if (max_zoom != self->max_zoom)
{
self->max_zoom = max_zoom;
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_ZOOM]);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
}
}
void
sp_zoom_manager_zoom_in (SpZoomManager *self)
{
gdouble zoom;
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
if (!sp_zoom_manager_get_can_zoom_in (self))
return;
zoom = self->zoom;
for (guint i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
{
if (zoom_levels[i] > zoom)
{
zoom = zoom_levels[i];
break;
}
}
if (zoom == self->zoom)
zoom *= 2;
sp_zoom_manager_set_zoom (self, zoom);
}
void
sp_zoom_manager_zoom_out (SpZoomManager *self)
{
gdouble zoom;
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
if (!sp_zoom_manager_get_can_zoom_out (self))
return;
zoom = self->zoom;
for (guint i = G_N_ELEMENTS (zoom_levels); i > 0; i--)
{
if (zoom_levels[i-1] < zoom)
{
zoom = zoom_levels[i-1];
break;
}
}
if (zoom == self->zoom)
zoom /= 2.0;
sp_zoom_manager_set_zoom (self, zoom);
}
void
sp_zoom_manager_reset (SpZoomManager *self)
{
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
sp_zoom_manager_set_zoom (self, 1.0);
}
gdouble
sp_zoom_manager_get_zoom (SpZoomManager *self)
{
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), 0.0);
return self->zoom;
}
void
sp_zoom_manager_set_zoom (SpZoomManager *self,
gdouble zoom)
{
gdouble min_zoom;
gdouble max_zoom;
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
min_zoom = (self->min_zoom == 0.0) ? -G_MAXDOUBLE : self->min_zoom;
max_zoom = (self->max_zoom == 0.0) ? G_MAXDOUBLE : self->max_zoom;
zoom = CLAMP (zoom, min_zoom, max_zoom);
if (zoom == 0.0)
zoom = 1.0;
if (zoom != self->zoom)
{
self->zoom = zoom;
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM]);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
}
}
static gchar **
sp_zoom_manager_list_actions (GActionGroup *action_group)
{
SpZoomManager *self = (SpZoomManager *)action_group;
g_assert (SP_IS_ZOOM_MANAGER (self));
return g_action_group_list_actions (G_ACTION_GROUP (self->actions));
}
static gboolean
sp_zoom_manager_query_action (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
SpZoomManager *self = (SpZoomManager *)action_group;
g_assert (SP_IS_ZOOM_MANAGER (self));
g_assert (action_name != NULL);
return g_action_group_query_action (G_ACTION_GROUP (self->actions),
action_name,
enabled,
parameter_type,
state_type,
state_hint,
state);
}
static void
sp_zoom_manager_change_action_state (GActionGroup *action_group,
const gchar *action_name,
GVariant *value)
{
SpZoomManager *self = (SpZoomManager *)action_group;
g_assert (SP_IS_ZOOM_MANAGER (self));
g_assert (action_name != NULL);
g_action_group_change_action_state (G_ACTION_GROUP (self->actions), action_name, value);
}
static void
sp_zoom_manager_activate_action (GActionGroup *action_group,
const gchar *action_name,
GVariant *parameter)
{
SpZoomManager *self = (SpZoomManager *)action_group;
g_assert (SP_IS_ZOOM_MANAGER (self));
g_assert (action_name != NULL);
g_action_group_activate_action (G_ACTION_GROUP (self->actions), action_name, parameter);
}
static void
action_group_iface_init (GActionGroupInterface *iface)
{
iface->list_actions = sp_zoom_manager_list_actions;
iface->query_action = sp_zoom_manager_query_action;
iface->change_action_state = sp_zoom_manager_change_action_state;
iface->activate_action = sp_zoom_manager_activate_action;
}

View File

@ -0,0 +1,48 @@
/* sp-zoom-manager.h
*
* Copyright (C) 2016 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/>.
*/
#ifndef SP_ZOOM_MANAGER_H
#define SP_ZOOM_MANAGER_H
#include <glib-object.h>
G_BEGIN_DECLS
#define SP_TYPE_ZOOM_MANAGER (sp_zoom_manager_get_type())
G_DECLARE_FINAL_TYPE (SpZoomManager, sp_zoom_manager, SP, ZOOM_MANAGER, GObject)
SpZoomManager *sp_zoom_manager_new (void);
gboolean sp_zoom_manager_get_can_zoom_in (SpZoomManager *self);
gboolean sp_zoom_manager_get_can_zoom_out (SpZoomManager *self);
gboolean sp_zoom_manager_get_min_zoom (SpZoomManager *self);
gboolean sp_zoom_manager_get_max_zoom (SpZoomManager *self);
void sp_zoom_manager_set_min_zoom (SpZoomManager *self,
gdouble min_zoom);
void sp_zoom_manager_set_max_zoom (SpZoomManager *self,
gdouble max_zoom);
void sp_zoom_manager_zoom_in (SpZoomManager *self);
void sp_zoom_manager_zoom_out (SpZoomManager *self);
void sp_zoom_manager_reset (SpZoomManager *self);
gdouble sp_zoom_manager_get_zoom (SpZoomManager *self);
void sp_zoom_manager_set_zoom (SpZoomManager *self,
gdouble zoom);
G_END_DECLS
#endif /* SP_ZOOM_MANAGER_H */

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stackstash.h"
#include "util/stackstash.h"
struct StackStash
{