ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / memscroller.c
diff --git a/hacks/memscroller.c b/hacks/memscroller.c
new file mode 100644 (file)
index 0000000..b4b98ba
--- /dev/null
@@ -0,0 +1,496 @@
+/* xscreensaver, Copyright (c) 2002, 2004 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * Memscroller -- scrolls a dump of its own RAM across the screen.
+ */
+
+#include "screenhack.h"
+#include <stdio.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+#endif
+
+typedef struct {
+  int which;
+  XRectangle rect;
+  XImage *image;
+  int rez;
+  int speed;
+  int scroll_tick;
+  unsigned char *data;
+  int count_zero;
+} scroller;
+
+typedef struct {
+  Display *dpy;
+  Window window;
+  XWindowAttributes xgwa;
+  GC draw_gc, erase_gc, text_gc;
+  XFontStruct *font;
+  int border;
+
+  enum { SEED_RAM, SEED_RANDOM } seed_mode;
+  enum { DRAW_COLOR, DRAW_MONO } draw_mode;
+
+  int nscrollers;
+  scroller *scrollers;
+
+# ifdef HAVE_XSHM_EXTENSION
+  Bool shm_p;
+  XShmSegmentInfo shm_info;
+# endif
+
+} state;
+
+
+state *
+init_memscroller (Display *dpy, Window window)
+{
+  int i;
+  XGCValues gcv;
+  state *st = (state *) calloc (1, sizeof (*st));
+  char *s;
+  st->dpy = dpy;
+  st->window = window;
+  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
+  XSelectInput(st->dpy, st->window, ExposureMask);
+
+  /* Fill up the colormap with random colors.
+     We don't actually use these explicitly, but in 8-bit mode,
+     they will be used implicitly by the random image bits. */
+  {
+    int ncolors = 255;
+    XColor colors[256];
+    make_random_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap,
+                          colors, &ncolors, True, True, 0, False);
+  }
+
+  st->border = get_integer_resource ("borderSize", "BorderSize");
+
+  {
+    char *fontname = get_string_resource ("font", "Font");
+    st->font = XLoadQueryFont (dpy, fontname);
+
+    if (!st->font)
+      {
+       static const char *fonts[] = {
+          "-*-courier-medium-r-*-*-*-1400-*-*-p-*-*-*",
+          "-*-courier-medium-r-*-*-*-600-*-*-p-*-*-*",
+          "-*-utopia-*-r-*-*-*-1400-*-*-p-*-*-*",
+          "-*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
+          "-*-utopia-*-r-*-*-*-240-*-*-p-*-*-*",
+          "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
+          "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
+          "fixed", 0 };
+        i = 0;
+        while (fonts[i])
+          {
+            st->font = XLoadQueryFont (dpy, fonts[i]);
+            if (st->font) break;
+            i++;
+          }
+        if (st->font)
+          fprintf (stderr, "%s: couldn't load font \"%s\", using \"%s\"\n",
+                   progname, fontname, fonts[i]);
+        else
+          {
+            fprintf(stderr, "%s: couldn't load any font\n", progname);
+            exit(-1);
+          }
+      }
+  }
+
+  gcv.line_width = st->border;
+
+  gcv.background = get_pixel_resource("background", "Background",
+                                      st->dpy, st->xgwa.colormap);
+  gcv.foreground = get_pixel_resource("textColor", "Foreground",
+                                      st->dpy, st->xgwa.colormap);
+  gcv.font = st->font->fid;
+  st->text_gc = XCreateGC (st->dpy, st->window,
+                           GCForeground|GCBackground|GCFont, &gcv);
+
+  gcv.foreground = get_pixel_resource("foreground", "Foreground",
+                                      st->dpy, st->xgwa.colormap);
+  st->draw_gc = XCreateGC (st->dpy, st->window,
+                           GCForeground|GCBackground|GCLineWidth,
+                           &gcv);
+  gcv.foreground = gcv.background;
+  st->erase_gc = XCreateGC (st->dpy, st->window,
+                            GCForeground|GCBackground, &gcv);
+
+
+  s = get_string_resource ("drawMode", "DrawMode");
+  if (!s || !*s || !strcasecmp (s, "color"))
+    st->draw_mode = DRAW_COLOR;
+  else if (!strcasecmp (s, "mono"))
+    st->draw_mode = DRAW_MONO;
+  else
+    {
+      fprintf (stderr, "%s: drawMode must be 'mono' or 'color', not '%s'\n",
+               progname, s);
+      exit (1);
+    }
+  if (s) free (s);
+
+
+  s = get_string_resource ("seedMode", "SeedMode");
+  if (!s || !*s ||
+      !strcasecmp (s, "ram") ||
+      !strcasecmp (s, "mem") ||
+      !strcasecmp (s, "memory"))
+    st->seed_mode = SEED_RAM;
+  else if (!strcasecmp (s, "rand") ||
+           !strcasecmp (s, "random"))
+    st->seed_mode = SEED_RANDOM;
+  else
+    {
+      fprintf (stderr, "%s: seedMode must be 'RAM' or 'random', not '%s'\n",
+               progname, s);
+      exit (1);
+    }
+  if (s) free (s);
+  s = 0;
+
+
+  st->nscrollers = 3;
+  st->scrollers = (scroller *) calloc (st->nscrollers, sizeof(scroller));
+
+  for (i = 0; i < st->nscrollers; i++)
+    {
+      scroller *sc = &st->scrollers[i];
+      int max_height = 4096;
+
+      sc->which = i;
+      sc->speed = i+1;
+
+      sc->image = 0;
+# ifdef HAVE_XSHM_EXTENSION
+      st->shm_p = get_boolean_resource ("useSHM", "Boolean");
+      if (st->shm_p)
+        {
+          sc->image = create_xshm_image (st->dpy, st->xgwa.visual,
+                                         st->xgwa.depth,
+                                         ZPixmap, 0, &st->shm_info,
+                                         1, max_height);
+          if (! sc->image)
+            st->shm_p = False;
+        }
+# endif /* HAVE_XSHM_EXTENSION */
+
+      if (!sc->image)
+        sc->image = XCreateImage (st->dpy, st->xgwa.visual, st->xgwa.depth,
+                                  ZPixmap, 0, 0, 1, max_height, 8, 0);
+
+      if (sc->image && !sc->image->data)
+        sc->image->data = (char *)
+          malloc (sc->image->bytes_per_line * sc->image->height + 1);
+
+      if (!sc->image || !sc->image->data)
+        {
+          fprintf (stderr, "%s: out of memory (allocating 1x%d image)\n",
+                   progname, sc->image->height);
+          exit (1);
+        }
+    }
+
+  return st;
+}
+
+
+static void
+reshape_memscroller (state *st)
+{
+  int i;
+
+  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
+
+  for (i = 0; i < st->nscrollers; i++)
+    {
+      scroller *sc = &st->scrollers[i];
+
+      if (i == 0)
+        {
+          sc->rez = 6;  /* #### */
+
+          sc->rect.width  = (((int) (st->xgwa.width * 0.8)
+                              / sc->rez) * sc->rez);
+          sc->rect.height = (((int) (st->xgwa.height * 0.3)
+                              / sc->rez) * sc->rez);
+
+          sc->rect.x = (st->xgwa.width  - sc->rect.width)  / 2;
+          sc->rect.y = (st->xgwa.height - sc->rect.height) / 2;
+        }
+      else
+        {
+          scroller *sc0 = &st->scrollers[i-1];
+          sc->rez = sc0->rez * 1.8;
+
+          sc->rect.x      = sc0->rect.x;
+          sc->rect.y      = (sc0->rect.y + sc0->rect.height + st->border
+                             + (st->border + 2) * 7);
+          sc->rect.width  = sc0->rect.width;
+          sc->rect.height = (((int) (st->xgwa.height * 0.1)
+                              / sc->rez) * sc->rez);
+        }
+
+      XDrawRectangle (st->dpy, st->window, st->draw_gc,
+                      sc->rect.x - st->border*2,
+                      sc->rect.y - st->border*2,
+                      sc->rect.width  + st->border*4,
+                      sc->rect.height + st->border*4);
+    }
+}
+
+
+
+static unsigned int
+more_bits (state *st, scroller *sc)
+{
+  static unsigned char *lomem = 0;
+  static unsigned char *himem = 0;
+  unsigned char r, g, b;
+  unsigned int v;
+
+  /* Pack bytes as RGBR so that we don't have to worry about actually
+     figuring out the color channel order.
+   */
+# undef PACK
+# define PACK(r,g,b) ((r) | ((g) << 8) | ((b) << 16) | ((r) << 24))
+
+  switch (st->seed_mode)
+    {
+    case SEED_RAM:
+      if (himem == 0)
+        {
+          lomem = (unsigned char *) progname; /* not first malloc, but early */
+          himem = (unsigned char *)           /* not last malloc, but late */
+            st->scrollers[st->nscrollers-1].image->data;
+        }
+
+      if (sc->data < lomem)
+        sc->data = lomem;
+
+# ifdef HAVE_SBRK  /* re-get it each time through */
+      himem = ((unsigned char *) sbrk(0)) - (2 * sizeof(void *));
+# endif
+
+      if (lomem >= himem) abort();
+
+    RETRY:
+      if (sc->data >= himem)
+        sc->data = lomem;
+
+      switch (st->draw_mode)
+        {
+        case DRAW_COLOR:
+          r = *sc->data++;
+          g = *sc->data++;
+          b = *sc->data++;
+          break;
+        case DRAW_MONO:
+          r = 0;
+          g = *sc->data++;
+          b = 0;
+          break;
+        default:
+          abort();
+        }
+
+      v = PACK(r,g,b);
+
+      /* avoid having many seconds of blackness: truncate zeros at 24K.
+       */
+      if (v == 0)
+        sc->count_zero++;
+      else
+        sc->count_zero = 0;
+      if (sc->count_zero > 1024 * (st->draw_mode == DRAW_COLOR ? 24 : 8))
+        goto RETRY;
+
+      break;
+
+    case SEED_RANDOM:
+      v = random();
+      switch (st->draw_mode)
+        {
+        case DRAW_COLOR:
+          r = (v >> 16) & 0xFF;
+          g = (v >>  8) & 0xFF;
+          b = (v      ) & 0xFF;
+          break;
+        case DRAW_MONO:
+          r = 0;
+          g = v & 0xFF;
+          b = 0;
+          break;
+        default:
+          abort();
+        }
+      v = PACK(r,g,b);
+      break;
+
+    default:
+      abort();
+    }
+
+# undef PACK
+  return v;
+}
+
+
+static void
+draw_string (state *st, unsigned int n)
+{
+  char buf[40];
+  int direction, ascent, descent;
+  XCharStruct overall;
+  int x, y, w, h;
+  int bot = st->scrollers[0].rect.y;
+  const char *fmt = "%08X";
+
+  sprintf (buf, fmt, 0);
+  XTextExtents (st->font, buf, strlen(buf), 
+                &direction, &ascent, &descent, &overall);
+  sprintf (buf, "%08X", n);
+
+  w = overall.width;
+  h = st->font->ascent + st->font->descent + 1;
+  x = (st->xgwa.width - w) / 2;
+  y = (bot - h) / 2;
+
+  if (y + h + 10 > bot) return;
+
+  XFillRectangle (st->dpy, st->window, st->erase_gc,
+                  x-w, y, w*3, h);
+  XDrawString (st->dpy, st->window, st->text_gc,
+               x, y + st->font->ascent, buf, strlen(buf));
+}
+
+
+static void
+draw_memscroller (state *st)
+{
+  int i;
+
+  draw_string (st, *((unsigned int *) st->scrollers[0].image->data));
+
+  for (i = 0; i < st->nscrollers; i++)
+    {
+      scroller *sc = &st->scrollers[i];
+      int j;
+
+      XCopyArea (st->dpy, st->window, st->window, st->draw_gc,
+                 sc->rect.x + sc->speed, sc->rect.y,
+                 sc->rect.width - sc->speed, sc->rect.height,
+                 sc->rect.x, sc->rect.y);
+
+      if (sc->scroll_tick == 0)
+        {
+          int top = ((sc->image->bytes_per_line * sc->rect.height) /
+                     (4 * sc->rez));
+          unsigned int *out = (unsigned int *) sc->image->data;
+          for (j = 0; j < top; j++)
+            {
+              unsigned int v = more_bits(st, sc);
+              int k;
+              for (k = 0; k < sc->rez; k++)
+                *out++ = v;
+            }
+        }
+
+      sc->scroll_tick++;
+      if (sc->scroll_tick * sc->speed >= sc->rez)
+        sc->scroll_tick = 0;
+
+      for (j = 0; j < sc->speed; j++)
+        {
+# ifdef HAVE_XSHM_EXTENSION
+          if (st->shm_p)
+            XShmPutImage (st->dpy, st->window, st->draw_gc, sc->image,
+                          0, 0,
+                          sc->rect.x + sc->rect.width - sc->image->width - j,
+                          sc->rect.y,
+                          sc->rect.width, sc->rect.height,
+                          False);
+          else
+# endif /* HAVE_XSHM_EXTENSION */
+            XPutImage (st->dpy, st->window, st->draw_gc, sc->image,
+                       0, 0,
+                       sc->rect.x + sc->rect.width - sc->image->width - j,
+                       sc->rect.y,
+                       sc->rect.width, sc->rect.height);
+        }
+    }
+}
+
+static void
+handle_events (state *st)
+{
+  XSync (st->dpy, False);
+  while (XPending (st->dpy))
+    {
+      XEvent event;
+      XNextEvent (st->dpy, &event);
+      if (event.xany.type == ConfigureNotify ||
+          event.xany.type == Expose)
+        {
+          XClearWindow (st->dpy, st->window);
+          reshape_memscroller (st);
+        }
+
+      screenhack_handle_event (st->dpy, &event);
+    }
+}
+
+
+\f
+char *progclass = "MemScroller";
+
+char *defaults [] = {
+  ".background:                   black",
+  "*drawMode:             color",
+  "*seedMode:             ram",
+  ".textColor:            #00FF00",
+  ".foreground:                   #00FF00",
+  "*borderSize:                   2",
+  ".font:                 -*-courier-medium-r-*-*-*-1400-*-*-m-*-*-*",
+  "*delay:                10000",
+  "*offset:               0",
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-delay",          ".delay",               XrmoptionSepArg, 0 },
+  { "-font",           ".font",                XrmoptionSepArg, 0 },
+  { "-color",          ".drawMode",            XrmoptionNoArg, "color"  },
+  { "-mono",           ".drawMode",            XrmoptionNoArg, "mono"   },
+  { "-ram",            ".seedMode",            XrmoptionNoArg, "ram"    },
+  { "-random",         ".seedMode",            XrmoptionNoArg, "random" },
+  { 0, 0, 0, 0 }
+};
+
+
+void
+screenhack (Display *dpy, Window window)
+{
+  state *st = init_memscroller (dpy, window);
+  int delay = get_integer_resource ("delay", "Integer");
+  reshape_memscroller (st);
+  while (1)
+    {
+      draw_memscroller (st);
+      XSync (dpy, False);
+      handle_events (st);
+      if (delay) usleep (delay);
+    }
+}