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