/* Copyright (c) 2003 Levi Burton * * 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 "screenhack.h" #include "colors.h" #ifdef HAVE_DOUBLE_BUFFER_EXTENSION # include "xdbe.h" #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ typedef struct _square { int x, y, w, h; int color; } square; static void randomize_square_colors(square *squares, int nsquares, int ncolors) { int i; square *s = squares; for (i = 0; i < nsquares; i++) s[i].color = random() % ncolors; } struct state { Display *dpy; Window window; int delay, subdivisionx, subdivisiony, border, ncolors, twitch, dbuf; XWindowAttributes xgwa; GC gc; XColor *colors; int sw, sh, gw, gh, nsquares; square *squares; Pixmap b, ba, bb; #ifdef HAVE_DOUBLE_BUFFER_EXTENSION XdbeBackBuffer backb; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ }; static void popsquares_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; int s = get_integer_resource(st->dpy, "subdivision", "Integer"); int x, y; XGetWindowAttributes (st->dpy, st->window, &st->xgwa); if (st->xgwa.width < 100 || st->xgwa.height < 100) /* tiny window */ { int ss = (st->xgwa.width < st->xgwa.height ? st->xgwa.width : st->xgwa.height); s = ss / 15; if (s < 1) s = 1; } if (st->xgwa.width > st->xgwa.height * 5 || /* weird aspect ratio */ st->xgwa.height > st->xgwa.width * 5) { double r = st->xgwa.width / (double) st->xgwa.height; if (r > 1) { st->subdivisiony = s; st->subdivisionx = s * r; } else { st->subdivisionx = s; st->subdivisiony = s / r; } } else { st->subdivisionx = st->subdivisiony = s; } st->sw = st->xgwa.width / st->subdivisionx; st->sh = st->xgwa.height / st->subdivisiony; st->gw = st->sw ? st->xgwa.width / st->sw : 0; st->gh = st->sh ? st->xgwa.height / st->sh : 0; st->nsquares = st->gw * st->gh; free (st->squares); if (st->nsquares < 1) st->nsquares = 1; st->squares = (square *) calloc (st->nsquares, sizeof(square)); for (y = 0; y < st->gh; y++) for (x = 0; x < st->gw; x++) { square *s = (square *) &st->squares[st->gw * y + x]; s->w = st->sw; s->h = st->sh; s->x = x * st->sw; s->y = y * st->sh; } randomize_square_colors(st->squares, st->nsquares, st->ncolors); if (st->dbuf) { XFreePixmap (dpy, st->ba); XFreePixmap (dpy, st->bb); st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth); st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth); st->b = st->ba; } } static void * popsquares_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); int x, y; double s1, v1, s2, v2 = 0; int h1, h2 = 0; /* Not sure how to use DBEClear */ /* Bool dbeclear_p = get_boolean_resource(dpy, "useDBEClear", "Boolean"); */ XColor fg, bg; XGCValues gcv; st->dpy = dpy; st->window = window; st->delay = get_integer_resource (st->dpy, "delay", "Integer"); st->subdivisionx = get_integer_resource(st->dpy, "subdivision", "Integer"); st->subdivisiony = st->subdivisionx; st->border = get_integer_resource(st->dpy, "border", "Integer"); st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer"); st->twitch = get_boolean_resource(st->dpy, "twitch", "Boolean"); st->dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean"); # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ st->dbuf = False; # endif XGetWindowAttributes (st->dpy, st->window, &st->xgwa); fg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "foreground", "Foreground"); bg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background"); XQueryColor (st->dpy, st->xgwa.colormap, &fg); XQueryColor (st->dpy, st->xgwa.colormap, &bg); st->sw = st->xgwa.width / st->subdivisionx; st->sh = st->xgwa.height / st->subdivisiony; st->gw = st->sw ? st->xgwa.width / st->sw : 0; st->gh = st->sh ? st->xgwa.height / st->sh : 0; st->nsquares = st->gw * st->gh; if (st->nsquares < 1) st->nsquares = 1; if (st->ncolors < 1) st->ncolors = 1; gcv.foreground = fg.pixel; gcv.background = bg.pixel; st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv); st->colors = (XColor *) calloc (st->ncolors, sizeof(XColor)); st->squares = (square *) calloc (st->nsquares, sizeof(square)); 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 (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap, h1, s1, v1, h2, s2, v2, st->colors, &st->ncolors, /* would this be considered a value-result argument? */ True, True, False); if (st->ncolors < 2) { fprintf (stderr, "%s: insufficient colors!\n", progname); exit (1); } for (y = 0; y < st->gh; y++) for (x = 0; x < st->gw; x++) { square *s = (square *) &st->squares[st->gw * y + x]; s->w = st->sw; s->h = st->sh; s->x = x * st->sw; s->y = y * st->sh; } randomize_square_colors(st->squares, st->nsquares, st->ncolors); if (st->dbuf) { #ifdef HAVE_DOUBLE_BUFFER_EXTENSION st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined); st->backb = st->b; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (!st->b) { st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth); st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth); st->b = st->ba; } } else { st->b = st->window; } popsquares_reshape (dpy, window, st, st->xgwa.width, st->xgwa.height); return st; } static unsigned long popsquares_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int x, y; for (y = 0; y < st->gh; y++) for (x = 0; x < st->gw; x++) { square *s = (square *) &st->squares[st->gw * y + x]; XSetForeground (st->dpy, st->gc, st->colors[s->color].pixel); XFillRectangle (st->dpy, st->b, st->gc, s->x, s->y, st->border ? s->w - st->border : s->w, st->border ? s->h - st->border : s->h); s->color++; if (s->color == st->ncolors) { if (st->twitch && ((random() % 4) == 0)) randomize_square_colors (st->squares, st->nsquares, st->ncolors); else s->color = random() % st->ncolors; } } #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->backb) { XdbeSwapInfo info[1]; info[0].swap_window = st->window; info[0].swap_action = XdbeUndefined; XdbeSwapBuffers (st->dpy, info, 1); } else #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (st->dbuf) { XCopyArea (st->dpy, st->b, st->window, st->gc, 0, 0, st->xgwa.width, st->xgwa.height, 0, 0); st->b = (st->b == st->ba ? st->bb : st->ba); } return st->delay; } static Bool popsquares_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void popsquares_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; XFreeGC (dpy, st->gc); free (st->colors); free (st->squares); free (st); } static const char *popsquares_defaults [] = { ".background: #0000FF", ".foreground: #00008B", "*delay: 25000", "*subdivision: 5", "*border: 1", "*ncolors: 128", "*twitch: False", "*doubleBuffer: False", #ifdef HAVE_DOUBLE_BUFFER_EXTENSION "*useDBE: True", "*useDBEClear: True", #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0 }; static XrmOptionDescRec popsquares_options [] = { { "-fg", ".foreground", XrmoptionSepArg, 0}, { "-bg", ".background", XrmoptionSepArg, 0}, { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-subdivision", ".subdivision", XrmoptionSepArg, 0 }, { "-border", ".border", XrmoptionSepArg, 0}, { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, { "-twitch", ".twitch", XrmoptionNoArg, "True" }, { "-no-twitch", ".twitch", XrmoptionNoArg, "False" }, { "-db", ".doubleBuffer", XrmoptionNoArg, "True" }, { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("PopSquares", popsquares)