/* * spotlight - an xscreensaver module * Copyright (c) 1999, 2001 Rick Schultz * * 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 * * 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 #include #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)