1327 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1327 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* PIKA - Photo and Image Kooker Application
 | 
						|
 * a rebranding of The GNU Image Manipulation Program (created with heckimp)
 | 
						|
 * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
 | 
						|
 *
 | 
						|
 * Original copyright, applying to most contents (license remains unchanged): 
 | 
						|
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 | 
						|
 *
 | 
						|
 * pikameter.c
 | 
						|
 * Copyright (C) 2017 Ell
 | 
						|
 *
 | 
						|
 * 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 <https://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <gegl.h>
 | 
						|
#include <gtk/gtk.h>
 | 
						|
 | 
						|
#include "libpikabase/pikabase.h"
 | 
						|
#include "libpikamath/pikamath.h"
 | 
						|
#include "libpikacolor/pikacolor.h"
 | 
						|
#include "libpikawidgets/pikawidgets.h"
 | 
						|
 | 
						|
#include "widgets-types.h"
 | 
						|
 | 
						|
#include "pikameter.h"
 | 
						|
 | 
						|
 | 
						|
#define BORDER_WIDTH 1.0
 | 
						|
#define REV          (2.0 * G_PI)
 | 
						|
 | 
						|
#define SAMPLE(i)    (meter->priv->samples + (i) * meter->priv->n_values)
 | 
						|
#define SAMPLE_SIZE  (meter->priv->n_values * sizeof (gdouble))
 | 
						|
#define VALUE(i, j)  ((SAFE_CLAMP (SAMPLE (i)[j],                              \
 | 
						|
                                   meter->priv->range_min,                     \
 | 
						|
                                   meter->priv->range_max) -                   \
 | 
						|
                        meter->priv->range_min) /                              \
 | 
						|
                       (meter->priv->range_max - meter->priv->range_min))
 | 
						|
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  PROP_0,
 | 
						|
  PROP_SIZE,
 | 
						|
  PROP_REFRESH_RATE,
 | 
						|
  PROP_RANGE_MIN,
 | 
						|
  PROP_RANGE_MAX,
 | 
						|
  PROP_N_VALUES,
 | 
						|
  PROP_HISTORY_VISIBLE,
 | 
						|
  PROP_HISTORY_DURATION,
 | 
						|
  PROP_HISTORY_RESOLUTION,
 | 
						|
  PROP_LED_ACTIVE,
 | 
						|
  PROP_LED_COLOR
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  gboolean              active;
 | 
						|
  gboolean              show_in_gauge;
 | 
						|
  gboolean              show_in_history;
 | 
						|
  PikaRGB               color;
 | 
						|
  PikaInterpolationType interpolation;
 | 
						|
} Value;
 | 
						|
 | 
						|
struct _PikaMeterPrivate
 | 
						|
{
 | 
						|
  GMutex   mutex;
 | 
						|
 | 
						|
  gint      size;
 | 
						|
  gdouble   refresh_rate;
 | 
						|
  gdouble   range_min;
 | 
						|
  gdouble   range_max;
 | 
						|
  gint      n_values;
 | 
						|
  Value    *values;
 | 
						|
  gboolean  history_visible;
 | 
						|
  gdouble   history_duration;
 | 
						|
  gdouble   history_resolution;
 | 
						|
  gboolean  led_active;
 | 
						|
  PikaRGB   led_color;
 | 
						|
 | 
						|
  gdouble  *samples;
 | 
						|
  gint      n_samples;
 | 
						|
  gint      sample_duration;
 | 
						|
  gint64    last_sample_time;
 | 
						|
  gint64    current_time;
 | 
						|
  gdouble  *uniform_sample;
 | 
						|
  gint      timeout_id;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*  local function prototypes  */
 | 
						|
 | 
						|
static void       pika_meter_dispose                (GObject        *object);
 | 
						|
static void       pika_meter_finalize               (GObject        *object);
 | 
						|
static void       pika_meter_set_property           (GObject        *object,
 | 
						|
                                                     guint           property_id,
 | 
						|
                                                     const GValue   *value,
 | 
						|
                                                     GParamSpec     *pspec);
 | 
						|
static void       pika_meter_get_property           (GObject        *object,
 | 
						|
                                                     guint           property_id,
 | 
						|
                                                     GValue         *value,
 | 
						|
                                                     GParamSpec     *pspec);
 | 
						|
 | 
						|
static void       pika_meter_map                    (GtkWidget      *widget);
 | 
						|
static void       pika_meter_unmap                  (GtkWidget      *widget);
 | 
						|
static void       pika_meter_get_preferred_width    (GtkWidget      *widget,
 | 
						|
                                                     gint           *minimum_width,
 | 
						|
                                                     gint           *natural_width);
 | 
						|
static void       pika_meter_get_preferred_height   (GtkWidget      *widget,
 | 
						|
                                                     gint           *minimum_height,
 | 
						|
                                                     gint           *natural_height);
 | 
						|
static gboolean   pika_meter_draw                   (GtkWidget      *widget,
 | 
						|
                                                     cairo_t        *cr);
 | 
						|
 | 
						|
static gboolean   pika_meter_timeout                (PikaMeter      *meter);
 | 
						|
 | 
						|
static void       pika_meter_clear_history_unlocked (PikaMeter      *meter);
 | 
						|
static void       pika_meter_update_samples         (PikaMeter      *meter);
 | 
						|
static void       pika_meter_shift_samples          (PikaMeter      *meter);
 | 
						|
 | 
						|
static void       pika_meter_mask_sample            (PikaMeter      *meter,
 | 
						|
                                                     const gdouble  *sample,
 | 
						|
                                                     gdouble        *result);
 | 
						|
 | 
						|
 | 
						|
G_DEFINE_TYPE_WITH_PRIVATE (PikaMeter, pika_meter, GTK_TYPE_WIDGET)
 | 
						|
 | 
						|
#define parent_class pika_meter_parent_class
 | 
						|
 | 
						|
 | 
						|
/*  private functions  */
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_class_init (PikaMeterClass *klass)
 | 
						|
{
 | 
						|
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 | 
						|
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 | 
						|
 | 
						|
  object_class->dispose              = pika_meter_dispose;
 | 
						|
  object_class->finalize             = pika_meter_finalize;
 | 
						|
  object_class->get_property         = pika_meter_get_property;
 | 
						|
  object_class->set_property         = pika_meter_set_property;
 | 
						|
 | 
						|
  widget_class->map                  = pika_meter_map;
 | 
						|
  widget_class->unmap                = pika_meter_unmap;
 | 
						|
  widget_class->get_preferred_width  = pika_meter_get_preferred_width;
 | 
						|
  widget_class->get_preferred_height = pika_meter_get_preferred_height;
 | 
						|
  widget_class->draw                 = pika_meter_draw;
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_SIZE,
 | 
						|
                                   g_param_spec_int ("size",
 | 
						|
                                                     NULL, NULL,
 | 
						|
                                                     32, 1024, 48,
 | 
						|
                                                     PIKA_PARAM_READWRITE |
 | 
						|
                                                     G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_REFRESH_RATE,
 | 
						|
                                   g_param_spec_double ("refresh-rate",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        0.001, 1000.0, 8.0,
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_RANGE_MIN,
 | 
						|
                                   g_param_spec_double ("range-min",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        0.0, G_MAXDOUBLE, 0.0,
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_RANGE_MAX,
 | 
						|
                                   g_param_spec_double ("range-max",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        0.0, G_MAXDOUBLE, 1.0,
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_N_VALUES,
 | 
						|
                                   g_param_spec_int ("n-values",
 | 
						|
                                                     NULL, NULL,
 | 
						|
                                                     0, 32, 0,
 | 
						|
                                                     PIKA_PARAM_READWRITE |
 | 
						|
                                                     G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_HISTORY_VISIBLE,
 | 
						|
                                   g_param_spec_boolean ("history-visible",
 | 
						|
                                                         NULL, NULL,
 | 
						|
                                                         TRUE,
 | 
						|
                                                         PIKA_PARAM_READWRITE |
 | 
						|
                                                         G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_HISTORY_DURATION,
 | 
						|
                                   g_param_spec_double ("history-duration",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        0.0, 3600.0, 60.0,
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_HISTORY_RESOLUTION,
 | 
						|
                                   g_param_spec_double ("history-resolution",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        0.1, 3600.0, 1.0,
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_LED_ACTIVE,
 | 
						|
                                   g_param_spec_boolean ("led-active",
 | 
						|
                                                         NULL, NULL,
 | 
						|
                                                         FALSE,
 | 
						|
                                                         PIKA_PARAM_READWRITE |
 | 
						|
                                                         G_PARAM_CONSTRUCT));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class, PROP_LED_COLOR,
 | 
						|
                                   pika_param_spec_rgb ("led-color",
 | 
						|
                                                        NULL, NULL,
 | 
						|
                                                        TRUE, &(PikaRGB) {},
 | 
						|
                                                        PIKA_PARAM_READWRITE |
 | 
						|
                                                        G_PARAM_CONSTRUCT));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_init (PikaMeter *meter)
 | 
						|
{
 | 
						|
  meter->priv = pika_meter_get_instance_private (meter);
 | 
						|
 | 
						|
  g_mutex_init (&meter->priv->mutex);
 | 
						|
 | 
						|
  gtk_widget_set_has_window (GTK_WIDGET (meter), FALSE);
 | 
						|
 | 
						|
  meter->priv->range_min          = 0.0;
 | 
						|
  meter->priv->range_max          = 1.0;
 | 
						|
  meter->priv->n_values           = 0;
 | 
						|
  meter->priv->history_duration   = 60.0;
 | 
						|
  meter->priv->history_resolution = 1.0;
 | 
						|
 | 
						|
  pika_meter_update_samples (meter);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_dispose (GObject *object)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (object);
 | 
						|
 | 
						|
  g_clear_pointer (&meter->priv->values, g_free);
 | 
						|
  g_clear_pointer (&meter->priv->samples, g_free);
 | 
						|
  g_clear_pointer (&meter->priv->uniform_sample, g_free);
 | 
						|
 | 
						|
  if (meter->priv->timeout_id)
 | 
						|
    {
 | 
						|
      g_source_remove (meter->priv->timeout_id);
 | 
						|
      meter->priv->timeout_id = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  G_OBJECT_CLASS (parent_class)->dispose (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_finalize (GObject *object)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (object);
 | 
						|
 | 
						|
  g_mutex_clear (&meter->priv->mutex);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (parent_class)->finalize (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_set_property (GObject      *object,
 | 
						|
                          guint         property_id,
 | 
						|
                          const GValue *value,
 | 
						|
                          GParamSpec   *pspec)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (object);
 | 
						|
 | 
						|
  switch (property_id)
 | 
						|
    {
 | 
						|
    case PROP_SIZE:
 | 
						|
      pika_meter_set_size (meter, g_value_get_int (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_REFRESH_RATE:
 | 
						|
      pika_meter_set_refresh_rate (meter, g_value_get_double (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_RANGE_MIN:
 | 
						|
      pika_meter_set_range (meter,
 | 
						|
                            g_value_get_double (value),
 | 
						|
                            pika_meter_get_range_max (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_RANGE_MAX:
 | 
						|
      pika_meter_set_range (meter,
 | 
						|
                            pika_meter_get_range_min (meter),
 | 
						|
                            g_value_get_double (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_N_VALUES:
 | 
						|
      pika_meter_set_n_values (meter, g_value_get_int (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_VISIBLE:
 | 
						|
      pika_meter_set_history_visible (meter, g_value_get_boolean (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_DURATION:
 | 
						|
      pika_meter_set_history_duration (meter, g_value_get_double (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_RESOLUTION:
 | 
						|
      pika_meter_set_history_resolution (meter, g_value_get_double (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_LED_ACTIVE:
 | 
						|
      pika_meter_set_led_active (meter, g_value_get_boolean (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_LED_COLOR:
 | 
						|
      pika_meter_set_led_color (meter, g_value_get_boxed (value));
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_get_property (GObject    *object,
 | 
						|
                          guint       property_id,
 | 
						|
                          GValue     *value,
 | 
						|
                          GParamSpec *pspec)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (object);
 | 
						|
 | 
						|
  switch (property_id)
 | 
						|
    {
 | 
						|
    case PROP_SIZE:
 | 
						|
      g_value_set_int (value, pika_meter_get_size (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_REFRESH_RATE:
 | 
						|
      g_value_set_double (value, pika_meter_get_refresh_rate (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_RANGE_MIN:
 | 
						|
      g_value_set_double (value, pika_meter_get_range_min (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_RANGE_MAX:
 | 
						|
      g_value_set_double (value, pika_meter_get_range_max (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_N_VALUES:
 | 
						|
      g_value_set_int (value, pika_meter_get_n_values (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_VISIBLE:
 | 
						|
      g_value_set_boolean (value, pika_meter_get_history_visible (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_DURATION:
 | 
						|
      g_value_set_int (value, pika_meter_get_history_duration (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_HISTORY_RESOLUTION:
 | 
						|
      g_value_set_int (value, pika_meter_get_history_resolution (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_LED_ACTIVE:
 | 
						|
      g_value_set_boolean (value, pika_meter_get_led_active (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    case PROP_LED_COLOR:
 | 
						|
      g_value_set_boxed (value, pika_meter_get_led_color (meter));
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_map (GtkWidget *widget)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (widget);
 | 
						|
 | 
						|
  GTK_WIDGET_CLASS (parent_class)->map (widget);
 | 
						|
 | 
						|
  if (! meter->priv->timeout_id)
 | 
						|
    {
 | 
						|
      gint interval = ROUND (1000.0 / meter->priv->refresh_rate);
 | 
						|
 | 
						|
      meter->priv->timeout_id = g_timeout_add (interval,
 | 
						|
                                               (GSourceFunc) pika_meter_timeout,
 | 
						|
                                               meter);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_unmap (GtkWidget *widget)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (widget);
 | 
						|
 | 
						|
  if (meter->priv->timeout_id)
 | 
						|
    {
 | 
						|
      g_source_remove (meter->priv->timeout_id);
 | 
						|
      meter->priv->timeout_id = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_get_preferred_width (GtkWidget *widget,
 | 
						|
                                gint      *minimum_width,
 | 
						|
                                gint      *natural_width)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (widget);
 | 
						|
  gint       hsize = meter->priv->size;
 | 
						|
 | 
						|
  if (meter->priv->history_visible)
 | 
						|
    hsize *= 3;
 | 
						|
 | 
						|
  *minimum_width = *natural_width = ceil (hsize + 2.0 * BORDER_WIDTH);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_get_preferred_height (GtkWidget *widget,
 | 
						|
                                 gint      *minimum_height,
 | 
						|
                                 gint      *natural_height)
 | 
						|
{
 | 
						|
  PikaMeter *meter = PIKA_METER (widget);
 | 
						|
  gint       vsize = meter->priv->size;
 | 
						|
 | 
						|
  *minimum_height = *natural_height = ceil (3.0 / 4.0 * vsize + 4.0 * BORDER_WIDTH);
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
pika_meter_draw (GtkWidget *widget,
 | 
						|
                 cairo_t   *cr)
 | 
						|
{
 | 
						|
  PikaMeter       *meter = PIKA_METER (widget);
 | 
						|
  GtkAllocation    allocation;
 | 
						|
  gint             size  = meter->priv->size;
 | 
						|
  GtkStyleContext *style = gtk_widget_get_style_context (widget);
 | 
						|
  GtkStateFlags    state = gtk_style_context_get_state (style);
 | 
						|
  GdkRGBA          fg;
 | 
						|
  gint             i;
 | 
						|
  gint             j;
 | 
						|
  gint             k;
 | 
						|
 | 
						|
  g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
  gtk_widget_get_allocation (widget, &allocation);
 | 
						|
 | 
						|
  gtk_style_context_get_color (style, state, &fg);
 | 
						|
 | 
						|
  /* translate to gauge center */
 | 
						|
  cairo_translate (cr,
 | 
						|
                   0.5 * BORDER_WIDTH +       0.5 * size,
 | 
						|
                   1.5 * BORDER_WIDTH + 2.0 / 3.0 * (allocation.height - 4.0 * BORDER_WIDTH));
 | 
						|
 | 
						|
  cairo_save (cr);
 | 
						|
 | 
						|
  /* paint led */
 | 
						|
  if (meter->priv->led_active)
 | 
						|
    {
 | 
						|
      cairo_arc (cr,
 | 
						|
                 0.0, 0.0,
 | 
						|
                 0.06 * size,
 | 
						|
                 0.0 * REV, 1.0 * REV);
 | 
						|
 | 
						|
      pika_cairo_set_source_rgba (cr, &meter->priv->led_color);
 | 
						|
      cairo_fill (cr);
 | 
						|
    }
 | 
						|
 | 
						|
  /* clip to gauge interior */
 | 
						|
  cairo_arc          (cr,
 | 
						|
                      0.0, 0.0,
 | 
						|
                      0.5 * size,
 | 
						|
                      5.0 / 12.0 * REV, 1.0 / 12.0 * REV);
 | 
						|
  cairo_arc_negative (cr,
 | 
						|
                      0.0, 0.0,
 | 
						|
                      0.1 * size,
 | 
						|
                      1.0 / 12.0 * REV, 5.0 / 12.0 * REV);
 | 
						|
  cairo_close_path (cr);
 | 
						|
  cairo_clip (cr);
 | 
						|
 | 
						|
  /* paint gauge background */
 | 
						|
  gdk_cairo_set_source_rgba (cr, &fg);
 | 
						|
  cairo_paint_with_alpha (cr, 0.2);
 | 
						|
 | 
						|
  /* paint values of last sample */
 | 
						|
  if (meter->priv->range_min < meter->priv->range_max)
 | 
						|
    {
 | 
						|
      for (i = 0; i < meter->priv->n_values; i++)
 | 
						|
        {
 | 
						|
          gdouble v = VALUE (0, i);
 | 
						|
 | 
						|
          if (! meter->priv->values[i].active ||
 | 
						|
              ! meter->priv->values[i].show_in_gauge)
 | 
						|
            {
 | 
						|
              continue;
 | 
						|
            }
 | 
						|
 | 
						|
          pika_cairo_set_source_rgba (cr, &meter->priv->values[i].color);
 | 
						|
          cairo_move_to (cr, 0.0, 0.0);
 | 
						|
          cairo_arc (cr,
 | 
						|
                     0.0, 0.0,
 | 
						|
 | 
						|
                     0.5 * size,
 | 
						|
                     5.0 / 12.0 * REV, (5.0 / 12.0 + 2.0 / 3.0 * v) * REV);
 | 
						|
          cairo_line_to (cr, 0.0, 0.0);
 | 
						|
          cairo_close_path (cr);
 | 
						|
          cairo_fill (cr);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  cairo_restore (cr);
 | 
						|
 | 
						|
  /* paint gauge border */
 | 
						|
  gdk_cairo_set_source_rgba (cr, &fg);
 | 
						|
  cairo_set_line_width (cr, BORDER_WIDTH);
 | 
						|
  cairo_arc          (cr,
 | 
						|
                      0.0, 0.0,
 | 
						|
                      0.5 * size,
 | 
						|
                      5.0 / 12.0 * REV, 1.0 / 12.0 * REV);
 | 
						|
  cairo_arc_negative (cr,
 | 
						|
                      0.0, 0.0,
 | 
						|
                      0.1 * size,
 | 
						|
                      1.0 / 12.0 * REV, 5.0 / 12.0 * REV);
 | 
						|
  cairo_close_path (cr);
 | 
						|
  cairo_stroke (cr);
 | 
						|
 | 
						|
  /* history */
 | 
						|
  if (meter->priv->history_visible)
 | 
						|
    {
 | 
						|
      gdouble a1, a2;
 | 
						|
      gdouble history_x1, history_y1;
 | 
						|
      gdouble history_x2, history_y2;
 | 
						|
 | 
						|
      cairo_save (cr);
 | 
						|
 | 
						|
      a1 = +asin (0.25 / 0.6);
 | 
						|
      a2 = -asin (0.50 / 0.6);
 | 
						|
 | 
						|
      /* clip to history interior */
 | 
						|
      cairo_arc_negative (cr,
 | 
						|
                          0.0, 0.0,
 | 
						|
                          0.6 * size,
 | 
						|
                          a1, a2);
 | 
						|
      cairo_line_to (cr,
 | 
						|
                     allocation.width - BORDER_WIDTH - 0.5 * size,
 | 
						|
                     -0.50 * size);
 | 
						|
      cairo_line_to (cr,
 | 
						|
                     allocation.width - BORDER_WIDTH - 0.5 * size,
 | 
						|
                     0.25 * size);
 | 
						|
      cairo_close_path (cr);
 | 
						|
 | 
						|
      cairo_path_extents (cr,
 | 
						|
                          &history_x1, &history_y1,
 | 
						|
                          &history_x2, &history_y2);
 | 
						|
 | 
						|
      history_x1 = floor (history_x1);
 | 
						|
      history_y1 = floor (history_y1);
 | 
						|
      history_x2 = ceil  (history_x2);
 | 
						|
      history_y2 = ceil  (history_y2);
 | 
						|
 | 
						|
      cairo_clip (cr);
 | 
						|
 | 
						|
      /* paint history background */
 | 
						|
      gdk_cairo_set_source_rgba (cr, &fg);
 | 
						|
      cairo_paint_with_alpha (cr, 0.2);
 | 
						|
 | 
						|
      /* history graph */
 | 
						|
      if (meter->priv->range_min < meter->priv->range_max)
 | 
						|
        {
 | 
						|
          gdouble sample_width = (history_x2 - history_x1) /
 | 
						|
                                 (meter->priv->n_samples - 4);
 | 
						|
          gdouble dx           = 1.0 / sample_width;
 | 
						|
 | 
						|
          cairo_save (cr);
 | 
						|
 | 
						|
          /* translate to history bottom-right, and scale so that the
 | 
						|
           * x-axis points left, and has a length of one sample, and
 | 
						|
           * the y-axis points up, and has a length of the history
 | 
						|
           * window.
 | 
						|
           */
 | 
						|
          cairo_translate (cr, history_x2, history_y2);
 | 
						|
          cairo_scale (cr, -sample_width, -(history_y2 - history_y1));
 | 
						|
          cairo_translate (cr,
 | 
						|
                           (gdouble) (meter->priv->current_time     -
 | 
						|
                                      meter->priv->last_sample_time *
 | 
						|
                                      meter->priv->sample_duration) /
 | 
						|
                           meter->priv->sample_duration             -
 | 
						|
                           2.0,
 | 
						|
                           0.0);
 | 
						|
 | 
						|
          /* paint history graph for each value */
 | 
						|
          for (i = 0; i < meter->priv->n_values; i++)
 | 
						|
            {
 | 
						|
              gdouble y;
 | 
						|
 | 
						|
              if (! meter->priv->values[i].active ||
 | 
						|
                  ! meter->priv->values[i].show_in_history)
 | 
						|
                {
 | 
						|
                  continue;
 | 
						|
                }
 | 
						|
 | 
						|
              pika_cairo_set_source_rgba (cr, &meter->priv->values[i].color);
 | 
						|
              cairo_move_to (cr, 0.0, 0.0);
 | 
						|
 | 
						|
              switch (meter->priv->values[i].interpolation)
 | 
						|
                {
 | 
						|
                case PIKA_INTERPOLATION_NONE:
 | 
						|
                  {
 | 
						|
                    for (j = 1; j < meter->priv->n_samples - 2; j++)
 | 
						|
                      {
 | 
						|
                        gdouble y0 = VALUE (j - 1, i);
 | 
						|
                        gdouble y1 = VALUE (j,     i);
 | 
						|
 | 
						|
                        cairo_line_to (cr, j, y0);
 | 
						|
                        cairo_line_to (cr, j, y1);
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
                  break;
 | 
						|
 | 
						|
                case PIKA_INTERPOLATION_LINEAR:
 | 
						|
                  {
 | 
						|
                    for (j = 1; j < meter->priv->n_samples - 2; j++)
 | 
						|
                      {
 | 
						|
                        gdouble y = VALUE (j, i);
 | 
						|
 | 
						|
                        cairo_line_to (cr, j, y);
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
                  break;
 | 
						|
 | 
						|
                case PIKA_INTERPOLATION_CUBIC:
 | 
						|
                default:
 | 
						|
                  {
 | 
						|
                    for (j = 1; j < meter->priv->n_samples - 2; j++)
 | 
						|
                      {
 | 
						|
                        gdouble y[4];
 | 
						|
                        gdouble t[2];
 | 
						|
                        gdouble c[4];
 | 
						|
                        gdouble x;
 | 
						|
 | 
						|
                        for (k = 0; k < 4; k++)
 | 
						|
                          y[k] = VALUE (j + k - 1, i);
 | 
						|
 | 
						|
                        for (k = 0; k < 2; k++)
 | 
						|
                          {
 | 
						|
                            t[k] = (y[k + 2] - y[k]) / 2.0;
 | 
						|
                            t[k] = CLAMP (t[k], y[k + 1] - 1.0, y[k + 1]);
 | 
						|
                            t[k] = CLAMP (t[k], -y[k + 1], 1.0 - y[k + 1]);
 | 
						|
                          }
 | 
						|
 | 
						|
                        c[0] = y[1];
 | 
						|
                        c[1] = t[0];
 | 
						|
                        c[2] = 3 * (y[2] - y[1]) - 2 * t[0] - t[1];
 | 
						|
                        c[3] = t[0] + t[1] - 2 * (y[2] - y[1]);
 | 
						|
 | 
						|
                        for (x = 0.0; x < 1.0; x += dx)
 | 
						|
                          {
 | 
						|
                            gdouble y = ((c[3] * x + c[2]) * x + c[1]) * x + c[0];
 | 
						|
 | 
						|
                            cairo_line_to (cr, j + x, y);
 | 
						|
                          }
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
                  break;
 | 
						|
                }
 | 
						|
 | 
						|
              y = VALUE (j, i);
 | 
						|
 | 
						|
              cairo_line_to (cr, meter->priv->n_samples - 2, y);
 | 
						|
              cairo_line_to (cr, meter->priv->n_samples - 2, 0.0);
 | 
						|
              cairo_close_path (cr);
 | 
						|
              cairo_fill (cr);
 | 
						|
            }
 | 
						|
 | 
						|
          cairo_restore (cr);
 | 
						|
        }
 | 
						|
 | 
						|
      /* paint history grid */
 | 
						|
      cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
 | 
						|
      cairo_set_source_rgba (cr, fg.red, fg.green, fg.blue, 0.3);
 | 
						|
 | 
						|
      for (i = 1; i < 4; i++)
 | 
						|
        {
 | 
						|
          cairo_move_to (cr,
 | 
						|
                         history_x1,
 | 
						|
                         history_y1 + i / 4.0 * (history_y2 - history_y1));
 | 
						|
          cairo_rel_line_to (cr, history_x2 - history_x1, 0.0);
 | 
						|
          cairo_stroke (cr);
 | 
						|
        }
 | 
						|
 | 
						|
      for (i = 1; i < 6; i++)
 | 
						|
        {
 | 
						|
          cairo_move_to (cr,
 | 
						|
                         history_x1 + i / 6.0 * (history_x2 - history_x1),
 | 
						|
                         history_y1);
 | 
						|
          cairo_rel_line_to (cr, 0.0, history_y2 - history_y1);
 | 
						|
          cairo_stroke (cr);
 | 
						|
        }
 | 
						|
 | 
						|
      cairo_restore (cr);
 | 
						|
 | 
						|
      /* paint history border */
 | 
						|
      cairo_arc_negative (cr,
 | 
						|
                          0.0, 0.0,
 | 
						|
                          0.6 * size,
 | 
						|
                          a1, a2);
 | 
						|
      cairo_line_to (cr,
 | 
						|
                     allocation.width - BORDER_WIDTH - 0.5 * size,
 | 
						|
                     -0.50 * size);
 | 
						|
      cairo_line_to (cr,
 | 
						|
                     allocation.width - BORDER_WIDTH - 0.5 * size,
 | 
						|
                     0.25 * size);
 | 
						|
      cairo_close_path (cr);
 | 
						|
      cairo_stroke (cr);
 | 
						|
    }
 | 
						|
 | 
						|
  g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
pika_meter_timeout (PikaMeter *meter)
 | 
						|
{
 | 
						|
  gboolean uniform = TRUE;
 | 
						|
  gboolean redraw  = TRUE;
 | 
						|
  gdouble  sample0[meter->priv->n_values];
 | 
						|
  gint     i;
 | 
						|
 | 
						|
  g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
  pika_meter_shift_samples (meter);
 | 
						|
 | 
						|
  pika_meter_mask_sample (meter, SAMPLE (0), sample0);
 | 
						|
 | 
						|
  if (meter->priv->history_visible)
 | 
						|
    {
 | 
						|
      for (i = 1; uniform && i < meter->priv->n_samples; i++)
 | 
						|
        {
 | 
						|
          gdouble sample[meter->priv->n_values];
 | 
						|
 | 
						|
          pika_meter_mask_sample (meter, SAMPLE (i), sample);
 | 
						|
 | 
						|
          uniform = ! memcmp (sample0, sample, SAMPLE_SIZE);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  if (uniform && meter->priv->uniform_sample)
 | 
						|
    redraw = memcmp (sample0, meter->priv->uniform_sample, SAMPLE_SIZE);
 | 
						|
 | 
						|
  if (uniform)
 | 
						|
    {
 | 
						|
      if (! meter->priv->uniform_sample)
 | 
						|
        meter->priv->uniform_sample = g_malloc (SAMPLE_SIZE);
 | 
						|
 | 
						|
      memcpy (meter->priv->uniform_sample, sample0, SAMPLE_SIZE);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      g_clear_pointer (&meter->priv->uniform_sample, g_free);
 | 
						|
    }
 | 
						|
 | 
						|
  g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
  if (redraw)
 | 
						|
    gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
  return G_SOURCE_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_clear_history_unlocked (PikaMeter *meter)
 | 
						|
{
 | 
						|
  meter->priv->current_time     = g_get_monotonic_time ();
 | 
						|
  meter->priv->last_sample_time = meter->priv->current_time /
 | 
						|
                                  meter->priv->sample_duration;
 | 
						|
 | 
						|
  memset (meter->priv->samples, 0, meter->priv->n_samples * SAMPLE_SIZE);
 | 
						|
 | 
						|
  g_clear_pointer (&meter->priv->uniform_sample, g_free);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_update_samples (PikaMeter *meter)
 | 
						|
{
 | 
						|
  meter->priv->n_samples = ceil (meter->priv->history_duration /
 | 
						|
                                 meter->priv->history_resolution) + 4;
 | 
						|
 | 
						|
  meter->priv->samples = g_renew (gdouble, meter->priv->samples,
 | 
						|
                                  meter->priv->n_samples *
 | 
						|
                                  meter->priv->n_values);
 | 
						|
 | 
						|
  meter->priv->sample_duration = ROUND (meter->priv->history_resolution *
 | 
						|
                                        G_TIME_SPAN_SECOND);
 | 
						|
 | 
						|
  pika_meter_clear_history_unlocked (meter);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_shift_samples (PikaMeter *meter)
 | 
						|
{
 | 
						|
  gint64 time;
 | 
						|
  gint   n_new_samples;
 | 
						|
 | 
						|
  meter->priv->current_time = g_get_monotonic_time ();
 | 
						|
 | 
						|
  time = meter->priv->current_time / meter->priv->sample_duration;
 | 
						|
 | 
						|
  n_new_samples = MIN (time - meter->priv->last_sample_time,
 | 
						|
                       meter->priv->n_samples - 1);
 | 
						|
 | 
						|
  memmove (SAMPLE (n_new_samples), SAMPLE (0),
 | 
						|
           (meter->priv->n_samples - n_new_samples) * SAMPLE_SIZE);
 | 
						|
  gegl_memset_pattern (SAMPLE (0), SAMPLE (n_new_samples), SAMPLE_SIZE,
 | 
						|
                       n_new_samples);
 | 
						|
 | 
						|
  meter->priv->last_sample_time = time;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_meter_mask_sample (PikaMeter     *meter,
 | 
						|
                        const gdouble *sample,
 | 
						|
                        gdouble       *result)
 | 
						|
{
 | 
						|
  gint i;
 | 
						|
 | 
						|
  for (i = 0; i < meter->priv->n_values; i++)
 | 
						|
    {
 | 
						|
      if (meter->priv->values[i].active         &&
 | 
						|
          (meter->priv->values[i].show_in_gauge ||
 | 
						|
           meter->priv->values[i].show_in_history))
 | 
						|
        {
 | 
						|
          result[i] = sample[i];
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          result[i] = 0.0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*  public functions  */
 | 
						|
 | 
						|
 | 
						|
GtkWidget *
 | 
						|
pika_meter_new (gint n_values)
 | 
						|
{
 | 
						|
  return g_object_new (PIKA_TYPE_METER,
 | 
						|
                       "n-values", n_values,
 | 
						|
                       NULL);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_size (PikaMeter *meter,
 | 
						|
                     gint       size)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (size > 0);
 | 
						|
 | 
						|
  if (size != meter->priv->size)
 | 
						|
    {
 | 
						|
      meter->priv->size = size;
 | 
						|
 | 
						|
      gtk_widget_queue_resize (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "size");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gint
 | 
						|
pika_meter_get_size (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0);
 | 
						|
 | 
						|
  return meter->priv->size;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_refresh_rate (PikaMeter *meter,
 | 
						|
                             gdouble    rate)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (rate > 0.0);
 | 
						|
 | 
						|
  if (rate != meter->priv->refresh_rate)
 | 
						|
    {
 | 
						|
      meter->priv->refresh_rate = rate;
 | 
						|
 | 
						|
      if (meter->priv->timeout_id)
 | 
						|
        {
 | 
						|
          gint interval = ROUND (1000.0 / meter->priv->refresh_rate);
 | 
						|
 | 
						|
          g_source_remove (meter->priv->timeout_id);
 | 
						|
 | 
						|
          meter->priv->timeout_id = g_timeout_add (interval,
 | 
						|
                                                   (GSourceFunc) pika_meter_timeout,
 | 
						|
                                                   meter);
 | 
						|
        }
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "refresh-rate");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gdouble
 | 
						|
pika_meter_get_refresh_rate (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0);
 | 
						|
 | 
						|
  return meter->priv->refresh_rate;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_range (PikaMeter *meter,
 | 
						|
                      gdouble    min,
 | 
						|
                      gdouble    max)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (min <= max);
 | 
						|
 | 
						|
  if (min != meter->priv->range_min)
 | 
						|
    {
 | 
						|
      g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
      meter->priv->range_min = min;
 | 
						|
 | 
						|
      g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "range-min");
 | 
						|
    }
 | 
						|
 | 
						|
  if (max != meter->priv->range_max)
 | 
						|
    {
 | 
						|
      g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
      meter->priv->range_max = max;
 | 
						|
 | 
						|
      g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "range-max");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gdouble
 | 
						|
pika_meter_get_range_min (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0.0);
 | 
						|
 | 
						|
  return meter->priv->range_min;
 | 
						|
}
 | 
						|
 | 
						|
gdouble
 | 
						|
pika_meter_get_range_max (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0.0);
 | 
						|
 | 
						|
  return meter->priv->range_max;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_n_values (PikaMeter *meter,
 | 
						|
                         gint       n_values)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (n_values >= 0);
 | 
						|
 | 
						|
  if (n_values != meter->priv->n_values)
 | 
						|
    {
 | 
						|
      g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
      meter->priv->values = g_renew (Value, meter->priv->values, n_values);
 | 
						|
 | 
						|
      if (n_values > meter->priv->n_values)
 | 
						|
        {
 | 
						|
          gegl_memset_pattern (meter->priv->values,
 | 
						|
                               &(Value) { .active          = TRUE,
 | 
						|
                                          .show_in_gauge   = TRUE,
 | 
						|
                                          .show_in_history = TRUE,
 | 
						|
                                          .interpolation   = PIKA_INTERPOLATION_CUBIC},
 | 
						|
                               sizeof (Value),
 | 
						|
                               n_values - meter->priv->n_values);
 | 
						|
        }
 | 
						|
 | 
						|
      meter->priv->n_values = n_values;
 | 
						|
 | 
						|
      pika_meter_update_samples (meter);
 | 
						|
 | 
						|
      g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "n-values");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gint
 | 
						|
pika_meter_get_n_values (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0);
 | 
						|
 | 
						|
  return meter->priv->n_values;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_value_active (PikaMeter *meter,
 | 
						|
                             gint       value,
 | 
						|
                             gboolean   active)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (value >= 0 && value < meter->priv->n_values);
 | 
						|
 | 
						|
  if (active != meter->priv->values[value].active)
 | 
						|
    {
 | 
						|
      meter->priv->values[value].active = active;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
pika_meter_get_value_active (PikaMeter *meter,
 | 
						|
                             gint       value)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), FALSE);
 | 
						|
  g_return_val_if_fail (value >= 0 && value < meter->priv->n_values, FALSE);
 | 
						|
 | 
						|
  return meter->priv->values[value].active;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_value_color (PikaMeter     *meter,
 | 
						|
                            gint           value,
 | 
						|
                            const PikaRGB *color)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (value >= 0 && value < meter->priv->n_values);
 | 
						|
  g_return_if_fail (color != NULL);
 | 
						|
 | 
						|
  if (memcmp (color, &meter->priv->values[value].color, sizeof (PikaRGB)))
 | 
						|
    {
 | 
						|
      meter->priv->values[value].color = *color;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const PikaRGB *
 | 
						|
pika_meter_get_value_color (PikaMeter *meter,
 | 
						|
                            gint       value)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), NULL);
 | 
						|
  g_return_val_if_fail (value >= 0 && value < meter->priv->n_values, NULL);
 | 
						|
 | 
						|
  return &meter->priv->values[value].color;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_value_interpolation (PikaMeter             *meter,
 | 
						|
                                    gint                   value,
 | 
						|
                                    PikaInterpolationType  interpolation)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (value >= 0 && value < meter->priv->n_values);
 | 
						|
 | 
						|
  if (meter->priv->values[value].interpolation != interpolation)
 | 
						|
    {
 | 
						|
      meter->priv->values[value].interpolation = interpolation;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
PikaInterpolationType
 | 
						|
pika_meter_get_value_interpolation (PikaMeter *meter,
 | 
						|
                                    gint       value)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), PIKA_INTERPOLATION_NONE);
 | 
						|
  g_return_val_if_fail (value >= 0 && value < meter->priv->n_values,
 | 
						|
                        PIKA_INTERPOLATION_NONE);
 | 
						|
 | 
						|
  return meter->priv->values[value].interpolation;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_value_show_in_gauge (PikaMeter *meter,
 | 
						|
                                    gint       value,
 | 
						|
                                    gboolean   show)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (value >= 0 && value < meter->priv->n_values);
 | 
						|
 | 
						|
  if (meter->priv->values[value].show_in_gauge != show)
 | 
						|
    {
 | 
						|
      meter->priv->values[value].show_in_gauge = show;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
pika_meter_get_value_show_in_gauge (PikaMeter *meter,
 | 
						|
                                    gint       value)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), FALSE);
 | 
						|
  g_return_val_if_fail (value >= 0 && value < meter->priv->n_values, FALSE);
 | 
						|
 | 
						|
  return meter->priv->values[value].show_in_gauge;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_value_show_in_history (PikaMeter *meter,
 | 
						|
                                      gint       value,
 | 
						|
                                      gboolean   show)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (value >= 0 && value < meter->priv->n_values);
 | 
						|
 | 
						|
  if (meter->priv->values[value].show_in_history != show)
 | 
						|
    {
 | 
						|
      meter->priv->values[value].show_in_history = show;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
pika_meter_get_value_show_in_history (PikaMeter *meter,
 | 
						|
                                      gint       value)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), FALSE);
 | 
						|
  g_return_val_if_fail (value >= 0 && value < meter->priv->n_values, FALSE);
 | 
						|
 | 
						|
  return meter->priv->values[value].show_in_history;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_history_visible (PikaMeter *meter,
 | 
						|
                                gboolean   visible)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
 | 
						|
  if (visible != meter->priv->history_visible)
 | 
						|
    {
 | 
						|
      meter->priv->history_visible = visible;
 | 
						|
 | 
						|
      gtk_widget_queue_resize (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "history-visible");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
pika_meter_get_history_visible (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), FALSE);
 | 
						|
 | 
						|
  return meter->priv->history_visible;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_history_duration (PikaMeter *meter,
 | 
						|
                                 gdouble    duration)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (duration >= 0.0);
 | 
						|
 | 
						|
  if (duration != meter->priv->history_duration)
 | 
						|
    {
 | 
						|
      g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
      meter->priv->history_duration = duration;
 | 
						|
 | 
						|
      pika_meter_update_samples (meter);
 | 
						|
 | 
						|
      g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "history-duration");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gdouble
 | 
						|
pika_meter_get_history_duration (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0.0);
 | 
						|
 | 
						|
  return meter->priv->history_duration;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_history_resolution (PikaMeter *meter,
 | 
						|
                                   gdouble    resolution)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (resolution > 0.0);
 | 
						|
 | 
						|
  if (resolution != meter->priv->history_resolution)
 | 
						|
    {
 | 
						|
      g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
      meter->priv->history_resolution = resolution;
 | 
						|
 | 
						|
      pika_meter_update_samples (meter);
 | 
						|
 | 
						|
      g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "history-resolution");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gdouble
 | 
						|
pika_meter_get_history_resolution (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), 0.0);
 | 
						|
 | 
						|
  return meter->priv->history_resolution;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_clear_history (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
 | 
						|
  g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
  pika_meter_clear_history_unlocked (meter);
 | 
						|
 | 
						|
  g_mutex_unlock (&meter->priv->mutex);
 | 
						|
 | 
						|
  gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_add_sample (PikaMeter     *meter,
 | 
						|
                       const gdouble *sample)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (sample != NULL || meter->priv->n_values == 0);
 | 
						|
 | 
						|
  g_mutex_lock (&meter->priv->mutex);
 | 
						|
 | 
						|
  pika_meter_shift_samples (meter);
 | 
						|
 | 
						|
  memcpy (SAMPLE (0), sample, SAMPLE_SIZE);
 | 
						|
 | 
						|
  g_mutex_unlock (&meter->priv->mutex);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_led_active (PikaMeter *meter,
 | 
						|
                           gboolean   active)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
 | 
						|
  if (active != meter->priv->led_active)
 | 
						|
    {
 | 
						|
      meter->priv->led_active = active;
 | 
						|
 | 
						|
      gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "led-active");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
pika_meter_get_led_active (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), FALSE);
 | 
						|
 | 
						|
  return meter->priv->led_active;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
pika_meter_set_led_color (PikaMeter     *meter,
 | 
						|
                          const PikaRGB *color)
 | 
						|
{
 | 
						|
  g_return_if_fail (PIKA_IS_METER (meter));
 | 
						|
  g_return_if_fail (color != NULL);
 | 
						|
 | 
						|
  if (memcmp (color, &meter->priv->led_color, sizeof (PikaRGB)))
 | 
						|
    {
 | 
						|
      meter->priv->led_color = *color;
 | 
						|
 | 
						|
      if (meter->priv->led_active)
 | 
						|
        gtk_widget_queue_draw (GTK_WIDGET (meter));
 | 
						|
 | 
						|
      g_object_notify (G_OBJECT (meter), "led-color");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const PikaRGB *
 | 
						|
pika_meter_get_led_color (PikaMeter *meter)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (PIKA_IS_METER (meter), NULL);
 | 
						|
 | 
						|
  return &meter->priv->led_color;
 | 
						|
}
 |