ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / memscroller.c
1 /* xscreensaver, Copyright (c) 2002, 2004 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Memscroller -- scrolls a dump of its own RAM across the screen.
12  */
13
14 #include "screenhack.h"
15 #include <stdio.h>
16 #include <X11/Xutil.h>
17
18 #ifdef HAVE_XSHM_EXTENSION
19 #include "xshm.h"
20 #endif
21
22 typedef struct {
23   int which;
24   XRectangle rect;
25   XImage *image;
26   int rez;
27   int speed;
28   int scroll_tick;
29   unsigned char *data;
30   int count_zero;
31 } scroller;
32
33 typedef struct {
34   Display *dpy;
35   Window window;
36   XWindowAttributes xgwa;
37   GC draw_gc, erase_gc, text_gc;
38   XFontStruct *font;
39   int border;
40
41   enum { SEED_RAM, SEED_RANDOM } seed_mode;
42   enum { DRAW_COLOR, DRAW_MONO } draw_mode;
43
44   int nscrollers;
45   scroller *scrollers;
46
47 # ifdef HAVE_XSHM_EXTENSION
48   Bool shm_p;
49   XShmSegmentInfo shm_info;
50 # endif
51
52 } state;
53
54
55 state *
56 init_memscroller (Display *dpy, Window window)
57 {
58   int i;
59   XGCValues gcv;
60   state *st = (state *) calloc (1, sizeof (*st));
61   char *s;
62   st->dpy = dpy;
63   st->window = window;
64   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
65   XSelectInput(st->dpy, st->window, ExposureMask);
66
67   /* Fill up the colormap with random colors.
68      We don't actually use these explicitly, but in 8-bit mode,
69      they will be used implicitly by the random image bits. */
70   {
71     int ncolors = 255;
72     XColor colors[256];
73     make_random_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
74                           colors, &ncolors, True, True, 0, False);
75   }
76
77   st->border = get_integer_resource ("borderSize", "BorderSize");
78
79   {
80     char *fontname = get_string_resource ("font", "Font");
81     st->font = XLoadQueryFont (dpy, fontname);
82
83     if (!st->font)
84       {
85         static const char *fonts[] = {
86           "-*-courier-medium-r-*-*-*-1400-*-*-p-*-*-*",
87           "-*-courier-medium-r-*-*-*-600-*-*-p-*-*-*",
88           "-*-utopia-*-r-*-*-*-1400-*-*-p-*-*-*",
89           "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
90           "-*-utopia-*-r-*-*-*-240-*-*-p-*-*-*",
91           "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
92           "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
93           "fixed", 0 };
94         i = 0;
95         while (fonts[i])
96           {
97             st->font = XLoadQueryFont (dpy, fonts[i]);
98             if (st->font) break;
99             i++;
100           }
101         if (st->font)
102           fprintf (stderr, "%s: couldn't load font \"%s\", using \"%s\"\n",
103                    progname, fontname, fonts[i]);
104         else
105           {
106             fprintf(stderr, "%s: couldn't load any font\n", progname);
107             exit(-1);
108           }
109       }
110   }
111
112   gcv.line_width = st->border;
113
114   gcv.background = get_pixel_resource("background", "Background",
115                                       st->dpy, st->xgwa.colormap);
116   gcv.foreground = get_pixel_resource("textColor", "Foreground",
117                                       st->dpy, st->xgwa.colormap);
118   gcv.font = st->font->fid;
119   st->text_gc = XCreateGC (st->dpy, st->window,
120                            GCForeground|GCBackground|GCFont, &gcv);
121
122   gcv.foreground = get_pixel_resource("foreground", "Foreground",
123                                       st->dpy, st->xgwa.colormap);
124   st->draw_gc = XCreateGC (st->dpy, st->window,
125                            GCForeground|GCBackground|GCLineWidth,
126                            &gcv);
127   gcv.foreground = gcv.background;
128   st->erase_gc = XCreateGC (st->dpy, st->window,
129                             GCForeground|GCBackground, &gcv);
130
131
132   s = get_string_resource ("drawMode", "DrawMode");
133   if (!s || !*s || !strcasecmp (s, "color"))
134     st->draw_mode = DRAW_COLOR;
135   else if (!strcasecmp (s, "mono"))
136     st->draw_mode = DRAW_MONO;
137   else
138     {
139       fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
140                progname, s);
141       exit (1);
142     }
143   if (s) free (s);
144
145
146   s = get_string_resource ("seedMode", "SeedMode");
147   if (!s || !*s ||
148       !strcasecmp (s, "ram") ||
149       !strcasecmp (s, "mem") ||
150       !strcasecmp (s, "memory"))
151     st->seed_mode = SEED_RAM;
152   else if (!strcasecmp (s, "rand") ||
153            !strcasecmp (s, "random"))
154     st->seed_mode = SEED_RANDOM;
155   else
156     {
157       fprintf (stderr, "%s: seedMode must be 'RAM' or 'random', not '%s'\n",
158                progname, s);
159       exit (1);
160     }
161   if (s) free (s);
162   s = 0;
163
164
165   st->nscrollers = 3;
166   st->scrollers = (scroller *) calloc (st->nscrollers, sizeof(scroller));
167
168   for (i = 0; i < st->nscrollers; i++)
169     {
170       scroller *sc = &st->scrollers[i];
171       int max_height = 4096;
172
173       sc->which = i;
174       sc->speed = i+1;
175
176       sc->image = 0;
177 # ifdef HAVE_XSHM_EXTENSION
178       st->shm_p = get_boolean_resource ("useSHM", "Boolean");
179       if (st->shm_p)
180         {
181           sc->image = create_xshm_image (st->dpy, st->xgwa.visual,
182                                          st->xgwa.depth,
183                                          ZPixmap, 0, &st->shm_info,
184                                          1, max_height);
185           if (! sc->image)
186             st->shm_p = False;
187         }
188 # endif /* HAVE_XSHM_EXTENSION */
189
190       if (!sc->image)
191         sc->image = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
192                                   ZPixmap, 0, 0, 1, max_height, 8, 0);
193
194       if (sc->image && !sc->image->data)
195         sc->image->data = (char *)
196           malloc (sc->image->bytes_per_line * sc->image->height + 1);
197
198       if (!sc->image || !sc->image->data)
199         {
200           fprintf (stderr, "%s: out of memory (allocating 1x%d image)\n",
201                    progname, sc->image->height);
202           exit (1);
203         }
204     }
205
206   return st;
207 }
208
209
210 static void
211 reshape_memscroller (state *st)
212 {
213   int i;
214
215   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
216
217   for (i = 0; i < st->nscrollers; i++)
218     {
219       scroller *sc = &st->scrollers[i];
220
221       if (i == 0)
222         {
223           sc->rez = 6;  /* #### */
224
225           sc->rect.width  = (((int) (st->xgwa.width * 0.8)
226                               / sc->rez) * sc->rez);
227           sc->rect.height = (((int) (st->xgwa.height * 0.3)
228                               / sc->rez) * sc->rez);
229
230           sc->rect.x = (st->xgwa.width  - sc->rect.width)  / 2;
231           sc->rect.y = (st->xgwa.height - sc->rect.height) / 2;
232         }
233       else
234         {
235           scroller *sc0 = &st->scrollers[i-1];
236           sc->rez = sc0->rez * 1.8;
237
238           sc->rect.x      = sc0->rect.x;
239           sc->rect.y      = (sc0->rect.y + sc0->rect.height + st->border
240                              + (st->border + 2) * 7);
241           sc->rect.width  = sc0->rect.width;
242           sc->rect.height = (((int) (st->xgwa.height * 0.1)
243                               / sc->rez) * sc->rez);
244         }
245
246       XDrawRectangle (st->dpy, st->window, st->draw_gc,
247                       sc->rect.x - st->border*2,
248                       sc->rect.y - st->border*2,
249                       sc->rect.width  + st->border*4,
250                       sc->rect.height + st->border*4);
251     }
252 }
253
254
255
256 static unsigned int
257 more_bits (state *st, scroller *sc)
258 {
259   static unsigned char *lomem = 0;
260   static unsigned char *himem = 0;
261   unsigned char r, g, b;
262   unsigned int v;
263
264   /* Pack bytes as RGBR so that we don't have to worry about actually
265      figuring out the color channel order.
266    */
267 # undef PACK
268 # define PACK(r,g,b) ((r) | ((g) << 8) | ((b) << 16) | ((r) << 24))
269
270   switch (st->seed_mode)
271     {
272     case SEED_RAM:
273       if (himem == 0)
274         {
275           lomem = (unsigned char *) progname; /* not first malloc, but early */
276           himem = (unsigned char *)           /* not last malloc, but late */
277             st->scrollers[st->nscrollers-1].image->data;
278         }
279
280       if (sc->data < lomem)
281         sc->data = lomem;
282
283 # ifdef HAVE_SBRK  /* re-get it each time through */
284       himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
285 # endif
286
287       if (lomem >= himem) abort();
288
289     RETRY:
290       if (sc->data >= himem)
291         sc->data = lomem;
292
293       switch (st->draw_mode)
294         {
295         case DRAW_COLOR:
296           r = *sc->data++;
297           g = *sc->data++;
298           b = *sc->data++;
299           break;
300         case DRAW_MONO:
301           r = 0;
302           g = *sc->data++;
303           b = 0;
304           break;
305         default:
306           abort();
307         }
308
309       v = PACK(r,g,b);
310
311       /* avoid having many seconds of blackness: truncate zeros at 24K.
312        */
313       if (v == 0)
314         sc->count_zero++;
315       else
316         sc->count_zero = 0;
317       if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
318         goto RETRY;
319
320       break;
321
322     case SEED_RANDOM:
323       v = random();
324       switch (st->draw_mode)
325         {
326         case DRAW_COLOR:
327           r = (v >> 16) & 0xFF;
328           g = (v >>  8) & 0xFF;
329           b = (v      ) & 0xFF;
330           break;
331         case DRAW_MONO:
332           r = 0;
333           g = v & 0xFF;
334           b = 0;
335           break;
336         default:
337           abort();
338         }
339       v = PACK(r,g,b);
340       break;
341
342     default:
343       abort();
344     }
345
346 # undef PACK
347   return v;
348 }
349
350
351 static void
352 draw_string (state *st, unsigned int n)
353 {
354   char buf[40];
355   int direction, ascent, descent;
356   XCharStruct overall;
357   int x, y, w, h;
358   int bot = st->scrollers[0].rect.y;
359   const char *fmt = "%08X";
360
361   sprintf (buf, fmt, 0);
362   XTextExtents (st->font, buf, strlen(buf), 
363                 &direction, &ascent, &descent, &overall);
364   sprintf (buf, "%08X", n);
365
366   w = overall.width;
367   h = st->font->ascent + st->font->descent + 1;
368   x = (st->xgwa.width - w) / 2;
369   y = (bot - h) / 2;
370
371   if (y + h + 10 > bot) return;
372
373   XFillRectangle (st->dpy, st->window, st->erase_gc,
374                   x-w, y, w*3, h);
375   XDrawString (st->dpy, st->window, st->text_gc,
376                x, y + st->font->ascent, buf, strlen(buf));
377 }
378
379
380 static void
381 draw_memscroller (state *st)
382 {
383   int i;
384
385   draw_string (st, *((unsigned int *) st->scrollers[0].image->data));
386
387   for (i = 0; i < st->nscrollers; i++)
388     {
389       scroller *sc = &st->scrollers[i];
390       int j;
391
392       XCopyArea (st->dpy, st->window, st->window, st->draw_gc,
393                  sc->rect.x + sc->speed, sc->rect.y,
394                  sc->rect.width - sc->speed, sc->rect.height,
395                  sc->rect.x, sc->rect.y);
396
397       if (sc->scroll_tick == 0)
398         {
399           int top = ((sc->image->bytes_per_line * sc->rect.height) /
400                      (4 * sc->rez));
401           unsigned int *out = (unsigned int *) sc->image->data;
402           for (j = 0; j < top; j++)
403             {
404               unsigned int v = more_bits(st, sc);
405               int k;
406               for (k = 0; k < sc->rez; k++)
407                 *out++ = v;
408             }
409         }
410
411       sc->scroll_tick++;
412       if (sc->scroll_tick * sc->speed >= sc->rez)
413         sc->scroll_tick = 0;
414
415       for (j = 0; j < sc->speed; j++)
416         {
417 # ifdef HAVE_XSHM_EXTENSION
418           if (st->shm_p)
419             XShmPutImage (st->dpy, st->window, st->draw_gc, sc->image,
420                           0, 0,
421                           sc->rect.x + sc->rect.width - sc->image->width - j,
422                           sc->rect.y,
423                           sc->rect.width, sc->rect.height,
424                           False);
425           else
426 # endif /* HAVE_XSHM_EXTENSION */
427             XPutImage (st->dpy, st->window, st->draw_gc, sc->image,
428                        0, 0,
429                        sc->rect.x + sc->rect.width - sc->image->width - j,
430                        sc->rect.y,
431                        sc->rect.width, sc->rect.height);
432         }
433     }
434 }
435
436 static void
437 handle_events (state *st)
438 {
439   XSync (st->dpy, False);
440   while (XPending (st->dpy))
441     {
442       XEvent event;
443       XNextEvent (st->dpy, &event);
444       if (event.xany.type == ConfigureNotify ||
445           event.xany.type == Expose)
446         {
447           XClearWindow (st->dpy, st->window);
448           reshape_memscroller (st);
449         }
450
451       screenhack_handle_event (st->dpy, &event);
452     }
453 }
454
455
456 \f
457 char *progclass = "MemScroller";
458
459 char *defaults [] = {
460   ".background:            black",
461   "*drawMode:              color",
462   "*seedMode:              ram",
463   ".textColor:             #00FF00",
464   ".foreground:            #00FF00",
465   "*borderSize:            2",
466   ".font:                  -*-courier-medium-r-*-*-*-1400-*-*-m-*-*-*",
467   "*delay:                 10000",
468   "*offset:                0",
469   0
470 };
471
472 XrmOptionDescRec options [] = {
473   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
474   { "-font",            ".font",                XrmoptionSepArg, 0 },
475   { "-color",           ".drawMode",            XrmoptionNoArg, "color"  },
476   { "-mono",            ".drawMode",            XrmoptionNoArg, "mono"   },
477   { "-ram",             ".seedMode",            XrmoptionNoArg, "ram"    },
478   { "-random",          ".seedMode",            XrmoptionNoArg, "random" },
479   { 0, 0, 0, 0 }
480 };
481
482
483 void
484 screenhack (Display *dpy, Window window)
485 {
486   state *st = init_memscroller (dpy, window);
487   int delay = get_integer_resource ("delay", "Integer");
488   reshape_memscroller (st);
489   while (1)
490     {
491       draw_memscroller (st);
492       XSync (dpy, False);
493       handle_events (st);
494       if (delay) usleep (delay);
495     }
496 }