http://se.aminet.net/pub/Linux/distributions/slackware/slackware-10.1/source/xap...
[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_FILE } seed_mode;
42   enum { DRAW_COLOR, DRAW_MONO } draw_mode;
43
44   char *filename;
45   FILE *in;
46
47   int nscrollers;
48   scroller *scrollers;
49
50 # ifdef HAVE_XSHM_EXTENSION
51   Bool shm_p;
52   XShmSegmentInfo shm_info;
53 # endif
54
55 } state;
56
57
58 state *
59 init_memscroller (Display *dpy, Window window)
60 {
61   int i;
62   XGCValues gcv;
63   state *st = (state *) calloc (1, sizeof (*st));
64   char *s;
65   st->dpy = dpy;
66   st->window = window;
67   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
68   XSelectInput(st->dpy, st->window, ExposureMask);
69
70   /* Fill up the colormap with random colors.
71      We don't actually use these explicitly, but in 8-bit mode,
72      they will be used implicitly by the random image bits. */
73   {
74     int ncolors = 255;
75     XColor colors[256];
76     make_random_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
77                           colors, &ncolors, True, True, 0, False);
78   }
79
80   st->border = get_integer_resource ("borderSize", "BorderSize");
81
82   {
83     char *fontname = get_string_resource ("font", "Font");
84     st->font = XLoadQueryFont (dpy, fontname);
85
86     if (!st->font)
87       {
88         static const char *fonts[] = {
89           "-*-courier-medium-r-*-*-*-1400-*-*-p-*-*-*",
90           "-*-courier-medium-r-*-*-*-600-*-*-p-*-*-*",
91           "-*-utopia-*-r-*-*-*-1400-*-*-p-*-*-*",
92           "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
93           "-*-utopia-*-r-*-*-*-240-*-*-p-*-*-*",
94           "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
95           "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
96           "fixed", 0 };
97         i = 0;
98         while (fonts[i])
99           {
100             st->font = XLoadQueryFont (dpy, fonts[i]);
101             if (st->font) break;
102             i++;
103           }
104         if (st->font)
105           fprintf (stderr, "%s: couldn't load font \"%s\", using \"%s\"\n",
106                    progname, fontname, fonts[i]);
107         else
108           {
109             fprintf(stderr, "%s: couldn't load any font\n", progname);
110             exit(-1);
111           }
112       }
113   }
114
115   gcv.line_width = st->border;
116
117   gcv.background = get_pixel_resource("background", "Background",
118                                       st->dpy, st->xgwa.colormap);
119   gcv.foreground = get_pixel_resource("textColor", "Foreground",
120                                       st->dpy, st->xgwa.colormap);
121   gcv.font = st->font->fid;
122   st->text_gc = XCreateGC (st->dpy, st->window,
123                            GCForeground|GCBackground|GCFont, &gcv);
124
125   gcv.foreground = get_pixel_resource("foreground", "Foreground",
126                                       st->dpy, st->xgwa.colormap);
127   st->draw_gc = XCreateGC (st->dpy, st->window,
128                            GCForeground|GCBackground|GCLineWidth,
129                            &gcv);
130   gcv.foreground = gcv.background;
131   st->erase_gc = XCreateGC (st->dpy, st->window,
132                             GCForeground|GCBackground, &gcv);
133
134
135   s = get_string_resource ("drawMode", "DrawMode");
136   if (!s || !*s || !strcasecmp (s, "color"))
137     st->draw_mode = DRAW_COLOR;
138   else if (!strcasecmp (s, "mono"))
139     st->draw_mode = DRAW_MONO;
140   else
141     {
142       fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
143                progname, s);
144       exit (1);
145     }
146   if (s) free (s);
147   s = 0;
148
149
150   st->filename = get_string_resource ("filename", "Filename");
151
152   if (!st->filename ||
153       !*st->filename ||
154       !strcasecmp (st->filename, "(ram)") ||
155       !strcasecmp (st->filename, "(mem)") ||
156       !strcasecmp (st->filename, "(memory)"))
157     st->seed_mode = SEED_RAM;
158   else if (st->filename &&
159            (!strcasecmp (st->filename, "(rand)") ||
160             !strcasecmp (st->filename, "(random)")))
161     st->seed_mode = SEED_RANDOM;
162   else
163     st->seed_mode = SEED_FILE;
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 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   unsigned int v;
283
284   /* Pack bytes as RGBR so that we don't have to worry about actually
285      figuring out the color channel order.
286    */
287 # undef PACK
288 # define PACK(r,g,b) ((r) | ((g) << 8) | ((b) << 16) | ((r) << 24))
289
290   switch (st->seed_mode)
291     {
292     case SEED_RAM:
293       if (himem == 0)
294         {
295           lomem = (unsigned char *) progname; /* not first malloc, but early */
296           himem = (unsigned char *)           /* not last malloc, but late */
297             st->scrollers[st->nscrollers-1].image->data;
298         }
299
300       if (sc->data < lomem)
301         sc->data = lomem;
302
303 # ifdef HAVE_SBRK  /* re-get it each time through */
304       himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
305 # endif
306
307       /* I don't understand what's going on there, but on MacOS X,
308          we're getting insane values for lomem and himem.  Is there
309          more than one heap? */
310       if ((unsigned long) himem - (unsigned long) lomem > 0x0FFFFFFF)
311         himem = lomem + 0xFFFF;
312
313       if (lomem >= himem) abort();
314
315     RETRY:
316       if (sc->data >= himem)
317         sc->data = lomem;
318
319       switch (st->draw_mode)
320         {
321         case DRAW_COLOR:
322           r = *sc->data++;
323           g = *sc->data++;
324           b = *sc->data++;
325           break;
326         case DRAW_MONO:
327           r = 0;
328           g = *sc->data++;
329           b = 0;
330           break;
331         default:
332           abort();
333         }
334
335       v = PACK(r,g,b);
336
337       /* avoid having many seconds of blackness: truncate zeros at 24K.
338        */
339       if (v == 0)
340         sc->count_zero++;
341       else
342         sc->count_zero = 0;
343       if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
344         goto RETRY;
345
346       break;
347
348     case SEED_RANDOM:
349       v = random();
350       switch (st->draw_mode)
351         {
352         case DRAW_COLOR:
353           r = (v >> 16) & 0xFF;
354           g = (v >>  8) & 0xFF;
355           b = (v      ) & 0xFF;
356           break;
357         case DRAW_MONO:
358           r = 0;
359           g = v & 0xFF;
360           b = 0;
361           break;
362         default:
363           abort();
364         }
365       v = PACK(r,g,b);
366       break;
367
368     case SEED_FILE:
369       {
370         int i;
371
372   /* this one returns only bytes from the file */
373 # define GETC(V) \
374             do { \
375               i = fgetc (st->in); \
376             } while (i == EOF \
377                      ? (open_file (st), 1) \
378                      : 0); \
379             V = i
380
381   /* this one returns a null at EOF -- else we hang on zero-length files */
382 # undef GETC
383 # define GETC(V) \
384             i = fgetc (st->in); \
385             if (i == EOF) { i = 0; open_file (st); } \
386             V = i
387
388         if (!st->in)
389           open_file (st);
390
391         switch (st->draw_mode)
392           {
393           case DRAW_COLOR:
394             GETC(r);
395             GETC(g);
396             GETC(b);
397             break;
398           case DRAW_MONO:
399             r = 0;
400             GETC(g);
401             b = 0;
402             break;
403           default:
404             abort();
405           }
406 # undef GETC
407         v = PACK(r,g,b);
408       }
409       break;
410
411     default:
412       abort();
413     }
414
415 # undef PACK
416   return v;
417 }
418
419
420 static void
421 draw_string (state *st, unsigned int n)
422 {
423   char buf[40];
424   int direction, ascent, descent;
425   XCharStruct overall;
426   int x, y, w, h;
427   int bot = st->scrollers[0].rect.y;
428   const char *fmt = "%08X";
429
430   sprintf (buf, fmt, 0);
431   XTextExtents (st->font, buf, strlen(buf), 
432                 &direction, &ascent, &descent, &overall);
433   sprintf (buf, "%08X", n);
434
435   w = overall.width;
436   h = st->font->ascent + st->font->descent + 1;
437   x = (st->xgwa.width - w) / 2;
438   y = (bot - h) / 2;
439
440   if (y + h + 10 > bot) return;
441
442   XFillRectangle (st->dpy, st->window, st->erase_gc,
443                   x-w, y, w*3, h);
444   XDrawString (st->dpy, st->window, st->text_gc,
445                x, y + st->font->ascent, buf, strlen(buf));
446 }
447
448
449 static void
450 draw_memscroller (state *st)
451 {
452   int i;
453
454   draw_string (st, *((unsigned int *) st->scrollers[0].image->data));
455
456   for (i = 0; i < st->nscrollers; i++)
457     {
458       scroller *sc = &st->scrollers[i];
459       int j;
460
461       XCopyArea (st->dpy, st->window, st->window, st->draw_gc,
462                  sc->rect.x + sc->speed, sc->rect.y,
463                  sc->rect.width - sc->speed, sc->rect.height,
464                  sc->rect.x, sc->rect.y);
465
466       if (sc->scroll_tick == 0)
467         {
468           int top = ((sc->image->bytes_per_line * sc->rect.height) /
469                      (4 * sc->rez));
470           unsigned int *out = (unsigned int *) sc->image->data;
471           for (j = 0; j < top; j++)
472             {
473               unsigned int v = more_bits(st, sc);
474               int k;
475               for (k = 0; k < sc->rez; k++)
476                 *out++ = v;
477             }
478         }
479
480       sc->scroll_tick++;
481       if (sc->scroll_tick * sc->speed >= sc->rez)
482         sc->scroll_tick = 0;
483
484       for (j = 0; j < sc->speed; j++)
485         {
486 # ifdef HAVE_XSHM_EXTENSION
487           if (st->shm_p)
488             XShmPutImage (st->dpy, st->window, st->draw_gc, sc->image,
489                           0, 0,
490                           sc->rect.x + sc->rect.width - sc->image->width - j,
491                           sc->rect.y,
492                           sc->rect.width, sc->rect.height,
493                           False);
494           else
495 # endif /* HAVE_XSHM_EXTENSION */
496             XPutImage (st->dpy, st->window, st->draw_gc, sc->image,
497                        0, 0,
498                        sc->rect.x + sc->rect.width - sc->image->width - j,
499                        sc->rect.y,
500                        sc->rect.width, sc->rect.height);
501         }
502     }
503 }
504
505 static void
506 handle_events (state *st)
507 {
508   XSync (st->dpy, False);
509   while (XPending (st->dpy))
510     {
511       XEvent event;
512       XNextEvent (st->dpy, &event);
513       if (event.xany.type == ConfigureNotify ||
514           event.xany.type == Expose)
515         {
516           XClearWindow (st->dpy, st->window);
517           reshape_memscroller (st);
518         }
519
520       screenhack_handle_event (st->dpy, &event);
521     }
522 }
523
524
525 \f
526 char *progclass = "MemScroller";
527
528 char *defaults [] = {
529   ".background:            black",
530   "*drawMode:              color",
531   "*filename:              (RAM)",
532   ".textColor:             #00FF00",
533   ".foreground:            #00FF00",
534   "*borderSize:            2",
535   ".font:                  -*-courier-medium-r-*-*-*-1400-*-*-m-*-*-*",
536   "*delay:                 10000",
537   "*offset:                0",
538   0
539 };
540
541 XrmOptionDescRec options [] = {
542   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
543   { "-font",            ".font",                XrmoptionSepArg, 0 },
544   { "-filename",        ".filename",            XrmoptionSepArg, 0 },
545   { "-color",           ".drawMode",            XrmoptionNoArg, "color"    },
546   { "-mono",            ".drawMode",            XrmoptionNoArg, "mono"     },
547   { "-ram",             ".filename",            XrmoptionNoArg, "(RAM)"    },
548   { "-random",          ".filename",            XrmoptionNoArg, "(RANDOM)" },
549   { 0, 0, 0, 0 }
550 };
551
552
553 void
554 screenhack (Display *dpy, Window window)
555 {
556   state *st = init_memscroller (dpy, window);
557   int delay = get_integer_resource ("delay", "Integer");
558   reshape_memscroller (st);
559   while (1)
560     {
561       draw_memscroller (st);
562       XSync (dpy, False);
563       handle_events (st);
564       if (delay) usleep (delay);
565     }
566 }