From 6881658bc50ed66294861c3ece0c57607e18611b Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Wed, 10 Mar 2021 03:08:15 -0800 Subject: [PATCH] Imported the greynetic hack and README.hacking document from xscreensaver as a reference. --- reference/README.hacking | 213 ++++++++++++++++++++++++++++ reference/greynetic.c | 296 +++++++++++++++++++++++++++++++++++++++ reference/greynetic.man | 56 ++++++++ 3 files changed, 565 insertions(+) create mode 100644 reference/README.hacking create mode 100644 reference/greynetic.c create mode 100644 reference/greynetic.man diff --git a/reference/README.hacking b/reference/README.hacking new file mode 100644 index 0000000..09147f7 --- /dev/null +++ b/reference/README.hacking @@ -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 index 0000000..586fdff --- /dev/null +++ b/reference/greynetic.c @@ -0,0 +1,296 @@ +/* xscreensaver, Copyright (c) 1992-2008 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. + */ + +#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 + * # include + * # include + * # include + * # include + * # include + * # include + * # include + * # include + * # include + * # include + * # include +*/ + +#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; +} + + +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 index 0000000..6d2a8c3 --- /dev/null +++ b/reference/greynetic.man @@ -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 , 13-aug-92. -- 2.20.1