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