/*
* Copyright (C) 2000 James Macnicol
*
* 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.
*/
#include <math.h>
#include <limits.h>
#include "screenhack.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b))?(a):(b))
#endif
#define MINX 0.0
#define MINY 0.0
/* This should be *way* slower than the spotlight hack was */
#define X_PERIOD (45000.0 * 3)
#define Y_PERIOD (36000.0 * 3)
struct state {
Display *dpy;
Window window;
Screen *screen;
int sizex, sizey;
int delay;
int duration;
int pixwidth, pixheight, pixspacex, pixspacey, lensoffsetx, lensoffsety;
Bool lenses;
GC window_gc;
XImage *orig_map;
Pixmap pm;
int tlx, tly, s;
int sinusoid_offset;
time_t start_time;
async_load_state *img_loader;
};
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 *
zoom_init (Display *dpy, Window window)
{
struct state *st = (struct state *) calloc (1, sizeof(*st));
XGCValues gcv;
XWindowAttributes xgwa;
Colormap cmap;
unsigned long bg;
long gcflags;
int nblocksx, nblocksy;
st->dpy = dpy;
st->window = window;
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");
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->pixwidth = get_integer_resource(st->dpy, "pixwidth", "Integer");
if (st->pixwidth < 1)
st->pixwidth = 1;
st->pixheight = get_integer_resource(st->dpy, "pixheight", "Integer");
if (st->pixheight < 1)
st->pixheight = 1;
st->pixspacex = get_integer_resource(st->dpy, "pixspacex", "Integer");
if (st->pixspacex < 0)
st->pixspacex = 0;
st->pixspacey = get_integer_resource(st->dpy, "pixspacey", "Integer");
if (st->pixspacey < 0)
st->pixspacey = 0;
if (st->sizex < 50 || st->sizey < 50) { /* tiny window */
st->pixwidth = 10;
st->pixheight = 10;
}
st->lenses = get_boolean_resource(st->dpy, "lenses", "Boolean");
st->lensoffsetx = get_integer_resource(st->dpy, "lensoffsetx", "Integer");
st->lensoffsetx = MAX(0, MIN(st->pixwidth, st->lensoffsetx));
st->lensoffsety = get_integer_resource(st->dpy, "lensoffsety", "Integer");
st->lensoffsety = MAX(0, MIN(st->pixwidth, st->lensoffsety));
gcv.function = GXcopy;
gcv.subwindow_mode = IncludeInferiors;
gcflags = GCForeground|GCFunction;
gcv.foreground = bg;
if (!st->lenses && use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
gcflags |= GCSubwindowMode;
st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
st->orig_map = NULL;
st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
XSetWindowBackground(st->dpy, st->window, bg);
st->start_time = time ((time_t *) 0);
st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
st->pm, 0, 0);
/* We might have needed this to grab the image, but if we leave this set
to GCSubwindowMode, then we'll *draw* right over subwindows too. */
XSetSubwindowMode (st->dpy, st->window_gc, ClipByChildren);
nblocksx = (int)ceil((double)st->sizex / (double)(st->pixwidth + st->pixspacex));
nblocksy = (int)ceil((double)st->sizey / (double)(st->pixheight + st->pixspacey));
if (st->lenses)
st->s = MAX((nblocksx - 1) * st->lensoffsetx + st->pixwidth,
(nblocksy - 1) * st->lensoffsety + st->pixheight) * 2;
else
st->s = MAX(nblocksx, nblocksy) * 2;
st->sinusoid_offset = random();
return st;
}
static unsigned long
zoom_draw (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
unsigned x, y, i, j;
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, 0);
if (! st->img_loader) { /* just finished */
XClearWindow (st->dpy, st->window);
st->start_time = time ((time_t *) 0);
if (!st->lenses) {
st->orig_map = XGetImage(st->dpy, st->pm, 0, 0, st->sizex, st->sizey, ~0L, ZPixmap);
/* XFreePixmap(st->dpy, st->pm);
st->pm = 0;*/
}
}
return st->delay;
}
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, 0);
return st->delay;
}
#define nrnd(x) (random() % (x))
now = currentTimeInMs(st);
now_unsigned = (unsigned long) now + st->sinusoid_offset; /* don't run multiple screens in lock-step */
now = (now_unsigned <= LONG_MAX) ? now_unsigned : -1 - (long)(ULONG_MAX - now_unsigned);
/* find new x,y */
st->tlx = ((1. + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
* (st->sizex - st->s/2) /* -s/4 */ + MINX;
st->tly = ((1. + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
* (st->sizey - st->s/2) /* -s/4 */ + MINY;
if (st->lenses) {
for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
XCopyArea(st->dpy, st->pm /* src */, st->window /* dest */, st->window_gc,
st->tlx + i * st->lensoffsetx /* src_x */,
st->tly + j * st->lensoffsety /* src_y */,
st->pixwidth, st->pixheight,
x /* dest_x */, y /* dest_y */);
}
} else {
for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
XSetForeground(st->dpy, st->window_gc, XGetPixel(st->orig_map, st->tlx+i, st->tly+j));
XFillRectangle(st->dpy, st->window, st->window_gc,
i * (st->pixwidth + st->pixspacex),
j * (st->pixheight + st->pixspacey), st->pixwidth, st->pixheight);
}
}
return st->delay;
}
static void
zoom_reshape (Display *dpy, Window window, void *closure,
unsigned int w, unsigned int h)
{
}
static Bool
zoom_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
zoom_free (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
XFreeGC (st->dpy, st->window_gc);
if (st->orig_map) XDestroyImage (st->orig_map);
if (st->pm) XFreePixmap (st->dpy, st->pm);
free (st);
}
static const char *zoom_defaults[] = {
"*dontClearRoot: True",
".foreground: white",
".background: #111111",
".lowrez: true",
"*fpsSolid: true",
#ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
"*visualID: Best",
#endif
"*lenses: true",
"*delay: 10000",
"*duration: 120",
"*pixwidth: 40",
"*pixheight: 40",
"*pixspacex: 2",
"*pixspacey: 2",
"*lensoffsetx: 5",
"*lensoffsety: 5",
#ifdef HAVE_MOBILE
"*ignoreRotation: True",
"*rotateImages: True",
#endif
0
};
static XrmOptionDescRec zoom_options[] = {
{ "-lenses", ".lenses", XrmoptionNoArg, "true" },
{ "-no-lenses", ".lenses", XrmoptionNoArg, "false" },
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-duration", ".duration", XrmoptionSepArg, 0 },
{ "-pixwidth", ".pixwidth", XrmoptionSepArg, 0 },
{ "-pixheight", ".pixheight", XrmoptionSepArg, 0 },
{ "-pixspacex", ".pixspacex", XrmoptionSepArg, 0 },
{ "-pixspacey", ".pixspacey", XrmoptionSepArg, 0 },
{ "-lensoffsetx", ".lensoffsetx", XrmoptionSepArg, 0 },
{ "-lensoffsety", ".lensoffsety", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 }
};
XSCREENSAVER_MODULE ("Zoom", zoom)