summaryrefslogtreecommitdiff
path: root/deps/uber-graph
diff options
context:
space:
mode:
Diffstat (limited to 'deps/uber-graph')
-rw-r--r--deps/uber-graph/README39
-rw-r--r--deps/uber-graph/g-ring.c227
-rw-r--r--deps/uber-graph/g-ring.h84
-rw-r--r--deps/uber-graph/uber-frame-source.c177
-rw-r--r--deps/uber-graph/uber-frame-source.h43
-rw-r--r--deps/uber-graph/uber-graph.c2166
-rw-r--r--deps/uber-graph/uber-graph.h104
-rw-r--r--deps/uber-graph/uber-heat-map.c347
-rw-r--r--deps/uber-graph/uber-heat-map.h66
-rw-r--r--deps/uber-graph/uber-label.c395
-rw-r--r--deps/uber-graph/uber-label.h60
-rw-r--r--deps/uber-graph/uber-line-graph.c1028
-rw-r--r--deps/uber-graph/uber-line-graph.h100
-rw-r--r--deps/uber-graph/uber-range.c64
-rw-r--r--deps/uber-graph/uber-range.h50
-rw-r--r--deps/uber-graph/uber-scale.c54
-rw-r--r--deps/uber-graph/uber-scale.h38
-rw-r--r--deps/uber-graph/uber-scatter.c421
-rw-r--r--deps/uber-graph/uber-scatter.h66
-rw-r--r--deps/uber-graph/uber-timeout-interval.c140
-rw-r--r--deps/uber-graph/uber-timeout-interval.h56
-rw-r--r--deps/uber-graph/uber-window.c330
-rw-r--r--deps/uber-graph/uber-window.h65
-rw-r--r--deps/uber-graph/uber.h30
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__ */