1 /* imsmap, Copyright (c) 1992-2013 Juergen Nickelsen and Jamie Zawinski.
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
20 #include "screenhack.h"
21
22 #define NSTEPS 7
23 #define COUNT (1 << NSTEPS)
24 #define CELL(c, r) st->cell[((unsigned int)(c)) + ((unsigned int) (r)) * st->xmax]
25
26 #if defined(sun) && !__STDC__ /* sun cc doesn't know "signed char" */
27 #define signed /**/
28 #endif
29
30 struct state {
31 Display *dpy;
32 Window window;
33 Colormap cmap;
34 int ncolors;
35 XColor *colors;
36 Bool extra_krinkly_p;
37
38 int delay, delay2;
39 signed char *cell;
40 int xmax, ymax;
41 int iteration, iterations;
42
43 int cx, xstep, ystep, xnextStep, ynextStep;
44
45 unsigned int last_pixel, last_valid;
46 int flip_x;
47 int flip_xy;
48
49 GC gc, gc2;
50 XWindowAttributes xgwa;
51
52 struct timeval then;
53 };
54
55
56 #define HEIGHT_TO_PIXEL(height) \
57 ((height) < 0 \
58 ? (st->extra_krinkly_p \
59 ? st->ncolors - 1 - ((-(height)) % st->ncolors) \
60 : 0) \
61 : ((height) >= st->ncolors \
62 ? (st->extra_krinkly_p \
63 ? (height) % st->ncolors \
64 : st->ncolors-1) \
65 : (height)))
66
67
68 static unsigned int
set(struct state * st,unsigned int l,unsigned int c,unsigned int size,int height)69 set (struct state *st,
70 unsigned int l,
71 unsigned int c,
72 unsigned int size,
73 int height)
74 {
75 int rang = 1 << (NSTEPS - size);
76 height = height + (random () % rang) - rang / 2;
77 height = HEIGHT_TO_PIXEL(height);
78 CELL (l, c) = height;
79 return st->colors[height].pixel;
80 }
81
82
83 static void
floyd_steinberg(struct state * st)84 floyd_steinberg (struct state *st)
85 {
86 int x, y, err;
87
88 /* Instead of repeatedly calling XPutPixel(), we make an Image and then
89 send its bits over all at once. This consumes much less network
90 bandwidth. The image we create is Wx1 intead of WxH, so that we
91 don't use enormous amounts of memory.
92 */
93 XImage *image =
94 XCreateImage (st->dpy, st->xgwa.visual,
95 1, XYBitmap, 0, /* depth, format, offset */
96 (char *) calloc ((st->xmax + 8) / 8, 1), /* data */
97 st->xmax, 1, 8, 0); /* w, h, pad, bpl */
98
99 XSetForeground (st->dpy, st->gc, st->colors[0].pixel);
100 XSetBackground (st->dpy, st->gc, st->colors[1].pixel);
101
102 for (y = 0; y < st->ymax - 1; y++)
103 {
104 for (x = 0; x < st->xmax - 1; x++)
105 {
106 if (CELL(x, y) < 0)
107 {
108 err = CELL (x, y);
109 XPutPixel (image, x, 0, 1);
110 }
111 else
112 {
113 err = CELL (x, y) - 1;
114 XPutPixel (image, x, 0, 0);
115 }
116 /* distribute error */
117 CELL (x, y+1) += (int) (((float) err) * 3.0/8.0);
118 CELL (x+1, y) += (int) (((float) err) * 3.0/8.0);
119 CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0);
120 }
121 XPutImage (st->dpy, st->window, st->gc, image, 0, 0, 0, y, st->xmax, 1);
122 }
123 XDestroyImage (image);
124 }
125
126
127 static void
draw(struct state * st,int x,int y,unsigned long pixel,int grid_size)128 draw (struct state *st,
129 int x, int y, unsigned long pixel, int grid_size)
130 {
131 if (st->flip_x)
132 x = st->xmax - x;
133
134 if (st->flip_xy)
135 {
136 int swap = x;
137 x = y;
138 y = swap;
139 }
140
141 if (! (st->last_valid && pixel == st->last_pixel))
142 XSetForeground (st->dpy, st->gc, pixel);
143 st->last_valid = 1, st->last_pixel = pixel;
144 if (grid_size == 1)
145 XDrawPoint (st->dpy, st->window, st->gc, x, y);
146 else
147 XFillRectangle (st->dpy, st->window, st->gc, x, y, grid_size, grid_size);
148 }
149
150
151 static void
init_map(struct state * st)152 init_map (struct state *st)
153 {
154 XGCValues gcv;
155
156 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
157 st->cmap = st->xgwa.colormap;
158
159 st->flip_x = (random() % 2);
160 st->flip_xy = (random() % 2);
161
162 if (mono_p)
163 st->flip_xy = 0;
164 else if (st->colors) {
165 free_colors (st->xgwa.screen, st->cmap, st->colors, st->ncolors);
166 free (st->colors);
167 }
168 st->colors = 0;
169
170 st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
171 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
172 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
173 st->iterations = get_integer_resource (st->dpy, "iterations", "Integer");
174 if (st->iterations < 0) st->iterations = 0;
175 else if (st->iterations > 7) st->iterations = 7;
176
177 if (st->ncolors <= 2) st->ncolors = 0;
178 if (st->ncolors == 0) mono_p = True;
179 if (st->ncolors > 255) st->ncolors = 255; /* too many look bad */
180
181 if (!st->gc) st->gc = XCreateGC (st->dpy, st->window, 0, &gcv);
182 if (!st->gc2) st->gc2 = XCreateGC (st->dpy, st->window, 0, &gcv);
183
184 if (mono_p)
185 st->extra_krinkly_p = !(random() % 15);
186 else
187 st->extra_krinkly_p = !(random() % 5);
188
189 if (!mono_p)
190 {
191 if (st->ncolors < 1) st->ncolors = 1;
192 if (st->colors) free (st->colors);
193 st->colors = (XColor *) malloc (st->ncolors * sizeof(*st->colors));
194
195 make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->cmap,
196 st->colors, &st->ncolors,
197 True, 0, False);
198 if (st->ncolors <= 2)
199 mono_p = 1;
200 }
201
202 if (mono_p)
203 {
204 int i;
205 unsigned long fg_pixel =
206 get_pixel_resource (st->dpy, st->xgwa.colormap,
207 "foreground", "Foreground");
208 unsigned long bg_pixel =
209 get_pixel_resource (st->dpy, st->xgwa.colormap,
210 "background", "Background");
211 if (!st->colors)
212 {
213 st->ncolors = 50;
214 st->colors = (XColor *) calloc (st->ncolors, sizeof(*st->colors));
215 }
216 st->colors[0].pixel = fg_pixel;
217 for (i = 1; i < st->ncolors; i++)
218 st->colors[i].pixel = bg_pixel;
219 }
220
221 XSetForeground (st->dpy, st->gc, st->colors[1].pixel);
222 XFillRectangle (st->dpy, st->window, st->gc, 0, 0,
223 st->xgwa.width, st->xgwa.height);
224
225 if (st->flip_xy)
226 {
227 st->xmax = st->xgwa.height;
228 st->ymax = st->xgwa.width;
229 }
230 else
231 {
232 st->xmax = st->xgwa.width;
233 st->ymax = st->xgwa.height;
234 }
235
236 if (st->cell) free (st->cell);
237 st->cell = (signed char *) calloc (st->xmax * st->ymax, 1);
238
239 CELL (0, 0) = 0;
240 st->xstep = COUNT;
241 st->ystep = COUNT;
242
243 st->iteration = 0;
244 st->cx = 0;
245 }
246
247
248 static void *
imsmap_init(Display * dpy,Window window)249 imsmap_init (Display *dpy, Window window)
250 {
251 struct state *st = (struct state *) calloc (1, sizeof(*st));
252 st->dpy = dpy;
253 st->window = window;
254 init_map (st);
255 return st;
256 }
257
258
259 static unsigned long
imsmap_draw(Display * dpy,Window window,void * closure)260 imsmap_draw (Display *dpy, Window window, void *closure)
261 {
262 struct state *st = (struct state *) closure;
263 int this_delay = st->delay2;
264 int i;
265
266 /* do this many lines at a time without pausing */
267 int col_chunk = st->iteration * 2 + 1;
268
269 if (st->iteration > st->iterations)
270 init_map (st);
271
272 if (st->cx == 0)
273 {
274 st->xnextStep = st->xstep / 2;
275 st->ynextStep = st->ystep / 2;
276 }
277
278 for (i = 0; i < col_chunk; i++)
279 {
280 int x1, x2, y1, y2;
281 int y;
282 int x = st->cx;
283
284 x1 = x + st->xnextStep;
285 if (x1 < 0)
286 x1 = st->xmax-1;
287 else if (x1 >= st->xmax)
288 x1 = 0;
289
290 x2 = x + st->xstep;
291 if (x2 < 0)
292 x2 = st->xmax-1;
293 else if (x2 >= st->xmax)
294 x2 = 0;
295
296 for (y = 0; y < st->ymax; y += st->ystep)
297 {
298 unsigned int pixel, qpixels [4];
299
300 y1 = y + st->ynextStep;
301 if (y1 < 0)
302 y1 = st->ymax-1;
303 else if (y1 >= st->ymax)
304 y1 = 0;
305
306 y2 = y + st->ystep;
307 if (y2 < 0)
308 y2 = st->ymax-1;
309 else if (y2 >= st->ymax)
310 y2 = 0;
311
312 qpixels [0] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y))].pixel;
313 qpixels [1] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y2))].pixel;
314 qpixels [2] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y))].pixel;
315 qpixels [3] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y2))].pixel;
316
317 pixel = set (st, x, y1, st->iteration,
318 ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
319
320 if (! mono_p &&
321 (pixel != qpixels[0] || pixel != qpixels[1] ||
322 pixel != qpixels[2] || pixel != qpixels[3]))
323 draw (st, x, y1, pixel, st->ynextStep);
324
325 pixel = set (st, x1, y, st->iteration,
326 ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
327 if (! mono_p &&
328 (pixel != qpixels[0] || pixel != qpixels[1] ||
329 pixel != qpixels[2] || pixel != qpixels[3]))
330 draw (st, x1, y, pixel, st->ynextStep);
331
332 pixel = set (st, x1, y1, st->iteration,
333 ((int) CELL (x, y) + (int) CELL (x, y2) +
334 (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
335 / 4);
336 if (! mono_p &&
337 (pixel != qpixels[0] || pixel != qpixels[1] ||
338 pixel != qpixels[2] || pixel != qpixels[3]))
339 draw (st, x1, y1, pixel, st->ynextStep);
340 }
341
342 st->cx += st->xstep;
343 if (st->cx >= st->xmax)
344 break;
345 }
346
347 if (st->cx >= st->xmax)
348 {
349 st->cx = 0;
350 st->xstep = st->xnextStep;
351 st->ystep = st->ynextStep;
352
353 st->iteration++;
354
355 if (st->iteration > st->iterations)
356 this_delay = st->delay * 1000000;
357
358 if (mono_p)
359 floyd_steinberg (st); /* in mono, do all drawing at the end */
360 }
361
362 return this_delay;
363 }
364
365
366 static void
imsmap_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)367 imsmap_reshape (Display *dpy, Window window, void *closure,
368 unsigned int w, unsigned int h)
369 {
370 struct state *st = (struct state *) closure;
371 init_map (st);
372 }
373
374
375 static Bool
imsmap_event(Display * dpy,Window window,void * closure,XEvent * event)376 imsmap_event (Display *dpy, Window window, void *closure, XEvent *event)
377 {
378 struct state *st = (struct state *) closure;
379 if (screenhack_event_helper (dpy, window, event))
380 {
381 init_map (st);
382 return True;
383 }
384
385 return False;
386 }
387
388
389 static void
imsmap_free(Display * dpy,Window window,void * closure)390 imsmap_free (Display *dpy, Window window, void *closure)
391 {
392 struct state *st = (struct state *) closure;
393 XFreeGC (dpy, st->gc);
394 XFreeGC (dpy, st->gc2);
395 if (st->colors) free (st->colors);
396 if (st->cell) free (st->cell);
397 free (st);
398 }
399
400
401 static const char *imsmap_defaults [] = {
402 ".background: #000066",
403 ".foreground: #FF00FF",
404 "*fpsSolid: true",
405 "*mode: random",
406 "*ncolors: 50",
407 "*iterations: 7",
408 "*delay: 5",
409 "*delay2: 20000",
410 #ifdef HAVE_MOBILE
411 "*ignoreRotation: True",
412 #endif
413 0
414 };
415
416 static XrmOptionDescRec imsmap_options [] = {
417 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
418 { "-delay", ".delay", XrmoptionSepArg, 0 },
419 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
420 { "-mode", ".mode", XrmoptionSepArg, 0 },
421 { "-iterations", ".iterations", XrmoptionSepArg, 0 },
422 { 0, 0, 0, 0 }
423 };
424
425 XSCREENSAVER_MODULE ("IMSMap", imsmap)
426