http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / memscroller.c
1 /* xscreensaver, Copyright (c) 2002, 2004, 2006 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
17 #ifdef HAVE_XSHM_EXTENSION
18 #include "xshm.h"
19 #endif
20
21 #undef countof
22 #define countof(x) (sizeof(x)/sizeof(*(x)))
23
24 typedef struct {
25   int which;
26   XRectangle rect;
27   XImage *image;
28   int rez;
29   int speed;
30   int scroll_tick;
31   unsigned int value;
32   unsigned char *data;
33   int count_zero;
34 } scroller;
35
36 typedef struct {
37   Display *dpy;
38   Window window;
39   XWindowAttributes xgwa;
40   GC draw_gc, erase_gc, text_gc;
41   XFontStruct *fonts[4];
42   int border;
43
44   enum { SEED_RAM, SEED_RANDOM, SEED_FILE } seed_mode;
45   enum { DRAW_COLOR, DRAW_MONO } draw_mode;
46
47   char *filename;
48   FILE *in;
49
50   int nscrollers;
51   scroller *scrollers;
52
53 # ifdef HAVE_XSHM_EXTENSION
54   Bool shm_p;
55   XShmSegmentInfo shm_info;
56 # endif
57
58   int delay;
59
60 } state;
61
62
63 static void reshape_memscroller (state *st);
64
65
66 static void *
67 memscroller_init (Display *dpy, Window window)
68 {
69   int i;
70   XGCValues gcv;
71   state *st = (state *) calloc (1, sizeof (*st));
72   char *s;
73   st->dpy = dpy;
74   st->window = window;
75   st->delay = get_integer_resource (dpy, "delay", "Integer");
76
77   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
78
79   /* Fill up the colormap with random colors.
80      We don't actually use these explicitly, but in 8-bit mode,
81      they will be used implicitly by the random image bits. */
82   {
83     int ncolors = 255;
84     XColor colors[256];
85     make_random_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
86                           colors, &ncolors, True, True, 0, False);
87   }
88
89   st->border = get_integer_resource (dpy, "borderSize", "BorderSize");
90
91   {
92     int i;
93     int nfonts = countof (st->fonts);
94     for (i = nfonts-1; i >= 0; i--)
95       {
96         char *fontname;
97         char res[20];
98         sprintf (res, "font%d", i+1);
99         fontname = get_string_resource (dpy, res, "Font");
100         st->fonts[i] = XLoadQueryFont (dpy, fontname);
101         if (!st->fonts[i] && i < nfonts-1)
102           {
103             fprintf (stderr, "%s: unable to load font: \"%s\"\n",
104                      progname, fontname);
105             st->fonts[i] = st->fonts[i+1];
106           }
107       }
108     if (!st->fonts[0])
109       {
110         fprintf (stderr, "%s: unable to load any fonts!", progname);
111         exit (1);
112       }
113   }
114
115   gcv.line_width = st->border;
116
117   gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
118                                       "background", "Background");
119   gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
120                                       "textColor", "Foreground");
121   st->text_gc = XCreateGC (st->dpy, st->window,
122                            GCForeground|GCBackground, &gcv);
123
124   gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
125                                       "foreground", "Foreground");
126   st->draw_gc = XCreateGC (st->dpy, st->window,
127                            GCForeground|GCBackground|GCLineWidth,
128                            &gcv);
129   gcv.foreground = gcv.background;
130   st->erase_gc = XCreateGC (st->dpy, st->window,
131                             GCForeground|GCBackground, &gcv);
132
133
134   s = get_string_resource (dpy, "drawMode", "DrawMode");
135   if (!s || !*s || !strcasecmp (s, "color"))
136     st->draw_mode = DRAW_COLOR;
137   else if (!strcasecmp (s, "mono"))
138     st->draw_mode = DRAW_MONO;
139   else
140     {
141       fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
142                progname, s);
143       exit (1);
144     }
145   if (s) free (s);
146   s = 0;
147
148
149   st->filename = get_string_resource (dpy, "filename", "Filename");
150
151   if (!st->filename ||
152       !*st->filename ||
153       !strcasecmp (st->filename, "(ram)") ||
154       !strcasecmp (st->filename, "(mem)") ||
155       !strcasecmp (st->filename, "(memory)"))
156     st->seed_mode = SEED_RAM;
157   else if (st->filename &&
158            (!strcasecmp (st->filename, "(rand)") ||
159             !strcasecmp (st->filename, "(random)")))
160     st->seed_mode = SEED_RANDOM;
161   else
162     st->seed_mode = SEED_FILE;
163
164   st->nscrollers = 3;
165   st->scrollers = (scroller *) calloc (st->nscrollers, sizeof(scroller));
166
167   for (i = 0; i < st->nscrollers; i++)
168     {
169       scroller *sc = &st->scrollers[i];
170       int max_height = 4096;
171
172       sc->which = i;
173       sc->speed = i+1;
174
175       sc->image = 0;
176 # ifdef HAVE_XSHM_EXTENSION
177       st->shm_p = get_boolean_resource (dpy, "useSHM", "Boolean");
178       if (st->shm_p)
179         {
180           sc->image = create_xshm_image (st->dpy, st->xgwa.visual,
181                                          st->xgwa.depth,
182                                          ZPixmap, 0, &st->shm_info,
183                                          1, max_height);
184           if (! sc->image)
185             st->shm_p = False;
186         }
187 # endif /* HAVE_XSHM_EXTENSION */
188
189       if (!sc->image)
190         sc->image = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
191                                   ZPixmap, 0, 0, 1, max_height, 8, 0);
192
193       if (sc->image && !sc->image->data)
194         sc->image->data = (char *)
195           malloc (sc->image->bytes_per_line * sc->image->height + 1);
196
197       if (!sc->image || !sc->image->data)
198         {
199           fprintf (stderr, "%s: out of memory (allocating 1x%d image)\n",
200                    progname, sc->image->height);
201           exit (1);
202         }
203     }
204
205   reshape_memscroller (st);
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 void
257 open_file (state *st)
258 {
259   if (st->in)
260     {
261       fclose (st->in);
262       st->in = 0;
263     }
264
265   st->in = fopen (st->filename, "r");
266   if (!st->in)
267     {
268       char buf[1024];
269       sprintf (buf, "%s: %s", progname, st->filename);
270       perror (buf);
271       exit (1);
272     }
273 }
274
275
276 static unsigned int
277 more_bits (state *st, scroller *sc)
278 {
279   static unsigned char *lomem = 0;
280   static unsigned char *himem = 0;
281   unsigned char r, g, b;
282
283   /* vv: Each incoming byte rolls through all 4 bytes of this (it is sc->value)
284          This is the number displayed at the top.
285      pv: the pixel color value.  incoming bytes land in R,G,B, or maybe just G.
286    */
287   unsigned int vv, pv;
288
289   unsigned int rmsk = st->scrollers[0].image->red_mask;
290   unsigned int gmsk = st->scrollers[0].image->green_mask;
291   unsigned int bmsk = st->scrollers[0].image->blue_mask;
292   unsigned int amsk = ~(rmsk | gmsk | bmsk);
293
294   vv = sc->value;
295
296   /* Pack RGB into a pixel according to the XImage component masks;
297      set the remaining bits to 1 for the benefit of HAVE_COCOA alpha.
298    */
299 # undef PACK
300 # define PACK() ((((r << 24) | (r << 16) | (r << 8) | r) & rmsk) | \
301                  (((g << 24) | (g << 16) | (g << 8) | g) & gmsk) | \
302                  (((b << 24) | (b << 16) | (b << 8) | b) & bmsk) | \
303                  amsk)
304
305   switch (st->seed_mode)
306     {
307     case SEED_RAM:
308       if (himem == 0)
309         {
310           lomem = (unsigned char *) progname; /* not first malloc, but early */
311           himem = (unsigned char *)           /* not last malloc, but late */
312             st->scrollers[st->nscrollers-1].image->data;
313         }
314
315       if (sc->data < lomem)
316         sc->data = lomem;
317
318 # ifdef HAVE_SBRK  /* re-get it each time through */
319       /* "The brk and sbrk functions are historical curiosities left over
320          from earlier days before the advent of virtual memory management."
321             -- sbrk(2) man page on MacOS
322        */
323       himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
324 # endif
325
326       if (!lomem || !himem) 
327         {
328           /* bad craziness! give up! */
329           st->seed_mode = SEED_RANDOM;
330           return 0;
331         }
332
333       /* I don't understand what's going on there, but on MacOS X, we're
334          getting insane values for lomem and himem (both Xlib and HAVE_COCOA).
335          Does malloc() draw from more than one heap? */
336       if ((unsigned long) himem - (unsigned long) lomem > 0x0FFFFFFF) {
337 # if 0
338         fprintf (stderr, "%s: wonky: 0x%08x - 0x%08x = 0x%08x\n", progname,
339                  (unsigned int) himem,  (unsigned int) lomem,
340                  (unsigned int) himem - (unsigned int) lomem);
341 # endif
342         himem = lomem + 0xFFFF;
343       }
344
345       if (lomem >= himem) abort();
346
347     RETRY:
348       if (sc->data >= himem)
349         sc->data = lomem;
350
351       switch (st->draw_mode)
352         {
353         case DRAW_COLOR:
354           r = *sc->data++;
355           g = *sc->data++;
356           b = *sc->data++;
357           vv = (vv << 24) | (r << 16) | (g << 8) | b;
358           break;
359         case DRAW_MONO:
360           r = 0;
361           g = *sc->data++;
362           b = 0;
363           vv = (vv << 8) | g;
364           break;
365         default:
366           abort();
367         }
368
369       pv = PACK();
370
371       /* avoid having many seconds of blackness: truncate zeros at 24K.
372        */
373       if (vv == 0)
374         sc->count_zero++;
375       else
376         sc->count_zero = 0;
377       if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
378         goto RETRY;
379
380       break;
381
382     case SEED_RANDOM:
383       vv = random();
384       switch (st->draw_mode)
385         {
386         case DRAW_COLOR:
387           r = (vv >> 16) & 0xFF;
388           g = (vv >>  8) & 0xFF;
389           b = (vv      ) & 0xFF;
390           break;
391         case DRAW_MONO:
392           r = 0;
393           g = vv & 0xFF;
394           b = 0;
395           break;
396         default:
397           abort();
398         }
399       pv = PACK();
400       break;
401
402     case SEED_FILE:
403       {
404         int i;
405
406   /* this one returns only bytes from the file */
407 # define GETC(V) \
408             do { \
409               i = fgetc (st->in); \
410             } while (i == EOF \
411                      ? (open_file (st), 1) \
412                      : 0); \
413             V = i
414
415   /* this one returns a null at EOF -- else we hang on zero-length files */
416 # undef GETC
417 # define GETC(V) \
418             i = fgetc (st->in); \
419             if (i == EOF) { i = 0; open_file (st); } \
420             V = i
421
422         if (!st->in)
423           open_file (st);
424
425         switch (st->draw_mode)
426           {
427           case DRAW_COLOR:
428             GETC(r);
429             GETC(g);
430             GETC(b);
431             vv = (vv << 24) | (r << 16) | (g << 8) | b;
432             break;
433           case DRAW_MONO:
434             r = 0;
435             GETC(g);
436             b = 0;
437             vv = (vv << 8) | g;
438             break;
439           default:
440             abort();
441           }
442 # undef GETC
443         pv = PACK();
444       }
445       break;
446
447     default:
448       abort();
449     }
450
451 # undef PACK
452
453   sc->value = vv;
454   return pv;
455 }
456
457
458 static void
459 draw_string (state *st)
460 {
461   char buf[40];
462   int direction, ascent, descent;
463   int bot = st->scrollers[0].rect.y;
464   const char *fmt = "%08X";
465   int i;
466
467   /* Draw the first font that fits.
468    */
469   for (i = 0; i < countof (st->fonts); i++)
470     {
471       XCharStruct overall;
472       int x, y, w, h;
473
474       if (! st->fonts[i]) continue;
475
476       sprintf (buf, fmt, 0);
477       XTextExtents (st->fonts[i], buf, strlen(buf), 
478                     &direction, &ascent, &descent, &overall);
479       sprintf (buf, "%08X", st->scrollers[0].value);
480
481       w = overall.width;
482       h = ascent + descent + 1;
483       x = (st->xgwa.width - w) / 2;
484       y = (bot - h) / 2;
485
486       if (y + h + 10 <= bot)
487         {
488           XSetFont (st->dpy, st->text_gc, st->fonts[i]->fid);
489           XFillRectangle (st->dpy, st->window, st->erase_gc,
490                           x-w, y, w*3, h);
491           XDrawString (st->dpy, st->window, st->text_gc,
492                        x, y + ascent, buf, strlen(buf));
493           break;
494         }
495     }
496 }
497
498
499 static unsigned long
500 memscroller_draw (Display *dpy, Window window, void *closure)
501 {
502   state *st = (state *) closure;
503   int i;
504   draw_string (st);
505
506   for (i = 0; i < st->nscrollers; i++)
507     {
508       scroller *sc = &st->scrollers[i];
509       int j;
510
511       XCopyArea (st->dpy, st->window, st->window, st->draw_gc,
512                  sc->rect.x + sc->speed, sc->rect.y,
513                  sc->rect.width - sc->speed, sc->rect.height,
514                  sc->rect.x, sc->rect.y);
515
516       if (sc->scroll_tick == 0)
517         {
518           int top = ((sc->image->bytes_per_line * sc->rect.height) /
519                      (4 * sc->rez));
520           unsigned int *out = (unsigned int *) sc->image->data;
521           for (j = 0; j < top; j++)
522             {
523               unsigned int v = more_bits(st, sc);
524               int k;
525               for (k = 0; k < sc->rez; k++)
526                 *out++ = v;
527             }
528         }
529
530       sc->scroll_tick++;
531       if (sc->scroll_tick * sc->speed >= sc->rez)
532         sc->scroll_tick = 0;
533
534       for (j = 0; j < sc->speed; j++)
535         {
536 # ifdef HAVE_XSHM_EXTENSION
537           if (st->shm_p)
538             XShmPutImage (st->dpy, st->window, st->draw_gc, sc->image,
539                           0, 0,
540                           sc->rect.x + sc->rect.width - sc->image->width - j,
541                           sc->rect.y,
542                           sc->rect.width, sc->rect.height,
543                           False);
544           else
545 # endif /* HAVE_XSHM_EXTENSION */
546             XPutImage (st->dpy, st->window, st->draw_gc, sc->image,
547                        0, 0,
548                        sc->rect.x + sc->rect.width - sc->image->width - j,
549                        sc->rect.y,
550                        sc->rect.width, sc->rect.height);
551         }
552     }
553
554   return st->delay;
555 }
556
557
558 static void
559 memscroller_reshape (Display *dpy, Window window, void *closure, 
560                  unsigned int w, unsigned int h)
561 {
562   state *st = (state *) closure;
563   XClearWindow (st->dpy, st->window);
564   reshape_memscroller (st);
565 }
566
567 static Bool
568 memscroller_event (Display *dpy, Window window, void *closure, XEvent *event)
569 {
570   return False;
571 }
572
573
574 static void
575 memscroller_free (Display *dpy, Window window, void *closure)
576 {
577 }
578
579
580 static const char *memscroller_defaults [] = {
581   ".background:            black",
582   "*drawMode:              color",
583   "*filename:              (RAM)",
584   ".textColor:             #00FF00",
585   ".foreground:            #00FF00",
586   "*borderSize:            2",
587   ".font1:                 -*-courier-bold-r-*-*-*-1400-*-*-m-*-*-*",
588   ".font2:                 -*-courier-bold-r-*-*-*-600-*-*-m-*-*-*",
589   ".font3:                 -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
590   ".font4:                 fixed",
591   "*delay:                 10000",
592   "*offset:                0",
593   0
594 };
595
596 static XrmOptionDescRec memscroller_options [] = {
597   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
598   { "-font",            ".font",                XrmoptionSepArg, 0 },
599   { "-filename",        ".filename",            XrmoptionSepArg, 0 },
600   { "-color",           ".drawMode",            XrmoptionNoArg, "color"    },
601   { "-mono",            ".drawMode",            XrmoptionNoArg, "mono"     },
602   { "-ram",             ".filename",            XrmoptionNoArg, "(RAM)"    },
603   { "-random",          ".filename",            XrmoptionNoArg, "(RANDOM)" },
604   { 0, 0, 0, 0 }
605 };
606
607 XSCREENSAVER_MODULE ("MemScroller", memscroller)