1 | /* imsmap, Copyright (c) 1992 Juergen Nickelsen <nickel@cs.tu-berlin.de> |
---|
2 | * Derived from code by Markus Schirmer, TU Berlin. |
---|
3 | * |
---|
4 | * Permission to use, copy, modify, distribute, and sell this software and its |
---|
5 | * documentation for any purpose is hereby granted without fee, provided that |
---|
6 | * the above copyright notice appear in all copies and that both that |
---|
7 | * copyright notice and this permission notice appear in supporting |
---|
8 | * documentation. No representations are made about the suitability of this |
---|
9 | * software for any purpose. It is provided "as is" without express or |
---|
10 | * implied warranty. |
---|
11 | * |
---|
12 | * Revision History: |
---|
13 | * 24-aug-92: jwz: hacked. |
---|
14 | * 17-May-97: jwz: hacked more. |
---|
15 | */ |
---|
16 | |
---|
17 | #include <stdio.h> |
---|
18 | #include <math.h> |
---|
19 | #include <time.h> |
---|
20 | #include <sys/time.h> /* for gettimeofday() */ |
---|
21 | |
---|
22 | #include <X11/Xlib.h> |
---|
23 | #include <X11/Xutil.h> |
---|
24 | |
---|
25 | #include "screenhack.h" |
---|
26 | |
---|
27 | #define NSTEPS 7 |
---|
28 | #define COUNT (1 << NSTEPS) |
---|
29 | #define CELL(c, r) cell[((unsigned int)(c)) + ((unsigned int) (r)) * xmax] |
---|
30 | |
---|
31 | static enum imsmap_mode { MODE_H, MODE_S, MODE_V, MODE_RANDOM } mode; |
---|
32 | |
---|
33 | static GC gc, gc2; |
---|
34 | static XWindowAttributes xgwa; |
---|
35 | |
---|
36 | #if defined(sun) && !__STDC__ /* sun cc doesn't know "signed char" */ |
---|
37 | #define signed /**/ |
---|
38 | #endif |
---|
39 | |
---|
40 | static Colormap cmap; |
---|
41 | static int ncolors; |
---|
42 | static XColor *colors; |
---|
43 | static Bool cycle_p; |
---|
44 | static int cycle_direction; |
---|
45 | static Bool extra_krinkly_p; |
---|
46 | |
---|
47 | static int delay, cycle_delay; |
---|
48 | static signed char *cell = NULL; |
---|
49 | static int xmax, ymax; |
---|
50 | static int iterations; |
---|
51 | |
---|
52 | static void |
---|
53 | init_map (Display *dpy, Window window) |
---|
54 | { |
---|
55 | unsigned long fg_pixel = 0, bg_pixel = 0; |
---|
56 | int fg_h, bg_h; |
---|
57 | double fg_s, fg_v, bg_s, bg_v; |
---|
58 | |
---|
59 | enum imsmap_mode this_mode; |
---|
60 | static Bool rv_p; |
---|
61 | |
---|
62 | XGCValues gcv; |
---|
63 | |
---|
64 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
65 | cmap = xgwa.colormap; |
---|
66 | |
---|
67 | if (!ncolors) |
---|
68 | { |
---|
69 | char *mode_str = get_string_resource ("mode", "Mode"); |
---|
70 | rv_p = get_boolean_resource ("reverseVideo", "ReverseVideo"); |
---|
71 | cycle_p = get_boolean_resource ("cycle", "Cycle"); |
---|
72 | ncolors = get_integer_resource ("ncolors", "Integer"); |
---|
73 | delay = get_integer_resource ("delay", "Integer"); |
---|
74 | cycle_delay = get_integer_resource ("cycleDelay", "Integer"); |
---|
75 | iterations = get_integer_resource ("iterations", "Integer"); |
---|
76 | if (iterations < 0) iterations = 0; |
---|
77 | else if (iterations > 7) iterations = 7; |
---|
78 | |
---|
79 | if (ncolors <= 2) ncolors = 0; |
---|
80 | if (ncolors == 0) mono_p = True; |
---|
81 | if (ncolors > 255) ncolors = 255; /* too many look bad */ |
---|
82 | |
---|
83 | fg_pixel = get_pixel_resource ("background", "Background", dpy, cmap); |
---|
84 | bg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap); |
---|
85 | |
---|
86 | if (fg_pixel == bg_pixel) |
---|
87 | { |
---|
88 | XColor black, white; |
---|
89 | black.red = black.green = black.blue = 0; |
---|
90 | white.red = white.green = white.blue = 0xFFFF; |
---|
91 | black.flags = white.flags = DoRed|DoGreen|DoBlue; |
---|
92 | XAllocColor(dpy, cmap, &black); |
---|
93 | XAllocColor(dpy, cmap, &white); |
---|
94 | if (bg_pixel == black.pixel) |
---|
95 | fg_pixel = white.pixel; |
---|
96 | else |
---|
97 | fg_pixel = black.pixel; |
---|
98 | } |
---|
99 | |
---|
100 | if (mono_p) cycle_p = False; |
---|
101 | |
---|
102 | gcv.foreground = fg_pixel; |
---|
103 | gcv.background = bg_pixel; |
---|
104 | gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv); |
---|
105 | gcv.foreground = bg_pixel; |
---|
106 | gc2 = XCreateGC (dpy, window, GCForeground, &gcv); |
---|
107 | |
---|
108 | if (!mode_str || !strcmp (mode_str, "random")) |
---|
109 | mode = MODE_RANDOM; |
---|
110 | else if (!strcmp (mode_str, "h") || !strcmp (mode_str, "hue")) |
---|
111 | mode = MODE_H; |
---|
112 | else if (!strcmp (mode_str, "s") || !strcmp (mode_str, "saturation")) |
---|
113 | mode = MODE_S; |
---|
114 | else if (!strcmp (mode_str, "v") || !strcmp (mode_str, "value")) |
---|
115 | mode = MODE_V; |
---|
116 | else |
---|
117 | { |
---|
118 | fprintf (stderr, |
---|
119 | "%s: mode must be hue, saturation, value, or random, not \"%s\"\n", |
---|
120 | progname, mode_str); |
---|
121 | mode = MODE_RANDOM; |
---|
122 | } |
---|
123 | } |
---|
124 | |
---|
125 | this_mode = mode; |
---|
126 | if (!mono_p && mode == MODE_RANDOM) |
---|
127 | switch (random () % 6) { |
---|
128 | case 0: this_mode = MODE_H; break; |
---|
129 | case 1: this_mode = MODE_S; break; |
---|
130 | case 2: this_mode = MODE_V; break; |
---|
131 | default: break; |
---|
132 | } |
---|
133 | |
---|
134 | if (mono_p) |
---|
135 | extra_krinkly_p = !(random() % 15); |
---|
136 | else |
---|
137 | extra_krinkly_p = !(random() % 5); |
---|
138 | |
---|
139 | if (!mono_p) |
---|
140 | { |
---|
141 | double distance, fg_H, bg_H, dh; |
---|
142 | |
---|
143 | RETRY: |
---|
144 | fg_h = random() % 360; |
---|
145 | fg_s = frand(1.0); |
---|
146 | fg_v = frand(1.0); |
---|
147 | |
---|
148 | bg_h = fg_h; |
---|
149 | bg_s = fg_s; |
---|
150 | bg_v = fg_v; |
---|
151 | |
---|
152 | switch (this_mode) |
---|
153 | { |
---|
154 | case MODE_H: |
---|
155 | bg_h = random() % 360; |
---|
156 | if (fg_v < 0.4) |
---|
157 | goto RETRY; |
---|
158 | distance = fg_h - bg_h; |
---|
159 | if (distance < 0) |
---|
160 | distance = -distance; |
---|
161 | if (distance > 360) |
---|
162 | distance = 180 - (distance - 180); |
---|
163 | if (distance < 30) |
---|
164 | goto RETRY; |
---|
165 | break; |
---|
166 | |
---|
167 | case MODE_S: |
---|
168 | bg_s = frand(1.0); |
---|
169 | if (fg_v < 0.4) |
---|
170 | goto RETRY; |
---|
171 | distance = fg_s - bg_s; |
---|
172 | if (distance < 0) |
---|
173 | distance = -distance; |
---|
174 | if (distance < 0.2) |
---|
175 | goto RETRY; |
---|
176 | break; |
---|
177 | |
---|
178 | case MODE_V: |
---|
179 | bg_v = frand(1.0); |
---|
180 | distance = fg_v - bg_v; |
---|
181 | if (distance < 0) |
---|
182 | distance = -distance; |
---|
183 | if (distance < 0.4) |
---|
184 | goto RETRY; |
---|
185 | break; |
---|
186 | |
---|
187 | default: |
---|
188 | bg_h = random() % 360; |
---|
189 | bg_s = frand(1.0); |
---|
190 | bg_v = frand(1.0); |
---|
191 | |
---|
192 | fg_H = ((double) fg_h) / 360; |
---|
193 | bg_H = ((double) bg_h) / 360; |
---|
194 | dh = fg_H - bg_H; |
---|
195 | if (dh < 0) dh = -dh; |
---|
196 | if (dh > 0.5) dh = 0.5 - (dh - 0.5); |
---|
197 | distance = sqrt ((dh * dh) + |
---|
198 | ((fg_s - bg_s) * (fg_s - bg_s)) + |
---|
199 | ((fg_v - bg_v) * (fg_v - bg_v))); |
---|
200 | if (distance < 0.2) |
---|
201 | goto RETRY; |
---|
202 | } |
---|
203 | |
---|
204 | cycle_p = True; |
---|
205 | if (colors) |
---|
206 | free_colors (dpy, cmap, colors, ncolors); |
---|
207 | else |
---|
208 | colors = (XColor *) malloc (ncolors * sizeof(*colors)); |
---|
209 | |
---|
210 | cycle_direction = (random() & 1 ? 1 : -1); |
---|
211 | |
---|
212 | RETRY_NON_WRITABLE: |
---|
213 | { |
---|
214 | int n = ncolors; |
---|
215 | make_color_ramp (dpy, cmap, |
---|
216 | fg_h, fg_s, fg_v, |
---|
217 | bg_h, bg_s, bg_v, |
---|
218 | colors, &n, |
---|
219 | True, True, cycle_p); |
---|
220 | if (n == 0 && cycle_p) |
---|
221 | { |
---|
222 | cycle_p = False; |
---|
223 | goto RETRY_NON_WRITABLE; |
---|
224 | } |
---|
225 | ncolors = n; |
---|
226 | } |
---|
227 | |
---|
228 | if (ncolors <= 0) |
---|
229 | mono_p = 1; |
---|
230 | } |
---|
231 | |
---|
232 | if (mono_p) |
---|
233 | { |
---|
234 | static Bool done = False; |
---|
235 | static XColor c[50]; |
---|
236 | colors = c; |
---|
237 | cycle_p = False; |
---|
238 | ncolors = sizeof(c)/sizeof(*c); |
---|
239 | if (!done) |
---|
240 | { |
---|
241 | int i; |
---|
242 | done = True; |
---|
243 | colors[0].pixel = fg_pixel; |
---|
244 | for (i = 1; i < ncolors; i++) |
---|
245 | colors[i].pixel = bg_pixel; |
---|
246 | } |
---|
247 | } |
---|
248 | |
---|
249 | XSetForeground (dpy, gc, colors[1].pixel); |
---|
250 | XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height); |
---|
251 | } |
---|
252 | |
---|
253 | |
---|
254 | #define HEIGHT_TO_PIXEL(height) \ |
---|
255 | ((height) < 0 \ |
---|
256 | ? (extra_krinkly_p \ |
---|
257 | ? ncolors - ((-(height)) % ncolors) \ |
---|
258 | : 0) \ |
---|
259 | : ((height) >= ncolors \ |
---|
260 | ? (extra_krinkly_p \ |
---|
261 | ? (height) % ncolors \ |
---|
262 | : ncolors-1) \ |
---|
263 | : (height))) |
---|
264 | |
---|
265 | |
---|
266 | static unsigned int |
---|
267 | set (unsigned int l, |
---|
268 | unsigned int c, |
---|
269 | unsigned int size, |
---|
270 | int height) |
---|
271 | { |
---|
272 | int rang = 1 << (NSTEPS - size); |
---|
273 | height = height + (random () % rang) - rang / 2; |
---|
274 | height = HEIGHT_TO_PIXEL(height); |
---|
275 | CELL (l, c) = height; |
---|
276 | return colors[height].pixel; |
---|
277 | } |
---|
278 | |
---|
279 | static void |
---|
280 | floyd_steinberg (Display *dpy, Window window) |
---|
281 | { |
---|
282 | int x, y, err; |
---|
283 | |
---|
284 | /* Instead of repeatedly calling XPutPixel(), we make an Image and then |
---|
285 | send its bits over all at once. This consumes much less network |
---|
286 | bandwidth. The image we create is Wx1 intead of WxH, so that we |
---|
287 | don't use enormous amounts of memory. |
---|
288 | */ |
---|
289 | XImage *image = |
---|
290 | XCreateImage (dpy, xgwa.visual, |
---|
291 | 1, XYBitmap, 0, /* depth, format, offset */ |
---|
292 | (char *) calloc ((xmax + 8) / 8, 1), /* data */ |
---|
293 | xmax, 1, 8, 0); /* w, h, pad, bpl */ |
---|
294 | |
---|
295 | XSetForeground (dpy, gc, colors[0].pixel); |
---|
296 | XSetBackground (dpy, gc, colors[1].pixel); |
---|
297 | |
---|
298 | for (y = 0; y < ymax - 1; y++) |
---|
299 | { |
---|
300 | for (x = 0; x < xmax - 1; x++) |
---|
301 | { |
---|
302 | if (CELL(x, y) < 0) |
---|
303 | { |
---|
304 | err = CELL (x, y); |
---|
305 | XPutPixel (image, x, 0, 1); |
---|
306 | } |
---|
307 | else |
---|
308 | { |
---|
309 | err = CELL (x, y) - 1; |
---|
310 | XPutPixel (image, x, 0, 0); |
---|
311 | } |
---|
312 | /* distribute error */ |
---|
313 | CELL (x, y+1) += (int) (((float) err) * 3.0/8.0); |
---|
314 | CELL (x+1, y) += (int) (((float) err) * 3.0/8.0); |
---|
315 | CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0); |
---|
316 | } |
---|
317 | XPutImage (dpy, window, gc, image, 0, 0, 0, y, xmax, 1); |
---|
318 | } |
---|
319 | XDestroyImage (image); |
---|
320 | } |
---|
321 | |
---|
322 | static void |
---|
323 | draw (Display *dpy, Window window, |
---|
324 | int x, int y, unsigned long pixel, int grid_size) |
---|
325 | { |
---|
326 | static unsigned int last_pixel, last_valid = 0; |
---|
327 | if (! (last_valid && pixel == last_pixel)) |
---|
328 | XSetForeground (dpy, gc, pixel); |
---|
329 | last_valid = 1, last_pixel = pixel; |
---|
330 | if (grid_size == 1) |
---|
331 | XDrawPoint (dpy, window, gc, x, y); |
---|
332 | else |
---|
333 | XFillRectangle (dpy, window, gc, x, y, grid_size, grid_size); |
---|
334 | } |
---|
335 | |
---|
336 | |
---|
337 | static void |
---|
338 | draw_map (Display *dpy, Window window) |
---|
339 | { |
---|
340 | int xstep, ystep, xnextStep, ynextStep; |
---|
341 | int x, y, i, x1, x2, y1, y2; |
---|
342 | unsigned int pixel, qpixels [4]; |
---|
343 | |
---|
344 | int backwards = random() & 1; |
---|
345 | |
---|
346 | xmax = xgwa.width; |
---|
347 | ymax = xgwa.height; |
---|
348 | |
---|
349 | cell = (signed char *) calloc (xmax * ymax, 1); |
---|
350 | if (cell == NULL) |
---|
351 | exit (1); |
---|
352 | |
---|
353 | CELL (0, 0) = 0; |
---|
354 | xstep = (backwards ? -COUNT : COUNT); |
---|
355 | ystep = COUNT; |
---|
356 | for (i = 0; i < iterations; i++) |
---|
357 | { |
---|
358 | xnextStep = xstep / 2; |
---|
359 | ynextStep = ystep / 2; |
---|
360 | for (x = (backwards ? xmax-1 : 0); |
---|
361 | (backwards ? x >= 0 : x < xmax); |
---|
362 | x += xstep) |
---|
363 | { |
---|
364 | x1 = x + xnextStep; |
---|
365 | if (x1 < 0) |
---|
366 | x1 = xmax-1; |
---|
367 | else if (x1 >= xmax) |
---|
368 | x1 = 0; |
---|
369 | |
---|
370 | x2 = x + xstep; |
---|
371 | if (x2 < 0) |
---|
372 | x2 = xmax-1; |
---|
373 | else if (x2 >= xmax) |
---|
374 | x2 = 0; |
---|
375 | |
---|
376 | for (y = 0; y < ymax; y += ystep) |
---|
377 | { |
---|
378 | y1 = y + ynextStep; |
---|
379 | if (y1 < 0) |
---|
380 | y1 = ymax-1; |
---|
381 | else if (y1 >= ymax) |
---|
382 | y1 = 0; |
---|
383 | |
---|
384 | y2 = y + ystep; |
---|
385 | if (y2 < 0) |
---|
386 | y2 = ymax-1; |
---|
387 | else if (y2 >= ymax) |
---|
388 | y2 = 0; |
---|
389 | |
---|
390 | qpixels [0] = colors [HEIGHT_TO_PIXEL (CELL (x, y))].pixel; |
---|
391 | qpixels [1] = colors [HEIGHT_TO_PIXEL (CELL (x, y2))].pixel; |
---|
392 | qpixels [2] = colors [HEIGHT_TO_PIXEL (CELL (x2, y))].pixel; |
---|
393 | qpixels [3] = colors [HEIGHT_TO_PIXEL (CELL (x2, y2))].pixel; |
---|
394 | |
---|
395 | pixel = set (x, y1, i, |
---|
396 | ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2); |
---|
397 | if (! mono_p && |
---|
398 | (pixel != qpixels[0] || pixel != qpixels[1] || |
---|
399 | pixel != qpixels[2] || pixel != qpixels[3])) |
---|
400 | draw (dpy, window, x, y1, pixel, ynextStep); |
---|
401 | |
---|
402 | pixel = set (x1, y, i, |
---|
403 | ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2); |
---|
404 | if (! mono_p && |
---|
405 | (pixel != qpixels[0] || pixel != qpixels[1] || |
---|
406 | pixel != qpixels[2] || pixel != qpixels[3])) |
---|
407 | draw (dpy, window, x1, y, pixel, ynextStep); |
---|
408 | |
---|
409 | pixel = set (x1, y1, i, |
---|
410 | ((int) CELL (x, y) + (int) CELL (x, y2) + |
---|
411 | (int) CELL (x2, y) + (int) CELL (x2, y2) + 2) |
---|
412 | / 4); |
---|
413 | if (! mono_p && |
---|
414 | (pixel != qpixels[0] || pixel != qpixels[1] || |
---|
415 | pixel != qpixels[2] || pixel != qpixels[3])) |
---|
416 | draw (dpy, window, x1, y1, pixel, ynextStep); |
---|
417 | |
---|
418 | |
---|
419 | if (cycle_p) |
---|
420 | { |
---|
421 | struct timeval now; |
---|
422 | static struct timeval then = { 0, 0 }; |
---|
423 | unsigned long diff; |
---|
424 | #ifdef GETTIMEOFDAY_TWO_ARGS |
---|
425 | struct timezone tzp; |
---|
426 | gettimeofday(&now, &tzp); |
---|
427 | #else |
---|
428 | gettimeofday(&now); |
---|
429 | #endif |
---|
430 | diff = (((now.tv_sec - then.tv_sec) * 1000000) + |
---|
431 | (now.tv_usec - then.tv_usec)); |
---|
432 | if (diff > cycle_delay) |
---|
433 | { |
---|
434 | rotate_colors (dpy, cmap, colors, ncolors, |
---|
435 | cycle_direction); |
---|
436 | then = now; |
---|
437 | } |
---|
438 | } |
---|
439 | } |
---|
440 | } |
---|
441 | xstep = xnextStep; |
---|
442 | ystep = ynextStep; |
---|
443 | if (!mono_p) |
---|
444 | XSync (dpy, False); |
---|
445 | screenhack_handle_events (dpy); |
---|
446 | } |
---|
447 | if (mono_p) |
---|
448 | /* in mono-mode, we do all the drawing at the end */ |
---|
449 | floyd_steinberg (dpy, window); |
---|
450 | |
---|
451 | free (cell); |
---|
452 | XSync (dpy, False); |
---|
453 | } |
---|
454 | |
---|
455 | |
---|
456 | char *progclass = "Imsmap"; |
---|
457 | |
---|
458 | char *defaults [] = { |
---|
459 | ".background: black", |
---|
460 | ".foreground: black", |
---|
461 | "*mode: random", |
---|
462 | "*ncolors: 50", |
---|
463 | "*iterations: 7", |
---|
464 | "*delay: 10", |
---|
465 | "*cycleDelay: 100000", |
---|
466 | "*cycle: true", |
---|
467 | 0 |
---|
468 | }; |
---|
469 | |
---|
470 | XrmOptionDescRec options [] = { |
---|
471 | { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, |
---|
472 | { "-delay", ".delay", XrmoptionSepArg, 0 }, |
---|
473 | { "-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0 }, |
---|
474 | { "-mode", ".mode", XrmoptionSepArg, 0 }, |
---|
475 | { "-iterations", ".iterations", XrmoptionSepArg, 0 }, |
---|
476 | { "-cycle", ".cycle", XrmoptionNoArg, "True" }, |
---|
477 | { "-no-cycle", ".cycle", XrmoptionNoArg, "False" }, |
---|
478 | { 0, 0, 0, 0 } |
---|
479 | }; |
---|
480 | |
---|
481 | |
---|
482 | void |
---|
483 | screenhack (Display *dpy, Window window) |
---|
484 | { |
---|
485 | while (1) |
---|
486 | { |
---|
487 | init_map (dpy, window); |
---|
488 | draw_map (dpy, window); |
---|
489 | if (delay) |
---|
490 | { |
---|
491 | if (cycle_p) |
---|
492 | { |
---|
493 | time_t start = time((time_t) 0); |
---|
494 | while (start + delay > time((time_t) 0)) |
---|
495 | { |
---|
496 | rotate_colors (dpy, cmap, colors, ncolors, |
---|
497 | cycle_direction); |
---|
498 | if (cycle_delay) usleep(cycle_delay); |
---|
499 | screenhack_handle_events (dpy); |
---|
500 | } |
---|
501 | } |
---|
502 | else |
---|
503 | { |
---|
504 | screenhack_handle_events (dpy); |
---|
505 | sleep (delay); |
---|
506 | } |
---|
507 | } |
---|
508 | } |
---|
509 | } |
---|