summaryrefslogtreecommitdiff
path: root/savers/gste-popsquares.c
diff options
context:
space:
mode:
Diffstat (limited to 'savers/gste-popsquares.c')
-rw-r--r--savers/gste-popsquares.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/savers/gste-popsquares.c b/savers/gste-popsquares.c
new file mode 100644
index 0000000..6a6f2e3
--- /dev/null
+++ b/savers/gste-popsquares.c
@@ -0,0 +1,622 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "gs-theme-engine.h"
+#include "gste-popsquares.h"
+
+static void gste_popsquares_class_init (GSTEPopsquaresClass *klass);
+static void gste_popsquares_init (GSTEPopsquares *engine);
+static void gste_popsquares_finalize (GObject *object);
+
+typedef struct _square
+{
+ int x, y, w, h;
+ int color;
+} square;
+
+struct GSTEPopsquaresPrivate
+{
+ guint timeout_id;
+
+ int ncolors;
+ int subdivision;
+
+ GdkGC *gc;
+ GdkColor *colors;
+ square *squares;
+
+};
+
+#define GSTE_POPSQUARES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSTE_TYPE_POPSQUARES, GSTEPopsquaresPrivate))
+
+static GObjectClass *parent_class = NULL;
+
+G_DEFINE_TYPE (GSTEPopsquares, gste_popsquares, GS_TYPE_THEME_ENGINE)
+
+static void
+hsv_to_rgb (int h,
+ double s,
+ double v,
+ unsigned short *r,
+ unsigned short *g,
+ unsigned short *b)
+{
+ double H, S, V, R, G, B;
+ double p1, p2, p3;
+ double f;
+ int i;
+
+ if (s < 0)
+ {
+ s = 0;
+ }
+ if (v < 0)
+ {
+ v = 0;
+ }
+ if (s > 1)
+ {
+ s = 1;
+ }
+ if (v > 1)
+ {
+ v = 1;
+ }
+
+ S = s;
+ V = v;
+ H = (h % 360) / 60.0;
+ i = H;
+ f = H - i;
+ p1 = V * (1 - S);
+ p2 = V * (1 - (S * f));
+ p3 = V * (1 - (S * (1 - f)));
+
+ if (i == 0)
+ {
+ R = V;
+ G = p3;
+ B = p1;
+ }
+ else if (i == 1)
+ {
+ R = p2;
+ G = V;
+ B = p1;
+ }
+ else if (i == 2)
+ {
+ R = p1;
+ G = V;
+ B = p3;
+ }
+ else if (i == 3)
+ {
+ R = p1;
+ G = p2;
+ B = V;
+ }
+ else if (i == 4)
+ {
+ R = p3;
+ G = p1;
+ B = V;
+ }
+ else
+ {
+ R = V;
+ G = p1;
+ B = p2;
+ }
+
+ *r = R * 65535;
+ *g = G * 65535;
+ *b = B * 65535;
+}
+
+static void
+rgb_to_hsv (unsigned short r,
+ unsigned short g,
+ unsigned short b,
+ int *h,
+ double *s,
+ double *v)
+{
+ double R, G, B, H, S, V;
+ double cmax, cmin;
+ double cmm;
+ int imax;
+
+ R = ((double) r) / 65535.0;
+ G = ((double) g) / 65535.0;
+ B = ((double) b) / 65535.0;
+ cmax = R;
+ cmin = G;
+ imax = 1;
+
+ if (cmax < G)
+ {
+ cmax = G;
+ cmin = R;
+ imax = 2;
+ }
+ if (cmax < B)
+ {
+ cmax = B;
+ imax = 3;
+ }
+ if (cmin > B)
+ {
+ cmin = B;
+ }
+
+ cmm = cmax - cmin;
+ V = cmax;
+
+ if (cmm == 0)
+ {
+ S = H = 0;
+ }
+ else
+ {
+ S = cmm / cmax;
+ if (imax == 1)
+ {
+ H = (G - B) / cmm;
+ }
+ else if (imax == 2)
+ {
+ H = 2.0 + (B - R) / cmm;
+ }
+ else
+ {
+ /*if (imax == 3)*/
+ H = 4.0 + (R - G) / cmm;
+ }
+
+ if (H < 0)
+ {
+ H += 6.0;
+ }
+ }
+
+ *h = (H * 60.0);
+ *s = S;
+ *v = V;
+}
+
+static void
+make_color_ramp (GdkColormap *colormap,
+ int h1,
+ double s1,
+ double v1,
+ int h2,
+ double s2,
+ double v2,
+ GdkColor *colors,
+ int n_colors,
+ gboolean closed,
+ gboolean allocate,
+ gboolean writable)
+{
+ double dh, ds, dv; /* deltas */
+ int i;
+ int ncolors, wanted;
+ int total_ncolors = n_colors;
+
+ wanted = total_ncolors;
+ if (closed)
+ {
+ wanted = (wanted / 2) + 1;
+ }
+
+ ncolors = total_ncolors;
+
+ memset (colors, 0, n_colors * sizeof (*colors));
+
+ if (closed)
+ {
+ ncolors = (ncolors / 2) + 1;
+ }
+
+ /* Note: unlike other routines in this module, this function assumes that
+ if h1 and h2 are more than 180 degrees apart, then the desired direction
+ is always from h1 to h2 (rather than the shorter path.) make_uniform
+ depends on this.
+ */
+ dh = ((double)h2 - (double)h1) / ncolors;
+ ds = (s2 - s1) / ncolors;
+ dv = (v2 - v1) / ncolors;
+
+ for (i = 0; i < ncolors; i++)
+ {
+ hsv_to_rgb ((int) (h1 + (i * dh)),
+ (s1 + (i * ds)),
+ (v1 + (i * dv)),
+ &colors [i].red,
+ &colors [i].green,
+ &colors [i].blue);
+ if (allocate)
+ {
+ gdk_colormap_alloc_color (colormap,
+ &colors [i],
+ writable,
+ TRUE);
+ }
+ }
+
+ if (closed)
+ {
+ for (i = ncolors; i < n_colors; i++)
+ {
+ colors [i] = colors [n_colors - i];
+ }
+ }
+
+}
+
+static void
+randomize_square_colors (square *squares,
+ int nsquares,
+ int ncolors)
+{
+ int i;
+ square *s;
+
+ s = squares;
+
+ for (i = 0; i < nsquares; i++)
+ {
+ s[i].color = g_random_int_range (0, ncolors);
+ }
+}
+
+static void
+set_colors (GdkWindow *window,
+ GdkColor *fg,
+ GdkColor *bg)
+{
+ GtkWidget *widget;
+ GdkColor color;
+
+ widget = gtk_invisible_new ();
+
+ color = widget->style->dark [GTK_STATE_SELECTED];
+ fg->red = color.red;
+ fg->green = color.green;
+ fg->blue = color.blue;
+ color = widget->style->bg [GTK_STATE_SELECTED];
+ bg->red = color.red;
+ bg->green = color.green;
+ bg->blue = color.blue;
+}
+
+static void
+gste_popsquares_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSTEPopsquares *self;
+
+ self = GSTE_POPSQUARES (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gste_popsquares_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSTEPopsquares *self;
+
+ self = GSTE_POPSQUARES (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+setup_squares (GSTEPopsquares *pop)
+{
+ int window_width;
+ int window_height;
+ int nsquares;
+ int x, y;
+ int sw, sh, gw, gh;
+ GdkWindow *window;
+
+ window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop));
+
+ if (window == NULL)
+ {
+ return;
+ }
+
+ gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop), &window_width, &window_height);
+
+ sw = window_width / pop->priv->subdivision;
+ sh = window_height / pop->priv->subdivision;
+
+ gw = pop->priv->subdivision;
+ gh = pop->priv->subdivision;
+ nsquares = gw * gh;
+
+ if (pop->priv->squares)
+ {
+ g_free (pop->priv->squares);
+ }
+ pop->priv->squares = g_new0 (square, nsquares);
+
+ for (y = 0; y < gh; y++)
+ {
+ for (x = 0; x < gw; x++)
+ {
+ square *s = (square *) &pop->priv->squares [gw * y + x];
+ s->w = sw;
+ s->h = sh;
+ s->x = x * sw;
+ s->y = y * sh;
+ }
+ }
+}
+
+static void
+setup_colors (GSTEPopsquares *pop)
+{
+ double s1, v1, s2, v2 = 0;
+ int h1, h2 = 0;
+ int nsquares;
+ GdkColor fg;
+ GdkColor bg;
+ GdkWindow *window;
+
+ window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop));
+
+ if (window == NULL)
+ {
+ return;
+ }
+
+ set_colors (window, &fg, &bg);
+
+ if (pop->priv->gc)
+ {
+ g_object_unref (pop->priv->gc);
+ }
+ pop->priv->gc = gdk_gc_new (window);
+
+ if (pop->priv->colors)
+ {
+ g_free (pop->priv->colors);
+ }
+ pop->priv->colors = g_new0 (GdkColor, pop->priv->ncolors);
+
+ rgb_to_hsv (fg.red, fg.green, fg.blue, &h1, &s1, &v1);
+ rgb_to_hsv (bg.red, bg.green, bg.blue, &h2, &s2, &v2);
+
+ make_color_ramp (gtk_widget_get_colormap (GTK_WIDGET (pop)),
+ h1, s1, v1,
+ h2, s2, v2,
+ pop->priv->colors,
+ pop->priv->ncolors,
+ TRUE,
+ TRUE,
+ FALSE);
+
+ nsquares = pop->priv->subdivision * pop->priv->subdivision;
+
+ randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors);
+}
+
+static void
+gste_popsquares_real_show (GtkWidget *widget)
+{
+ GSTEPopsquares *pop = GSTE_POPSQUARES (widget);
+
+ /* start */
+ setup_squares (pop);
+ setup_colors (pop);
+
+ if (GTK_WIDGET_CLASS (parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (parent_class)->show (widget);
+ }
+}
+
+static gboolean
+gste_popsquares_real_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ gboolean handled = FALSE;
+
+ /* draw */
+
+ /* FIXME: should double buffer? */
+
+ if (GTK_WIDGET_CLASS (parent_class)->expose_event)
+ {
+ handled = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+ }
+
+ return handled;
+}
+
+static gboolean
+gste_popsquares_real_configure (GtkWidget *widget,
+ GdkEventConfigure *event)
+{
+ GSTEPopsquares *pop = GSTE_POPSQUARES (widget);
+ gboolean handled = FALSE;
+
+ /* resize */
+
+ /* just reset everything */
+ setup_squares (pop);
+ setup_colors (pop);
+
+ /* schedule a redraw */
+ gtk_widget_queue_draw (widget);
+
+ if (GTK_WIDGET_CLASS (parent_class)->configure_event)
+ {
+ handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event);
+ }
+
+ return handled;
+}
+
+static void
+gste_popsquares_class_init (GSTEPopsquaresClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = gste_popsquares_finalize;
+ object_class->get_property = gste_popsquares_get_property;
+ object_class->set_property = gste_popsquares_set_property;
+
+ widget_class->show = gste_popsquares_real_show;
+ widget_class->expose_event = gste_popsquares_real_expose;
+ widget_class->configure_event = gste_popsquares_real_configure;
+
+ g_type_class_add_private (klass, sizeof (GSTEPopsquaresPrivate));
+}
+
+static gboolean
+draw_iter (GSTEPopsquares *pop)
+{
+ int border = 1;
+ gboolean twitch = FALSE;
+ int x, y;
+ int sw, sh, gw, gh;
+ int nsquares;
+ int window_width;
+ int window_height;
+ GdkWindow *window;
+
+ window = gs_theme_engine_get_window (GS_THEME_ENGINE (pop));
+
+ if (window == NULL)
+ {
+ return TRUE;
+ }
+
+ gs_theme_engine_get_window_size (GS_THEME_ENGINE (pop),
+ &window_width,
+ &window_height);
+ sw = window_width / pop->priv->subdivision;
+ sh = window_height / pop->priv->subdivision;
+
+ gw = pop->priv->subdivision;
+ gh = pop->priv->subdivision;
+ nsquares = gw * gh;
+
+ for (y = 0; y < gh; y++)
+ {
+ for (x = 0; x < gw; x++)
+ {
+ square *s = (square *) &pop->priv->squares [gw * y + x];
+
+ gdk_gc_set_foreground (pop->priv->gc, &(pop->priv->colors [s->color]));
+ gdk_draw_rectangle (window, pop->priv->gc, TRUE, s->x, s->y,
+ border ? s->w - border : s->w,
+ border ? s->h - border : s->h);
+ s->color++;
+
+ if (s->color == pop->priv->ncolors)
+ {
+ if (twitch && ((g_random_int_range (0, 4)) == 0))
+ {
+ randomize_square_colors (pop->priv->squares, nsquares, pop->priv->ncolors);
+ }
+ else
+ {
+ s->color = g_random_int_range (0, pop->priv->ncolors);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+gste_popsquares_init (GSTEPopsquares *pop)
+{
+ int delay;
+
+ pop->priv = GSTE_POPSQUARES_GET_PRIVATE (pop);
+
+ pop->priv->ncolors = 128;
+ pop->priv->subdivision = 5;
+
+ delay = 25;
+ pop->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, pop);
+}
+
+static void
+gste_popsquares_finalize (GObject *object)
+{
+ GSTEPopsquares *pop;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSTE_IS_POPSQUARES (object));
+
+ pop = GSTE_POPSQUARES (object);
+
+ g_return_if_fail (pop->priv != NULL);
+
+ if (pop->priv->timeout_id > 0)
+ {
+ g_source_remove (pop->priv->timeout_id);
+ pop->priv->timeout_id = 0;
+ }
+
+ g_free (pop->priv->squares);
+ g_free (pop->priv->colors);
+ g_object_unref (pop->priv->gc);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}