1 /* xscreensaver, Copyright (c) 2002, 2004, 2006 Jamie Zawinski <jwz@jwz.org>
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
11 * Memscroller -- scrolls a dump of its own RAM across the screen.
14 #include "screenhack.h"
17 #ifdef HAVE_XSHM_EXTENSION
22 #define countof(x) (sizeof(x)/sizeof(*(x)))
39 XWindowAttributes xgwa;
40 GC draw_gc, erase_gc, text_gc;
41 XFontStruct *fonts[4];
44 enum { SEED_RAM, SEED_RANDOM, SEED_FILE } seed_mode;
45 enum { DRAW_COLOR, DRAW_MONO } draw_mode;
53 # ifdef HAVE_XSHM_EXTENSION
55 XShmSegmentInfo shm_info;
63 static void reshape_memscroller (state *st);
67 memscroller_init (Display *dpy, Window window)
71 state *st = (state *) calloc (1, sizeof (*st));
75 st->delay = get_integer_resource (dpy, "delay", "Integer");
77 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
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. */
85 make_random_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
86 colors, &ncolors, True, True, 0, False);
89 st->border = get_integer_resource (dpy, "borderSize", "BorderSize");
93 int nfonts = countof (st->fonts);
94 for (i = nfonts-1; i >= 0; i--)
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)
103 fprintf (stderr, "%s: unable to load font: \"%s\"\n",
105 st->fonts[i] = st->fonts[i+1];
110 fprintf (stderr, "%s: unable to load any fonts!", progname);
115 gcv.line_width = st->border;
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);
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,
129 gcv.foreground = gcv.background;
130 st->erase_gc = XCreateGC (st->dpy, st->window,
131 GCForeground|GCBackground, &gcv);
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;
141 fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
149 st->filename = get_string_resource (dpy, "filename", "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;
162 st->seed_mode = SEED_FILE;
165 st->scrollers = (scroller *) calloc (st->nscrollers, sizeof(scroller));
167 for (i = 0; i < st->nscrollers; i++)
169 scroller *sc = &st->scrollers[i];
170 int max_height = 4096;
176 # ifdef HAVE_XSHM_EXTENSION
177 st->shm_p = get_boolean_resource (dpy, "useSHM", "Boolean");
180 sc->image = create_xshm_image (st->dpy, st->xgwa.visual,
182 ZPixmap, 0, &st->shm_info,
187 # endif /* HAVE_XSHM_EXTENSION */
190 sc->image = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
191 ZPixmap, 0, 0, 1, max_height, 8, 0);
193 if (sc->image && !sc->image->data)
194 sc->image->data = (char *)
195 malloc (sc->image->bytes_per_line * sc->image->height + 1);
197 if (!sc->image || !sc->image->data)
199 fprintf (stderr, "%s: out of memory (allocating 1x%d image)\n",
200 progname, sc->image->height);
205 reshape_memscroller (st);
211 reshape_memscroller (state *st)
215 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
217 for (i = 0; i < st->nscrollers; i++)
219 scroller *sc = &st->scrollers[i];
223 sc->rez = 6; /* #### */
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);
230 sc->rect.x = (st->xgwa.width - sc->rect.width) / 2;
231 sc->rect.y = (st->xgwa.height - sc->rect.height) / 2;
235 scroller *sc0 = &st->scrollers[i-1];
236 sc->rez = sc0->rez * 1.8;
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);
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);
257 open_file (state *st)
265 st->in = fopen (st->filename, "r");
269 sprintf (buf, "%s: %s", progname, st->filename);
277 more_bits (state *st, scroller *sc)
279 static unsigned char *lomem = 0;
280 static unsigned char *himem = 0;
281 unsigned char r, g, b;
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.
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);
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.
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) | \
305 switch (st->seed_mode)
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;
315 if (sc->data < lomem)
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
323 himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
326 if (!lomem || !himem)
328 /* bad craziness! give up! */
329 st->seed_mode = SEED_RANDOM;
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) {
338 fprintf (stderr, "%s: wonky: 0x%08x - 0x%08x = 0x%08x\n", progname,
339 (unsigned long) himem, (unsigned long) lomem,
340 (unsigned long) himem - (unsigned long) lomem);
342 himem = lomem + 0xFFFF;
345 if (lomem >= himem) abort();
348 if (sc->data >= himem)
351 switch (st->draw_mode)
357 vv = (vv << 24) | (r << 16) | (g << 8) | b;
371 /* avoid having many seconds of blackness: truncate zeros at 24K.
377 if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
384 switch (st->draw_mode)
387 r = (vv >> 16) & 0xFF;
388 g = (vv >> 8) & 0xFF;
406 /* this one returns only bytes from the file */
409 i = fgetc (st->in); \
411 ? (open_file (st), 1) \
415 /* this one returns a null at EOF -- else we hang on zero-length files */
418 i = fgetc (st->in); \
419 if (i == EOF) { i = 0; open_file (st); } \
425 switch (st->draw_mode)
431 vv = (vv << 24) | (r << 16) | (g << 8) | b;
459 draw_string (state *st)
462 int direction, ascent, descent;
463 int bot = st->scrollers[0].rect.y;
464 const char *fmt = "%08X";
467 /* Draw the first font that fits.
469 for (i = 0; i < countof (st->fonts); i++)
474 if (! st->fonts[i]) continue;
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);
482 h = ascent + descent + 1;
483 x = (st->xgwa.width - w) / 2;
486 if (y + h + 10 <= bot)
488 XSetFont (st->dpy, st->text_gc, st->fonts[i]->fid);
489 XFillRectangle (st->dpy, st->window, st->erase_gc,
491 XDrawString (st->dpy, st->window, st->text_gc,
492 x, y + ascent, buf, strlen(buf));
500 memscroller_draw (Display *dpy, Window window, void *closure)
502 state *st = (state *) closure;
506 for (i = 0; i < st->nscrollers; i++)
508 scroller *sc = &st->scrollers[i];
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);
516 if (sc->scroll_tick == 0)
518 int top = ((sc->image->bytes_per_line * sc->rect.height) /
520 unsigned int *out = (unsigned int *) sc->image->data;
521 for (j = 0; j < top; j++)
523 unsigned int v = more_bits(st, sc);
525 for (k = 0; k < sc->rez; k++)
531 if (sc->scroll_tick * sc->speed >= sc->rez)
534 for (j = 0; j < sc->speed; j++)
536 # ifdef HAVE_XSHM_EXTENSION
538 XShmPutImage (st->dpy, st->window, st->draw_gc, sc->image,
540 sc->rect.x + sc->rect.width - sc->image->width - j,
542 sc->rect.width, sc->rect.height,
545 # endif /* HAVE_XSHM_EXTENSION */
546 XPutImage (st->dpy, st->window, st->draw_gc, sc->image,
548 sc->rect.x + sc->rect.width - sc->image->width - j,
550 sc->rect.width, sc->rect.height);
559 memscroller_reshape (Display *dpy, Window window, void *closure,
560 unsigned int w, unsigned int h)
562 state *st = (state *) closure;
563 XClearWindow (st->dpy, st->window);
564 reshape_memscroller (st);
568 memscroller_event (Display *dpy, Window window, void *closure, XEvent *event)
575 memscroller_free (Display *dpy, Window window, void *closure)
580 static const char *memscroller_defaults [] = {
581 ".background: black",
584 ".textColor: #00FF00",
585 ".foreground: #00FF00",
587 ".font1: -*-courier-medium-r-*-*-*-1400-*-*-m-*-*-*",
588 ".font2: -*-courier-medium-r-*-*-*-600-*-*-m-*-*-*",
589 ".font3: -*-courier-medium-r-*-*-*-180-*-*-m-*-*-*",
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)" },
607 XSCREENSAVER_MODULE ("MemScroller", memscroller)