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