summaryrefslogtreecommitdiffstats
path: root/hacks/spotlight.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/spotlight.c')
-rw-r--r--hacks/spotlight.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/hacks/spotlight.c b/hacks/spotlight.c
new file mode 100644
index 0000000..7385899
--- /dev/null
+++ b/hacks/spotlight.c
@@ -0,0 +1,353 @@
+/*
+ * spotlight - an xscreensaver module
+ * Copyright (c) 1999, 2001 Rick Schultz <rick.schultz@gmail.com>
+ *
+ * loosely based on the BackSpace module "StefView" by Darcy Brockbank
+ */
+
+/* modified from a module from the xscreensaver distribution */
+
+/*
+ * xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+
+/* #define DEBUG */
+#include <math.h>
+#include <limits.h>
+#include "screenhack.h"
+
+#define X_PERIOD 15000.0
+#define Y_PERIOD 12000.0
+
+struct state {
+ Display *dpy;
+ Window window;
+ Screen *screen;
+
+ int sizex, sizey; /* screen size */
+ int delay;
+ int duration;
+ time_t start_time;
+ int first_time;
+ GC window_gc;
+#ifdef DEBUG
+ GC white_gc;
+#endif
+ GC buffer_gc; /* draw in buffer, then flush to screen
+ to avoid flicker */
+ int radius; /* radius of spotlight in pixels */
+
+ Pixmap pm; /* pixmap grabbed from screen */
+ Pixmap buffer; /* pixmap for the buffer */
+
+ int x, y, s; /* x & y coords of buffer (upper left corner) */
+ /* s is the width of the buffer */
+
+ int off; /* random offset from currentTimeInMs(), so that
+ two concurrent copies of spotlight have different
+ behavior. */
+
+ int oldx, oldy, max_x_speed, max_y_speed;
+ /* used to keep the new buffer position
+ over the old spotlight image to make sure
+ the old image is completely erased */
+
+ Bool first_p;
+ async_load_state *img_loader;
+ XRectangle geom;
+};
+
+
+/* The path the spotlight follows around the screen is sinusoidal.
+ This function is fed to sin() to get the x & y coords */
+static long
+currentTimeInMs(struct state *st)
+{
+ struct timeval curTime;
+ unsigned long ret_unsigned;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tz = {0,0};
+ gettimeofday(&curTime, &tz);
+#else
+ gettimeofday(&curTime);
+#endif
+ ret_unsigned = curTime.tv_sec *1000U + curTime.tv_usec / 1000;
+ return (ret_unsigned <= LONG_MAX) ? ret_unsigned : -1 - (long)(ULONG_MAX - ret_unsigned);
+}
+
+
+static void *
+spotlight_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ XGCValues gcv;
+ XWindowAttributes xgwa;
+ long gcflags;
+ Colormap cmap;
+ unsigned long bg;
+ GC clip_gc;
+ Pixmap clip_pm;
+
+ st->dpy = dpy;
+ st->window = window;
+ st->first_p = True;
+
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+ st->screen = xgwa.screen;
+ st->sizex = xgwa.width;
+ st->sizey = xgwa.height;
+ cmap = xgwa.colormap;
+ bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
+
+ /* read parameters, keep em sane */
+ st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+ if (st->delay < 1) st->delay = 1;
+ st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
+ if (st->duration < 1) st->duration = 1;
+ st->radius = get_integer_resource (st->dpy, "radius", "Integer");
+ if (st->radius < 0) st->radius = 125;
+
+ if (xgwa.width > 2560) st->radius *= 2; /* Retina displays */
+
+ /* Don't let the spotlight be bigger than the window */
+ while (st->radius > xgwa.width * 0.45)
+ st->radius /= 2;
+ while (st->radius > xgwa.height * 0.45)
+ st->radius /= 2;
+
+ if (st->radius < 4)
+ st->radius = 4;
+
+ /* do the dance */
+ gcv.function = GXcopy;
+ gcv.subwindow_mode = IncludeInferiors;
+ gcflags = GCForeground | GCFunction;
+ gcv.foreground = bg;
+
+#ifdef NOPE
+ if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
+ gcflags |= GCSubwindowMode;
+#endif
+ st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
+
+ st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
+ XClearWindow(st->dpy, st->window);
+
+ st->first_time = 1;
+
+ /* create buffer to reduce flicker */
+#ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
+ st->buffer = 0;
+#else
+ st->buffer = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
+#endif
+
+ st->buffer_gc = XCreateGC(st->dpy, (st->buffer ? st->buffer : window), gcflags, &gcv);
+ if (st->buffer)
+ XFillRectangle(st->dpy, st->buffer, st->buffer_gc, 0, 0, st->sizex, st->sizey);
+
+ /* create clip mask (so it's a circle, not a square) */
+ clip_pm = XCreatePixmap(st->dpy, st->window, st->radius*4, st->radius*4, 1);
+ st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm,
+ 0, &st->geom);
+ st->start_time = time ((time_t *) 0);
+
+ gcv.foreground = 0L;
+ clip_gc = XCreateGC(st->dpy, clip_pm, gcflags, &gcv);
+ XFillRectangle(st->dpy, clip_pm, clip_gc, 0, 0, st->radius*4, st->radius*4);
+
+ XSetForeground(st->dpy, clip_gc, 1L);
+ XFillArc(st->dpy, clip_pm, clip_gc, st->radius , st->radius,
+ st->radius*2, st->radius*2, 0, 360*64);
+
+ /* set buffer's clip mask to the one we just made */
+ XSetClipMask(st->dpy, st->buffer_gc, clip_pm);
+
+ /* free everything */
+ XFreeGC(st->dpy, clip_gc);
+ XFreePixmap(st->dpy, clip_pm);
+
+ /* avoid remants */
+ st->max_x_speed = st->max_y_speed = st->radius;
+
+ st->off = random();
+
+ /* blank out screen */
+ XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
+
+ return st;
+}
+
+
+/*
+ * perform one iteration
+ */
+static void
+onestep (struct state *st, Bool first_p)
+{
+ long now;
+ unsigned long now_unsigned;
+
+ if (st->img_loader) /* still loading */
+ {
+ st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0,
+ &st->geom);
+ if (! st->img_loader) { /* just finished */
+ st->start_time = time ((time_t *) 0);
+ }
+ return;
+ }
+
+ if (!st->img_loader &&
+ st->start_time + st->duration < time ((time_t *) 0)) {
+ st->img_loader = load_image_async_simple (0, st->screen, st->window,
+ st->pm, 0, &st->geom);
+ return;
+ }
+
+#define nrnd(x) (random() % (x))
+
+ st->oldx = st->x;
+ st->oldy = st->y;
+
+ st->s = st->radius *4 ; /* s = width of buffer */
+
+ now_unsigned = (unsigned long) currentTimeInMs(st) + st->off;
+ now = (now_unsigned <= LONG_MAX) ? now_unsigned : -1 - (long)(ULONG_MAX - now_unsigned);
+
+ /* find new x,y */
+ st->x = st->geom.x +
+ ((1 + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
+ * (st->geom.width - st->s/2) -st->s/4;
+ st->y = st->geom.y +
+ ((1 + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
+ * (st->geom.height - st->s/2) -st->s/4;
+
+ if (!st->first_p)
+ {
+ /* limit change in x and y to buffer width */
+ if ( st->x < (st->oldx - st->max_x_speed) ) st->x = st->oldx - st->max_x_speed;
+ if ( st->x > (st->oldx + st->max_x_speed) ) st->x = st->oldx + st->max_x_speed;
+ if ( st->y < (st->oldy - st->max_y_speed) ) st->y = st->oldy - st->max_y_speed;
+ if ( st->y > (st->oldy + st->max_y_speed) ) st->y = st->oldy + st->max_y_speed;
+ }
+
+ if (! st->buffer)
+ {
+ XClearWindow (st->dpy, st->window);
+ XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
+ XCopyArea(st->dpy, st->pm, st->window, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
+ }
+ else
+ {
+ /* clear buffer */
+ XFillRectangle(st->dpy, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s);
+
+ /* copy area of screen image (pm) to buffer
+ Clip to a circle */
+ XSetClipOrigin(st->dpy, st->buffer_gc, st->x,st->y);
+ XCopyArea(st->dpy, st->pm, st->buffer, st->buffer_gc, st->x, st->y, st->s, st->s, st->x, st->y);
+
+ if (st->first_time) {
+ /* blank out screen */
+ XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
+ st->first_time = 0;
+ }
+
+ /* copy buffer to screen (window) */
+ XCopyArea(st->dpy, st->buffer, st->window, st->window_gc, st->x , st->y, st->s, st->s, st->x, st->y);
+
+# if 0
+ XSetForeground (st->dpy, st->window_gc,
+ WhitePixel (st->dpy, DefaultScreen (st->dpy)));
+ XDrawRectangle(st->dpy, st->window, st->window_gc,
+ st->geom.x, st->geom.y, st->geom.width, st->geom.height);
+# endif
+ }
+
+#ifdef DEBUG
+ /* draw a box around the buffer */
+ XDrawRectangle(st->dpy, st->window, st->white_gc, st->x , st->y, st->s, st->s);
+#endif
+
+}
+
+
+static unsigned long
+spotlight_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ onestep(st, st->first_p);
+ st->first_p = False;
+ return st->delay;
+}
+
+static void
+spotlight_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+spotlight_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ struct state *st = (struct state *) closure;
+ if (screenhack_event_helper (dpy, window, event))
+ {
+ st->start_time = 0;
+ return True;
+ }
+ return False;
+}
+
+static void
+spotlight_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ XFreeGC (dpy, st->window_gc);
+ XFreeGC (dpy, st->buffer_gc);
+ if (st->pm) XFreePixmap (dpy, st->pm);
+ if (st->buffer) XFreePixmap (dpy, st->buffer);
+ free (st);
+}
+
+
+
+
+static const char *spotlight_defaults [] = {
+ ".background: black",
+ ".foreground: white",
+ "*dontClearRoot: True",
+ "*fpsSolid: true",
+
+#ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
+ "*visualID: Best",
+#endif
+
+ "*delay: 10000",
+ "*duration: 120",
+ "*radius: 125",
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+ "*rotateImages: True",
+#endif
+ 0
+};
+
+static XrmOptionDescRec spotlight_options [] = {
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-duration", ".duration", XrmoptionSepArg, 0 },
+ { "-radius", ".radius", XrmoptionSepArg, 0 },
+ { 0, 0, 0, 0 }
+};
+
+XSCREENSAVER_MODULE ("Spotlight", spotlight)