/* xscreensaver, Copyright (c) 1999-2018 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. */ #include #include "screenhack.h" #include "alpha.h" #ifdef HAVE_DOUBLE_BUFFER_EXTENSION # include "xdbe.h" #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ #define countof(x) (sizeof(x)/sizeof(*(x))) #define ABS(x) ((x)<0?-(x):(x)) struct state { Display *dpy; Window window; Bool transparent_p; int nplanes; unsigned long base_pixel, *plane_masks; int count; int delay; int ncolors; Bool dbuf; XColor *colors; GC erase_gc; struct throbber **throbbers; XWindowAttributes xgwa; Pixmap b, ba, bb; /* double-buffer to reduce flicker */ # ifdef HAVE_DOUBLE_BUFFER_EXTENSION Bool dbeclear_p; XdbeBackBuffer backb; # endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ }; struct throbber { int x, y; int size; int max_size; int thickness; int speed; int fuse; GC gc; void (*draw) (struct state *, Drawable, struct throbber *); }; static void draw_star (struct state *st, Drawable w, struct throbber *t) { XPoint points[11]; int x = t->x; int y = t->y; /* The following constant is really: sqrt(5 - sqrt(5)) / sqrt(25 - 11 * sqrt(5)) Reference: http://mathworld.wolfram.com/Pentagram.html */ int s = t->size * 2.6180339887498985; int s2 = t->size; double c = M_PI * 2; double o = -M_PI / 2; points[0].x = x + s * cos(o + 0.0*c); points[0].y = y + s * sin(o + 0.0*c); points[1].x = x + s2 * cos(o + 0.1*c); points[1].y = y + s2 * sin(o + 0.1*c); points[2].x = x + s * cos(o + 0.2*c); points[2].y = y + s * sin(o + 0.2*c); points[3].x = x + s2 * cos(o + 0.3*c); points[3].y = y + s2 * sin(o + 0.3*c); points[4].x = x + s * cos(o + 0.4*c); points[4].y = y + s * sin(o + 0.4*c); points[5].x = x + s2 * cos(o + 0.5*c); points[5].y = y + s2 * sin(o + 0.5*c); points[6].x = x + s * cos(o + 0.6*c); points[6].y = y + s * sin(o + 0.6*c); points[7].x = x + s2 * cos(o + 0.7*c); points[7].y = y + s2 * sin(o + 0.7*c); points[8].x = x + s * cos(o + 0.8*c); points[8].y = y + s * sin(o + 0.8*c); points[9].x = x + s2 * cos(o + 0.9*c); points[9].y = y + s2 * sin(o + 0.9*c); points[10] = points[0]; XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin); } static void draw_circle (struct state *st, Drawable w, struct throbber *t) { XDrawArc (st->dpy, w, t->gc, t->x - t->size / 2, t->y - t->size / 2, t->size, t->size, 0, 360*64); } static void draw_hlines (struct state *st, Drawable w, struct throbber *t) { XDrawLine (st->dpy, w, t->gc, 0, t->y - t->size, t->max_size, t->y - t->size); XDrawLine (st->dpy, w, t->gc, 0, t->y + t->size, t->max_size, t->y + t->size); } static void draw_vlines (struct state *st, Drawable w, struct throbber *t) { XDrawLine (st->dpy, w, t->gc, t->x - t->size, 0, t->x - t->size, t->max_size); XDrawLine (st->dpy, w, t->gc, t->x + t->size, 0, t->x + t->size, t->max_size); } static void draw_corners (struct state *st, Drawable w, struct throbber *t) { int s = (t->size + t->thickness) / 2; XPoint points[3]; if (t->y > s) { points[0].x = 0; points[0].y = t->y - s; points[1].x = t->x - s; points[1].y = t->y - s; points[2].x = t->x - s; points[2].y = 0; XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin); points[0].x = t->x + s; points[0].y = 0; points[1].x = t->x + s; points[1].y = t->y - s; points[2].x = t->max_size; points[2].y = t->y - s; XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin); } if (t->x > s) { points[0].x = 0; points[0].y = t->y + s; points[1].x = t->x - s; points[1].y = t->y + s; points[2].x = t->x - s; points[2].y = t->max_size; XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin); points[0].x = t->x + s; points[0].y = t->max_size; points[1].x = t->x + s; points[1].y = t->y + s; points[2].x = t->max_size; points[2].y = t->y + s; XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin); } } static struct throbber * make_throbber (struct state *st, Drawable d, int w, int h, unsigned long pixel) { XGCValues gcv; unsigned long flags; struct throbber *t = (struct throbber *) malloc (sizeof (*t)); t->x = w / 2; t->y = h / 2; t->max_size = (w > h ? w : h); t->speed = get_integer_resource (st->dpy, "speed", "Speed"); t->fuse = 1 + (random() % 4); t->thickness = get_integer_resource (st->dpy, "thickness", "Thickness"); if (st->xgwa.width > 2560) t->thickness *= 3; /* Retina displays */ if (t->speed < 0) t->speed = -t->speed; t->speed += (((random() % t->speed) / 2) - (t->speed / 2)); if (t->speed > 0) t->speed = -t->speed; flags = GCForeground; # ifndef HAVE_JWXYZ if (st->transparent_p) { gcv.foreground = ~0L; gcv.plane_mask = st->base_pixel | st->plane_masks[random() % st->nplanes]; flags |= GCPlaneMask; } else # endif /* !HAVE_JWXYZ */ { gcv.foreground = pixel; } gcv.line_width = t->thickness; gcv.cap_style = CapProjecting; gcv.join_style = JoinMiter; flags |= (GCLineWidth | GCCapStyle | GCJoinStyle); t->gc = XCreateGC (st->dpy, d, flags, &gcv); # ifdef HAVE_JWXYZ if (st->transparent_p) { /* give a non-opaque alpha to the color */ unsigned long pixel = gcv.foreground; unsigned long amask = BlackPixelOfScreen (st->xgwa.screen); unsigned long a = (0xCCCCCCCC & amask); pixel = (pixel & (~amask)) | a; jwxyz_XSetAlphaAllowed (st->dpy, t->gc, True); XSetForeground (st->dpy, t->gc, pixel); } # endif /* HAVE_JWXYZ */ switch (random() % 11) { case 0: case 1: case 2: case 3: t->draw = draw_star; break; case 4: case 5: case 6: case 7: t->draw = draw_circle; break; case 8: t->draw = draw_hlines; break; case 9: t->draw = draw_vlines; break; case 10: t->draw = draw_corners; break; default: abort(); break; } if (t->draw == draw_circle) t->max_size *= 1.5; if (random() % 4) t->size = t->max_size; else t->size = t->thickness, t->speed = -t->speed; return t; } static int throb (struct state *st, Drawable window, struct throbber *t) { t->size += t->speed; if (t->size <= (t->thickness / 2)) { t->speed = -t->speed; t->size += (t->speed * 2); } else if (t->size > t->max_size) { t->speed = -t->speed; t->size += (t->speed * 2); t->fuse--; } if (t->fuse <= 0) { XFreeGC (st->dpy, t->gc); memset (t, 0, sizeof(*t)); free (t); return -1; } else { t->draw (st, window, t); return 0; } } static void * deluxe_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); XGCValues gcv; int i; st->dpy = dpy; st->window = window; st->count = get_integer_resource (st->dpy, "count", "Integer"); st->delay = get_integer_resource (st->dpy, "delay", "Integer"); st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer"); st->dbuf = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean"); # ifdef HAVE_DOUBLE_BUFFER_EXTENSION st->dbeclear_p = get_boolean_resource (st->dpy, "useDBEClear", "Boolean"); #endif # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ st->dbuf = False; # endif XGetWindowAttributes (st->dpy, st->window, &st->xgwa); st->transparent_p = get_boolean_resource(st->dpy, "transparent", "Transparent"); st->colors = (XColor *) calloc (sizeof(*st->colors), st->ncolors); if (get_boolean_resource(st->dpy, "mono", "Boolean")) { MONO: st->ncolors = 1; st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap, "foreground", "Foreground"); } #ifndef HAVE_JWXYZ else if (st->transparent_p) { st->nplanes = get_integer_resource (st->dpy, "planes", "Planes"); if (st->nplanes <= 0) st->nplanes = (random() % (st->xgwa.depth-2)) + 2; allocate_alpha_colors (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap, &st->nplanes, True, &st->plane_masks, &st->base_pixel); if (st->nplanes <= 1) { # if 0 fprintf (stderr, "%s: couldn't allocate any color planes; turning transparency off.\n", progname); # endif st->transparent_p = False; goto COLOR; } } #endif /* !HAVE_JWXYZ */ else { #ifndef HAVE_JWXYZ COLOR: #endif make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap, st->colors, &st->ncolors, True, True, 0, True); if (st->ncolors < 2) goto MONO; } if (st->dbuf) { #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->dbeclear_p) st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeBackground); else 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; } st->throbbers = (struct throbber **) calloc (st->count, sizeof(struct throbber *)); for (i = 0; i < st->count; i++) st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height, st->colors[random() % st->ncolors].pixel); gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background"); st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &gcv); if (st->ba) XFillRectangle (st->dpy, st->ba, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height); if (st->bb) XFillRectangle (st->dpy, st->bb, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height); return st; } static unsigned long deluxe_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int i; #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (!st->dbeclear_p || !st->backb) #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height); for (i = 0; i < st->count; i++) if (throb (st, st->b, st->throbbers[i]) < 0) st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height, st->colors[random() % st->ncolors].pixel); #ifdef HAVE_DOUBLE_BUFFER_EXTENSION if (st->backb) { XdbeSwapInfo info[1]; info[0].swap_window = st->window; info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined); XdbeSwapBuffers (st->dpy, info, 1); } else #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ if (st->dbuf) { XCopyArea (st->dpy, st->b, st->window, st->erase_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 void deluxe_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; if (! st->dbuf) { /* #### more complicated if we have a back buffer... */ int i; XGetWindowAttributes (st->dpy, st->window, &st->xgwa); XClearWindow (dpy, window); for (i = 0; i < st->count; i++) if (st->throbbers[i]) st->throbbers[i]->fuse = 0; } } static Bool deluxe_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void deluxe_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int i; XFreeGC (dpy, st->erase_gc); if (st->ba) XFreePixmap (dpy, st->ba); if (st->bb) XFreePixmap (dpy, st->bb); if (st->plane_masks) free (st->plane_masks); for (i = 0; i < st->count; i++) if (st->throbbers[i]) { XFreeGC (dpy, st->throbbers[i]->gc); free (st->throbbers[i]); } free (st->throbbers); free (st->colors); free (st); } static const char *deluxe_defaults [] = { ".background: black", ".foreground: white", "*delay: 10000", "*count: 5", "*thickness: 50", "*speed: 15", "*ncolors: 20", "*transparent: True", "*doubleBuffer: True", #ifdef HAVE_DOUBLE_BUFFER_EXTENSION "*useDBE: True", "*useDBEClear: True", #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0 }; static XrmOptionDescRec deluxe_options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-thickness", ".thickness", XrmoptionSepArg, 0 }, { "-count", ".count", XrmoptionSepArg, 0 }, { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, { "-speed", ".speed", XrmoptionSepArg, 0 }, { "-transparent", ".transparent", XrmoptionNoArg, "True" }, { "-no-transparent", ".transparent", XrmoptionNoArg, "False" }, { "-opaque", ".transparent", XrmoptionNoArg, "False" }, { "-no-opaque", ".transparent", XrmoptionNoArg, "True" }, { "-db", ".doubleBuffer", XrmoptionNoArg, "True" }, { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("Deluxe", deluxe)