Imported the greynetic hack and README.hacking document from xscreensaver as a reference.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Wed, 10 Mar 2021 11:08:15 +0000 (03:08 -0800)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Wed, 10 Mar 2021 11:08:15 +0000 (03:08 -0800)
reference/README.hacking [new file with mode: 0644]
reference/greynetic.c [new file with mode: 0644]
reference/greynetic.man [new file with mode: 0644]

diff --git a/reference/README.hacking b/reference/README.hacking
new file mode 100644 (file)
index 0000000..09147f7
--- /dev/null
@@ -0,0 +1,213 @@
+
+==========================================================================
+
+                     Writing new XScreenSaver modules
+
+==========================================================================
+
+Any program that can be made to render on an X window created by another
+process can be used as a screen saver.  Just get the window ID out of
+$XSCREENSAVER_WINDOW, draw on that, and you're done.
+
+In theory, you can write a screen saver in any language you like.  In
+practice, however, languages other than C or C++ tend not to allow you to
+draw to windows that they did not create themselves.  Unfortunately, this
+means that if you want to write a screen saver, you must write it in C.
+
+Given that you're going to be writing in C, you might as well take
+advantage of the various utility functions that I have written to make
+that easier.  Writing a new screen saver in C using the frameworks
+included with xscreensaver simplifies things enormously.
+
+Generally, the best way to learn how to do something is to find a similar
+program, and play around with it until you understand it.  Another
+approach is to not worry about understanding it, but to just hack it out.
+Either way, your best bet is probably to start with one of the existing
+xscreensaver demos, included in the "hacks/" directory, rename the file,
+and edit it until it does what you want.
+
+The "Greynetic" and "Deluxe" hacks are probably good ones to start with,
+since they are so very simple.  For OpenGL programs, "DangerBall" is a
+good example.
+
+
+==========================================================================
+Requirements for inclusion with the XScreenSaver collection
+==========================================================================
+
+  If you come up with anything, send it to me!  If it's good, I'd love to
+  include it in the xscreensaver distribution.  However, there are a few
+  requirements for me to distribute it:
+
+  - Write in portable ANSI C.  No C++.  No nonstandard libraries.
+
+  - Write a .man page describing all command-line options.
+
+  - Write an .xml file describing the graphical configuration dialog box.
+
+  - Include a BSD-like copyright/license notice at the top of each source
+    file (preferably, just use the one from "screenhack.h", and include
+    your name and the current year).  The GNU GPL is not compatible with
+    the rest of XScreenSaver.
+
+  - No clocks! Just as time travellers always try to kill Hitler on their
+    first trip, everyone seems to think that their first screen saver
+    should be a clock of some kind.  Nobody needs to know what time it is
+    with such frequency.  Fight The Tyranny Of The Clock.
+
+
+==========================================================================
+The XScreenSaver API
+==========================================================================
+
+  - Start with #include "screenhack.h"
+
+  - Define 2 global variables:
+
+      yoursavername_defaults -- Default values for the resources you use.
+      yoursavername_options  -- The command-line options you accept.
+
+  - Define 5 functions:
+
+      yoursavername_init     -- Return an object holding your global state.
+      yoursavername_draw     -- Draw a single frame, quickly.
+      yoursavername_free     -- Free everything you've allocated.
+      yoursavername_reshape  -- Called when the window is resized.
+      yoursavername_event    -- Called when a keyboard or mouse event happens.
+
+      The "event" function will only be called when running in a window
+      (not as a screen saver).  The "reshape" event will be called when the
+      window size changes, or (as a screen saver) when the display size
+      changes as a result of a RANDR event (e.g., plugging in a new monitor).
+
+      It's ok for both the "event" and "resize" functions to do nothing.
+
+  - All other functions should be static.
+
+  - The last line of the file should be 
+    XSCREENSAVER_MODULE ("YourSaverName", yoursavername)
+
+  - Finally, edit the Makefile to add a rule for your program.
+    Just cut-and-paste one of the existing rules.
+
+  Your "draw" must not run for more than a fraction of a second without
+  returning.  This means "don't call usleep()".  Everything has to be a
+  state machine.
+
+  You may not store global state in global variables, or in function-local
+  static variables.  All of your runtime state must be encapsulated in the
+  "state" object created by your "init" function.  If you use global or
+  static variables, your screen saver will not work properly on macOS.
+
+  Do not call XSync() or XFlush().  If you think you need to do that, it
+  probably means that you are you are relying on the speed of the graphics
+  card for timing, which probably means that your "draw" function is
+  taking too long.
+
+
+==========================================================================
+The xlockmore API
+==========================================================================
+
+  Some of the display modes that come with xscreensaver were ported from
+  xlock, and so, for historical reasons, they follow a slightly different
+  programming convention.  For newly-written Xlib programs, you'd be
+  better off following the pattern used in hacks like "Deluxe" than in
+  hacks like "Flag".  The xlockmore ones are the ones that begin with
+  "#ifdef STANDALONE" and #include "xlockmore.h".
+
+  But, all OpenGL screen savers have to follow the xlockmore API.
+
+  The xlockmore API is similar to the XScreenSaver API, in that you define
+  (roughly) the same set of functions, but the naming conventions are
+  slightly different.  Instead of "hackname_init", it's "init_hackname",
+  and it should be preceeded with the pseudo-declaration ENTRYPOINT.
+
+  One annoying mis-feature of the xlockmore API is that it *requires* you
+  to make use of global variables for two things: first, for each of your
+  command line options; and second, for an array that holds your global
+  state objects.  These are the only exceptions to the "never use global
+  variables" rule.
+
+  There are a few important differences between the original xlockmore API
+  and XScreenSaver's implementation:
+
+  - XScreenSaver does not use refresh_hackname or change_hackname.
+
+  - XScreenSaver provides two additional hooks not present in xlockmore:
+    reshape_hackname, and hackname_handle_event. These are respectively
+    equivalent to hackname_reshape and hackname_event in the XScreenSaver
+    API.
+
+  - Under Xlib, MI_CLEARWINDOW doesn't clear the window immediately.
+    Instead, due to double buffering on macOS/iOS/Android, it waits until
+    after init_hackname or draw_hackname returns before clearing. Make
+    sure not to issue any Xlib drawing calls immediately after a call to
+    MI_CLEARWINDOW, as these will be erased. To clear the window
+    immediately, just use XClearWindow.
+
+
+==========================================================================
+Programming Tips
+==========================================================================  
+
+  - Your screen saver should look reasonable at 20-30 frames per second.
+    That is, do not assume that your "draw" function will be called more
+    than 20 times a second.  Even if you return a smaller requested delay
+    than that, you might not get it.  Likewise, if your "draw" function
+    takes longer than 1/20th of a second to run, your screen saver may be
+    consuming too much CPU.
+
+  - Don't make assumptions about the depth of the display, or whether it
+    is colormapped.  You must allocate all your colors explicitly: do not
+    assume you can construct an RGB value and use that as a pixel value
+    directly.  In particular, you can't make assumptions about whether
+    pixels are RGB, RGBA, ARGB, ABGR, or even whether they are 32, 24,
+    16 or 8 bits.  Use the utility routines provided by "utils/colors.h"
+    to simplify color allocation.
+
+  - It is better to eliminate flicker by double-buffering to a Pixmap
+    than by erasing and re-drawing objects.  Do not use drawing tricks
+    involving XOR.
+
+  - If you use double-buffering, have a resource to turn it off. (MacOS,
+    iOS and Android have double-buffering built in, so you'd end up
+    triple-buffering.)
+
+  - Understand the differences between Pixmaps and XImages, and keep in
+    mind which operations are happening in client memory and which are in
+    server memory, and which cause latency due to server round-trips.
+    Sometimes using the Shared Memory extension can be helpful, but
+    probably not as often as you might think.
+
+  - On modern machines, OpenGL will always run faster than Xlib.  It's
+    also more portable.  Consider writing in OpenGL whenever possible.
+
+  - Free any memory you allocate. While screen savers under X11 have
+    their memory freed automatically when their process is killed by
+    the XScreenSaver daemon, under iOS and Android screen savers exist
+    in long-lived processes where no such cleanup takes place.
+    Consider Valgrind or gcc -fsanitize=leak to find memory leaks.
+
+  - Again, don't use global variables.  If you are doing your developent
+    under X11, test your saver from the command line with the "-pair"
+    argument.  If that crashes, you're using global variables!
+
+
+==========================================================================
+macOS, iOS and Android
+==========================================================================
+
+  Though XScreenSaver started its life as an X11 program, it also now runs
+  on macOS, iOS and Android, due to a maniacal collection of compatibility
+  shims.  If you do your development on an X11 system, and follow the
+  usual XScreenSaver APIs, you shouldn't need to do anything special for
+  it to also work on macOS and on phones.
+
+  See the READMEs in the OSX/ and android/ directories for instructions on
+  compiling for those platforms.
+
+  To check that an X11 saver will fit well on a mobile device, test it
+  with -geometry 640x1136 and 640x960.  That's a good first step, anyway.
+
+==========================================================================
diff --git a/reference/greynetic.c b/reference/greynetic.c
new file mode 100644 (file)
index 0000000..586fdff
--- /dev/null
@@ -0,0 +1,296 @@
+/* xscreensaver, Copyright (c) 1992-2008 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.
+ */
+
+#include "screenhack.h"
+
+#ifndef HAVE_JWXYZ
+# define DO_STIPPLE
+#endif
+
+#define NBITS 12
+
+/* On some systems (notably MacOS X) these files are messed up.
+ * They're tiny, so we might as well just inline them here.
+ *
+ * # include <X11/bitmaps/stipple>
+ * # include <X11/bitmaps/cross_weave>
+ * # include <X11/bitmaps/dimple1>
+ * # include <X11/bitmaps/dimple3>
+ * # include <X11/bitmaps/flipped_gray>
+ * # include <X11/bitmaps/gray1>
+ * # include <X11/bitmaps/gray3>
+ * # include <X11/bitmaps/hlines2>
+ * # include <X11/bitmaps/light_gray>
+ * # include <X11/bitmaps/root_weave>
+ * # include <X11/bitmaps/vlines2>
+ * # include <X11/bitmaps/vlines3>
+*/
+
+#ifdef DO_STIPPLE
+#define stipple_width  16
+#define stipple_height 4
+static unsigned char stipple_bits[] = {
+  0x55, 0x55, 0xee, 0xee, 0x55, 0x55, 0xba, 0xbb};
+
+#define cross_weave_width  16
+#define cross_weave_height 16
+static unsigned char cross_weave_bits[] = {
+   0x55, 0x55, 0x88, 0x88, 0x55, 0x55, 0x22, 0x22, 0x55, 0x55, 0x88, 0x88,
+   0x55, 0x55, 0x22, 0x22, 0x55, 0x55, 0x88, 0x88, 0x55, 0x55, 0x22, 0x22,
+   0x55, 0x55, 0x88, 0x88, 0x55, 0x55, 0x22, 0x22};
+
+#define dimple1_width 16
+#define dimple1_height 16
+static unsigned char dimple1_bits[] = {
+   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00,
+   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00,
+   0x55, 0x55, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00};
+
+#define dimple3_width 16
+#define dimple3_height 16
+static unsigned char dimple3_bits[] = {
+   0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+#define flipped_gray_width  4
+#define flipped_gray_height 2
+static char flipped_gray_bits[] = { 0x07, 0x0d};
+#define gray1_width  2
+#define gray1_height 2
+static char gray1_bits[] = { 0x01, 0x02};
+#define gray3_width  4
+#define gray3_height 4
+static char gray3_bits[] = { 0x01, 0x00, 0x04, 0x00};
+#define hlines2_width  1
+#define hlines2_height 2
+static char hlines2_bits[] = { 0x01, 0x00};
+#define light_gray_width  4
+#define light_gray_height 2
+static char light_gray_bits[] = { 0x08, 0x02};
+#define root_weave_width  4
+#define root_weave_height 4
+static char root_weave_bits[] = { 0x07, 0x0d, 0x0b, 0x0e};
+#define vlines2_width  2
+#define vlines2_height 1
+static char vlines2_bits[] = { 0x01};
+#define vlines3_width  3
+#define vlines3_height 1
+static char vlines3_bits[] = { 0x02};
+
+#endif /* DO_STIPPLE */
+
+struct state {
+  Display *dpy;
+  Window window;
+
+  Pixmap pixmaps [NBITS];
+
+  GC gc;
+  int delay;
+  unsigned long fg, bg, pixels [512];
+  int npixels;
+  int xlim, ylim;
+  Bool grey_p;
+  Colormap cmap;
+};
+
+
+static void *
+greynetic_init (Display *dpy, Window window)
+{
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+# ifdef DO_STIPPLE
+  int i;
+# endif /* DO_STIPPLE */
+  XGCValues gcv;
+  XWindowAttributes xgwa;
+  st->dpy = dpy;
+  st->window = window;
+
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  st->xlim = xgwa.width;
+  st->ylim = xgwa.height;
+  st->cmap = xgwa.colormap;
+  st->npixels = 0;
+  st->grey_p = get_boolean_resource(st->dpy, "grey", "Boolean");
+  gcv.foreground= st->fg= get_pixel_resource(st->dpy, st->cmap, "foreground","Foreground");
+  gcv.background= st->bg= get_pixel_resource(st->dpy, st->cmap, "background","Background");
+
+  st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+  if (st->delay < 0) st->delay = 0;
+
+# ifndef DO_STIPPLE
+  st->gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+#  ifdef HAVE_JWXYZ /* allow non-opaque alpha components in pixel values */
+  jwxyz_XSetAlphaAllowed (st->dpy, st->gc, True);
+#  endif /* HAVE_JWXYZ */
+# else /* DO_STIPPLE */
+  gcv.fill_style= FillOpaqueStippled;
+  st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground|GCFillStyle, &gcv);
+  
+  i = 0;
+# define BITS(n,w,h) \
+  st->pixmaps [i++] = \
+    XCreatePixmapFromBitmapData (st->dpy, st->window, (char *) n, w, h, 1, 0, 1)
+
+  BITS (stipple_bits, stipple_width, stipple_height);
+  BITS (cross_weave_bits, cross_weave_width, cross_weave_height);
+  BITS (dimple1_bits, dimple1_width, dimple1_height);
+  BITS (dimple3_bits, dimple3_width, dimple3_height);
+  BITS (flipped_gray_bits, flipped_gray_width, flipped_gray_height);
+  BITS (gray1_bits, gray1_width, gray1_height);
+  BITS (gray3_bits, gray3_width, gray3_height);
+  BITS (hlines2_bits, hlines2_width, hlines2_height);
+  BITS (light_gray_bits, light_gray_width, light_gray_height);
+  BITS (root_weave_bits, root_weave_width, root_weave_height);
+  BITS (vlines2_bits, vlines2_width, vlines2_height);
+  BITS (vlines3_bits, vlines3_width, vlines3_height);
+# endif /* DO_STIPPLE */
+  return st;
+}
+
+static unsigned long
+greynetic_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  int x, y, w=0, h=0, i;
+  XGCValues gcv;
+
+  for (i = 0; i < 10; i++) /* minimize area, but don't try too hard */
+    {
+      w = 50 + random () % (st->xlim - 50);
+      h = 50 + random () % (st->ylim - 50);
+      if (w + h < st->xlim && w + h < st->ylim)
+       break;
+    }
+  x = random () % (st->xlim - w);
+  y = random () % (st->ylim - h);
+# ifdef DO_STIPPLE
+  gcv.stipple = st->pixmaps [random () % NBITS];
+# endif /* !DO_STIPPLE */
+  if (mono_p)
+    {
+    MONO:
+      if (random () & 1)
+       gcv.foreground = st->fg, gcv.background = st->bg;
+      else
+       gcv.foreground = st->bg, gcv.background = st->fg;
+    }
+  else
+    {
+      XColor fgc, bgc;
+      if (st->npixels == sizeof (st->pixels) / sizeof (unsigned long))
+       goto REUSE;
+      fgc.flags = bgc.flags = DoRed|DoGreen|DoBlue;
+      fgc.red = random ();
+      fgc.green = random ();
+      fgc.blue = random ();
+# ifdef DO_STIPPLE
+      bgc.red = random ();
+      bgc.green = random ();
+      bgc.blue = random ();
+# endif /* DO_STIPPLE */
+
+      if (st->grey_p)
+        {
+          fgc.green = fgc.blue = fgc.red;
+          bgc.green = bgc.blue = bgc.red;
+        }
+
+      if (! XAllocColor (st->dpy, st->cmap, &fgc))
+       goto REUSE;
+      st->pixels [st->npixels++] = fgc.pixel;
+      gcv.foreground = fgc.pixel;
+# ifdef DO_STIPPLE
+      if (! XAllocColor (st->dpy, st->cmap, &bgc))
+       goto REUSE;
+      st->pixels [st->npixels++] = bgc.pixel;
+      gcv.background = bgc.pixel;
+# endif /* DO_STIPPLE */
+      goto DONE;
+    REUSE:
+      if (st->npixels <= 0)
+       {
+         mono_p = 1;
+         goto MONO;
+       }
+      gcv.foreground = st->pixels [random () % st->npixels];
+# ifdef DO_STIPPLE
+      gcv.background = st->pixels [random () % st->npixels];
+# endif /* DO_STIPPLE */
+    DONE:
+      ;
+
+# ifdef HAVE_JWXYZ
+      {
+        /* give a non-opaque alpha to the color */
+        unsigned long pixel = gcv.foreground;
+        unsigned long amask = BlackPixel (dpy,0);
+        unsigned long a = (random() & amask);
+        pixel = (pixel & (~amask)) | a;
+        gcv.foreground = pixel;
+      }
+# endif /* !HAVE_JWXYZ */
+    }
+# ifndef DO_STIPPLE
+  XChangeGC (st->dpy, st->gc, GCForeground, &gcv);
+# else  /* DO_STIPPLE */
+  XChangeGC (st->dpy, st->gc, GCStipple|GCForeground|GCBackground, &gcv);
+# endif /* DO_STIPPLE */
+  XFillRectangle (st->dpy, st->window, st->gc, x, y, w, h);
+  return st->delay;
+}
+
+\f
+static const char *greynetic_defaults [] = {
+  ".background:        black",
+  ".foreground:        white",
+  "*fpsSolid:  true",
+  "*delay:     10000",
+  "*grey:      false",
+#ifdef HAVE_MOBILE
+  "*ignoreRotation: True",
+#endif
+  0
+};
+
+static XrmOptionDescRec greynetic_options [] = {
+  { "-delay",          ".delay",       XrmoptionSepArg, 0 },
+  { "-grey",           ".grey",        XrmoptionNoArg, "True" },
+  { 0, 0, 0, 0 }
+};
+
+static void
+greynetic_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
+  struct state *st = (struct state *) closure;
+  st->xlim = w;
+  st->ylim = h;
+}
+
+static Bool
+greynetic_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
+
+static void
+greynetic_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  XFreeGC (st->dpy, st->gc);
+  free (st);
+}
+
+XSCREENSAVER_MODULE ("Greynetic", greynetic)
+
diff --git a/reference/greynetic.man b/reference/greynetic.man
new file mode 100644 (file)
index 0000000..6d2a8c3
--- /dev/null
@@ -0,0 +1,56 @@
+.TH XScreenSaver 1 "13-aug-92" "X Version 11"
+.SH NAME
+greynetic \- draw random stippled/color rectangles
+.SH SYNOPSIS
+.B greynetic
+[\-display \fIhost:display.screen\fP] [\-foreground \fIcolor\fP] [\-background \fIcolor\fP] [\-window] [\-root] [\-mono] [\-install] [\-visual \fIvisual\fP] [\-delay \fIusecs\fP]
+[\-fps]
+.SH DESCRIPTION
+The \fIgreynetic\fP program draws random rectangles.
+.SH OPTIONS
+.I greynetic
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-mono 
+If on a color display, pretend we're on a monochrome display.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-delay \fImicroseconds\fP
+Slow it down.
+.TP 8
+.B \-fps
+Display the current frame rate and CPU load.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 1992 by Jamie Zawinski.  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.
+.SH AUTHOR
+Jamie Zawinski <jwz@jwz.org>, 13-aug-92.