2 * Substrate (dragorn@kismetwireless.net)
3 * Directly ported code from complexification.net Substrate art
4 * http://complexification.net/gallery/machines/substrate/applet_s/substrate_s.pde
8 * Albuquerque, New Mexico
13 * 1.1 dragorn Jan 04 2005 Fixed some indenting, typo in errors for parsing
15 * 1.1 dagraz Jan 04 2005 Added option for circular cracks (David Agraz)
16 * Cleaned up issues with timeouts in start_crack (DA)
17 * 1.0 dragorn Oct 10 2004 First port done
19 * Directly based the hacks of:
21 * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
23 * Permission to use, copy, modify, distribute, and sell this software and its
24 * documentation for any purpose is hereby granted without fee, provided that
25 * the above copyright notice appear in all copies and that both that
26 * copyright notice and this permission notice appear in supporting
27 * documentation. No representations are made about the suitability of this
28 * software for any purpose. It is provided "as is" without express or
33 #include "screenhack.h"
34 #include <X11/Xutil.h>
40 #define MAX_WIDTH SHRT_MAX
49 /* this program goes faster if some functions are inline. The following is
50 * borrowed from ifs.c */
51 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
58 /* Raw colormap extracted from pollockEFF.gif */
59 char *rgb_colormap[] = {
60 "rgb:20/1F/21", "rgb:26/2C/2E", "rgb:35/26/26", "rgb:37/2B/27",
61 "rgb:30/2C/2E", "rgb:39/2B/2D", "rgb:32/32/29", "rgb:3F/32/29",
62 "rgb:38/32/2E", "rgb:2E/33/3D", "rgb:33/3A/3D", "rgb:47/33/29",
63 "rgb:40/39/2C", "rgb:40/39/2E", "rgb:47/40/2C", "rgb:47/40/2E",
64 "rgb:4E/40/2C", "rgb:4F/40/2E", "rgb:4E/47/38", "rgb:58/40/37",
65 "rgb:65/47/2D", "rgb:6D/5D/3D", "rgb:74/55/30", "rgb:75/55/32",
66 "rgb:74/5D/32", "rgb:74/64/33", "rgb:7C/6C/36", "rgb:52/31/52",
67 "rgb:44/48/42", "rgb:4C/56/47", "rgb:65/5D/45", "rgb:6D/5D/44",
68 "rgb:6C/5D/4E", "rgb:74/6C/43", "rgb:7C/6C/42", "rgb:7C/6C/4B",
69 "rgb:6B/73/4B", "rgb:73/73/4B", "rgb:7B/7B/4A", "rgb:6B/6C/55",
70 "rgb:69/6D/5E", "rgb:7B/6C/5D", "rgb:6B/73/53", "rgb:6A/74/5D",
71 "rgb:72/7B/52", "rgb:7B/7B/52", "rgb:57/74/6E", "rgb:68/74/66",
72 "rgb:9C/54/2B", "rgb:9D/54/32", "rgb:9D/5B/35", "rgb:93/6B/36",
73 "rgb:AA/73/30", "rgb:C4/5A/27", "rgb:D9/52/23", "rgb:D8/5A/20",
74 "rgb:DB/5A/23", "rgb:E5/70/37", "rgb:83/6C/4B", "rgb:8C/6B/4B",
75 "rgb:82/73/5C", "rgb:93/73/52", "rgb:81/7B/63", "rgb:81/7B/6D",
76 "rgb:92/7B/63", "rgb:D9/89/3B", "rgb:E4/98/32", "rgb:DF/A1/33",
77 "rgb:E5/A0/37", "rgb:F0/AB/3B", "rgb:8A/8A/59", "rgb:B2/9A/58",
78 "rgb:89/82/6B", "rgb:9A/82/62", "rgb:88/8B/7C", "rgb:90/9A/7A",
79 "rgb:A2/82/62", "rgb:A1/8A/69", "rgb:A9/99/68", "rgb:99/A1/60",
80 "rgb:99/A1/68", "rgb:CA/81/48", "rgb:EB/8D/43", "rgb:C2/91/60",
81 "rgb:C2/91/68", "rgb:D1/A9/77", "rgb:C9/B9/7F", "rgb:F0/E2/7B",
82 "rgb:9F/92/8B", "rgb:C0/B9/99", "rgb:E6/B8/8F", "rgb:C8/C1/87",
83 "rgb:E0/C8/86", "rgb:F2/CC/85", "rgb:F5/DA/83", "rgb:EC/DE/9D",
84 "rgb:F5/D2/94", "rgb:F5/DA/94", "rgb:F4/E7/84", "rgb:F4/E1/8A",
85 "rgb:F4/E1/93", "rgb:E7/D8/A7", "rgb:F1/D4/A5", "rgb:F1/DC/A5",
86 "rgb:F4/DB/AD", "rgb:F1/DC/AE", "rgb:F4/DB/B5", "rgb:F5/DB/BD",
87 "rgb:F4/E2/AD", "rgb:F5/E9/AD", "rgb:F4/E3/BE", "rgb:F5/EA/BE",
88 "rgb:F7/F0/B6", "rgb:D9/D1/C1", "rgb:E0/D0/C0", "rgb:E7/D8/C0",
89 "rgb:F1/DD/C6", "rgb:E8/E1/C0", "rgb:F3/ED/C7", "rgb:F6/EC/CE",
90 "rgb:F8/F2/C7", "rgb:EF/EF/D0", 0
94 /* Synthesis of data from Crack:: and SandPainter:: */
97 float ys, xs, t_inc; /* for curvature calculations */
101 unsigned long sandcolor;
114 unsigned int initial_cracks;
117 unsigned int max_num;
119 int grains; /* number of grains in the sand painting */
123 crack *cracks; /* grid of cracks */
124 int *cgrid; /* grid of actual crack placement */
126 /* Raw map of pixels we need to keep for alpha blending */
127 unsigned long int *off_img;
131 unsigned long *parsedcolors;
132 unsigned long fgcolor;
133 unsigned long bgcolor;
138 unsigned int wireframe;
142 *xrealloc(void *p, size_t size)
145 if ((ret = realloc(p, size)) == NULL) {
146 fprintf(stderr, "%s: out of memory\n", progname);
155 struct field *f = xrealloc(NULL, sizeof(struct field));
158 f->initial_cracks = 0;
165 f->parsedcolors = NULL;
172 f->circle_percent = 0;
176 /* Quick references to pixels in the offscreen map and in the crack grid */
177 #define ref_pixel(f, x, y) ((f)->off_img[(y) * (f)->width + (x)])
178 #define ref_cgrid(f, x, y) ((f)->cgrid[(y) * (f)->width + (x)])
180 inline void start_crack(struct field *f, crack *cr) {
181 /* synthesis of Crack::findStart() and crack::startCrack() */
188 /* shift until crack is found */
189 while ((!found) && (timeout++ < 10000)) {
190 px = (int) (random() % f->width);
191 py = (int) (random() % f->height);
193 if (ref_cgrid(f, px, py) < 10000)
198 /* We timed out. Use our default values */
201 ref_cgrid(f, px, py) = cr->t;
205 a = ref_cgrid(f, px, py);
207 if ((random() % 100) < 50) {
208 /* conversion of the java int(random(-2, 2.1)) */
209 a -= 90 + (frand(4.1) - 2.0);
211 a += 90 + (frand(4.1) - 2.0);
214 if ((random() % 100) < f->circle_percent) {
215 float r; /* radius */
219 cr->degrees_drawn = 0;
221 r = 10 + (random() % ((f->width + f->height) / 2));
223 if ((random() % 100) < 50) {
227 /* arc length = r * theta => theta = arc length / r */
228 radian_inc = STEP / r;
229 cr->t_inc = radian_inc * 360 / 2 / M_PI;
231 cr->ys = r * sin(radian_inc);
232 cr->xs = r * ( 1 - cos(radian_inc));
239 /* Condensed from Crack::startCrack */
240 cr->x = px + ((float) 0.61 * cos(a * M_PI / 180));
241 cr->y = py + ((float) 0.61 * sin(a * M_PI / 180));
246 inline void make_crack(struct field *f) {
249 if (f->num < f->max_num) {
250 /* make a new crack */
251 f->cracks = (crack *) xrealloc(f->cracks, sizeof(crack) * (f->num + 1));
253 cr = &(f->cracks[f->num]);
256 cr->sandg = (frand(0.2) - 0.01);
257 cr->sandcolor = f->parsedcolors[random() % f->numcolors];
258 cr->crack_num = f->num;
260 cr->degrees_drawn = 0;
262 /* We could use these values in the timeout case of start_crack */
264 cr->x = random() % f->width;
265 cr->y = random() % f->height;
266 cr->t = random() % 360;
275 inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) {
279 *g = (c & 0xff00) >> 8;
280 *r = (c & 0xff0000) >> 16;
284 *g = ((c >> 5) & 0x3f) << 2;
285 *r = ((c >> 11) & 0x1f) << 3;
286 *b = (c & 0x1f) << 3;
289 *g = ((c >> 5) & 0x1f) << 3;
290 *r = ((c >> 10) & 0x1f) << 3;
291 *b = (c & 0x1f) << 3;
296 inline unsigned long rgb2point(int depth, int r, int g, int b) {
297 unsigned long ret = 0;
303 ret |= (r << 16) | (g << 8) | b;
306 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
309 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
316 /* alpha blended point drawing -- this is Not Right and will likely fail on
317 * non-intel platforms as it is now, needs fixing */
318 inline unsigned long trans_point(int x1, int y1, unsigned long myc, float a,
320 if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
322 ref_pixel(f, x1, y1) = myc;
324 int or = 0, og = 0, ob = 0;
325 int r = 0, g = 0, b = 0;
329 c = ref_pixel(f, x1, y1);
331 point2rgb(f->visdepth, c, &or, &og, &ob);
332 point2rgb(f->visdepth, myc, &r, &g, &b);
334 nr = or + (r - or) * a;
335 ng = og + (g - og) * a;
336 nb = ob + (b - ob) * a;
338 c = rgb2point(f->visdepth, nr, ng, nb);
340 ref_pixel(f, x1, y1) = c;
349 inline void region_color(Display *dpy, Window window, GC fgc, struct field *f,
351 /* synthesis of Crack::regionColor() and SandPainter::render() */
364 /* move perpendicular to crack */
365 rx += (0.81 * sin(cr->t * M_PI/180));
366 ry -= (0.81 * cos(cr->t * M_PI/180));
371 if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
373 if (f->cgrid[cy * f->width + cx] > 10000) {
383 /* SandPainter stuff here */
386 cr->sandg += (frand(0.1) - 0.050);
392 if (cr->sandg > maxg)
397 /* Lay down grains of sand */
398 w = cr->sandg / (grains - 1);
400 for (i = 0; i < grains; i++) {
401 drawx = (cr->x + (rx - cr->x) * sin(cr->sandp + sin((float) i * w)));
402 drawy = (cr->y + (ry - cr->y) * sin(cr->sandp + sin((float) i * w)));
405 c = trans_point(drawx, drawy, cr->sandcolor, (0.1 - i / (grains * 10.0)), f);
407 XSetForeground(dpy, fgc, c);
408 XDrawPoint(dpy, window, fgc, (int) drawx, (int) drawy);
409 XSetForeground(dpy, fgc, f->fgcolor);
413 void build_substrate(struct field *f) {
431 /* erase the crack grid */
432 f->cgrid = (int *) xrealloc(f->cgrid, sizeof(int) * f->height * f->width);
433 memset(f->cgrid, 10001, f->height * f->width * sizeof(int));
435 /* Not necessary now that make_crack ensures we have usable default
436 * values in start_crack's timeout case
437 * make random crack seeds *
438 for (tx = 0; tx < 16; tx++) {
439 ty = (int) (random() % (f->width * f->height - 1));
440 f->cgrid[ty] = (int) random() % 360;
444 /* make the initial cracks */
445 for (tx = 0; tx < f->initial_cracks; tx++)
450 inline void movedrawcrack(Display *dpy, Window window, GC fgc, struct field *f,
452 /* Basically Crack::move() */
455 crack *cr = &(f->cracks[cracknum]);
457 /* continue cracking */
459 cr->x += ((float) STEP * cos(cr->t * M_PI/180));
460 cr->y += ((float) STEP * sin(cr->t * M_PI/180));
468 cr->x += ((float) cr->ys * cos(cr->t * M_PI/180));
469 cr->y += ((float) cr->ys * sin(cr->t * M_PI/180));
471 cr->x += ((float) cr->xs * cos(cr->t * M_PI/180 - M_PI / 2));
472 cr->x += ((float) cr->xs * sin(cr->t * M_PI/180 - M_PI / 2));
475 cr->degrees_drawn += abs(cr->t_inc);
479 /* modification of random(-0.33,0.33) */
480 cx = (int) (cr->x + (frand(0.66) - 0.33));
481 cy = (int) (cr->y + (frand(0.66) - 0.33));
484 if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
485 /* draw sand painter if we're not wireframe */
487 region_color(dpy, window, fgc, f, cr);
489 /* draw fgcolor crack */
490 ref_pixel(f, cx, cy) = f->fgcolor;
491 XDrawPoint(dpy, window, fgc, cx, cy);
493 if ( cr->curved && (cr->degrees_drawn > 360) ) {
494 /* completed the circle, stop cracking */
495 start_crack(f, cr); /* restart ourselves */
496 make_crack(f); /* generate a new crack */
499 else if ((f->cgrid[cy * f->width + cx] > 10000) ||
500 (abs(f->cgrid[cy * f->width + cx] - cr->t) < 5)) {
501 /* continue cracking */
502 f->cgrid[cy * f->width + cx] = (int) cr->t;
503 } else if (abs(f->cgrid[cy * f->width + cx] - cr->t) > 2) {
504 /* crack encountered (not self), stop cracking */
505 start_crack(f, cr); /* restart ourselves */
506 make_crack(f); /* generate a new crack */
509 /* out of bounds, stop cracking */
511 /* need these in case of timeout in start_crack */
512 cr->x = random() % f->width;
513 cr->y = random() % f->height;
514 cr->t = random() % 360;
516 start_crack(f, cr); /* restart ourselves */
517 make_crack(f); /* generate a new crack */
522 char *progclass = "Substrate";
525 ".background: white",
526 ".foreground: black",
529 "*growthDelay: 18000",
537 XrmOptionDescRec options[] = {
538 {"-background", ".background", XrmoptionSepArg, 0},
539 {"-foreground", ".foreground", XrmoptionSepArg, 0},
540 {"-wireframe", ".wireFrame", XrmoptionNoArg, "true"},
541 {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
542 {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
543 {"-initial-cracks", ".initialCracks", XrmoptionSepArg, 0},
544 {"-max-cracks", ".maxCracks", XrmoptionSepArg, 0},
545 {"-sand-grains", ".sandGrains", XrmoptionSepArg, 0},
546 {"-circle-percent", ".circlePercent", XrmoptionSepArg, 0},
550 void build_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc,
557 f->off_img = (unsigned long *) xrealloc(f->off_img, sizeof(unsigned long) *
558 f->width * f->height);
560 memset(f->off_img, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
563 void screenhack(Display * dpy, Window window)
565 struct field *f = init_field();
567 unsigned int max_cycles = 0;
568 int growth_delay = 0;
573 XWindowAttributes xgwa;
576 growth_delay = (get_integer_resource("growthDelay", "Integer"));
577 max_cycles = (get_integer_resource("maxCycles", "Integer"));
578 f->initial_cracks = (get_integer_resource("initialCracks", "Integer"));
579 f->max_num = (get_integer_resource("maxCracks", "Integer"));
580 f->wireframe = (get_boolean_resource("wireFrame", "Boolean"));
581 f->grains = (get_integer_resource("sandGrains", "Integer"));
582 f->circle_percent = (get_integer_resource("circlePercent", "Integer"));
584 if (f->initial_cracks <= 2) {
585 fprintf(stderr, "%s: Initial cracks must be greater than 2\n", progname);
589 if (f->max_num <= 10) {
590 fprintf(stderr, "%s: Maximum number of cracks must be less than 10\n",
595 if (f->circle_percent < 0) {
596 fprintf(stderr, "%s: circle percent must be at least 0\n", progname);
600 if (f->circle_percent > 100) {
601 fprintf(stderr, "%s: circle percent must be less than 100\n", progname);
605 XGetWindowAttributes(dpy, window, &xgwa);
607 f->height = xgwa.height;
608 f->width = xgwa.width;
609 f->visdepth = xgwa.depth;
611 /* Count the colors in our map and assign them in a horrifically inefficient
612 * manner but it only happens once */
613 while (rgb_colormap[f->numcolors] != NULL) {
614 f->parsedcolors = (unsigned long *) xrealloc(f->parsedcolors,
615 sizeof(unsigned long) *
617 if (!XParseColor(dpy, xgwa.colormap, rgb_colormap[f->numcolors], &tmpcolor)) {
618 fprintf(stderr, "%s: couldn't parse color %s\n", progname,
619 rgb_colormap[f->numcolors]);
623 if (!XAllocColor(dpy, xgwa.colormap, &tmpcolor)) {
624 fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
625 rgb_colormap[f->numcolors]);
629 f->parsedcolors[f->numcolors] = tmpcolor.pixel;
634 gcv.foreground = get_pixel_resource("foreground", "Foreground",
636 gcv.background = get_pixel_resource("background", "Background",
638 fgc = XCreateGC(dpy, window, GCForeground, &gcv);
640 f->fgcolor = gcv.foreground;
641 f->bgcolor = gcv.background;
643 /* Initialize stuff */
644 build_img(dpy, window, xgwa, fgc, f);
648 if ((f->cycles % 10) == 0) {
649 /* Restart if the window size changes */
650 XGetWindowAttributes(dpy, window, &xgwa);
652 if (f->height != xgwa.height || f->width != xgwa.width) {
653 f->height = xgwa.height;
654 f->width = xgwa.width;
655 f->visdepth = xgwa.depth;
658 build_img(dpy, window, xgwa, fgc, f);
659 XSetForeground(dpy, fgc, gcv.background);
660 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
661 XSetForeground(dpy, fgc, gcv.foreground);
665 for (tempx = 0; tempx < f->num; tempx++) {
666 movedrawcrack(dpy, window, fgc, f, tempx);
673 screenhack_handle_events(dpy);
675 if (f->cycles >= max_cycles && max_cycles != 0) {
677 build_img(dpy, window, xgwa, fgc, f);
678 XSetForeground(dpy, fgc, gcv.background);
679 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
680 XSetForeground(dpy, fgc, gcv.foreground);
684 usleep(growth_delay);