diff options
author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-04-22 00:35:56 -0300 |
---|---|---|
committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-04-22 00:35:56 -0300 |
commit | 754b5d1114f096778e483f8a6f3a5dc333225e26 (patch) | |
tree | 30911ec9da4cfd2f5572c27f7288fcbfa4cd212d /deps/uber-graph | |
parent | 35c2857da302ab8b3c308052f2cd1674fb4141a6 (diff) | |
parent | 5f01c706267c595de92406a32e7f31ef5056c2d0 (diff) |
Update upstream source from tag 'upstream/2.0.3pre'
Update to upstream version '2.0.3pre'
with Debian dir 6683980bf6b5c02f6847fd56765833301f75f4f3
Diffstat (limited to 'deps/uber-graph')
24 files changed, 6150 insertions, 0 deletions
diff --git a/deps/uber-graph/README b/deps/uber-graph/README new file mode 100644 index 00000000..e9a95ab1 --- /dev/null +++ b/deps/uber-graph/README @@ -0,0 +1,39 @@ +This is Christian Hergert's "Uber-Graph" +after commit d31c8014d8cc9f293dfecfcb4bd6a7bf4d61c0be. +https://gitlab.gnome.org/chergert/uber-graph +File headers give LGPL2 as the license. + +Original README follows: + +Some of this code is good. Some of this code is very bad. It is a prototype, +nothing more, nothing less. + +Particularly bad/nasty/unreadable code is in main.c. It is also Linux specific. + +UberGraph - A realtime graph similar to that found in Gnome System Monitor. + However, it is much faster and smoother. It runs at a higher + framerate with less X bandwidth. + + It uses multiple pixmaps on the server-side and shifts data between + them to lower the X bandwidth. New data is rendered and clipped so + that the transfer is small. If frame movement is < 1 pixel, the + framerate is dynamically reduced. + +UberHeatMap - This is going to eventually be similar to UberGraph but as a + heat map. It's not very far yet, however. + +GRing - A small circular buffer meant for values in the realtime graphs. This + is most definitely not meant to be used as a byte buffer, so don't use + it as such. + + Example: + + GRing *ring = g_ring_sized_new(sizeof(gdouble), 60, NULL); + + or + + static void free_array_func (gpointer data) { + GArray **ar = data; + g_array_unref(*ar); + } + GRing *ring = g_ring_sized_new(sizeof(GArray*), 60, free_array_func); diff --git a/deps/uber-graph/g-ring.c b/deps/uber-graph/g-ring.c new file mode 100644 index 00000000..b014bf74 --- /dev/null +++ b/deps/uber-graph/g-ring.c @@ -0,0 +1,227 @@ +/* g-ring.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 2.1 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/>. + */ + +#include <string.h> + +#include "g-ring.h" + +#ifndef g_malloc0_n +#define g_malloc0_n(x,y) g_malloc0((gsize)x * y) +#endif + +#define get_element(r,i) ((r)->data + ((r)->elt_size * i)) + +typedef struct _GRingImpl GRingImpl; + +struct _GRingImpl +{ + guint8 *data; /* Pointer to real data. */ + guint len; /* Length of data allocation. */ + guint pos; /* Position in ring. */ + guint elt_size; /* Size of each element. */ + gboolean looped; /* Have we wrapped around at least once. */ + GDestroyNotify destroy; /* Destroy element callback. */ + volatile gint ref_count; /* Reference count. */ +}; + +/** + * g_ring_sized_new: + * @element_size: (in): The size per element. + * @reserved_size: (in): The number of elements to allocate. + * @element_destroy: (in): Notification called when removing an element. + * + * Creates a new instance of #GRing with the given number of elements. + * + * Returns: A new #GRing. + * Side effects: None. + */ +GRing* +g_ring_sized_new (guint element_size, + guint reserved_size, + GDestroyNotify element_destroy) +{ + GRingImpl *ring_impl; + + ring_impl = g_slice_new0(GRingImpl); + ring_impl->elt_size = element_size; + ring_impl->len = reserved_size; + ring_impl->data = g_malloc0_n(reserved_size, element_size); + ring_impl->destroy = element_destroy; + ring_impl->ref_count = 1; + + return (GRing *)ring_impl; +} + +/** + * g_ring_append_vals: + * @ring: (in): A #GRing. + * @data: (in): A pointer to the array of values. + * @len: (in): The number of values. + * + * Appends @len values located at @data. + * + * Returns: None. + * Side effects: None. + */ +void +g_ring_append_vals (GRing *ring, + gconstpointer data, + guint len) +{ + GRingImpl *ring_impl = (GRingImpl *)ring; + gpointer idx; + gint x; + guint i; + + g_return_if_fail(ring_impl != NULL); + + for (i = 0; i < len; i++) { + x = (int)ring->pos - i; + if (x < 0) x += ring->len; + idx = ring->data + (ring_impl->elt_size * x); + if (ring_impl->destroy && ring_impl->looped == TRUE) { + ring_impl->destroy(idx); + } + memcpy(idx, data, ring_impl->elt_size); + ring->pos++; + if (ring->pos >= ring->len) { + ring_impl->looped = TRUE; + } + ring->pos %= ring->len; + data += ring_impl->elt_size; + } +} + +/** + * g_ring_foreach: + * @ring: (in): A #GRing. + * @func: (in): A #GFunc to call for each element. + * @user_data: (in): user data for @func. + * + * Calls @func for every item in the #GRing starting from the most recently + * inserted element to the least recently inserted. + * + * Returns: None. + * Side effects: None. + */ +void +g_ring_foreach (GRing *ring, + GFunc func, + gpointer user_data) +{ + GRingImpl *ring_impl = (GRingImpl *)ring; + gint i; + + g_return_if_fail(ring_impl != NULL); + g_return_if_fail(func != NULL); + + for (i = ring_impl->pos - 1; i >= 0; i--) { + func(get_element(ring_impl, i), user_data); + } + for (i = ring->len - 1; i >= (int)ring_impl->pos; i--) { + func(get_element(ring_impl, i), user_data); + } +} + +/** + * g_ring_destroy: + * @ring: (in): A #GRing. + * + * Cleans up after a #GRing that is no longer in use. + * + * Returns: None. + * Side effects: None. + */ +void +g_ring_destroy (GRing *ring) +{ + GRingImpl *ring_impl = (GRingImpl *)ring; + + g_return_if_fail(ring != NULL); + g_return_if_fail(ring_impl->ref_count == 0); + + g_free(ring_impl->data); + g_slice_free(GRingImpl, ring_impl); +} + +/** + * g_ring_ref: + * @ring: (in): A #GRing. + * + * Atomically increments the reference count of @ring by one. + * + * Returns: The @ring pointer. + * Side effects: None. + */ +GRing* +g_ring_ref (GRing *ring) +{ + GRingImpl *ring_impl = (GRingImpl *)ring; + + g_return_val_if_fail(ring != NULL, NULL); + g_return_val_if_fail(ring_impl->ref_count > 0, NULL); + + g_atomic_int_inc(&ring_impl->ref_count); + return ring; +} + +/** + * g_ring_unref: + * @ring: (in): A #GRing. + * + * Atomically decrements the reference count of @ring by one. When the + * reference count reaches zero, the structure is freed. + * + * Returns: None. + * Side effects: None. + */ +void +g_ring_unref (GRing *ring) +{ + GRingImpl *ring_impl = (GRingImpl *)ring; + + g_return_if_fail(ring != NULL); + g_return_if_fail(ring_impl->ref_count > 0); + + if (g_atomic_int_dec_and_test(&ring_impl->ref_count)) { + g_ring_destroy(ring); + } +} + +/** + * g_ring_get_type: + * + * Retrieves the #GType identifier for #GRing. + * + * Returns: The #GType for #GRing. + * Side effects: The type is registered on first call. + */ +GType +g_ring_get_type (void) +{ + static gsize initialized = FALSE; + static GType type_id = 0; + + if (g_once_init_enter(&initialized)) { + type_id = g_boxed_type_register_static("GRing", + (GBoxedCopyFunc)g_ring_ref, + (GBoxedFreeFunc)g_ring_unref); + g_once_init_leave(&initialized, TRUE); + } + return type_id; +} diff --git a/deps/uber-graph/g-ring.h b/deps/uber-graph/g-ring.h new file mode 100644 index 00000000..a4943878 --- /dev/null +++ b/deps/uber-graph/g-ring.h @@ -0,0 +1,84 @@ +/* g-ring.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 2.1 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/>. + */ + +#ifndef __G_RING_H__ +#define __G_RING_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/** + * g_ring_append_val: + * @ring: A #GRing. + * @val: A value to append to the #GRing. + * + * Appends a value to the ring buffer. @val must be a variable as it is + * referenced to. + * + * Returns: None. + * Side effects: None. + */ +#define g_ring_append_val(ring, val) g_ring_append_vals(ring, &(val), 1) + +/** + * g_ring_get_index: + * @ring: A #GRing. + * @type: The type to extract. + * @i: The index within the #GRing relative to the current position. + * + * Retrieves the value at the given index from the #GRing. The value + * is cast to @type. You may retrieve a pointer to the value within the + * array by using &. + * + * [[ + * gdouble *v = &g_ring_get_index(ring, gdouble, 0); + * gdouble v = g_ring_get_index(ring, gdouble, 0); + * ]] + * + * Returns: The value at the given index. + * Side effects: None. + */ +#define g_ring_get_index(ring, type, i) \ + (((type*)(ring)->data)[(((gint)(ring)->pos - 1 - (i)) >= 0) ? \ + ((ring)->pos - 1 - (i)) : \ + ((ring)->len + ((ring)->pos - 1 - (i)))]) + +typedef struct +{ + guint8 *data; + guint len; + guint pos; +} GRing; + +GType g_ring_get_type (void) G_GNUC_CONST; +GRing* g_ring_sized_new (guint element_size, + guint reserved_size, + GDestroyNotify element_destroy); +void g_ring_append_vals (GRing *ring, + gconstpointer data, + guint len); +void g_ring_foreach (GRing *ring, + GFunc func, + gpointer user_data); +GRing* g_ring_ref (GRing *ring); +void g_ring_unref (GRing *ring); + +G_END_DECLS + +#endif /* __G_RING_H__ */ diff --git a/deps/uber-graph/uber-frame-source.c b/deps/uber-graph/uber-frame-source.c new file mode 100644 index 00000000..3de4d537 --- /dev/null +++ b/deps/uber-graph/uber-frame-source.c @@ -0,0 +1,177 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Neil Roberts <neil@linux.intel.com> + * + * Copyright (C) 2008 OpenedHand + * + * 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 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/>. + * + * + */ + +/* Modified by Christian Hergert for use in Uber Graph. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "uber-frame-source.h" +#include "uber-timeout-interval.h" + +typedef struct _UberFrameSource UberFrameSource; + +struct _UberFrameSource +{ + GSource source; + + UberTimeoutInterval timeout; +}; + +static gboolean uber_frame_source_prepare (GSource *source, + gint *timeout); +static gboolean uber_frame_source_check (GSource *source); +static gboolean uber_frame_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); + +static GSourceFuncs uber_frame_source_funcs = +{ + uber_frame_source_prepare, + uber_frame_source_check, + uber_frame_source_dispatch, + NULL +}; + +/** + * uber_frame_source_add_full: + * @priority: the priority of the frame source. Typically this will be in the + * range between %G_PRIORITY_DEFAULT and %G_PRIORITY_HIGH. + * @fps: the number of times per second to call the function + * @func: function to call + * @data: data to pass to the function + * @notify: function to call when the timeout source is removed + * + * Sets a function to be called at regular intervals with the given + * priority. The function is called repeatedly until it returns + * %FALSE, at which point the timeout is automatically destroyed and + * the function will not be called again. The @notify function is + * called when the timeout is destroyed. The first call to the + * function will be at the end of the first @interval. + * + * This function is similar to g_timeout_add_full() except that it + * will try to compensate for delays. For example, if @func takes half + * the interval time to execute then the function will be called again + * half the interval time after it finished. In contrast + * g_timeout_add_full() would not fire until a full interval after the + * function completes so the delay between calls would be 1.0 / @fps * + * 1.5. This function does not however try to invoke the function + * multiple times to catch up missing frames if @func takes more than + * @interval ms to execute. + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.8 + */ +guint +uber_frame_source_add_full (gint priority, + guint fps, + GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + guint ret; + GSource *source = g_source_new (&uber_frame_source_funcs, + sizeof (UberFrameSource)); + UberFrameSource *frame_source = (UberFrameSource *) source; + + _uber_timeout_interval_init (&frame_source->timeout, fps); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + +#if GLIB_CHECK_VERSION (2, 25, 8) + g_source_set_name (source, "Uber frame timeout"); +#endif + + g_source_set_callback (source, func, data, notify); + + ret = g_source_attach (source, NULL); + + g_source_unref (source); + + return ret; +} + +/** + * uber_frame_source_add: + * @fps: the number of times per second to call the function + * @func: function to call + * @data: data to pass to the function + * + * Simple wrapper around uber_frame_source_add_full(). + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.8 + */ +guint +uber_frame_source_add (guint fps, + GSourceFunc func, + gpointer data) +{ + return uber_frame_source_add_full (G_PRIORITY_DEFAULT, + fps, func, data, NULL); +} + +static gboolean +uber_frame_source_prepare (GSource *source, + gint *delay) +{ + UberFrameSource *frame_source = (UberFrameSource *) source; + gint64 current_time; + +#if GLIB_CHECK_VERSION (2, 27, 3) + current_time = g_source_get_time (source) / 1000; +#else + { + GTimeVal source_time; + g_source_get_current_time (source, &source_time); + current_time = source_time.tv_sec * 1000 + source_time.tv_usec / 1000; + } +#endif + + return _uber_timeout_interval_prepare (current_time, + &frame_source->timeout, + delay); +} + +static gboolean +uber_frame_source_check (GSource *source) +{ + return uber_frame_source_prepare (source, NULL); +} + +static gboolean +uber_frame_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + UberFrameSource *frame_source = (UberFrameSource *) source; + + return _uber_timeout_interval_dispatch (&frame_source->timeout, + callback, user_data); +} diff --git a/deps/uber-graph/uber-frame-source.h b/deps/uber-graph/uber-frame-source.h new file mode 100644 index 00000000..8976beaa --- /dev/null +++ b/deps/uber-graph/uber-frame-source.h @@ -0,0 +1,43 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum <mallum@openedhand.com> + * + * Copyright (C) 2008 OpenedHand + * + * 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 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/>. + */ + +#ifndef __UBER_FRAME_SOURCE_H__ +#define __UBER_FRAME_SOURCE_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +guint uber_frame_source_add (guint fps, + GSourceFunc func, + gpointer data); + +guint uber_frame_source_add_full (gint priority, + guint fps, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); + +G_END_DECLS + +#endif /* __UBER_FRAME_SOURCE_H__ */ diff --git a/deps/uber-graph/uber-graph.c b/deps/uber-graph/uber-graph.c new file mode 100644 index 00000000..9118f263 --- /dev/null +++ b/deps/uber-graph/uber-graph.c @@ -0,0 +1,2166 @@ +/* uber-graph.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n.h> +#include <gdk/gdk.h> +#include <math.h> +#include <string.h> + +#include "uber-graph.h" +#include "uber-scale.h" +#include "uber-frame-source.h" + +#define WIDGET_CLASS (GTK_WIDGET_CLASS(uber_graph_parent_class)) +#define RECT_RIGHT(r) ((r).x + (r).width) +#define RECT_BOTTOM(r) ((r).y + (r).height) +#define UNSET_SURFACE(p) \ + G_STMT_START { \ + if (p) { \ + cairo_surface_destroy (p); \ + p = NULL; \ + } \ + } G_STMT_END +#define CLEAR_CAIRO(c, a) \ + G_STMT_START { \ + cairo_save(c); \ + cairo_rectangle(c, 0, 0, a.width, a.height); \ + cairo_set_operator(c, CAIRO_OPERATOR_CLEAR); \ + cairo_fill(c); \ + cairo_restore(c); \ + } G_STMT_END + +/** + * SECTION:uber-graph.h + * @title: UberGraph + * @short_description: Graphing of realtime data. + * + * #UberGraph is an abstract base class for building realtime graphs. It + * handles scrolling the graph based on the required time intervals and tries + * to render as little data as possible. + * + * Subclasses are responsible for acquiring data when #UberGraph notifies them + * of their next data sample. Additionally, there are two rendering methods. + * UberGraph::render is a render of the full scene. UberGraph::render_fast is + * a rendering of just a new data sample. Ideally, UberGraph::render_fast is + * going to be called. + * + * #UberGraph uses a #cairo_surface_t as a ring buffer to store the contents of the + * graph. Upon destructive changes to the widget such as allocation changed + * or a new #GtkStyle set, a full rendering of the graph will be required. + */ + + +struct _UberGraphPrivate +{ + cairo_surface_t *fg_surface; + cairo_surface_t *bg_surface; + + GdkRectangle content_rect; /* Content area rectangle. */ + GdkRectangle nonvis_rect; /* Non-visible drawing area larger than + * content rect. Used to draw over larger + * area so we can scroll and not fall out + * of view. + */ + UberGraphFormat format; /* Data type format. */ + gboolean paused; /* Is the graph paused. */ + gboolean have_rgba; /* Do we support 32-bit RGBA colormaps. */ + gint x_slots; /* Number of data points on x axis. */ + gint fps; /* Desired frames per second. */ + gint fps_real; /* Milleseconds between FPS callbacks. */ + gfloat fps_each; /* How far to move in each FPS tick. */ + guint fps_handler; /* Timeout for moving the content. */ + gfloat dps; /* Desired data points per second. */ + gint dps_slot; /* Which slot in the surface buffer. */ + gfloat dps_each; /* How many pixels between data points. */ + GTimeVal dps_tv; /* Timeval of last data point. */ + guint dps_handler; /* Timeout for getting new data. */ + guint dps_downscale; /* Count since last downscale. */ + gboolean fg_dirty; /* Does the foreground need to be redrawn. */ + gboolean bg_dirty; /* Does the background need to be redrawn. */ + guint tick_len; /* How long should axis-ticks be. */ + gboolean show_xlines; /* Show X axis lines. */ + gboolean show_xlabels; /* Show X axis labels. */ + gboolean show_ylines; /* Show Y axis lines. */ + gboolean full_draw; /* Do we need to redraw all foreground content. + * If false, draws will try to only add new + * content to the back buffer. + */ + GtkWidget *labels; /* Container for graph labels. */ + GtkWidget *align; /* Alignment for labels. */ + gint fps_count; /* Track actual FPS. */ +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(UberGraph, uber_graph, GTK_TYPE_DRAWING_AREA) + +static gboolean show_fps = FALSE; + +enum +{ + PROP_0, + PROP_FORMAT, +}; + +/** + * uber_graph_new: + * + * Creates a new instance of #UberGraph. + * + * Returns: the newly created instance of #UberGraph. + * Side effects: None. + */ +GtkWidget* +uber_graph_new (void) +{ + UberGraph *graph; + + graph = g_object_new(UBER_TYPE_GRAPH, NULL); + return GTK_WIDGET(graph); +} + +/** + * uber_graph_fps_timeout: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: %TRUE always. + * Side effects: None. + */ +static gboolean +uber_graph_fps_timeout (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + priv = graph->priv; + gtk_widget_queue_draw_area(GTK_WIDGET(graph), + priv->content_rect.x, + priv->content_rect.y, + priv->content_rect.width, + priv->content_rect.height); + return TRUE; +} + +/** + * uber_graph_get_content_area: + * @graph: A #UberGraph. + * + * Retrieves the content area of the graph. + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_get_content_area (UberGraph *graph, /* IN */ + GdkRectangle *rect) /* OUT */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + g_return_if_fail(rect != NULL); + + priv = graph->priv; + *rect = priv->content_rect; +} + +/** + * uber_graph_get_show_xlines: + * @graph: A #UberGraph. + * + * Retrieves if the X grid lines should be shown. + * + * Returns: None. + * Side effects: None. + */ +gboolean +uber_graph_get_show_xlines (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + priv = graph->priv; + return priv->show_xlines; +} + +/** + * uber_graph_set_show_xlines: + * @graph: A #UberGraph. + * @show_xlines: Show x lines. + * + * Sets if the x lines should be shown. + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_show_xlines (UberGraph *graph, /* IN */ + gboolean show_xlines) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->show_xlines = show_xlines; + priv->bg_dirty = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(graph)); +} + +/** + * uber_graph_get_show_ylines: + * @graph: A #UberGraph. + * + * Retrieves if the X grid lines should be shown. + * + * Returns: None. + * Side effects: None. + */ +gboolean +uber_graph_get_show_ylines (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + priv = graph->priv; + return priv->show_ylines; +} + +/** + * uber_graph_set_show_ylines: + * @graph: A #UberGraph. + * @show_ylines: Show x lines. + * + * Sets if the x lines should be shown. + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_show_ylines (UberGraph *graph, /* IN */ + gboolean show_ylines) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->show_ylines = show_ylines; + priv->bg_dirty = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(graph)); +} + +/** + * uber_graph_get_labels: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +GtkWidget* +uber_graph_get_labels (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), NULL); + + priv = graph->priv; + return priv->align; +} + +/** + * uber_graph_scale_changed: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_scale_changed (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + GdkRectangle rect; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + if (!priv->paused) { + priv->fg_dirty = TRUE; + priv->bg_dirty = TRUE; + priv->full_draw = TRUE; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + rect.x = 0; + rect.y = 0; + rect.width = alloc.width; + rect.height = alloc.height; + gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(graph)), + &rect, TRUE); + } +} + +/** + * uber_graph_get_next_data: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static inline gboolean +uber_graph_get_next_data (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + gboolean ret = TRUE; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + /* + * Get the current time for this data point. This is used to calculate + * the proper offset in the FPS callback. + */ + priv = graph->priv; + g_get_current_time(&priv->dps_tv); + /* + * Notify the subclass to retrieve the data point. + */ + if (UBER_GRAPH_GET_CLASS(graph)->get_next_data) { + ret = UBER_GRAPH_GET_CLASS(graph)->get_next_data(graph); + } + return ret; +} + +/** + * uber_graph_init_texture: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_init_texture (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + GdkWindow *window; + cairo_t *cr; + gint width; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + window = gtk_widget_get_window(GTK_WIDGET(graph)); + /* + * Get drawable to base surface upon. + */ + if (!window) { + g_critical("%s() called before GdkWindow is allocated.", G_STRFUNC); + return; + } + /* + * Initialize foreground and background surface. + */ + width = MAX(priv->nonvis_rect.x + priv->nonvis_rect.width, alloc.width); + priv->fg_surface = gdk_window_create_similar_surface(window, CAIRO_CONTENT_COLOR_ALPHA, width, alloc.height); + /* + * Clear foreground contents. + */ + cr = cairo_create(priv->fg_surface); + CLEAR_CAIRO(cr, alloc); + cairo_destroy(cr); +} + +/** + * uber_graph_init_bg: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_init_bg (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + GdkWindow *window; + cairo_t *cr; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + window = gtk_widget_get_window(GTK_WIDGET(graph)); + /* + * Get drawable for surface. + */ + if (!window) { + g_critical("%s() called before GdkWindow is allocated.", G_STRFUNC); + return; + } + /* + * Create the server-side surface. + */ + priv->bg_surface = gdk_window_create_similar_surface(window, CAIRO_CONTENT_COLOR_ALPHA, alloc.width, alloc.height); + /* + * Clear background contents. + */ + cr = cairo_create(priv->bg_surface); + CLEAR_CAIRO(cr, alloc); + cairo_destroy(cr); +} + +/** + * uber_graph_calculate_rects: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_calculate_rects (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + PangoLayout *layout; + PangoFontDescription *font_desc; + GdkWindow *window; + gint pango_width; + gint pango_height; + cairo_t *cr; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + window = gtk_widget_get_window(GTK_WIDGET(graph)); + /* + * We can't calculate rectangles before we have a GdkWindow. + */ + if (!window) { + return; + } + /* + * Determine the pixels required for labels. + */ + cr = gdk_cairo_create(window); + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_new(); + pango_font_description_set_family_static(font_desc, "Monospace"); + pango_font_description_set_size(font_desc, 6 * PANGO_SCALE); + pango_layout_set_font_description(layout, font_desc); + pango_layout_set_text(layout, "XXXXXXXXXX", -1); + pango_layout_get_pixel_size(layout, &pango_width, &pango_height); + pango_font_description_free(font_desc); + g_object_unref(layout); + cairo_destroy(cr); + /* + * Calculate content area rectangle. + */ + priv->content_rect.x = priv->tick_len + pango_width + 1.5; + priv->content_rect.y = (pango_height / 2.) + 1.5; + priv->content_rect.width = alloc.width - priv->content_rect.x - 3.0; + priv->content_rect.height = alloc.height - priv->tick_len - pango_height + - (pango_height / 2.) - 3.0; + if (!priv->show_xlabels) { + priv->content_rect.height += pango_height; + } + /* + * Adjust label offset. + */ + /* + * Calculate FPS/DPS adjustments. + */ + priv->dps_each = ceil((gfloat)priv->content_rect.width + / (gfloat)(priv->x_slots - 1)); + priv->fps_each = priv->dps_each + / ((gfloat)priv->fps / (gfloat)priv->dps); + /* + * XXX: Small hack to make things a bit smoother at small scales. + */ + if (priv->fps_each < .5) { + priv->fps_each = 1; + priv->fps_real = (1000. / priv->dps_each) / 2.; + } else { + priv->fps_real = 1000. / priv->fps; + } + /* + * Update FPS callback. + */ + if (priv->fps_handler) { + g_source_remove(priv->fps_handler); + priv->fps_handler = uber_frame_source_add(priv->fps, + (GSourceFunc)uber_graph_fps_timeout, + graph); + } + /* + * Calculate the non-visible area that drawing should happen within. + */ + priv->nonvis_rect = priv->content_rect; + priv->nonvis_rect.width = priv->dps_each * priv->x_slots; + /* + * Update positioning for label alignment. + */ + gtk_widget_set_margin_top(GTK_WIDGET(priv->align), 6); + gtk_widget_set_margin_bottom(GTK_WIDGET(priv->align), 6); + gtk_widget_set_margin_start(GTK_WIDGET(priv->align), priv->content_rect.x); + gtk_widget_set_margin_end(GTK_WIDGET(priv->align), 0); +} + +/** + * uber_graph_get_show_xlabels: + * @graph: A #UberGraph. + * + * Retrieves if the X grid labels should be shown. + * + * Returns: None. + * Side effects: None. + */ +gboolean +uber_graph_get_show_xlabels (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + priv = graph->priv; + return priv->show_xlabels; +} + +/** + * uber_graph_set_show_xlabels: + * @graph: A #UberGraph. + * @show_xlabels: Show x labels. + * + * Sets if the x labels should be shown. + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_show_xlabels (UberGraph *graph, /* IN */ + gboolean show_xlabels) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->show_xlabels = show_xlabels; + priv->bg_dirty = TRUE; + uber_graph_calculate_rects(graph); + gtk_widget_queue_draw(GTK_WIDGET(graph)); +} + +/** + * uber_graph_dps_timeout: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: %TRUE always. + * Side effects: None. + */ +static gboolean +uber_graph_dps_timeout (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), FALSE); + + priv = graph->priv; + if (!uber_graph_get_next_data(graph)) { + /* + * XXX: How should we handle failed data retrieval. + */ + } + if (G_UNLIKELY(show_fps)) { + g_print("UberGraph[%p] %02d FPS\n", graph, priv->fps_count); + priv->fps_count = 0; + } + /* + * Make sure the content is re-rendered. + */ + if (!priv->paused) { + priv->fg_dirty = TRUE; + /* + * We do not queue a draw here since the next FPS callback will happen + * when it is the right time to show the frame. + */ + } + /* + * Try to downscale the graph. We do this whether or not we are paused + * as redrawing is deferred if we are in a paused state. + */ + priv->dps_downscale++; + if (priv->dps_downscale >= 5) { + if (UBER_GRAPH_GET_CLASS(graph)->downscale) { + if (UBER_GRAPH_GET_CLASS(graph)->downscale(graph)) { + if (!priv->paused) { + uber_graph_redraw(graph); + } + } + } + priv->dps_downscale = 0; + } + return TRUE; +} + +/** + * uber_graph_register_dps_handler: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_register_dps_handler (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + guint dps_freq; + gboolean do_now = TRUE; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + if (priv->dps_handler) { + g_source_remove(priv->dps_handler); + do_now = FALSE; + } + /* + * Calculate the update frequency. + */ + dps_freq = 1000 / priv->dps; + /* + * Install the data handler. + */ + priv->dps_handler = g_timeout_add(dps_freq, + (GSourceFunc)uber_graph_dps_timeout, + graph); + /* + * Call immediately. + */ + if (do_now) { + uber_graph_dps_timeout(graph); + } +} + +/** + * uber_graph_register_fps_handler: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_register_fps_handler (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + /* + * Remove any existing FPS handler. + */ + if (priv->fps_handler) { + g_source_remove(priv->fps_handler); + } + /* + * Install the FPS timeout. + */ + priv->fps_handler = uber_frame_source_add(priv->fps, + (GSourceFunc)uber_graph_fps_timeout, + graph); +} + +/** + * uber_graph_set_dps: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_dps (UberGraph *graph, /* IN */ + gfloat dps) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->dps = dps; + /* + * TODO: Does this belong somewhere else? + */ + if (UBER_GRAPH_GET_CLASS(graph)->set_stride) { + UBER_GRAPH_GET_CLASS(graph)->set_stride(graph, priv->x_slots); + } + /* + * Recalculate frame rates and timeouts. + */ + uber_graph_calculate_rects(graph); + uber_graph_register_dps_handler(graph); + uber_graph_register_fps_handler(graph); +} + +/** + * uber_graph_set_fps: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_fps (UberGraph *graph, /* IN */ + guint fps) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->fps = fps; + uber_graph_register_fps_handler(graph); +} + +/** + * uber_graph_realize: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_realize (GtkWidget *widget) /* IN */ +{ + UberGraph *graph; + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + graph = UBER_GRAPH(widget); + priv = graph->priv; + WIDGET_CLASS->realize(widget); + /* + * Calculate new layout based on allocation. + */ + uber_graph_calculate_rects(graph); + /* + * Re-initialize textures for updated sizes. + */ + UNSET_SURFACE(priv->bg_surface); + UNSET_SURFACE(priv->fg_surface); + uber_graph_init_bg(graph); + uber_graph_init_texture(graph); + /* + * Notify subclass of current data stride (points per graph). + */ + if (UBER_GRAPH_GET_CLASS(widget)->set_stride) { + UBER_GRAPH_GET_CLASS(widget)->set_stride(UBER_GRAPH(widget), + priv->x_slots); + } + /* + * Install the data collector. + */ + uber_graph_register_dps_handler(graph); +} + +/** + * uber_graph_unrealize: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_unrealize (GtkWidget *widget) /* IN */ +{ + UberGraph *graph; + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + graph = UBER_GRAPH(widget); + priv = graph->priv; + /* + * Unregister any data acquisition handlers. + */ + if (priv->dps_handler) { + g_source_remove(priv->dps_handler); + priv->dps_handler = 0; + } + /* + * Destroy textures. + */ + UNSET_SURFACE(priv->bg_surface); + UNSET_SURFACE(priv->fg_surface); +} + +/** + * uber_graph_screen_changed: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_screen_changed (GtkWidget *widget, /* IN */ + GdkScreen *old_screen) /* IN */ +{ + UberGraphPrivate *priv; + GdkScreen *screen; + GdkVisual *visual; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + screen = gtk_widget_get_screen (GTK_WIDGET (widget)); + visual = gdk_screen_get_rgba_visual (screen); + + priv = UBER_GRAPH(widget)->priv; + /* + * Check if we have RGBA colormaps available. + */ + priv->have_rgba = visual != NULL; +} + +/** + * uber_graph_show: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_show (GtkWidget *widget) /* IN */ +{ + g_return_if_fail(UBER_IS_GRAPH(widget)); + + WIDGET_CLASS->show(widget); + /* + * Only run the FPS timeout when we are visible. + */ + uber_graph_register_fps_handler(UBER_GRAPH(widget)); +} + +/** + * uber_graph_hide: + * @widget: A #GtkWIdget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_hide (GtkWidget *widget) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + priv = UBER_GRAPH(widget)->priv; + /* + * Disable the FPS timeout when we are not visible. + */ + if (priv->fps_handler) { + g_source_remove(priv->fps_handler); + priv->fps_handler = 0; + } +} + +static inline void +uber_graph_get_pixmap_rect (UberGraph *graph, /* IN */ + GdkRectangle *rect) /* OUT */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + rect->x = 0; + rect->y = 0; + rect->width = MAX(alloc.width, + priv->nonvis_rect.x + priv->nonvis_rect.width); + rect->height = alloc.height; +} + +/** + * uber_graph_render_fg: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_render_fg (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; + GdkRectangle rect; + cairo_t *cr; + gfloat each; + gfloat x_epoch; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + /* + * Acquire resources. + */ + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + uber_graph_get_pixmap_rect(graph, &rect); + cr = cairo_create(priv->fg_surface); + /* + * Render to texture if needed. + */ + if (priv->fg_dirty) { + /* + * Caclulate relative positionings for use in renderers. + */ + each = priv->content_rect.width / (gfloat)(priv->x_slots - 1); + x_epoch = RECT_RIGHT(priv->nonvis_rect); + /* + * If we are in a fast draw, lets copy the content from the other + * buffer at the next offset. + */ + if (!priv->full_draw && UBER_GRAPH_GET_CLASS(graph)->render_fast) { + /* + * Determine next rendering slot. + */ + rect.x = priv->content_rect.x + + (priv->dps_each * priv->dps_slot); + rect.width = priv->dps_each; + rect.y = priv->content_rect.y; + rect.height = priv->content_rect.height; + priv->dps_slot = (priv->dps_slot + 1) % priv->x_slots; + x_epoch = RECT_RIGHT(rect); + /* + * Clear content area. + */ + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_rectangle(cr, &rect); + cairo_fill(cr); + cairo_restore(cr); + +#if 0 + /* + * XXX: Draw line helper for debugging. + */ + cairo_save(cr); + cairo_set_source_rgb(cr, .3, .3, .3); + cairo_rectangle(cr, + rect.x, + rect.y + (rect.height / (gfloat)priv->x_slots * priv->dps_slot), + rect.width, + rect.height / priv->x_slots); + cairo_fill(cr); + cairo_restore(cr); +#endif + + /* + * Render new content clipped. + */ + cairo_save(cr); + cairo_reset_clip(cr); + gdk_cairo_rectangle(cr, &rect); + cairo_clip(cr); + /* + * Determine area for this draw. + */ + UBER_GRAPH_GET_CLASS(graph)->render_fast(graph, + cr, + &rect, + x_epoch, + each + .5); + cairo_restore(cr); + } else { + /* + * Clear content area. + */ + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_rectangle(cr, &rect); + cairo_fill(cr); + cairo_restore(cr); + /* + * Draw the entire foreground. + */ + if (UBER_GRAPH_GET_CLASS(graph)->render) { + priv->dps_slot = 0; + cairo_save(cr); + gdk_cairo_rectangle(cr, &priv->nonvis_rect); + cairo_clip(cr); + UBER_GRAPH_GET_CLASS(graph)->render(graph, + cr, + &priv->nonvis_rect, + x_epoch, + each); + cairo_restore(cr); + } + } + } + /* + * Foreground is no longer dirty. + */ + priv->fg_dirty = FALSE; + priv->full_draw = FALSE; + /* + * Cleanup. + */ + cairo_destroy(cr); +} + +/** + * uber_graph_redraw: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_redraw (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->fg_dirty = TRUE; + priv->bg_dirty = TRUE; + priv->full_draw = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(graph)); +} + +/** + * uber_graph_get_yrange: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static inline void +uber_graph_get_yrange (UberGraph *graph, /* IN */ + UberRange *range) /* OUT */ +{ + g_return_if_fail(UBER_IS_GRAPH(graph)); + g_return_if_fail(range != NULL); + + memset(range, 0, sizeof(*range)); + if (UBER_GRAPH_GET_CLASS(graph)->get_yrange) { + UBER_GRAPH_GET_CLASS(graph)->get_yrange(graph, range); + } +} + +/** + * uber_graph_set_format: + * @graph: A UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_set_format (UberGraph *graph, /* IN */ + UberGraphFormat format) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->format = format; + priv->bg_dirty = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(graph)); +} + +static void +uber_graph_render_x_axis (UberGraph *graph, /* IN */ + cairo_t *cr) /* IN */ +{ + UberGraphPrivate *priv; + const gdouble dashes[] = { 1.0, 2.0 }; + PangoFontDescription *fd; + PangoLayout *pl; + GtkStyleContext *style; + GdkRGBA fg_color; + gfloat each; + gfloat x; + gfloat y; + gfloat h; + gchar text[16] = { 0 }; + gint count; + gint wi; + gint hi; + gint i; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &fg_color); + + count = priv->x_slots / 10; + each = priv->content_rect.width / (gfloat)count; + /* + * Draw ticks. + */ + cairo_save(cr); + pl = pango_cairo_create_layout(cr); + fd = pango_font_description_new(); + pango_font_description_set_family_static(fd, "Monospace"); + pango_font_description_set_size(fd, 6 * PANGO_SCALE); + pango_layout_set_font_description(pl, fd); + gdk_cairo_set_source_rgba(cr, &fg_color); + cairo_set_line_width(cr, 1.0); + cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0); + for (i = 0; i <= count; i++) { + x = RECT_RIGHT(priv->content_rect) - (gint)(i * each) + .5; + if (priv->show_xlines && (i != 0 && i != count)) { + y = priv->content_rect.y; + h = priv->content_rect.height + priv->tick_len; + } else { + y = priv->content_rect.y + priv->content_rect.height; + h = priv->tick_len; + } + if (i != 0 && i != count) { + cairo_move_to(cr, x, y); + cairo_line_to(cr, x, y + h); + cairo_stroke(cr); + } + /* + * Render the label. + */ + if (priv->show_xlabels) { + g_snprintf(text, sizeof(text), "%d", i * 10); + pango_layout_set_text(pl, text, -1); + pango_layout_get_pixel_size(pl, &wi, &hi); + if (i != 0 && i != count) { + cairo_move_to(cr, x - (wi / 2), y + h); + } else if (i == 0) { + cairo_move_to(cr, + RECT_RIGHT(priv->content_rect) - (wi / 2), + RECT_BOTTOM(priv->content_rect) + priv->tick_len); + } else if (i == count) { + cairo_move_to(cr, + priv->content_rect.x - (wi / 2), + RECT_BOTTOM(priv->content_rect) + priv->tick_len); + } + pango_cairo_show_layout(cr, pl); + } + } + g_object_unref(pl); + pango_font_description_free(fd); + cairo_restore(cr); +} + +static void G_GNUC_PRINTF(6, 7) +uber_graph_render_y_line (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + gint y, /* IN */ + gboolean tick_only, /* IN */ + gboolean no_label, /* IN */ + const gchar *format, /* IN */ + ...) /* IN */ +{ + UberGraphPrivate *priv; + const gdouble dashes[] = { 1.0, 2.0 }; + PangoFontDescription *fd; + PangoLayout *pl; + GtkStyleContext *style; + GdkRGBA fg_color; + va_list args; + gchar *text; + gint width; + gint height; + gfloat real_y = y + .5; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + g_return_if_fail(cr != NULL); + g_return_if_fail(format != NULL); + + priv = graph->priv; + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &fg_color); + /* + * Draw grid line. + */ + cairo_save(cr); + cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0); + cairo_set_line_width(cr, 1.0); + gdk_cairo_set_source_rgba(cr, &fg_color); + cairo_move_to(cr, priv->content_rect.x - priv->tick_len, real_y); + if (tick_only) { + cairo_line_to(cr, priv->content_rect.x, real_y); + } else { + cairo_line_to(cr, RECT_RIGHT(priv->content_rect), real_y); + } + cairo_stroke(cr); + cairo_restore(cr); + /* + * Show label. + */ + if (!no_label) { + cairo_save(cr); + gdk_cairo_set_source_rgba(cr, &fg_color); + /* + * Format text. + */ + va_start(args, format); + text = g_strdup_vprintf(format, args); + va_end(args); + /* + * Render pango layout. + */ + pl = pango_cairo_create_layout(cr); + fd = pango_font_description_new(); + pango_font_description_set_family_static(fd, "Monospace"); + pango_font_description_set_size(fd, 6 * PANGO_SCALE); + pango_layout_set_font_description(pl, fd); + pango_layout_set_text(pl, text, -1); + pango_layout_get_pixel_size(pl, &width, &height); + cairo_move_to(cr, priv->content_rect.x - priv->tick_len - width - 3, + real_y - height / 2); + pango_cairo_show_layout(cr, pl); + /* + * Cleanup resources. + */ + g_free(text); + pango_font_description_free(fd); + g_object_unref(pl); + cairo_restore(cr); + } +} + +static void +uber_graph_render_y_axis_percent (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + UberRange *range, /* IN */ + UberRange *pixel_range, /* IN */ + gint n_lines) /* IN */ +{ + static const gchar format[] = "%0.0f %%"; + UberGraphPrivate *priv; + gdouble value; + gdouble y; + gint i; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + g_return_if_fail(range != NULL); + g_return_if_fail(pixel_range != NULL); + g_return_if_fail(n_lines >= 0); + g_return_if_fail(n_lines < 6); + + priv = graph->priv; + /* + * Render top and bottom lines. + */ + uber_graph_render_y_line(graph, cr, + priv->content_rect.y - 1, + TRUE, FALSE, format, 100.); + uber_graph_render_y_line(graph, cr, + RECT_BOTTOM(priv->content_rect), + TRUE, FALSE, format, 0.); + /* + * Render lines between the edges. + */ + for (i = 1; i < n_lines; i++) { + value = (n_lines - i) / (gfloat)n_lines; + y = pixel_range->end - (pixel_range->range * value); + value *= 100.; + uber_graph_render_y_line(graph, cr, y, + !priv->show_ylines, FALSE, + format, value); + } +} + +/** + * uber_graph_render_y_axis_direct: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_render_y_axis_direct (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + UberRange *range, /* IN */ + UberRange *pixel_range, /* IN */ + gint n_lines, /* IN */ + gboolean kibi) /* IN */ +{ + static const gchar format[] = "%0.1f%s"; + const gchar *modifier = ""; + UberGraphPrivate *priv; + gdouble value; + gdouble y; + gint i; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + +#define CONDENSE(v) \ +G_STMT_START { \ + if (kibi) { \ + if ((v) >= 1073741824.) { \ + (v) /= 1073741824.; \ + modifier = " Gi"; \ + } else if ((v) >= 1048576.) { \ + (v) /= 1048576.; \ + modifier = " Mi"; \ + } else if ((v) >= 1024.) { \ + (v) /= 1024.; \ + modifier = " Ki"; \ + } else { \ + modifier = ""; \ + } \ + } else { \ + if ((v) >= 1000000000.) { \ + (v) /= 1000000000.; \ + modifier = " G"; \ + } else if ((v) >= 1000000.) { \ + (v) /= 1000000.; \ + modifier = " M"; \ + } else if ((v) >= 1000.) { \ + (v) /= 1000.; \ + modifier = " K"; \ + } else { \ + modifier = ""; \ + } \ + } \ +} G_STMT_END + + priv = graph->priv; + /* + * Render top and bottom lines. + */ + value = range->end; + CONDENSE(value); + uber_graph_render_y_line(graph, cr, + priv->content_rect.y - 1, + TRUE, FALSE, format, value, modifier); + value = range->begin; + CONDENSE(value); + uber_graph_render_y_line(graph, cr, + RECT_BOTTOM(priv->content_rect), + TRUE, FALSE, format, value, modifier); + /* + * Render lines between the edges. + */ + for (i = 1; i < n_lines; i++) { + y = value = range->range / (gfloat)(n_lines) * (gfloat)i; + /* + * TODO: Use proper scale. + */ + uber_scale_linear(range, pixel_range, &y, NULL); + if (y == 0 || y == pixel_range->begin || y == pixel_range->end) { + continue; + } + y = pixel_range->end - y; + CONDENSE(value); + uber_graph_render_y_line(graph, cr, y, + !priv->show_ylines, + (range->begin == range->end), + format, value, modifier); + } +} + +/** + * uber_graph_render_y_axis: + * @graph: A #UberGraph. + * + * Render the Y axis grid lines and labels. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_render_y_axis (UberGraph *graph, /* IN */ + cairo_t *cr) /* IN */ +{ + UberGraphPrivate *priv; + UberRange pixel_range; + UberRange range; + gint n_lines; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + /* + * Calculate ranges. + */ + uber_graph_get_yrange(graph, &range); + pixel_range.begin = priv->content_rect.y; + pixel_range.end = priv->content_rect.y + priv->content_rect.height; + pixel_range.range = pixel_range.end - pixel_range.begin; + /* + * Render grid lines for given format. + */ + n_lines = MIN(priv->content_rect.height / 25, 5); + switch (priv->format) { + case UBER_GRAPH_FORMAT_PERCENT: + uber_graph_render_y_axis_percent(graph, cr, &range, &pixel_range, + n_lines); + break; + case UBER_GRAPH_FORMAT_DIRECT: + uber_graph_render_y_axis_direct(graph, cr, &range, &pixel_range, + n_lines, FALSE); + break; + case UBER_GRAPH_FORMAT_DIRECT1024: + uber_graph_render_y_axis_direct(graph, cr, &range, &pixel_range, + n_lines, TRUE); + break; + default: + g_assert_not_reached(); + } +} + +/** + * uber_graph_render_bg: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_render_bg (UberGraph *graph) /* IN */ +{ + const gdouble dashes[] = { 1.0, 2.0 }; + UberGraphPrivate *priv; + GtkAllocation alloc; + GtkStyleContext *style; + GdkRGBA fg_color; + GdkRGBA light_color; + cairo_t *cr; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + /* + * Acquire resources. + */ + priv = graph->priv; + gtk_widget_get_allocation(GTK_WIDGET(graph), &alloc); + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &fg_color); + gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &light_color); + cr = cairo_create(priv->bg_surface); + /* + * Ensure valid resources. + */ + g_assert(style); + g_assert(priv->bg_surface); + /* + * Clear entire background. Hopefully this looks okay for RGBA themes + * that are translucent. + */ + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_rectangle(cr, 0, 0, alloc.width, alloc.height); + cairo_fill(cr); + cairo_restore(cr); + /* + * Paint the content area background. + */ + cairo_save(cr); + gdk_cairo_set_source_rgba(cr, &light_color); + gdk_cairo_rectangle(cr, &priv->content_rect); + cairo_fill(cr); + cairo_restore(cr); + /* + * Stroke the border around the content area. + */ + cairo_save(cr); + gdk_cairo_set_source_rgba(cr, &fg_color); + cairo_set_line_width(cr, 1.0); + cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0); + cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); + cairo_rectangle(cr, + priv->content_rect.x - .5, + priv->content_rect.y - .5, + priv->content_rect.width + 1.0, + priv->content_rect.height + 1.0); + cairo_stroke(cr); + cairo_restore(cr); + /* + * Render the axis ticks. + */ + uber_graph_render_y_axis(graph, cr); + uber_graph_render_x_axis(graph, cr); + /* + * Background is no longer dirty. + */ + priv->bg_dirty = FALSE; + /* + * Cleanup. + */ + cairo_destroy(cr); +} + +static inline void +g_time_val_subtract (GTimeVal *a, /* IN */ + GTimeVal *b, /* IN */ + GTimeVal *c) /* OUT */ +{ + g_return_if_fail(a != NULL); + g_return_if_fail(b != NULL); + g_return_if_fail(c != NULL); + + c->tv_sec = a->tv_sec - b->tv_sec; + c->tv_usec = a->tv_usec - b->tv_usec; + if (c->tv_usec < 0) { + c->tv_usec += G_USEC_PER_SEC; + c->tv_sec -= 1; + } +} + +/** + * uber_graph_get_fps_offset: + * @graph: A #UberGraph. + * + * Calculates the number of pixels that the foreground should be rendered + * from the origin. + * + * Returns: The pixel offset to render the foreground. + * Side effects: None. + */ +static gfloat +uber_graph_get_fps_offset (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + GTimeVal rel = { 0 }; + GTimeVal tv; + gfloat f; + + g_return_val_if_fail(UBER_IS_GRAPH(graph), 0.); + + priv = graph->priv; + g_get_current_time(&tv); + g_time_val_subtract(&tv, &priv->dps_tv, &rel); + f = ((rel.tv_sec * 1000) + (rel.tv_usec / 1000)) + / (1000. / priv->dps) /* MSec Per Data Point */ + * priv->dps_each; /* Pixels Per Data Point */ + return MIN(f, (priv->dps_each - priv->fps_each)); +} + +/** + * uber_graph_draw: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: %FALSE always. + * Side effects: None. + */ +static gboolean +uber_graph_draw (GtkWidget *widget, /* IN */ + cairo_t *cr) /* IN */ +{ + UberGraphPrivate *priv; + GtkAllocation alloc; +// cairo_t *cr; + gfloat offset; + gint x; + + g_return_val_if_fail(UBER_IS_GRAPH(widget), FALSE); + + priv = UBER_GRAPH(widget)->priv; + gtk_widget_get_allocation(widget, &alloc); + priv->fps_count++; + /* + * Ensure that the texture is initialized. + */ + g_assert(priv->fg_surface); + g_assert(priv->bg_surface); + /* + * Clear window background. + */ +#if 0 + gdk_window_clear_area(expose->window, + expose->area.x, + expose->area.y, + expose->area.width, + expose->area.height); + /* + * Allocate resources. + */ + cr = gdk_cairo_create(expose->window); + /* + * Clip to exposure area. + */ + gdk_cairo_rectangle(cr, &expose->area); + cairo_clip(cr); +#endif + /* + * Render background or foreground if needed. + */ + if (priv->bg_dirty) { + uber_graph_render_bg(UBER_GRAPH(widget)); + } + if (priv->fg_dirty) { + uber_graph_render_fg(UBER_GRAPH(widget)); + } + /* + * Paint the background to the exposure area. + */ + cairo_save(cr); + cairo_set_source_surface(cr, priv->bg_surface, 0, 0); + cairo_rectangle(cr, 0, 0, alloc.width, alloc.height); + cairo_fill(cr); + cairo_restore(cr); + /* + * Draw the foreground. + */ + offset = uber_graph_get_fps_offset(UBER_GRAPH(widget)); + if (priv->have_rgba) { + cairo_save(cr); + /* + * Clip exposure to the content area. + */ + cairo_reset_clip(cr); + gdk_cairo_rectangle(cr, &priv->content_rect); + cairo_clip(cr); + /* + * Data in the fg surface is a ring bufer. Render the first portion + * at its given offset. + */ + x = ((priv->x_slots - priv->dps_slot) * priv->dps_each) - offset; + cairo_set_source_surface(cr, priv->fg_surface, (gint)x, 0); + gdk_cairo_rectangle(cr, &priv->content_rect); + cairo_fill(cr); + /* + * Render the second part of the ring surface buffer. + */ + x = (priv->dps_each * -priv->dps_slot) - offset; + cairo_set_source_surface(cr, priv->fg_surface, (gint)x, 0); + gdk_cairo_rectangle(cr, &priv->content_rect); + cairo_fill(cr); + /* + * Cleanup. + */ + cairo_restore(cr); + } else { + /* + * TODO: Use XOR command for fallback. + */ + g_warn_if_reached(); + } + /* + * Cleanup resources. + */ + //cairo_destroy(cr); + return FALSE; +} + +/** + * uber_graph_style_set: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_style_set (GtkWidget *widget, /* IN */ + GtkStyle *old_style) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + priv = UBER_GRAPH(widget)->priv; + WIDGET_CLASS->style_set(widget, old_style); + priv->fg_dirty = TRUE; + priv->bg_dirty = TRUE; + gtk_widget_queue_draw(widget); +} + +/** + * uber_graph_size_allocate: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_size_allocate (GtkWidget *widget, /* IN */ + GtkAllocation *alloc) /* IN */ +{ + UberGraph *graph; + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(widget)); + + graph = UBER_GRAPH(widget); + priv = graph->priv; + WIDGET_CLASS->size_allocate(widget, alloc); + /* + * If there is no window yet, we can defer setup. + */ + if (!gtk_widget_get_window(widget)) { + return; + } + /* + * Recalculate rectangles. + */ + uber_graph_calculate_rects(graph); + /* + * Recreate server side surface. + */ + UNSET_SURFACE(priv->bg_surface); + UNSET_SURFACE(priv->fg_surface); + uber_graph_init_bg(graph); + uber_graph_init_texture(graph); + /* + * Mark foreground and background as dirty. + */ + priv->fg_dirty = TRUE; + priv->bg_dirty = TRUE; + priv->full_draw = TRUE; + gtk_widget_queue_draw(widget); +} + +static void +uber_graph_size_request (GtkWidget *widget, /* IN */ + GtkRequisition *req) /* OUT */ +{ + g_return_if_fail(req != NULL); + + req->width = 150; + req->height = 50; +} + +static void +uber_graph_get_preferred_width (GtkWidget *widget, /* IN */ + gint *minimal_width, + gint *natural_width) +{ + GtkRequisition requisition; + uber_graph_size_request(widget, &requisition); + *minimal_width = * natural_width = requisition.width; +} + +static void +uber_graph_get_preferred_height (GtkWidget *widget, /* IN */ + gint *minimal_height, + gint *natural_height) +{ + GtkRequisition requisition; + uber_graph_size_request(widget, &requisition); + *minimal_height = * natural_height = requisition.height; +} + +/** + * uber_graph_add_label: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_graph_add_label (UberGraph *graph, /* IN */ + UberLabel *label) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + g_return_if_fail(UBER_IS_LABEL(label)); + + priv = graph->priv; + gtk_box_pack_start(GTK_BOX(priv->labels), GTK_WIDGET(label), + TRUE, TRUE, 0); + gtk_widget_show(GTK_WIDGET(label)); +} + +/** + * uber_graph_take_screenshot: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_take_screenshot (UberGraph *graph) /* IN */ +{ + GtkWidget *widget; + GtkWidget *dialog; + GtkAllocation alloc; + const gchar *filename; + cairo_status_t status; + cairo_surface_t *surface; + cairo_t *cr; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + widget = GTK_WIDGET(graph); + gtk_widget_get_allocation(widget, &alloc); + + /* + * Create save dialog and ask user for filename. + */ + dialog = gtk_file_chooser_dialog_new(_("Save As"), + GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_ACCEPT, + NULL); + if (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { + /* + * Create surface and cairo context. + */ + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + alloc.width, alloc.height); + cr = cairo_create(surface); + cairo_rectangle(cr, 0, 0, alloc.width, alloc.height); + cairo_clip(cr); + + /* Paint to the image surface instead of the screen */ + uber_graph_draw(widget, cr); + + /* + * Save surface to png. + */ + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + status = cairo_surface_write_to_png(surface, filename); + if (status != CAIRO_STATUS_SUCCESS) { + g_critical("Failed to save pixmap to file."); + goto cleanup; + } + /* + * Cleanup resources. + */ + cleanup: + cairo_destroy(cr); + cairo_surface_destroy(surface); + } + gtk_widget_destroy(dialog); +} + +/** + * uber_graph_toggle_paused: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_toggle_paused (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = graph->priv; + priv->paused = !priv->paused; + if (priv->fps_handler) { + g_source_remove(priv->fps_handler); + priv->fps_handler = 0; + } else { + if (!priv->paused) { + uber_graph_redraw(graph); + } + uber_graph_register_fps_handler(graph); + } +} + +/** + * uber_graph_button_press: + * @widget: A #GtkWidget. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static gboolean +uber_graph_button_press_event (GtkWidget *widget, /* IN */ + GdkEventButton *button) /* IN */ +{ + g_return_val_if_fail(UBER_IS_GRAPH(widget), FALSE); + + switch (button->button) { + case 2: /* Middle Click */ + if (button->state & GDK_CONTROL_MASK) { + uber_graph_take_screenshot(UBER_GRAPH(widget)); + } else { + uber_graph_toggle_paused(UBER_GRAPH(widget)); + } + break; + default: + break; + } + return FALSE; +} + +/** + * uber_graph_finalize: + * @object: A #UberGraph. + * + * Finalizer for a #UberGraph instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_finalize (GObject *object) /* IN */ +{ + G_OBJECT_CLASS(uber_graph_parent_class)->finalize(object); +} + +/** + * uber_graph_dispose: + * @object: A #GObject. + * + * Dispose callback for @object. This method releases references held + * by the #GObject instance. + * + * Returns: None. + * Side effects: Plenty. + */ +static void +uber_graph_dispose (GObject *object) /* IN */ +{ + UberGraph *graph; + UberGraphPrivate *priv; + + graph = UBER_GRAPH(object); + priv = graph->priv; + /* + * Stop any timeout handlers. + */ + if (priv->fps_handler) { + g_source_remove(priv->fps_handler); + } + if (priv->dps_handler) { + g_source_remove(priv->dps_handler); + } + /* + * Destroy textures. + */ + UNSET_SURFACE(priv->bg_surface); + UNSET_SURFACE(priv->fg_surface); + /* + * Call base class. + */ + G_OBJECT_CLASS(uber_graph_parent_class)->dispose(object); +} + +/** + * uber_graph_set_property: + * @object: (in): A #GObject. + * @prop_id: (in): The property identifier. + * @value: (out): The given property. + * @pspec: (in): A #ParamSpec. + * + * Get a given #GObject property. + */ +static void +uber_graph_get_property (GObject *object, /* IN */ + guint prop_id, /* IN */ + GValue *value, /* OUT */ + GParamSpec *pspec) /* IN */ +{ + UberGraph *graph = UBER_GRAPH(object); + + switch (prop_id) { + case PROP_FORMAT: + g_value_set_uint(value, graph->priv->format); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +/** + * uber_graph_set_property: + * @object: (in): A #GObject. + * @prop_id: (in): The property identifier. + * @value: (in): The given property. + * @pspec: (in): A #ParamSpec. + * + * Set a given #GObject property. + */ +static void +uber_graph_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + UberGraph *graph = UBER_GRAPH(object); + + switch (prop_id) { + case PROP_FORMAT: + uber_graph_set_format(graph, g_value_get_uint(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +/** + * uber_graph_class_init: + * @klass: A #UberGraphClass. + * + * Initializes the #UberGraphClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_class_init (UberGraphClass *klass) /* IN */ +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->dispose = uber_graph_dispose; + object_class->finalize = uber_graph_finalize; + object_class->get_property = uber_graph_get_property; + object_class->set_property = uber_graph_set_property; + + widget_class = GTK_WIDGET_CLASS(klass); + widget_class->draw = uber_graph_draw; + widget_class->hide = uber_graph_hide; + widget_class->realize = uber_graph_realize; + widget_class->screen_changed = uber_graph_screen_changed; + widget_class->show = uber_graph_show; + widget_class->size_allocate = uber_graph_size_allocate; + widget_class->style_set = uber_graph_style_set; + widget_class->unrealize = uber_graph_unrealize; + widget_class->get_preferred_width = uber_graph_get_preferred_width; + widget_class->get_preferred_height = uber_graph_get_preferred_height; + widget_class->button_press_event = uber_graph_button_press_event; + + show_fps = !!g_getenv("UBER_SHOW_FPS"); + + /* + * FIXME: Use enum. + */ + g_object_class_install_property(object_class, + PROP_FORMAT, + g_param_spec_uint("format", + "format", + "format", + 0, + UBER_GRAPH_FORMAT_PERCENT, + 0, + G_PARAM_READWRITE)); +} + +/** + * uber_graph_init: + * @graph: A #UberGraph. + * + * Initializes the newly created #UberGraph instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_graph_init (UberGraph *graph) /* IN */ +{ + UberGraphPrivate *priv; + + /* + * Store pointer to private data allocation. + */ + graph->priv = uber_graph_get_instance_private(graph); + + priv = graph->priv; + /* + * Enable required events. + */ + gtk_widget_set_events(GTK_WIDGET(graph), GDK_BUTTON_PRESS_MASK); + /* + * Prepare default values. + */ + priv->tick_len = 10; + priv->fps = 20; + priv->fps_real = 1000. / priv->fps; + priv->dps = 1.; + priv->x_slots = 60; + priv->fg_dirty = TRUE; + priv->bg_dirty = TRUE; + priv->full_draw = TRUE; + priv->show_xlines = TRUE; + priv->show_ylines = TRUE; + /* + * TODO: Support labels in a grid. + */ + priv->labels = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); + gtk_box_set_homogeneous (GTK_BOX(priv->labels), TRUE); + priv->align = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(priv->align), priv->labels); + gtk_widget_show(priv->labels); +} diff --git a/deps/uber-graph/uber-graph.h b/deps/uber-graph/uber-graph.h new file mode 100644 index 00000000..2c452d25 --- /dev/null +++ b/deps/uber-graph/uber-graph.h @@ -0,0 +1,104 @@ +/* uber-graph.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_GRAPH_H__ +#define __UBER_GRAPH_H__ + +#include <gtk/gtk.h> + +#include "uber-range.h" +#include "uber-label.h" + +G_BEGIN_DECLS + +#define UBER_TYPE_GRAPH (uber_graph_get_type()) +#define UBER_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_GRAPH, UberGraph)) +#define UBER_GRAPH_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_GRAPH, UberGraph const)) +#define UBER_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_GRAPH, UberGraphClass)) +#define UBER_IS_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_GRAPH)) +#define UBER_IS_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_GRAPH)) +#define UBER_GRAPH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_GRAPH, UberGraphClass)) + +typedef enum +{ + UBER_GRAPH_FORMAT_DIRECT, + UBER_GRAPH_FORMAT_DIRECT1024, + UBER_GRAPH_FORMAT_PERCENT, +} UberGraphFormat; + +typedef struct _UberGraph UberGraph; +typedef struct _UberGraphClass UberGraphClass; +typedef struct _UberGraphPrivate UberGraphPrivate; + +struct _UberGraph +{ + GtkDrawingArea parent; + + /*< private >*/ + UberGraphPrivate *priv; +}; + +struct _UberGraphClass +{ + GtkDrawingAreaClass parent_class; + + gboolean (*downscale) (UberGraph *graph); + gboolean (*get_next_data) (UberGraph *graph); + void (*get_yrange) (UberGraph *graph, + UberRange *range); + void (*render) (UberGraph *graph, + cairo_t *cairo, + GdkRectangle *content_area, + guint epoch, + gfloat each); + void (*render_fast) (UberGraph *graph, + cairo_t *cairo, + GdkRectangle *content_area, + guint epoch, + gfloat each); + void (*set_stride) (UberGraph *graph, + guint stride); +}; + +GType uber_graph_get_type (void) G_GNUC_CONST; +void uber_graph_set_dps (UberGraph *graph, + gfloat dps); +void uber_graph_set_fps (UberGraph *graph, + guint fps); +void uber_graph_redraw (UberGraph *graph); +void uber_graph_set_format (UberGraph *graph, + UberGraphFormat format); +GtkWidget* uber_graph_get_labels (UberGraph *graph); +void uber_graph_get_content_area (UberGraph *graph, + GdkRectangle *rect); +void uber_graph_add_label (UberGraph *graph, + UberLabel *label); +gboolean uber_graph_get_show_xlines (UberGraph *graph); +void uber_graph_set_show_xlines (UberGraph *graph, + gboolean show_xlines); +gboolean uber_graph_get_show_xlabels (UberGraph *graph); +void uber_graph_set_show_xlabels (UberGraph *graph, + gboolean show_xlabels); +gboolean uber_graph_get_show_ylines (UberGraph *graph); +void uber_graph_set_show_ylines (UberGraph *graph, + gboolean show_ylines); +void uber_graph_scale_changed (UberGraph *graph); + +G_END_DECLS + +#endif /* __UBER_GRAPH_H__ */ diff --git a/deps/uber-graph/uber-heat-map.c b/deps/uber-graph/uber-heat-map.c new file mode 100644 index 00000000..6c2caa3b --- /dev/null +++ b/deps/uber-graph/uber-heat-map.c @@ -0,0 +1,347 @@ +/* uber-heat-map.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "uber-heat-map.h" +#include "g-ring.h" + +/** + * SECTION:uber-heat-map.h + * @title: UberHeatMap + * @short_description: + * + * Section overview. + */ + +struct _UberHeatMapPrivate +{ + GRing *raw_data; + gboolean fg_color_set; + GdkRGBA fg_color; + UberHeatMapFunc func; + GDestroyNotify func_destroy; + gpointer func_user_data; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(UberHeatMap, uber_heat_map, UBER_TYPE_GRAPH) + + +/** + * uber_heat_map_new: + * + * Creates a new instance of #UberHeatMap. + * + * Returns: the newly created instance of #UberHeatMap. + * Side effects: None. + */ +GtkWidget* +uber_heat_map_new (void) +{ + UberHeatMap *map; + + map = g_object_new(UBER_TYPE_HEAT_MAP, NULL); + return GTK_WIDGET(map); +} + +/** + * uber_heat_map_destroy_array: + * @array: A #GArray. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_destroy_array (gpointer data) /* IN */ +{ + GArray **ar = data; + + if (ar) { + g_array_unref(*ar); + } +} + +/** + * uber_heat_map_set_stride: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_set_stride (UberGraph *graph, /* IN */ + guint stride) /* IN */ +{ + UberHeatMapPrivate *priv; + + g_return_if_fail(UBER_IS_HEAT_MAP(graph)); + + priv = UBER_HEAT_MAP(graph)->priv; + if (priv->raw_data) { + g_ring_unref(priv->raw_data); + } + priv->raw_data = g_ring_sized_new(sizeof(GArray*), stride, + uber_heat_map_destroy_array); +} + +/** + * uber_heat_map_set_data_func: + * @map: A #UberHeatMap. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_heat_map_set_data_func (UberHeatMap *map, /* IN */ + UberHeatMapFunc func, /* IN */ + gpointer user_data, /* IN */ + GDestroyNotify destroy) /* IN */ +{ + UberHeatMapPrivate *priv; + + g_return_if_fail(UBER_IS_HEAT_MAP(map)); + + priv = map->priv; + if (priv->func_destroy) { + priv->func_destroy(priv->func_user_data); + } + priv->func = func; + priv->func_destroy = destroy; + priv->func_user_data = user_data; +} + +/** + * uber_heat_map_render: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_render (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *area, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ +#if 0 + UberGraphPrivate *priv; + cairo_pattern_t *cp; + + g_return_if_fail(UBER_IS_HEAT_MAP(graph)); + + priv = graph->priv; + /* + * XXX: Temporarily draw a nice little gradient to test sliding. + */ + cp = cairo_pattern_create_linear(0, 0, area->width, 0); + cairo_pattern_add_color_stop_rgb(cp, 0, .1, .1, .1); + cairo_pattern_add_color_stop_rgb(cp, .2, .3, .3, .5); + cairo_pattern_add_color_stop_rgb(cp, .4, .2, .7, .4); + cairo_pattern_add_color_stop_rgb(cp, .7, .6, .2, .1); + cairo_pattern_add_color_stop_rgb(cp, .8, .6, .8, .1); + cairo_pattern_add_color_stop_rgb(cp, 1., .3, .8, .5); + gdk_cairo_rectangle(cr, area); + cairo_set_source(cr, cp); + cairo_fill(cr); + cairo_pattern_destroy(cp); +#endif +} + +/** + * uber_heat_map_render_fast: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_render_fast (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *area, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberHeatMapPrivate *priv; + GtkStyleContext *style; + GdkRGBA color; + gfloat height; + gint i; + + g_return_if_fail(UBER_IS_HEAT_MAP(graph)); + + priv = UBER_HEAT_MAP(graph)->priv; + color = priv->fg_color; + if (!priv->fg_color_set) { + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &color); + } + /* + * XXX: Temporarily draw nice little squares. + */ + #define COUNT 10 + height = area->height / (gfloat)COUNT; + for (i = 0; i < COUNT; i++) { + cairo_rectangle(cr, + area->x + area->width - each, + area->y + (i * height), + each, + height); + cairo_set_source_rgba(cr, + color.red, + color.green, + color.blue, + g_random_double_range(0., 1.)); + cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); + cairo_fill(cr); + } +} + +/** + * uber_heat_map_get_next_data: + * @graph: A #UberGraph. + * + * Retrieve the next data point for the graph. + * + * Returns: None. + * Side effects: None. + */ +static gboolean +uber_heat_map_get_next_data (UberGraph *graph) /* IN */ +{ + UberHeatMapPrivate *priv; + GArray *array = NULL; + + g_return_val_if_fail(UBER_IS_HEAT_MAP(graph), FALSE); + + priv = UBER_HEAT_MAP(graph)->priv; + if (!priv->func) { + return FALSE; + } + /* + * Retrieve the next data point. + */ + if (!priv->func(UBER_HEAT_MAP(graph), &array, priv->func_user_data)) { + return FALSE; + } + /* + * Store data points. + */ + g_ring_append_val(priv->raw_data, array); +// for (int i = 0; i < array->len; i++) { +// g_print("==> %f\n", g_array_index(array, gdouble, i)); +// } + return TRUE; +} + +/** + * uber_heat_map_set_fg_color: + * @map: A #UberHeatMap. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_heat_map_set_fg_color (UberHeatMap *map, /* IN */ + const GdkRGBA *color) /* IN */ +{ + UberHeatMapPrivate *priv; + + g_return_if_fail(UBER_IS_HEAT_MAP(map)); + + priv = map->priv; + if (!color) { + priv->fg_color_set = FALSE; + memset(&priv->fg_color, 0, sizeof(priv->fg_color)); + } else { + priv->fg_color = *color; + priv->fg_color_set = TRUE; + } +} + +/** + * uber_heat_map_finalize: + * @object: A #UberHeatMap. + * + * Finalizer for a #UberHeatMap instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_finalize (GObject *object) /* IN */ +{ + G_OBJECT_CLASS(uber_heat_map_parent_class)->finalize(object); +} + +/** + * uber_heat_map_class_init: + * @klass: A #UberHeatMapClass. + * + * Initializes the #UberHeatMapClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_class_init (UberHeatMapClass *klass) /* IN */ +{ + GObjectClass *object_class; + UberGraphClass *graph_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = uber_heat_map_finalize; + + graph_class = UBER_GRAPH_CLASS(klass); + graph_class->render = uber_heat_map_render; + graph_class->render_fast = uber_heat_map_render_fast; + graph_class->set_stride = uber_heat_map_set_stride; + graph_class->get_next_data = uber_heat_map_get_next_data; +} + +/** + * uber_heat_map_init: + * @map: A #UberHeatMap. + * + * Initializes the newly created #UberHeatMap instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_heat_map_init (UberHeatMap *map) /* IN */ +{ + map->priv = uber_heat_map_get_instance_private(map); +} diff --git a/deps/uber-graph/uber-heat-map.h b/deps/uber-graph/uber-heat-map.h new file mode 100644 index 00000000..fb1bd2bd --- /dev/null +++ b/deps/uber-graph/uber-heat-map.h @@ -0,0 +1,66 @@ +/* uber-heat-map.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_HEAT_MAP_H__ +#define __UBER_HEAT_MAP_H__ + +#include "uber-graph.h" + +G_BEGIN_DECLS + +#define UBER_TYPE_HEAT_MAP (uber_heat_map_get_type()) +#define UBER_HEAT_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_HEAT_MAP, UberHeatMap)) +#define UBER_HEAT_MAP_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_HEAT_MAP, UberHeatMap const)) +#define UBER_HEAT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_HEAT_MAP, UberHeatMapClass)) +#define UBER_IS_HEAT_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_HEAT_MAP)) +#define UBER_IS_HEAT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_HEAT_MAP)) +#define UBER_HEAT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_HEAT_MAP, UberHeatMapClass)) + +typedef struct _UberHeatMap UberHeatMap; +typedef struct _UberHeatMapClass UberHeatMapClass; +typedef struct _UberHeatMapPrivate UberHeatMapPrivate; + +typedef gboolean (*UberHeatMapFunc) (UberHeatMap *map, + GArray **values, + gpointer user_data); + +struct _UberHeatMap +{ + UberGraph parent; + + /*< private >*/ + UberHeatMapPrivate *priv; +}; + +struct _UberHeatMapClass +{ + UberGraphClass parent_class; +}; + +GType uber_heat_map_get_type (void) G_GNUC_CONST; +GtkWidget* uber_heat_map_new (void); +void uber_heat_map_set_fg_color (UberHeatMap *map, + const GdkRGBA *color); +void uber_heat_map_set_data_func (UberHeatMap *map, + UberHeatMapFunc func, + gpointer user_data, + GDestroyNotify destroy); + +G_END_DECLS + +#endif /* __UBER_HEAT_MAP_H__ */ diff --git a/deps/uber-graph/uber-label.c b/deps/uber-graph/uber-label.c new file mode 100644 index 00000000..abc865fc --- /dev/null +++ b/deps/uber-graph/uber-label.c @@ -0,0 +1,395 @@ +/* uber-label.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n.h> + +#include "uber-label.h" + +/** + * SECTION:uber-label.h + * @title: UberLabel + * @short_description: + * + * Section overview. + */ + + +struct _UberLabelPrivate +{ + GtkWidget *hbox; + GtkWidget *block; + GtkWidget *label; + GdkRGBA color; + gboolean in_block; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(UberLabel, uber_label, GTK_TYPE_WIDGET) + +enum +{ + COLOR_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_COLOR, + PROP_TEXT, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * uber_label_new: + * + * Creates a new instance of #UberLabel. + * + * Returns: the newly created instance of #UberLabel. + * Side effects: None. + */ +GtkWidget* +uber_label_new (void) +{ + UberLabel *label; + + label = g_object_new(UBER_TYPE_LABEL, NULL); + return GTK_WIDGET(label); +} + +/** + * uber_label_set_text: + * @label: A #UberLabel. + * @text: The label text. + * + * Sets the text for the label. + * + * Returns: None. + * Side effects: None. + */ +void +uber_label_set_text (UberLabel *label, /* IN */ + const gchar *text) /* IN */ +{ + UberLabelPrivate *priv; + + g_return_if_fail(UBER_IS_LABEL(label)); + + priv = label->priv; + gtk_label_set_text(GTK_LABEL(priv->label), text); +} + +/** + * uber_label_set_color: + * @label: A #UberLabel. + * @color: A #GdkRGBA. + * + * Sets the color of the label. + * + * Returns: None. + * Side effects: None. + */ +void +uber_label_set_color (UberLabel *label, /* IN */ + const GdkRGBA *color) /* IN */ +{ + UberLabelPrivate *priv; + + g_return_if_fail(UBER_IS_LABEL(label)); + + priv = label->priv; + priv->color = *color; +} + +static void +uber_label_block_draw (GtkWidget *block, /* IN */ + cairo_t *cr, /* IN */ + UberLabel *label) /* IN */ +{ + UberLabelPrivate *priv; + GtkAllocation alloc; + + g_return_if_fail(UBER_IS_LABEL(label)); + + priv = label->priv; + gtk_widget_get_allocation(block, &alloc); + /* + * Draw background. + */ + gdk_cairo_set_source_rgba(cr, &priv->color); + cairo_rectangle(cr, .5, .5, alloc.width - 1., alloc.height - 1.); + cairo_fill_preserve(cr); + /* + * Add highlight if mouse is in the block. + */ + if (priv->in_block) { + cairo_set_source_rgba(cr, 1., 1., 1., .3); + cairo_fill_preserve(cr); + } + /* + * Stroke the edge of the block. + */ + cairo_set_line_width(cr, 1.0); + cairo_set_source_rgba(cr, 0., 0., 0., .5); + cairo_stroke(cr); + /* + * Stroke the highlight of the block. + */ + cairo_rectangle(cr, 1.5, 1.5, alloc.width - 3., alloc.height - 3.); + cairo_set_source_rgba(cr, 1., 1., 1., .5); + cairo_stroke(cr); +} + +/** + * uber_label_block_enter_notify_event: + * @label: A #UberLabel. + * + * Tracks the mouse entering the block widget. + * + * Returns: %FALSE to allow further callbacks. + * Side effects: None. + */ +static gboolean +uber_label_block_enter_notify_event (GtkWidget *widget, /* IN */ + GdkEventCrossing *event, /* IN */ + UberLabel *label) /* IN */ +{ + UberLabelPrivate *priv; + + priv = label->priv; + priv->in_block = TRUE; + gtk_widget_queue_draw(widget); + return FALSE; +} + +/** + * uber_label_block_leave_notify_event: + * @label: A #UberLabel. + * + * Tracks the mouse leaving the block widget. + * + * Returns: %FALSE to allow further callbacks. + * Side effects: None. + */ +static gboolean +uber_label_block_leave_notify_event (GtkWidget *widget, /* IN */ + GdkEventCrossing *event, /* IN */ + UberLabel *label) /* IN */ +{ + UberLabelPrivate *priv; + + priv = label->priv; + priv->in_block = FALSE; + gtk_widget_queue_draw(widget); + return FALSE; +} + +/** + * uber_label_block_button_press_event: + * @widget: A #GtkWidget. + * @event: A #GdkEventButton. + * @label: An #UberLabel. + * + * Callback to handle a button press event within the colored block. + * + * Returns: %FALSE always to allow further signal emission. + * Side effects: None. + */ +static gboolean +uber_label_block_button_press_event (GtkWidget *widget, /* IN */ + GdkEventButton *event, /* IN */ + UberLabel *label) /* IN */ +{ + UberLabelPrivate *priv; + GtkWidget *dialog; + + g_return_val_if_fail(UBER_IS_LABEL(label), FALSE); + + priv = label->priv; + dialog = gtk_color_chooser_dialog_new("", GTK_WINDOW(gtk_widget_get_toplevel(widget))); + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE); + gtk_color_chooser_set_rgba( + GTK_COLOR_CHOOSER(dialog), + &priv->color); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + gtk_color_chooser_get_rgba( + GTK_COLOR_CHOOSER(dialog), + &priv->color); + gtk_widget_queue_draw(widget); + g_signal_emit(label, signals[COLOR_CHANGED], + 0, &priv->color); + } + gtk_widget_destroy(dialog); + return FALSE; +} + +/** + * uber_label_set_property: + * @object: (in): A #GObject. + * @prop_id: (in): The property identifier. + * @value: (in): The given property. + * @pspec: (in): A #ParamSpec. + * + * Set a given #GObject property. + */ +static void +uber_label_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + UberLabel *label = UBER_LABEL(object); + + switch (prop_id) { + case PROP_COLOR: + uber_label_set_color(label, g_value_get_boxed(value)); + break; + case PROP_TEXT: + uber_label_set_text(label, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +/** + * uber_label_finalize: + * @object: A #UberLabel. + * + * Finalizer for a #UberLabel instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_label_finalize (GObject *object) /* IN */ +{ + G_OBJECT_CLASS(uber_label_parent_class)->finalize(object); +} + +/** + * uber_label_class_init: + * @klass: A #UberLabelClass. + * + * Initializes the #UberLabelClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_label_class_init (UberLabelClass *klass) /* IN */ +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = uber_label_finalize; + object_class->set_property = uber_label_set_property; + + g_object_class_install_property(object_class, + PROP_COLOR, + g_param_spec_boxed("color", + "color", + "color", + GDK_TYPE_RGBA, + G_PARAM_WRITABLE)); + + g_object_class_install_property(object_class, + PROP_TEXT, + g_param_spec_string("text", + "text", + "text", + NULL, + G_PARAM_WRITABLE)); + + /** + * UberLabel::color-changed: + * @label: An #UberLabel. + * @color: A #GdkRGBA. + * + * Signal emitted when the color is changed. + */ + signals[COLOR_CHANGED] = g_signal_new("color-changed", + UBER_TYPE_LABEL, + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); +} + +/** + * uber_label_init: + * @label: A #UberLabel. + * + * Initializes the newly created #UberLabel instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_label_init (UberLabel *label) /* IN */ +{ + UberLabelPrivate *priv; + + label->priv = uber_label_get_instance_private(label); + + priv = label->priv; + priv->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + priv->block = gtk_drawing_area_new(); + priv->label = gtk_label_new(NULL); + gdk_rgba_parse(&priv->color, "#cc0000"); + gtk_widget_set_halign(GTK_WIDGET(priv->label), GTK_ALIGN_START); + gtk_widget_set_valign(GTK_WIDGET(priv->label), GTK_ALIGN_CENTER); + gtk_widget_set_size_request(priv->block, 32, 17); + gtk_container_add(GTK_CONTAINER(label), priv->hbox); + gtk_box_pack_start(GTK_BOX(priv->hbox), priv->block, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(priv->hbox), priv->label, TRUE, TRUE, 0); + gtk_widget_add_events(priv->block, + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK); + g_signal_connect(priv->block, + "draw", + G_CALLBACK(uber_label_block_draw), + label); + g_signal_connect(priv->block, + "enter-notify-event", + G_CALLBACK(uber_label_block_enter_notify_event), + label); + g_signal_connect(priv->block, + "leave-notify-event", + G_CALLBACK(uber_label_block_leave_notify_event), + label); + g_signal_connect(priv->block, + "button-press-event", + G_CALLBACK(uber_label_block_button_press_event), + label); + gtk_widget_set_tooltip_text(GTK_WIDGET(priv->block), + _("Click to select color")); + gtk_widget_show(priv->hbox); + gtk_widget_show(priv->block); + gtk_widget_show(priv->label); +} diff --git a/deps/uber-graph/uber-label.h b/deps/uber-graph/uber-label.h new file mode 100644 index 00000000..8c4c74f5 --- /dev/null +++ b/deps/uber-graph/uber-label.h @@ -0,0 +1,60 @@ +/* uber-label.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_LABEL_H__ +#define __UBER_LABEL_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define UBER_TYPE_LABEL (uber_label_get_type()) +#define UBER_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_LABEL, UberLabel)) +#define UBER_LABEL_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_LABEL, UberLabel const)) +#define UBER_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_LABEL, UberLabelClass)) +#define UBER_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_LABEL)) +#define UBER_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_LABEL)) +#define UBER_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_LABEL, UberLabelClass)) + +typedef struct _UberLabel UberLabel; +typedef struct _UberLabelClass UberLabelClass; +typedef struct _UberLabelPrivate UberLabelPrivate; + +struct _UberLabel +{ + GtkAlignment parent; + + /*< private >*/ + UberLabelPrivate *priv; +}; + +struct _UberLabelClass +{ + GtkAlignmentClass parent_class; +}; + +GType uber_label_get_type (void) G_GNUC_CONST; +GtkWidget* uber_label_new (void); +void uber_label_set_color (UberLabel *label, + const GdkRGBA *color); +void uber_label_set_text (UberLabel *label, + const gchar *text); + +G_END_DECLS + +#endif /* __UBER_LABEL_H__ */ diff --git a/deps/uber-graph/uber-line-graph.c b/deps/uber-graph/uber-line-graph.c new file mode 100644 index 00000000..776f5363 --- /dev/null +++ b/deps/uber-graph/uber-line-graph.c @@ -0,0 +1,1028 @@ +/* uber-line-graph.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <string.h> + +#include "uber-line-graph.h" +#include "uber-range.h" +#include "uber-scale.h" +#include "g-ring.h" + +#define RECT_BOTTOM(r) ((r).y + (r).height) +#define RECT_RIGHT(r) ((r).x + (r).width) +#define SCALE_FACTOR (0.2) + +/** + * SECTION:uber-line-graph.h + * @title: UberLineGraph + * @short_description: + * + * Section overview. + */ + + +typedef struct +{ + GRing *raw_data; + GdkRGBA color; + gdouble width; + gdouble *dashes; + gint num_dashes; + gdouble dash_offset; + UberLabel *label; + guint label_id; +} LineInfo; + +struct _UberLineGraphPrivate +{ + GArray *lines; + cairo_antialias_t antialias; + guint stride; + gboolean autoscale; + UberRange range; + UberScale scale; + gpointer scale_data; + GDestroyNotify scale_notify; + UberLineGraphFunc func; + gpointer func_data; + GDestroyNotify func_notify; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(UberLineGraph, uber_line_graph, UBER_TYPE_GRAPH) + + +enum +{ + PROP_0, + PROP_AUTOSCALE, + PROP_RANGE, +}; + +/** + * uber_line_graph_init_ring: + * @ring: A #GRing. + * + * Initialize the #GRing to default values (UBER_LINE_GRAPH_NO_VALUE). + * + * Returns: None. + * Side effects: None. + */ +static inline void +uber_line_graph_init_ring (GRing *ring) /* IN */ +{ + gdouble val = UBER_LINE_GRAPH_NO_VALUE; + guint i; + + g_return_if_fail(ring != NULL); + + for (i = 0; i < ring->len; i++) { + g_ring_append_val(ring, val); + } +} + +/** + * uber_line_graph_new: + * + * Creates a new instance of #UberLineGraph. + * + * Returns: the newly created instance of #UberLineGraph. + * Side effects: None. + */ +GtkWidget* +uber_line_graph_new (void) +{ + UberLineGraph *graph; + + graph = g_object_new(UBER_TYPE_LINE_GRAPH, NULL); + return GTK_WIDGET(graph); +} + +/** + * uber_line_graph_color: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_color_changed (UberLabel *label, /* IN */ + GdkRGBA *color, /* IN */ + UberLineGraph *graph) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *info; + guint i; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(color != NULL); + + priv = graph->priv; + for (i = 0; i < priv->lines->len; i++) { + info = &g_array_index(priv->lines, LineInfo, i); + if (info->label == label) { + info->color = *color; + } + } + uber_graph_redraw(UBER_GRAPH(graph)); +} + +void +uber_line_graph_bind_label (UberLineGraph *graph, /* IN */ + guint line, /* IN */ + UberLabel *label) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *info; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(UBER_IS_LABEL(label)); + g_return_if_fail(line > 0); + g_return_if_fail(line <= graph->priv->lines->len); + + priv = graph->priv; + info = &g_array_index(priv->lines, LineInfo, line - 1); + if (info->label_id) { + g_signal_handler_disconnect(info->label, info->label_id); + } + info->label = label; + info->label_id = g_signal_connect(label, + "color-changed", + G_CALLBACK(uber_line_graph_color_changed), + graph); +} + +/** + * uber_line_graph_set_autoscale: + * @graph: A #UberLineGraph. + * @autoscale: Should we autoscale. + * + * Sets if we should autoscale the range of the graph when a new input + * value is outside the visible range. + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_autoscale (UberLineGraph *graph, /* IN */ + gboolean autoscale) /* IN */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = graph->priv; + priv->autoscale = autoscale; +} + +/** + * uber_line_graph_get_autoscale: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +gboolean +uber_line_graph_get_autoscale (UberLineGraph *graph) /* IN */ +{ + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); + return graph->priv->autoscale; +} + +/** + * uber_line_graph_add_line: + * @graph: A #UberLineGraph. + * @color: A #GdkRGBA for the line or %NULL. + * + * Adds a new line to the graph. If color is %NULL, the next value + * in the default color list will be used. + * + * See uber_line_graph_remove_line(). + * + * Returns: The line identifier. + * Side effects: None. + */ +gint +uber_line_graph_add_line (UberLineGraph *graph, /* IN */ + const GdkRGBA *color, /* IN */ + UberLabel *label) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo info = { 0 }; + + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), 0); + + priv = graph->priv; + info.width = 1.0; + /* + * Retrieve the lines color. + */ + if (color) { + info.color = *color; + } else { + gdk_rgba_parse(&info.color, "#729fcf"); + } + /* + * Allocate buffers for data points. + */ + info.raw_data = g_ring_sized_new(sizeof(gdouble), priv->stride, NULL); + uber_line_graph_init_ring(info.raw_data); + /* + * Store the newly crated line. + */ + g_array_append_val(priv->lines, info); + /* + * Mark the graph for full redraw. + */ + uber_graph_redraw(UBER_GRAPH(graph)); + /* + * Attach label. + */ + if (label) { + uber_line_graph_bind_label(graph, priv->lines->len, label); + uber_graph_add_label(UBER_GRAPH(graph), label); + uber_label_set_color(label, &info.color); + } + /* + * Line indexes start from 1. + */ + return priv->lines->len; +} + +/** + * uber_line_graph_set_antialias: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_antialias (UberLineGraph *graph, /* IN */ + cairo_antialias_t antialias) /* IN */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = graph->priv; + priv->antialias = antialias; + uber_graph_redraw(UBER_GRAPH(graph)); +} + +/** + * uber_line_graph_get_antialias: + * @graph: A #UberLineGraph. + * + * Retrieves the antialias mode for the graph. + * + * Returns: A cairo_antialias_t. + * Side effects: None. + */ +cairo_antialias_t +uber_line_graph_get_antialias (UberLineGraph *graph) /* IN */ +{ + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), 0); + + return graph->priv->antialias; +} + +/** + * uber_line_graph_get_next_data: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static gboolean +uber_line_graph_get_next_data (UberGraph *graph) /* IN */ +{ + UberLineGraphPrivate *priv; + gboolean scale_changed = FALSE; + gboolean ret = FALSE; + LineInfo *line; + gdouble val; + guint i; + + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); + + priv = UBER_LINE_GRAPH(graph)->priv; + /* + * Retrieve the next data point. + */ + if (priv->func) { + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + val = priv->func(UBER_LINE_GRAPH(graph), i + 1, priv->func_data); + g_ring_append_val(line->raw_data, val); + if (priv->autoscale) { + if (val < priv->range.begin) { + priv->range.begin = val - (val * SCALE_FACTOR); + priv->range.range = priv->range.end - priv->range.begin; + scale_changed = TRUE; + } else if (val > priv->range.end) { + priv->range.end = val + (val * SCALE_FACTOR); + priv->range.range = priv->range.end - priv->range.begin; + scale_changed = TRUE; + } + } + } + } + if (scale_changed) { + uber_graph_scale_changed(graph); + } + return ret; +} + +/** + * uber_line_graph_set_data_func: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_data_func (UberLineGraph *graph, /* IN */ + UberLineGraphFunc func, /* IN */ + gpointer user_data, /* IN */ + GDestroyNotify notify) /* IN */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = graph->priv; + /* + * Free existing data func if neccessary. + */ + if (priv->func_notify) { + priv->func_notify(priv->func_data); + } + /* + * Store data func. + */ + priv->func = func; + priv->func_data = user_data; + priv->func_notify = notify; +} + +/** + * uber_line_graph_stylize_line: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_stylize_line (UberLineGraph *graph, /* IN */ + LineInfo *info, /* IN */ + cairo_t *cr) /* IN */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(info != NULL); + + priv = graph->priv; + if (info->dashes) { + cairo_set_dash(cr, info->dashes, info->num_dashes, info->dash_offset); + } else { + cairo_set_dash(cr, NULL, 0, 0); + } + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_width(cr, info->width); + cairo_set_antialias(cr, priv->antialias); + cairo_set_source_rgba(cr, + info->color.red, + info->color.green, + info->color.blue, + info->color.alpha); +} + +/** + * uber_line_graph_render: + * @graph: A #UberGraph. + * @cr: A #cairo_t context. + * @area: Full area to render contents within. + * @line: The line to render. + * + * Render a particular line to the graph. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_render_line (UberLineGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *area, /* IN */ + LineInfo *line, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberLineGraphPrivate *priv; + UberRange pixel_range; + GdkRectangle vis; + guint x; + guint last_x; + gdouble y; + gdouble last_y; + gdouble val; + guint i; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = graph->priv; + uber_graph_get_content_area(UBER_GRAPH(graph), &vis); + pixel_range.begin = area->y + 1; + pixel_range.end = area->y + area->height; + pixel_range.range = pixel_range.end - pixel_range.begin; + /* + * Prepare cairo settings. + */ + uber_line_graph_stylize_line(graph, line, cr); + /* + * Force a new path. + */ + cairo_new_path(cr); + /* + * Draw the line contents as bezier curves. + */ + for (i = 0; i < line->raw_data->len; i++) { + /* + * Retrieve data point. + */ + val = g_ring_get_index(line->raw_data, gdouble, (int)i); + /* + * Once we get to UBER_LINE_GRAPH_NO_VALUE, we must be at the end of the data + * sequence. This may not always be true in the future. + */ + if (val == UBER_LINE_GRAPH_NO_VALUE) { + break; + } + /* + * Translate value to coordinate system. + */ + if (!priv->scale(&priv->range, &pixel_range, &val, priv->scale_data)) { + break; + } + /* + * Calculate X/Y coordinate. + */ + y = (gint)(RECT_BOTTOM(*area) - val) - .5; + x = epoch - (each * i); + if (i == 0) { + /* + * Just move to the right position on first entry. + */ + cairo_move_to(cr, x, y); + goto next; + } else { + /* + * Draw curve to data point using the last X/Y positions as + * control points. + */ + cairo_curve_to(cr, + last_x - (each / 2.), + last_y, + last_x - (each / 2.), + y, x, y); + } + next: + last_y = y; + last_x = x; + } + /* + * Stroke the line content. + */ + cairo_stroke(cr); +} + +/** + * uber_line_graph_render: + * @graph: A #UberGraph. + * + * Render the entire contents of the graph. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_render (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *rect, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *line; + guint i; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = UBER_LINE_GRAPH(graph)->priv; + /* + * Render each line to the graph. + */ + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + uber_line_graph_render_line(UBER_LINE_GRAPH(graph), cr, rect, + line, epoch, each); + } +} + +/** + * uber_line_graph_render_fast: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_render_fast (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *rect, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberLineGraphPrivate *priv; + UberRange pixel_range; + LineInfo *line; + gdouble last_y; + gdouble y; + guint i; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(cr != NULL); + g_return_if_fail(rect != NULL); + + priv = UBER_LINE_GRAPH(graph)->priv; + pixel_range.begin = rect->y + 1; + pixel_range.end = rect->y + rect->height; + pixel_range.range = pixel_range.end - pixel_range.begin; + /* + * Render most recent data point for each line. + */ + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + uber_line_graph_stylize_line(UBER_LINE_GRAPH(graph), line, cr); + /* + * Calculate positions. + */ + y = g_ring_get_index(line->raw_data, gdouble, 0); + last_y = g_ring_get_index(line->raw_data, gdouble, 1); + /* + * Don't try to draw before we have real values. + */ + if ((isnan(y) || isinf(y)) || (isnan(last_y) || isinf(last_y))) { + continue; + } + /* + * Translate to coordinate scale. + */ + if (!priv->scale(&priv->range, &pixel_range, &y, priv->scale_data) || + !priv->scale(&priv->range, &pixel_range, &last_y, priv->scale_data)) { + continue; + } + /* + * Translate position from bottom right corner. + */ + y = (gint)(RECT_BOTTOM(*rect) - y) - .5; + last_y = (gint)(RECT_BOTTOM(*rect) - last_y) - .5; + /* + * Convert relative position to fixed from bottom pixel. + */ + cairo_new_path(cr); + cairo_move_to(cr, epoch, y); + cairo_curve_to(cr, + epoch - (each / 2.), + y, + epoch - (each / 2.), + last_y, + epoch - each, + last_y); + cairo_stroke(cr); + } +} + +/** + * uber_line_graph_set_stride: + * @graph: A #UberGraph. + * @stride: The number of data points within the graph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_set_stride (UberGraph *graph, /* IN */ + guint stride) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *line; + guint i; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + + priv = UBER_LINE_GRAPH(graph)->priv; + priv->stride = stride; + /* + * TODO: Support changing stride after lines have been added. + */ + if (priv->lines->len) { + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + g_ring_unref(line->raw_data); + line->raw_data = g_ring_sized_new(sizeof(gdouble), + priv->stride, NULL); + uber_line_graph_init_ring(line->raw_data); + } + return; + } +} + +/** + * uber_line_graph_get_range: + * @graph: (in): A #UberLineGraph. + * + * XXX + * + * Returns: An #UberRange which should not be modified or freed. + * Side effects: None. + */ +const UberRange* +uber_line_graph_get_range (UberLineGraph *graph) /* IN */ +{ + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), NULL); + return &graph->priv->range; +} + +/** + * uber_line_graph_set_range: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_range (UberLineGraph *graph, /* IN */ + const UberRange *range) /* IN */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(range != NULL); + + priv = graph->priv; + priv->range = *range; +} + +/** + * uber_line_graph_get_yrange: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_get_yrange (UberGraph *graph, /* IN */ + UberRange *range) /* OUT */ +{ + UberLineGraphPrivate *priv; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(range != NULL); + + priv = UBER_LINE_GRAPH(graph)->priv; + *range = priv->range; +} + +/** + * uber_line_graph_set_line_dash: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_line_dash (UberLineGraph *graph, /* IN */ + guint line, /* IN */ + const gdouble *dashes, /* IN */ + gint num_dashes, /* IN */ + gdouble offset) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *info; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(dashes != NULL); + g_return_if_fail(num_dashes > 0); + g_return_if_fail(line > 0); + g_return_if_fail(line <= graph->priv->lines->len); + + priv = graph->priv; + info = &g_array_index(priv->lines, LineInfo, line - 1); + if (info->dashes) { + g_free(info->dashes); + info->dashes = NULL; + info->num_dashes = 0; + info->dash_offset = 0; + } + if (dashes) { + info->dashes = g_new0(gdouble, num_dashes); + memcpy(info->dashes, dashes, sizeof(gdouble) * num_dashes); + info->num_dashes = num_dashes; + info->dash_offset = offset; + } +} + +/** + * uber_line_graph_set_line_width: + * @graph: A #UberLineGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_line_graph_set_line_width (UberLineGraph *graph, /* IN */ + gint line, /* IN */ + gdouble width) /* IN */ +{ + LineInfo *info; + + g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); + g_return_if_fail(line > 0); + g_return_if_fail(line <= (int)graph->priv->lines->len); + + info = &g_array_index(graph->priv->lines, LineInfo, line - 1); + info->width = width; + uber_graph_redraw(UBER_GRAPH(graph)); +} + +/** + * uber_line_graph_downscale: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static gboolean +uber_line_graph_downscale (UberGraph *graph) /* IN */ +{ + UberLineGraphPrivate *priv; + gboolean ret = FALSE; + gdouble val = 0; + gdouble cur; + LineInfo *line; + guint i; + guint j; + + g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); + + priv = UBER_LINE_GRAPH(graph)->priv; + /* + * If we are set to autoscale, ignore request. + */ + if (!priv->autoscale) { + return FALSE; + } + /* + * Determine the largest value available. + */ + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + for (j = 0; j < line->raw_data->len; j++) { + cur = g_ring_get_index(line->raw_data, gdouble, (int)j); + val = (cur > val) ? cur : val; + } + } + /* + * Downscale if we can. + */ + if (val != priv->range.begin) { + if ((val * (1. + SCALE_FACTOR)) < priv->range.end) { + priv->range.end = val * (1. + SCALE_FACTOR); + priv->range.range = priv->range.end - priv->range.begin; + ret = TRUE; + } + } + return ret; +} + +/** + * uber_line_graph_finalize: + * @object: A #UberLineGraph. + * + * Finalizer for a #UberLineGraph instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_finalize (GObject *object) /* IN */ +{ + UberLineGraphPrivate *priv; + LineInfo *line; + guint i; + + priv = UBER_LINE_GRAPH(object)->priv; + /* + * Clean up after cached values. + */ + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + g_ring_unref(line->raw_data); + g_free(line->dashes); + } + G_OBJECT_CLASS(uber_line_graph_parent_class)->finalize(object); +} + +/** + * uber_line_graph_set_property: + * @object: (in): A #GObject. + * @prop_id: (in): The property identifier. + * @value: (out): The given property. + * @pspec: (in): A #ParamSpec. + * + * Get a given #GObject property. + */ +static void +uber_line_graph_get_property (GObject *object, /* IN */ + guint prop_id, /* IN */ + GValue *value, /* OUT */ + GParamSpec *pspec) /* IN */ +{ + UberLineGraph *graph = UBER_LINE_GRAPH(object); + + switch (prop_id) { + case PROP_AUTOSCALE: + g_value_set_boolean(value, uber_line_graph_get_autoscale(graph)); + break; + case PROP_RANGE: + g_value_set_boxed(value, uber_line_graph_get_range(graph)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +/** + * uber_line_graph_set_property: + * @object: (in): A #GObject. + * @prop_id: (in): The property identifier. + * @value: (in): The given property. + * @pspec: (in): A #ParamSpec. + * + * Set a given #GObject property. + */ +static void +uber_line_graph_set_property (GObject *object, /* IN */ + guint prop_id, /* IN */ + const GValue *value, /* IN */ + GParamSpec *pspec) /* IN */ +{ + UberLineGraph *graph = UBER_LINE_GRAPH(object); + + switch (prop_id) { + case PROP_AUTOSCALE: + uber_line_graph_set_autoscale(graph, g_value_get_boolean(value)); + break; + case PROP_RANGE: + uber_line_graph_set_range(graph, g_value_get_boxed(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +/** + * uber_line_graph_class_init: + * @klass: A #UberLineGraphClass. + * + * Initializes the #UberLineGraphClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_class_init (UberLineGraphClass *klass) /* IN */ +{ + GObjectClass *object_class; + UberGraphClass *graph_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = uber_line_graph_finalize; + object_class->get_property = uber_line_graph_get_property; + object_class->set_property = uber_line_graph_set_property; + + graph_class = UBER_GRAPH_CLASS(klass); + graph_class->downscale = uber_line_graph_downscale; + graph_class->get_next_data = uber_line_graph_get_next_data; + graph_class->get_yrange = uber_line_graph_get_yrange; + graph_class->render = uber_line_graph_render; + graph_class->render_fast = uber_line_graph_render_fast; + graph_class->set_stride = uber_line_graph_set_stride; + + g_object_class_install_property(object_class, + PROP_AUTOSCALE, + g_param_spec_boolean("autoscale", + "autoscale", + "autoscale", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property(object_class, + PROP_RANGE, + g_param_spec_boxed("range", + "range", + "range", + UBER_TYPE_RANGE, + G_PARAM_READWRITE)); +} + +/** + * uber_line_graph_init: + * @graph: A #UberLineGraph. + * + * Initializes the newly created #UberLineGraph instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_line_graph_init (UberLineGraph *graph) /* IN */ +{ + UberLineGraphPrivate *priv; + + /* + * Keep pointer to private data. + */ + graph->priv = uber_line_graph_get_instance_private(graph); + + priv = graph->priv; + /* + * Initialize defaults. + */ + priv->stride = 60; + priv->antialias = CAIRO_ANTIALIAS_DEFAULT; + priv->lines = g_array_sized_new(FALSE, FALSE, sizeof(LineInfo), 2); + priv->scale = uber_scale_linear; + priv->autoscale = TRUE; +} + +void +uber_line_graph_clear (UberLineGraph *graph) /* IN */ +{ + UberLineGraphPrivate *priv = graph->priv; + LineInfo *line; + guint i; + + for (i = 0; i < priv->lines->len; i++) { + line = &g_array_index(priv->lines, LineInfo, i); + uber_line_graph_init_ring(line->raw_data); + } +} diff --git a/deps/uber-graph/uber-line-graph.h b/deps/uber-graph/uber-line-graph.h new file mode 100644 index 00000000..22a01681 --- /dev/null +++ b/deps/uber-graph/uber-line-graph.h @@ -0,0 +1,100 @@ +/* uber-line-graph.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_LINE_GRAPH_H__ +#define __UBER_LINE_GRAPH_H__ + +#include <math.h> + +#include "uber-graph.h" +#include "uber-range.h" +#include "uber-label.h" + +G_BEGIN_DECLS + +#define UBER_TYPE_LINE_GRAPH (uber_line_graph_get_type()) +#define UBER_LINE_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_LINE_GRAPH, UberLineGraph)) +#define UBER_LINE_GRAPH_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_LINE_GRAPH, UberLineGraph const)) +#define UBER_LINE_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_LINE_GRAPH, UberLineGraphClass)) +#define UBER_IS_LINE_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_LINE_GRAPH)) +#define UBER_IS_LINE_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_LINE_GRAPH)) +#define UBER_LINE_GRAPH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_LINE_GRAPH, UberLineGraphClass)) + +#define UBER_LINE_GRAPH_NO_VALUE (NAN) + +typedef struct _UberLineGraph UberLineGraph; +typedef struct _UberLineGraphClass UberLineGraphClass; +typedef struct _UberLineGraphPrivate UberLineGraphPrivate; + +/** + * UberLineGraphFunc: + * @graph: A #UberLineGraph. + * @user_data: User data supplied to uber_line_graph_set_data_func(). + * + * Callback prototype for retrieving the next data point in the graph. + * + * Returns: a value if successful, otherwise %UBER_LINE_GRAPH_NO_VALUE + * Side effects: Implementation dependent. + */ +typedef gdouble (*UberLineGraphFunc) (UberLineGraph *graph, + guint line, + gpointer user_data); + +struct _UberLineGraph +{ + UberGraph parent; + + /*< private >*/ + UberLineGraphPrivate *priv; +}; + +struct _UberLineGraphClass +{ + UberGraphClass parent_class; +}; + +gint uber_line_graph_add_line (UberLineGraph *graph, + const GdkRGBA *color, + UberLabel *label); +cairo_antialias_t uber_line_graph_get_antialias (UberLineGraph *graph); +GType uber_line_graph_get_type (void) G_GNUC_CONST; +GtkWidget* uber_line_graph_new (void); +void uber_line_graph_set_antialias (UberLineGraph *graph, + cairo_antialias_t antialias); +void uber_line_graph_set_data_func (UberLineGraph *graph, + UberLineGraphFunc func, + gpointer user_data, + GDestroyNotify notify); +gboolean uber_line_graph_get_autoscale (UberLineGraph *graph); +void uber_line_graph_set_autoscale (UberLineGraph *graph, + gboolean autoscale); +const UberRange* uber_line_graph_get_range (UberLineGraph *graph); +void uber_line_graph_set_range (UberLineGraph *graph, + const UberRange *range); +void uber_line_graph_set_line_dash (UberLineGraph *graph, + guint line, + const gdouble *dashes, + gint num_dashes, + gdouble offset); +void uber_line_graph_set_line_width (UberLineGraph *graph, + gint line, + gdouble width); +void uber_line_graph_clear (UberLineGraph *graph); +G_END_DECLS + +#endif /* __UBER_LINE_GRAPH_H__ */ diff --git a/deps/uber-graph/uber-range.c b/deps/uber-graph/uber-range.c new file mode 100644 index 00000000..e8afc0de --- /dev/null +++ b/deps/uber-graph/uber-range.c @@ -0,0 +1,64 @@ +/* uber-range.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 <string.h> + +#include "uber-range.h" + +UberRange* +uber_range_copy (UberRange *src) +{ + UberRange *dst = g_new0(UberRange, 1); + memcpy(dst, src, sizeof(UberRange)); + return dst; +} + +void +uber_range_free (UberRange *range) +{ + g_free(range); +} + +UberRange* +uber_range_new (gdouble begin, + gdouble end) +{ + UberRange *range; + + range = g_new0(UberRange, 1); + range->begin = begin; + range->end = end; + range->range = range->end - range->begin; + return range; +} + +GType +uber_range_get_type (void) +{ + static gsize initialized = FALSE; + GType type_id = 0; + + if (G_UNLIKELY(g_once_init_enter(&initialized))) { + type_id = g_boxed_type_register_static("UberRange", + (GBoxedCopyFunc)uber_range_copy, + (GBoxedFreeFunc)uber_range_free); + g_once_init_leave(&initialized, TRUE); + } + + return type_id; +} diff --git a/deps/uber-graph/uber-range.h b/deps/uber-graph/uber-range.h new file mode 100644 index 00000000..166a1313 --- /dev/null +++ b/deps/uber-graph/uber-range.h @@ -0,0 +1,50 @@ +/* uber-range.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_RANGE_H__ +#define __UBER_RANGE_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define UBER_TYPE_RANGE (uber_range_get_type()) + +/** + * UberRange: + * + * #UberRange is a structure that encapsulates the range of a particular + * scale. It contains the beginning value, ending value, and a pre-calculated + * range between the values. + */ +typedef struct +{ + gdouble begin; + gdouble end; + gdouble range; +} UberRange; + +GType uber_range_get_type (void) G_GNUC_CONST; +void uber_range_free (UberRange *range); +UberRange* uber_range_copy (UberRange *range); +UberRange* uber_range_new (gdouble begin, + gdouble end); + +G_END_DECLS + +#endif /* __UBER_RANGE_H__ */ diff --git a/deps/uber-graph/uber-scale.c b/deps/uber-graph/uber-scale.c new file mode 100644 index 00000000..ead9f988 --- /dev/null +++ b/deps/uber-graph/uber-scale.c @@ -0,0 +1,54 @@ +/* uber-scale.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "uber-scale.h" + +/** + * uber_scale_linear: + * @range: An #UberRange. + * @pixel_range: An #UberRange. + * @value: A pointer to the value to translate. + * @user_data: user data for scale. + * + * An #UberScale function to translate a value to the coordinate system in + * a linear fashion. + * + * Returns: %TRUE if successful; otherwise %FALSE. + * Side effects: None. + */ +gboolean +uber_scale_linear (const UberRange *range, /* IN */ + const UberRange *pixel_range, /* IN */ + gdouble *value, /* IN/OUT */ + gpointer user_data) /* IN */ +{ + #define A (range->range) + #define B (pixel_range->range) + #define C (*value) + if (*value != 0.) { + *value = C * B / A; + } + #undef A + #undef B + #undef C + return TRUE; +} diff --git a/deps/uber-graph/uber-scale.h b/deps/uber-graph/uber-scale.h new file mode 100644 index 00000000..5101275b --- /dev/null +++ b/deps/uber-graph/uber-scale.h @@ -0,0 +1,38 @@ +/* uber-scale.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_SCALE_H__ +#define __UBER_SCALE_H__ + +#include "uber-range.h" + +G_BEGIN_DECLS + +typedef gboolean (*UberScale) (const UberRange *range, + const UberRange *pixel_range, + gdouble *value, + gpointer user_data); + +gboolean uber_scale_linear (const UberRange *range, + const UberRange *pixel_range, + gdouble *value, + gpointer user_data); + +G_END_DECLS + +#endif /* __UBER_SCALE_H__ */ diff --git a/deps/uber-graph/uber-scatter.c b/deps/uber-graph/uber-scatter.c new file mode 100644 index 00000000..56c7b00d --- /dev/null +++ b/deps/uber-graph/uber-scatter.c @@ -0,0 +1,421 @@ +/* uber-scatter.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <string.h> + +#include "uber-scatter.h" +#include "uber-scale.h" +#include "uber-range.h" +#include "g-ring.h" + +#define RADIUS 3 + +/** + * SECTION:uber-scatter.h + * @title: UberScatter + * @short_description: + * + * Section overview. + */ + + +struct _UberScatterPrivate +{ + GRing *raw_data; + UberRange range; + guint stride; + GdkRGBA fg_color; + gboolean fg_color_set; + UberScatterFunc func; + gpointer func_user_data; + GDestroyNotify func_destroy; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(UberScatter, uber_scatter, UBER_TYPE_GRAPH) + +/** + * uber_scatter_new: + * + * Creates a new instance of #UberScatter. + * + * Returns: the newly created instance of #UberScatter. + * Side effects: None. + */ +GtkWidget* +uber_scatter_new (void) +{ + UberScatter *scatter; + + scatter = g_object_new(UBER_TYPE_SCATTER, NULL); + return GTK_WIDGET(scatter); +} + +/** + * uber_scatter_set_data_func: + * @scatter: A #UberScatter. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_scatter_set_data_func (UberScatter *scatter, /* IN */ + UberScatterFunc func, /* IN */ + gpointer user_data, /* IN */ + GDestroyNotify destroy) /* IN */ +{ + UberScatterPrivate *priv; + + g_return_if_fail(UBER_IS_SCATTER(scatter)); + g_return_if_fail(func != NULL); + + priv = scatter->priv; + /* + * Cleanup previous data func if necessary. + */ + if (priv->func_destroy) { + priv->func_destroy(priv->func_user_data); + } + priv->func = func; + priv->func_destroy = destroy; + priv->func_user_data = user_data; +} + +/** + * uber_scatter_destroy_array: + * @array: A #GArray. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_destroy_array (gpointer data) /* IN */ +{ + GArray **ar = data; + + if (ar) { + g_array_unref(*ar); + } +} + +/** + * uber_scatter_set_stride: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_set_stride (UberGraph *graph, /* IN */ + guint stride) /* IN */ +{ + UberScatterPrivate *priv; + + g_return_if_fail(UBER_IS_SCATTER(graph)); + + priv = UBER_SCATTER(graph)->priv; + if (priv->stride == stride) { + return; + } + priv->stride = stride; + if (priv->raw_data) { + g_ring_unref(priv->raw_data); + } + priv->raw_data = g_ring_sized_new(sizeof(GArray*), stride, + uber_scatter_destroy_array); +} + +/** + * uber_scatter_render: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_render (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *area, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberScatterPrivate *priv; + UberRange pixel_range; + GtkStyleContext *style; + GdkRGBA color; + GArray *ar; + gdouble x; + gdouble y; + guint i; + guint j; + + g_return_if_fail(UBER_IS_SCATTER(graph)); + + priv = UBER_SCATTER(graph)->priv; + color = priv->fg_color; + if (!priv->fg_color_set) { + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &color); + } + /* + * Calculate ranges. + */ + pixel_range.begin = area->y + (RADIUS / 2.); + pixel_range.end = area->y + area->height - RADIUS; + pixel_range.range = pixel_range.end - pixel_range.begin; + /* + * Retrieve the current data set. + */ + for (i = 0; i < priv->raw_data->len; i++) { + if (!(ar = g_ring_get_index(priv->raw_data, GArray*, (int)i))) { + continue; + } + x = epoch - (i * each) - (each / 2.); + for (j = 0; j < ar->len; j++) { + y = g_array_index(ar, gdouble, j); +// g_debug("Raw ==> %f", y); + uber_scale_linear(&priv->range, &pixel_range, &y, NULL); + /* + * Shadow. + */ + cairo_arc(cr, x + .5, y + .5, RADIUS, 0, 2 * M_PI); + cairo_set_source_rgb(cr, .1, .1, .1); + cairo_fill(cr); + /* + * Foreground. + */ + cairo_arc(cr, x, y, RADIUS, 0, 2 * M_PI); + cairo_set_source_rgb(cr, + color.red, + color.green, + color.blue); + cairo_fill(cr); + } + } +} + +/** + * uber_scatter_render_fast: + * @graph: A #UberGraph. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_render_fast (UberGraph *graph, /* IN */ + cairo_t *cr, /* IN */ + GdkRectangle *area, /* IN */ + guint epoch, /* IN */ + gfloat each) /* IN */ +{ + UberScatterPrivate *priv; + UberRange pixel_range; + GtkStyleContext *style; + GdkRGBA color; + GArray *ar; + gdouble x; + gdouble y; + guint i; + + g_return_if_fail(UBER_IS_SCATTER(graph)); + + priv = UBER_SCATTER(graph)->priv; + color = priv->fg_color; + if (!priv->fg_color_set) { + style = gtk_widget_get_style_context(GTK_WIDGET(graph)); + gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &color); + } + /* + * Calculate ranges. + */ + pixel_range.begin = area->y + (RADIUS / 2.); + pixel_range.end = area->y + area->height - RADIUS; + pixel_range.range = pixel_range.end - pixel_range.begin; + /* + * Retrieve the current data set. + */ + ar = g_ring_get_index(priv->raw_data, GArray*, 0); + if (!ar) { + return; + } + /* + * Calculate X position (Center of this chunk). + */ + x = epoch - (each / 2.); + /* + * Draw scatter dots. + */ + for (i = 0; i < ar->len; i++) { + /* + * Scale the value to our graph coordinates. + */ + y = g_array_index(ar, gdouble, i); + /* + * XXX: Support multiple scales. + */ + uber_scale_linear(&priv->range, &pixel_range, &y, NULL); + /* + * Shadow. + */ + cairo_arc(cr, x + .5, y + .5, RADIUS, 0, 2 * M_PI); + cairo_set_source_rgb(cr, .1, .1, .1); + cairo_fill(cr); + /* + * Foreground. + */ + cairo_arc(cr, x, y, RADIUS, 0, 2 * M_PI); + cairo_set_source_rgb(cr, + color.red, + color.green, + color.blue); + cairo_fill(cr); + } +} + +/** + * uber_scatter_get_next_data: + * @graph: A #UberGraph. + * + * Retrieve the next data point for the graph. + * + * Returns: None. + * Side effects: None. + */ +static gboolean +uber_scatter_get_next_data (UberGraph *graph) /* IN */ +{ + UberScatterPrivate *priv; + GArray *array = NULL; + + g_return_val_if_fail(UBER_IS_SCATTER(graph), FALSE); + + priv = UBER_SCATTER(graph)->priv; + if (priv->func) { + if (!priv->func(UBER_SCATTER(graph), &array, priv->func_user_data)) { + array = NULL; + } + g_ring_append_val(priv->raw_data, array); + return TRUE; + } + return FALSE; +} + +/** + * uber_scatter_set_fg_color: + * @scatter: A #UberScatter. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_scatter_set_fg_color (UberScatter *scatter, /* IN */ + const GdkRGBA *color) /* IN */ +{ + UberScatterPrivate *priv; + + g_return_if_fail(UBER_IS_SCATTER(scatter)); + + priv = scatter->priv; + if (color) { + priv->fg_color = *color; + priv->fg_color_set = TRUE; + } else { + memset(&priv->fg_color, 0, sizeof(priv->fg_color)); + priv->fg_color_set = FALSE; + } +} + +/** + * uber_scatter_finalize: + * @object: A #UberScatter. + * + * Finalizer for a #UberScatter instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_finalize (GObject *object) /* IN */ +{ + G_OBJECT_CLASS(uber_scatter_parent_class)->finalize(object); +} + +/** + * uber_scatter_class_init: + * @klass: A #UberScatterClass. + * + * Initializes the #UberScatterClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_class_init (UberScatterClass *klass) /* IN */ +{ + GObjectClass *object_class; + UberGraphClass *graph_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = uber_scatter_finalize; + + graph_class = UBER_GRAPH_CLASS(klass); + graph_class->render = uber_scatter_render; + graph_class->render_fast = uber_scatter_render_fast; + graph_class->set_stride = uber_scatter_set_stride; + graph_class->get_next_data = uber_scatter_get_next_data; +} + +/** + * uber_scatter_init: + * @scatter: A #UberScatter. + * + * Initializes the newly created #UberScatter instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_scatter_init (UberScatter *scatter) /* IN */ +{ + UberScatterPrivate *priv; + + scatter->priv = uber_scatter_get_instance_private(scatter); + + priv = scatter->priv; + + priv->range.begin = 0.; + priv->range.end = 15000.; + priv->range.range = priv->range.end - priv->range.begin; +} diff --git a/deps/uber-graph/uber-scatter.h b/deps/uber-graph/uber-scatter.h new file mode 100644 index 00000000..ef111456 --- /dev/null +++ b/deps/uber-graph/uber-scatter.h @@ -0,0 +1,66 @@ +/* uber-scatter.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_SCATTER_H__ +#define __UBER_SCATTER_H__ + +#include "uber-graph.h" + +G_BEGIN_DECLS + +#define UBER_TYPE_SCATTER (uber_scatter_get_type()) +#define UBER_SCATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_SCATTER, UberScatter)) +#define UBER_SCATTER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_SCATTER, UberScatter const)) +#define UBER_SCATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_SCATTER, UberScatterClass)) +#define UBER_IS_SCATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_SCATTER)) +#define UBER_IS_SCATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_SCATTER)) +#define UBER_SCATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_SCATTER, UberScatterClass)) + +typedef struct _UberScatter UberScatter; +typedef struct _UberScatterClass UberScatterClass; +typedef struct _UberScatterPrivate UberScatterPrivate; + +typedef gboolean (*UberScatterFunc) (UberScatter *scatter, + GArray **values, + gpointer user_data); + +struct _UberScatter +{ + UberGraph parent; + + /*< private >*/ + UberScatterPrivate *priv; +}; + +struct _UberScatterClass +{ + UberGraphClass parent_class; +}; + +GType uber_scatter_get_type (void) G_GNUC_CONST; +GtkWidget* uber_scatter_new (void); +void uber_scatter_set_fg_color (UberScatter *scatter, + const GdkRGBA *color); +void uber_scatter_set_data_func (UberScatter *scatter, + UberScatterFunc func, + gpointer user_data, + GDestroyNotify destroy); + +G_END_DECLS + +#endif /* __UBER_SCATTER_H__ */ diff --git a/deps/uber-graph/uber-timeout-interval.c b/deps/uber-graph/uber-timeout-interval.c new file mode 100644 index 00000000..baabbcca --- /dev/null +++ b/deps/uber-graph/uber-timeout-interval.c @@ -0,0 +1,140 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Neil Roberts <neil@linux.intel.com> + * + * Copyright (C) 2009 Intel Corporation. + * + * 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 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* This file contains the common code to check whether an interval has + expired used in uber-frame-source and uber-timeout-pool. */ + +#include "uber-timeout-interval.h" + +void +_uber_timeout_interval_init (UberTimeoutInterval *interval, + guint fps) +{ +#if GLIB_CHECK_VERSION (2, 27, 3) + interval->start_time = g_get_monotonic_time () / 1000; +#else + { + GTimeVal start_time; + g_get_current_time (&start_time); + interval->start_time = start_time.tv_sec * 1000 + + start_time.tv_usec / 1000; + } +#endif + + interval->fps = fps; + interval->frame_count = 0; +} + +static gint64 +_uber_timeout_interval_get_ticks (gint64 current_time, + UberTimeoutInterval *interval) +{ + return MAX (current_time - interval->start_time, 0); +} + +gboolean +_uber_timeout_interval_prepare (gint64 current_time, + UberTimeoutInterval *interval, + gint *delay) +{ + guint elapsed_time, new_frame_num; + + elapsed_time = _uber_timeout_interval_get_ticks (current_time, interval); + new_frame_num = elapsed_time * interval->fps / 1000; + + /* If time has gone backwards or the time since the last frame is + greater than the two frames worth then reset the time and do a + frame now */ + if (new_frame_num < interval->frame_count || + new_frame_num - interval->frame_count > 2) + { + /* Get the frame time rounded up to the nearest ms */ + guint frame_time = (1000 + interval->fps - 1) / interval->fps; + + /* Reset the start time */ + interval->start_time = current_time; + + /* Move the start time as if one whole frame has elapsed */ + interval->start_time -= frame_time; + + interval->frame_count = 0; + + if (delay) + *delay = 0; + + return TRUE; + } + else if (new_frame_num > interval->frame_count) + { + if (delay) + *delay = 0; + + return TRUE; + } + else + { + if (delay) + *delay = ((interval->frame_count + 1) * 1000 / interval->fps + - elapsed_time); + + return FALSE; + } +} + +gboolean +_uber_timeout_interval_dispatch (UberTimeoutInterval *interval, + GSourceFunc callback, + gpointer user_data) +{ + if ((* callback) (user_data)) + { + interval->frame_count++; + + return TRUE; + } + + return FALSE; +} + +gint +_uber_timeout_interval_compare_expiration (const UberTimeoutInterval *a, + const UberTimeoutInterval *b) +{ + guint a_delay = 1000 / a->fps; + guint b_delay = 1000 / b->fps; + gint64 b_difference; + gint comparison; + + b_difference = a->start_time - b->start_time; + + comparison = ((gint) (((gint64)a->frame_count + 1) * a_delay) + - (gint) (((gint64)b->frame_count + 1) * b_delay + b_difference)); + + return (comparison < 0 ? -1 + : comparison > 0 ? 1 + : 0); +} diff --git a/deps/uber-graph/uber-timeout-interval.h b/deps/uber-graph/uber-timeout-interval.h new file mode 100644 index 00000000..c1b8b892 --- /dev/null +++ b/deps/uber-graph/uber-timeout-interval.h @@ -0,0 +1,56 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Neil Roberts <neil@linux.intel.com> + * + * Copyright (C) 2009 Intel Corporation. + * + * 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 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/>. + */ + +#ifndef __UBER_TIMEOUT_INTERVAL_H__ +#define __UBER_TIMEOUT_INTERVAL_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _UberTimeoutInterval UberTimeoutInterval; + +struct _UberTimeoutInterval +{ + gint64 start_time; + guint frame_count, fps; +}; + +void _uber_timeout_interval_init (UberTimeoutInterval *interval, + guint fps); + +gboolean _uber_timeout_interval_prepare (gint64 current_time, + UberTimeoutInterval *interval, + gint *delay); + +gboolean _uber_timeout_interval_dispatch (UberTimeoutInterval *interval, + GSourceFunc callback, + gpointer user_data); + +gint _uber_timeout_interval_compare_expiration + (const UberTimeoutInterval *a, + const UberTimeoutInterval *b); + +G_END_DECLS + +#endif /* __UBER_TIMEOUT_INTERVAL_H__ */ diff --git a/deps/uber-graph/uber-window.c b/deps/uber-graph/uber-window.c new file mode 100644 index 00000000..989f3374 --- /dev/null +++ b/deps/uber-graph/uber-window.c @@ -0,0 +1,330 @@ +/* uber-window.c + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "uber-window.h" + +/** + * SECTION:uber-window.h + * @title: UberWindow + * @short_description: + * + * Section overview. + */ + + +struct _UberWindowPrivate +{ + gint graph_count; + GList *graphs; + GtkWidget *notebook; + GtkWidget *table; +}; +G_DEFINE_TYPE_WITH_PRIVATE(UberWindow, uber_window, GTK_TYPE_WINDOW) + +/** + * uber_window_new: + * + * Creates a new instance of #UberWindow. + * + * Returns: the newly created instance of #UberWindow. + * Side effects: None. + */ +GtkWidget* +uber_window_new (void) +{ + UberWindow *window; + + window = g_object_new(UBER_TYPE_WINDOW, NULL); + return GTK_WIDGET(window); +} + +/** + * uber_window_show_labels: + * @window: A #UberWindow. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_window_show_labels (UberWindow *window, /* IN */ + UberGraph *graph) /* IN */ +{ + UberWindowPrivate *priv; + GtkWidget *labels; + GtkWidget *align; + GList *list; + gboolean show; + + g_return_if_fail(UBER_IS_WINDOW(window)); + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = window->priv; + /* + * Get the widgets labels. + */ + labels = uber_graph_get_labels(graph); + /* + * Show/hide ticks and labels. + */ + show = !!labels; + if (labels) { + align = gtk_bin_get_child(GTK_BIN(labels)); + list = gtk_container_get_children(GTK_CONTAINER(align)); + if (list) { + gtk_widget_show(labels); + } else { + gtk_widget_hide(labels); + show = FALSE; + } + g_list_free(list); + } + if (graph == (gpointer)g_list_last(priv->graphs)) { + show = TRUE; + } + uber_graph_set_show_xlabels(graph, show); + /* + * Hide labels/xlabels for other graphs. + */ + for (list = priv->graphs; list && list->next; list = list->next) { + if (list->data != graph) { + uber_graph_set_show_xlabels(list->data, FALSE); + labels = uber_graph_get_labels(list->data); + if (labels) { + gtk_widget_hide(labels); + } + } + } + /* + * Ensure the last graph always has labels. + */ + if (list) { + uber_graph_set_show_xlabels(UBER_GRAPH(list->data), TRUE); + } +} + +/** + * uber_window_hide_labels: + * @window: A #UberWindow. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_window_hide_labels (UberWindow *window, /* IN */ + UberGraph *graph) /* IN */ +{ + UberWindowPrivate *priv; + GtkWidget *labels; + gboolean show; + + g_return_if_fail(UBER_IS_WINDOW(window)); + g_return_if_fail(UBER_IS_GRAPH(graph)); + + priv = window->priv; + labels = uber_graph_get_labels(graph); + if (labels) { + gtk_widget_hide(labels); + } + show = g_list_last(priv->graphs) == (gpointer)graph; + uber_graph_set_show_xlabels(graph, show); +} + +static gboolean +uber_window_graph_button_press_event (GtkWidget *widget, /* IN */ + GdkEventButton *button, /* IN */ + UberWindow *window) /* IN */ +{ + GtkWidget *labels; + + g_return_val_if_fail(UBER_IS_WINDOW(window), FALSE); + g_return_val_if_fail(UBER_IS_GRAPH(widget), FALSE); + + switch (button->button) { + case 1: /* Left click */ + labels = uber_graph_get_labels(UBER_GRAPH(widget)); + if (gtk_widget_get_visible(labels)) { + uber_window_hide_labels(window, UBER_GRAPH(widget)); + } else { + uber_window_show_labels(window, UBER_GRAPH(widget)); + } + break; + default: + break; + } + return FALSE; +} + +/** + * uber_window_add_graph: + * @window: A #UberWindow. + * + * XXX + * + * Returns: None. + * Side effects: None. + */ +void +uber_window_add_graph (UberWindow *window, /* IN */ + UberGraph *graph, /* IN */ + const gchar *title) /* IN */ +{ + UberWindowPrivate *priv; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *labels; + gchar *formatted; + gint left_attach; + gint top_attach; + + g_return_if_fail(UBER_IS_WINDOW(window)); + + priv = window->priv; + /* + * Format title string. + */ + formatted = g_markup_printf_escaped("<b>%s</b>", title); + /* + * Create container for graph. + */ + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); + label = gtk_label_new(NULL); + labels = uber_graph_get_labels(graph); + gtk_label_set_markup(GTK_LABEL(label), formatted); + gtk_label_set_angle(GTK_LABEL(label), -270.); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(graph), TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + if (labels) { + gtk_box_pack_start(GTK_BOX(vbox), labels, FALSE, TRUE, 0); + } + gtk_widget_show(label); + gtk_widget_show(hbox); + gtk_widget_show(vbox); + /* + * Append graph to table. + */ + left_attach = 0; + top_attach = priv->graph_count; // % 4; + gtk_grid_attach(GTK_GRID(priv->table), hbox, + left_attach, + top_attach, + 1, + 1); + /* + * Attach signal to show ticks when label is shown. + */ + g_signal_connect_after(graph, + "button-press-event", + G_CALLBACK(uber_window_graph_button_press_event), + window); + priv->graphs = g_list_append(priv->graphs, graph); + /* + * Cleanup. + */ + g_free(formatted); + priv->graph_count++; +} + +/** + * uber_window_finalize: + * @object: A #UberWindow. + * + * Finalizer for a #UberWindow instance. Frees any resources held by + * the instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_window_finalize (GObject *object) /* IN */ +{ + UberWindowPrivate *priv; + + priv = UBER_WINDOW(object)->priv; + g_list_free(priv->graphs); + G_OBJECT_CLASS(uber_window_parent_class)->finalize(object); +} + +/** + * uber_window_class_init: + * @klass: A #UberWindowClass. + * + * Initializes the #UberWindowClass and prepares the vtable. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_window_class_init (UberWindowClass *klass) /* IN */ +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = uber_window_finalize; +} + +/** + * uber_window_init: + * @window: A #UberWindow. + * + * Initializes the newly created #UberWindow instance. + * + * Returns: None. + * Side effects: None. + */ +static void +uber_window_init (UberWindow *window) /* IN */ +{ + UberWindowPrivate *priv; + + window->priv = uber_window_get_instance_private(window); + + /* + * Initialize defaults. + */ + priv = window->priv; + gtk_window_set_title(GTK_WINDOW(window), "Uber Graph"); + gtk_window_set_default_size(GTK_WINDOW(window), 750, 550); + gtk_container_set_border_width(GTK_CONTAINER(window), 12); + /* + * Create notebook container for pages. + */ + priv->notebook = gtk_notebook_new(); + gtk_notebook_set_show_border(GTK_NOTEBOOK(priv->notebook), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(priv->notebook), FALSE); + gtk_container_add(GTK_CONTAINER(window), priv->notebook); + gtk_widget_show(priv->notebook); + /* + * Create table for graphs. + */ + priv->table = gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(priv->table), TRUE); + gtk_grid_set_column_homogeneous(GTK_GRID(priv->table), TRUE); + gtk_notebook_append_page(GTK_NOTEBOOK(priv->notebook), priv->table, NULL); + gtk_widget_show(priv->table); +} diff --git a/deps/uber-graph/uber-window.h b/deps/uber-graph/uber-window.h new file mode 100644 index 00000000..e913e852 --- /dev/null +++ b/deps/uber-graph/uber-window.h @@ -0,0 +1,65 @@ +/* uber-window.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_WINDOW_H__ +#define __UBER_WINDOW_H__ + +#include <gtk/gtk.h> + +#include "uber-graph.h" + +G_BEGIN_DECLS + +#define UBER_TYPE_WINDOW (uber_window_get_type()) +#define UBER_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_WINDOW, UberWindow)) +#define UBER_WINDOW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UBER_TYPE_WINDOW, UberWindow const)) +#define UBER_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UBER_TYPE_WINDOW, UberWindowClass)) +#define UBER_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UBER_TYPE_WINDOW)) +#define UBER_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UBER_TYPE_WINDOW)) +#define UBER_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UBER_TYPE_WINDOW, UberWindowClass)) + +typedef struct _UberWindow UberWindow; +typedef struct _UberWindowClass UberWindowClass; +typedef struct _UberWindowPrivate UberWindowPrivate; + +struct _UberWindow +{ + GtkWindow parent; + + /*< private >*/ + UberWindowPrivate *priv; +}; + +struct _UberWindowClass +{ + GtkWindowClass parent_class; +}; + +GType uber_window_get_type (void) G_GNUC_CONST; +GtkWidget* uber_window_new (void); +void uber_window_add_graph (UberWindow *window, + UberGraph *graph, + const gchar *title); +void uber_window_show_labels (UberWindow *window, + UberGraph *graph); +void uber_window_hide_labels (UberWindow *window, + UberGraph *graph); + +G_END_DECLS + +#endif /* __UBER_WINDOW_H__ */ diff --git a/deps/uber-graph/uber.h b/deps/uber-graph/uber.h new file mode 100644 index 00000000..b101dfd7 --- /dev/null +++ b/deps/uber-graph/uber.h @@ -0,0 +1,30 @@ +/* uber.h + * + * Copyright (C) 2010 Christian Hergert <chris@dronelabs.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 __UBER_H__ +#define __UBER_H__ + +#include "uber-graph.h" +#include "uber-line-graph.h" +#include "uber-heat-map.h" +#include "uber-range.h" +#include "uber-scatter.h" +#include "uber-scale.h" +#include "uber-window.h" + +#endif /* __UBER_H__ */ |