1 /* xscreensaver, Copyright (c) 2002-2018 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"
19 #define countof(x) (sizeof(x)/sizeof(*(x)))
40 XWindowAttributes xgwa;
41 GC draw_gc, erase_gc, text_gc;
42 XFontStruct *fonts[6];
45 enum { SEED_RAM, SEED_RANDOM, SEED_FILE } seed_mode;
46 enum { DRAW_COLOR, DRAW_MONO } draw_mode;
54 XShmSegmentInfo shm_info;
61 static void reshape_memscroller (state *st);
65 memscroller_init (Display *dpy, Window window)
69 state *st = (state *) calloc (1, sizeof (*st));
73 st->delay = get_integer_resource (dpy, "delay", "Integer");
75 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
77 /* Fill up the colormap with random colors.
78 We don't actually use these explicitly, but in 8-bit mode,
79 they will be used implicitly by the random image bits. */
83 make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
84 colors, &ncolors, True, True, 0, False);
87 st->border = get_integer_resource (dpy, "borderSize", "BorderSize");
88 if (st->xgwa.width > 2560) st->border *= 2; /* Retina displays */
92 int nfonts = countof (st->fonts);
93 for (i = nfonts-1; i >= 0; i--)
97 sprintf (res, "font%d", i+1);
98 fontname = get_string_resource (dpy, res, "Font");
99 /* Each resource can be a comma-separated list of font names.
100 We use the first one that exists. */
101 if (fontname && *fontname)
103 char *f2 = strdup(fontname);
104 char *f, *token = f2;
105 while ((f = strtok(token, ",")) && !st->fonts[i])
108 while (*f == ' ' || *f == '\t') f++;
109 st->fonts[i] = load_font_retry (dpy, f);
112 if (!st->fonts[i] && i < nfonts-1)
114 fprintf (stderr, "%s: unable to load font: \"%s\"\n",
116 st->fonts[i] = st->fonts[i+1];
122 st->fonts[0] = load_font_retry (dpy, "fixed");
123 if (!st->fonts[0]) abort();
126 gcv.line_width = st->border;
128 gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
129 "background", "Background");
130 gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
131 "textColor", "Foreground");
132 st->text_gc = XCreateGC (st->dpy, st->window,
133 GCForeground|GCBackground, &gcv);
135 gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
136 "foreground", "Foreground");
137 st->draw_gc = XCreateGC (st->dpy, st->window,
138 GCForeground|GCBackground|GCLineWidth,
140 gcv.foreground = gcv.background;
141 st->erase_gc = XCreateGC (st->dpy, st->window,
142 GCForeground|GCBackground, &gcv);
145 s = get_string_resource (dpy, "drawMode", "DrawMode");
146 if (!s || !*s || !strcasecmp (s, "color"))
147 st->draw_mode = DRAW_COLOR;
148 else if (!strcasecmp (s, "mono"))
149 st->draw_mode = DRAW_MONO;
152 fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
161 st->filename = get_string_resource (dpy, "filename", "Filename");
166 !strcasecmp (st->filename, "(ram)") ||
167 !strcasecmp (st->filename, "(mem)") ||
168 !strcasecmp (st->filename, "(memory)"))
169 st->seed_mode = SEED_RAM;
171 else if (st->filename &&
172 (!strcasecmp (st->filename, "(rand)") ||
173 !strcasecmp (st->filename, "(random)")))
174 st->seed_mode = SEED_RANDOM;
176 st->seed_mode = SEED_FILE;
178 st->seed_mode = SEED_RANDOM;
182 st->scrollers = (scroller *) calloc (st->nscrollers, sizeof(scroller));
184 for (i = 0; i < st->nscrollers; i++)
186 scroller *sc = &st->scrollers[i];
187 int max_height = 4096;
192 if (st->xgwa.width > 2560) sc->speed *= 2.5; /* Retina displays */
194 sc->image = create_xshm_image (st->dpy, st->xgwa.visual,
196 ZPixmap, &st->shm_info,
201 fprintf (stderr, "%s: out of memory (allocating 1x%d image)\n",
202 progname, sc->image->height);
207 reshape_memscroller (st);
213 reshape_memscroller (state *st)
217 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
219 for (i = 0; i < st->nscrollers; i++)
221 scroller *sc = &st->scrollers[i];
225 sc->rez = 6; /* #### */
227 if (st->xgwa.width > 2560) sc->rez *= 2.5; /* Retina displays */
229 sc->rect.width = (((int) (st->xgwa.width * 0.8)
230 / sc->rez) * sc->rez);
231 sc->rect.height = (((int) (st->xgwa.height * 0.3)
232 / sc->rez) * sc->rez);
234 sc->rect.x = (st->xgwa.width - sc->rect.width) / 2;
235 sc->rect.y = (st->xgwa.height - sc->rect.height) / 2;
239 scroller *sc0 = &st->scrollers[i-1];
240 sc->rez = sc0->rez * 1.8;
242 sc->rect.x = sc0->rect.x;
243 sc->rect.y = (sc0->rect.y + sc0->rect.height + st->border
244 + (st->border + 2) * 7);
245 sc->rect.width = sc0->rect.width;
246 sc->rect.height = (((int) (st->xgwa.height * 0.1)
247 / sc->rez) * sc->rez);
250 XDrawRectangle (st->dpy, st->window, st->draw_gc,
251 sc->rect.x - st->border*2,
252 sc->rect.y - st->border*2,
253 sc->rect.width + st->border*4,
254 sc->rect.height + st->border*4);
262 open_file (state *st)
270 st->in = fopen (st->filename, "r");
274 sprintf (buf, "%s: %s", progname, st->filename);
282 /* "The brk and sbrk functions are historical curiosities left over
283 from earlier days before the advent of virtual memory management."
284 -- sbrk(2) man page on BSD systems, as of 1995 or so.
287 # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) /* gcc >= 4.2 */
288 /* Don't print "warning: 'sbrk' is deprecated". */
289 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
295 more_bits (state *st, scroller *sc)
297 static unsigned char *lomem = 0;
298 static unsigned char *himem = 0;
299 unsigned char r, g, b;
301 /* vv: Each incoming byte rolls through all 4 bytes of this (it is sc->value)
302 This is the number displayed at the top.
303 pv: the pixel color value. incoming bytes land in R,G,B, or maybe just G.
307 unsigned int rmsk = st->scrollers[0].image->red_mask;
308 unsigned int gmsk = st->scrollers[0].image->green_mask;
309 unsigned int bmsk = st->scrollers[0].image->blue_mask;
310 unsigned int amsk = ~(rmsk | gmsk | bmsk);
314 /* Pack RGB into a pixel according to the XImage component masks;
315 set the remaining bits to 1 for the benefit of HAVE_JWXYZ alpha.
318 # define PACK() ((((r << 24) | (r << 16) | (r << 8) | r) & rmsk) | \
319 (((g << 24) | (g << 16) | (g << 8) | g) & gmsk) | \
320 (((b << 24) | (b << 16) | (b << 8) | b) & bmsk) | \
323 switch (st->seed_mode)
328 lomem = (unsigned char *) progname; /* not first malloc, but early */
329 himem = (unsigned char *) /* not last malloc, but late */
330 st->scrollers[st->nscrollers-1].image->data;
333 if (sc->data < lomem)
336 # ifdef HAVE_SBRK /* re-get it each time through */
337 himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
340 if (!lomem || !himem)
342 /* bad craziness! give up! */
343 st->seed_mode = SEED_RANDOM;
347 /* I don't understand what's going on there, but on MacOS X, we're
348 getting insane values for lomem and himem (both Xlib and HAVE_JWXYZ).
349 Does malloc() draw from more than one heap? */
350 if ((unsigned long) himem - (unsigned long) lomem > 0x0FFFFFFF) {
352 fprintf (stderr, "%s: wonky: 0x%08x - 0x%08x = 0x%08x\n", progname,
353 (unsigned int) himem, (unsigned int) lomem,
354 (unsigned int) himem - (unsigned int) lomem);
356 himem = lomem + 0xFFFF;
359 if (lomem >= himem) abort();
362 if (sc->data >= himem)
365 switch (st->draw_mode)
371 vv = (vv << 24) | (r << 16) | (g << 8) | b;
385 /* avoid having many seconds of blackness: truncate zeros at 24K.
391 if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
398 switch (st->draw_mode)
401 r = (vv >> 16) & 0xFF;
402 g = (vv >> 8) & 0xFF;
421 /* this one returns only bytes from the file */
424 i = fgetc (st->in); \
426 ? (open_file (st), 1) \
430 /* this one returns a null at EOF -- else we hang on zero-length files */
433 i = fgetc (st->in); \
434 if (i == EOF) { i = 0; open_file (st); } \
440 switch (st->draw_mode)
446 vv = (vv << 24) | (r << 16) | (g << 8) | b;
461 # endif /* READ_FILES */
475 draw_string (state *st)
478 int direction, ascent, descent;
479 int bot = st->scrollers[0].rect.y;
480 const char *fmt = "%08X";
483 /* Draw the first font that fits.
485 for (i = 0; i < countof (st->fonts); i++)
490 if (! st->fonts[i]) continue;
492 sprintf (buf, fmt, 0);
493 XTextExtents (st->fonts[i], buf, strlen(buf),
494 &direction, &ascent, &descent, &overall);
495 sprintf (buf, "%08X", st->scrollers[0].value);
498 h = ascent + descent + 1;
499 x = (st->xgwa.width - w) / 2;
502 if (y + h + 10 <= bot && x > -10)
504 XSetFont (st->dpy, st->text_gc, st->fonts[i]->fid);
505 XFillRectangle (st->dpy, st->window, st->erase_gc,
506 x-w-1, y-1, w*3+2, h+2);
507 XDrawString (st->dpy, st->window, st->text_gc,
508 x, y + ascent, buf, strlen(buf));
516 memscroller_draw (Display *dpy, Window window, void *closure)
518 state *st = (state *) closure;
522 for (i = 0; i < st->nscrollers; i++)
524 scroller *sc = &st->scrollers[i];
527 XCopyArea (st->dpy, st->window, st->window, st->draw_gc,
528 sc->rect.x + sc->speed, sc->rect.y,
529 sc->rect.width - sc->speed, sc->rect.height,
530 sc->rect.x, sc->rect.y);
532 if (sc->scroll_tick == 0)
534 int top = ((sc->image->bytes_per_line * sc->rect.height) /
536 unsigned int *out = (unsigned int *) sc->image->data;
537 for (j = 0; j < top; j++)
539 unsigned int v = more_bits(st, sc);
541 for (k = 0; k < sc->rez; k++)
547 if (sc->scroll_tick * sc->speed >= sc->rez)
550 for (j = 0; j < sc->speed; j++)
552 put_xshm_image (st->dpy, st->window, st->draw_gc, sc->image,
554 sc->rect.x + sc->rect.width - sc->image->width - j,
556 sc->rect.width, sc->rect.height,
566 memscroller_reshape (Display *dpy, Window window, void *closure,
567 unsigned int w, unsigned int h)
569 state *st = (state *) closure;
570 XClearWindow (st->dpy, st->window);
571 reshape_memscroller (st);
575 memscroller_event (Display *dpy, Window window, void *closure, XEvent *event)
582 memscroller_free (Display *dpy, Window window, void *closure)
587 static const char *memscroller_defaults [] = {
588 ".background: black",
593 ".textColor: #00FF00",
594 ".foreground: #00FF00",
597 #if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
598 ".font1: OCR A Std 192, Lucida Console 192, Monaco 192",
599 ".font2: OCR A Std 144, Lucida Console 144, Monaco 144",
600 ".font3: OCR A Std 128, Lucida Console 128, Monaco 128",
601 ".font4: OCR A Std 96, Lucida Console 96, Monaco 96",
602 ".font5: OCR A Std 48, Lucida Console 48, Monaco 48",
603 ".font6: OCR A Std 24, Lucida Console 24, Monaco 24",
604 #elif 0 /* real X11, XQueryFont() */
605 ".font1: -*-courier-bold-r-*-*-*-1440-*-*-m-*-*-*",
606 ".font2: -*-courier-bold-r-*-*-*-960-*-*-m-*-*-*",
607 ".font3: -*-courier-bold-r-*-*-*-480-*-*-m-*-*-*",
608 ".font4: -*-courier-bold-r-*-*-*-320-*-*-m-*-*-*",
609 ".font5: -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
611 #else /* real X11, load_font_retry() */
612 ".font1: -*-ocr a std-medium-r-*-*-*-1440-*-*-m-*-*-*",
613 ".font2: -*-ocr a std-medium-r-*-*-*-960-*-*-m-*-*-*",
614 ".font3: -*-ocr a std-medium-r-*-*-*-480-*-*-m-*-*-*",
615 ".font4: -*-ocr a std-medium-r-*-*-*-320-*-*-m-*-*-*",
616 ".font5: -*-ocr a std-medium-r-*-*-*-180-*-*-m-*-*-*",
617 ".font6: -*-ocr a std-medium-r-*-*-*-120-*-*-m-*-*-*",
625 static XrmOptionDescRec memscroller_options [] = {
626 { "-delay", ".delay", XrmoptionSepArg, 0 },
627 { "-font", ".font", XrmoptionSepArg, 0 },
628 { "-filename", ".filename", XrmoptionSepArg, 0 },
629 { "-color", ".drawMode", XrmoptionNoArg, "color" },
630 { "-mono", ".drawMode", XrmoptionNoArg, "mono" },
631 { "-ram", ".filename", XrmoptionNoArg, "(RAM)" },
632 { "-random", ".filename", XrmoptionNoArg, "(RANDOM)" },
636 XSCREENSAVER_MODULE ("MemScroller", memscroller)