1c5272537718321a6dc68c84d9206c324ef7ec12
[xscreensaver] / hacks / blitspin.c
1 /* xscreensaver, Copyright (c) 1992-1997 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  */
11
12 /* Rotate a bitmap using using bitblts.
13    The bitmap must be square, and must be a power of 2 in size.
14    This was translated from SmallTalk code which appeared in the
15    August 1981 issue of Byte magazine.
16
17    The input bitmap may be non-square, it is padded and centered
18    with the background color.  Another way would be to subdivide
19    the bitmap into square components and rotate them independently
20    (and preferably in parallel), but I don't think that would be as
21    interesting looking.
22
23    It's too bad almost nothing uses blitter hardware these days,
24    or this might actually win.
25  */
26
27 #include "screenhack.h"
28 #include <stdio.h>
29
30 #ifdef HAVE_XPM
31 # include <X11/xpm.h>
32 # ifndef PIXEL_ALREADY_TYPEDEFED
33 #  define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
34 # endif
35 #endif
36
37 #ifdef HAVE_XMU
38 # ifndef VMS
39 #  include <X11/Xmu/Drawing.h>
40 #else  /* VMS */
41 #  include <Xmu/Drawing.h>
42 # endif /* VMS */
43 #endif
44
45 #include "images/som.xbm"
46
47 static Display *dpy;
48 static Window window;
49 static unsigned int size;
50 static Pixmap self, temp, mask;
51 static GC SET, CLR, CPY, IOR, AND, XOR;
52 static GC gc;
53 static int delay, delay2;
54 static Pixmap bitmap;
55 static int depth;
56 static unsigned int fg, bg;
57
58 static void display (Pixmap);
59
60 #define copy_all_to(from, xoff, yoff, to, gc)           \
61   XCopyArea (dpy, (from), (to), (gc), 0, 0,             \
62              size-(xoff), size-(yoff), (xoff), (yoff))
63
64 #define copy_all_from(to, xoff, yoff, from, gc)         \
65   XCopyArea (dpy, (from), (to), (gc), (xoff), (yoff),   \
66              size-(xoff), size-(yoff), 0, 0)
67
68 static void
69 rotate (void)
70 {
71   int qwad; /* fuckin' C, man... who needs namespaces? */
72   XFillRectangle (dpy, mask, CLR, 0, 0, size, size);
73   XFillRectangle (dpy, mask, SET, 0, 0, size>>1, size>>1);
74   for (qwad = size>>1; qwad > 0; qwad>>=1)
75     {
76       if (delay) usleep (delay);
77       copy_all_to   (mask,       0,       0, temp, CPY);  /* 1 */
78       copy_all_to   (mask,       0,    qwad, temp, IOR);  /* 2 */
79       copy_all_to   (self,       0,       0, temp, AND);  /* 3 */
80       copy_all_to   (temp,       0,       0, self, XOR);  /* 4 */
81       copy_all_from (temp,    qwad,       0, self, XOR);  /* 5 */
82       copy_all_from (self,    qwad,       0, self, IOR);  /* 6 */
83       copy_all_to   (temp,    qwad,       0, self, XOR);  /* 7 */
84       copy_all_to   (self,       0,       0, temp, CPY);  /* 8 */
85       copy_all_from (temp,    qwad,    qwad, self, XOR);  /* 9 */
86       copy_all_to   (mask,       0,       0, temp, AND);  /* A */
87       copy_all_to   (temp,       0,       0, self, XOR);  /* B */
88       copy_all_to   (temp,    qwad,    qwad, self, XOR);  /* C */
89       copy_all_from (mask, qwad>>1, qwad>>1, mask, AND);  /* D */
90       copy_all_to   (mask,    qwad,       0, mask, IOR);  /* E */
91       copy_all_to   (mask,       0,    qwad, mask, IOR);  /* F */
92       display (self);
93     }
94 }
95
96 static void
97 read_bitmap (char *bitmap_name, int *widthP, int *heightP)
98 {
99 #ifdef HAVE_XPM
100   XWindowAttributes xgwa;
101   XpmAttributes xpmattrs;
102   int result;
103   xpmattrs.valuemask = 0;
104   bitmap = 0;
105
106   XGetWindowAttributes (dpy, window, &xgwa);
107
108 # ifdef XpmCloseness
109   xpmattrs.valuemask |= XpmCloseness;
110   xpmattrs.closeness = 40000;
111 # endif
112 # ifdef XpmVisual
113   xpmattrs.valuemask |= XpmVisual;
114   xpmattrs.visual = xgwa.visual;
115 # endif
116 # ifdef XpmDepth
117   xpmattrs.valuemask |= XpmDepth;
118   xpmattrs.depth = xgwa.depth;
119 # endif
120 # ifdef XpmColormap
121   xpmattrs.valuemask |= XpmColormap;
122   xpmattrs.colormap = xgwa.colormap;
123 # endif
124
125   result = XpmReadFileToPixmap (dpy, window, bitmap_name, &bitmap, 0,
126                                 &xpmattrs);
127   switch (result)
128     {
129     case XpmColorError:
130       fprintf (stderr, "%s: warning: xpm color substitution performed\n",
131                progname);
132       /* fall through */
133     case XpmSuccess:
134       *widthP = xpmattrs.width;
135       *heightP = xpmattrs.height;
136       break;
137     case XpmFileInvalid:
138     case XpmOpenFailed:
139       bitmap = 0;
140       break;
141     case XpmColorFailed:
142       fprintf (stderr, "%s: xpm: color allocation failed\n", progname);
143       exit (-1);
144     case XpmNoMemory:
145       fprintf (stderr, "%s: xpm: out of memory\n", progname);
146       exit (-1);
147     default:
148       fprintf (stderr, "%s: xpm: unknown error code %d\n", progname, result);
149       exit (-1);
150     }
151   if (! bitmap)
152 #endif
153
154 #ifdef HAVE_XMU
155     {
156       int xh, yh;
157       Pixmap b2;
158       bitmap = XmuLocateBitmapFile (DefaultScreenOfDisplay (dpy), bitmap_name,
159                                     0, 0, widthP, heightP, &xh, &yh);
160       if (! bitmap)
161         {
162           fprintf (stderr, "%s: couldn't find bitmap %s\n", progname,
163                    bitmap_name);
164           exit (1);
165         }
166       b2 = XmuCreatePixmapFromBitmap (dpy, window, bitmap, *widthP, *heightP,
167                                       depth, fg, bg);
168       XFreePixmap (dpy, bitmap);
169       bitmap = b2;
170     }
171 #else  /* !XMU */
172     {
173       fprintf (stderr,
174                "%s: your vendor doesn't ship the standard Xmu library.\n",
175                progname);
176       fprintf (stderr, "\tWe can't load XBM files without it.\n");
177       exit (1);
178     }
179 #endif /* !XMU */
180 }
181
182
183 static Pixmap
184 read_screen (Display *dpy, Window window, int *widthP, int *heightP)
185 {
186   Pixmap p;
187   XWindowAttributes xgwa;
188   XGCValues gcv;
189   GC gc;
190   XGetWindowAttributes (dpy, window, &xgwa);
191   *widthP = xgwa.width;
192   *heightP = xgwa.height;
193
194   grab_screen_image(xgwa.screen, window);
195   p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
196   gcv.function = GXcopy;
197   gc = XCreateGC (dpy, window, GCFunction, &gcv);
198   XCopyArea (dpy, window, p, gc, 0, 0, *widthP, *heightP, 0, 0);
199
200   /* Reset the window's background color... */
201   XSetWindowBackground (dpy, window,
202                         get_pixel_resource ("background", "Background",
203                                             dpy, xgwa.colormap));
204   XCopyArea (dpy, p, window, gc, 0, 0, *widthP, *heightP, 0, 0);
205   XFreeGC (dpy, gc);
206
207   return p;
208 }
209
210
211 static int 
212 to_pow2(int n, Bool up)
213 {
214   /* sizeof(Dimension) == 2. */
215   int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
216                         2048, 4096, 8192, 16384, 32768, 65536 };
217   int i = 0;
218   if (n > 65536) size = 65536;
219   while (n >= powers_of_2[i]) i++;
220   if (n == powers_of_2[i-1])
221     return n;
222   else
223     return powers_of_2[up ? i : i-1];
224 }
225
226 static void
227 init (void)
228 {
229   XWindowAttributes xgwa;
230   Colormap cmap;
231   XGCValues gcv;
232   int width, height;
233   char *bitmap_name;
234   Bool scale_up;
235
236   XGetWindowAttributes (dpy, window, &xgwa);
237   cmap = xgwa.colormap;
238   depth = xgwa.depth;
239
240   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
241   bg = get_pixel_resource ("background", "Background", dpy, cmap);
242   delay = get_integer_resource ("delay", "Integer");
243   delay2 = get_integer_resource ("delay2", "Integer");
244   if (delay < 0) delay = 0;
245   if (delay2 < 0) delay2 = 0;
246   bitmap_name = get_string_resource ("bitmap", "Bitmap");
247   if (! bitmap_name || !*bitmap_name)
248     bitmap_name = "(default)";
249
250   if (!strcmp (bitmap_name, "(default)"))
251     {
252       width = som_width;
253       height = som_height;
254       bitmap = XCreatePixmapFromBitmapData (dpy, window, (char *) som_bits,
255                                             width, height, fg, bg, depth);
256       scale_up = True; /* definitely. */
257     }
258   else if (!strcmp (bitmap_name, "(screen)"))
259     {
260       bitmap = read_screen (dpy, window, &width, &height);
261       scale_up = True; /* maybe? */
262     }
263   else
264     {
265       read_bitmap (bitmap_name, &width, &height);
266       scale_up = True; /* probably? */
267     }
268
269   size = (width < height) ? height : width;     /* make it square */
270   size = to_pow2(size, scale_up);               /* round up to power of 2 */
271   {                                             /* don't exceed screen size */
272     int s = XScreenNumberOfScreen(xgwa.screen);
273     int w = to_pow2(XDisplayWidth(dpy, s), False);
274     int h = to_pow2(XDisplayHeight(dpy, s), False);
275     if (size > w) size = w;
276     if (size > h) size = h;
277   }
278
279   self = XCreatePixmap (dpy, window, size, size, depth);
280   temp = XCreatePixmap (dpy, window, size, size, depth);
281   mask = XCreatePixmap (dpy, window, size, size, depth);
282   gcv.foreground = (depth == 1 ? 1 : (~0));
283   gcv.function=GXset;  SET = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
284   gcv.function=GXclear;CLR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
285   gcv.function=GXcopy; CPY = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
286   gcv.function=GXor;   IOR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
287   gcv.function=GXand;  AND = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
288   gcv.function=GXxor;  XOR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
289
290   gcv.foreground = gcv.background = bg;
291   gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
292   /* Clear self to the background color (not to 0, which CLR does.) */
293   XFillRectangle (dpy, self, gc, 0, 0, size, size);
294   XSetForeground (dpy, gc, fg);
295
296   XCopyArea (dpy, bitmap, self, CPY, 0, 0, width, height,
297              (size - width)>>1, (size - height)>>1);
298   XFreePixmap(dpy, bitmap);
299
300   display (self);
301   XSync(dpy, False);
302   screenhack_handle_events (dpy);
303 }
304
305 static void
306 display (Pixmap pixmap)
307 {
308   XWindowAttributes xgwa;
309   static int last_w = 0, last_h = 0;
310   XGetWindowAttributes (dpy, window, &xgwa);
311   if (xgwa.width != last_w || xgwa.height != last_h)
312     {
313       XClearWindow (dpy, window);
314       last_w = xgwa.width;
315       last_h = xgwa.height;
316     }
317 #ifdef HAVE_XPM
318   if (depth != 1)
319     XCopyArea (dpy, pixmap, window, gc, 0, 0, size, size,
320                (xgwa.width-size)>>1, (xgwa.height-size)>>1);
321   else
322 #endif
323     XCopyPlane (dpy, pixmap, window, gc, 0, 0, size, size,
324                 (xgwa.width-size)>>1, (xgwa.height-size)>>1, 1);
325 /*
326   XDrawRectangle (dpy, window, gc,
327                   ((xgwa.width-size)>>1)-1, ((xgwa.height-size)>>1)-1,
328                   size+2, size+2);
329 */
330   XSync (dpy, False);
331   screenhack_handle_events (dpy);
332 }
333
334 \f
335 char *progclass = "BlitSpin";
336
337 char *defaults [] = {
338   ".background: black",
339   ".foreground: white",
340   "*delay:      500000",
341   "*delay2:     500000",
342   "*bitmap:     (default)",
343   "*geometry:   512x512",
344   0
345 };
346
347 XrmOptionDescRec options [] = {
348   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
349   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
350   { "-bitmap",          ".bitmap",      XrmoptionSepArg, 0 },
351   { "-grab-screen",     ".bitmap",      XrmoptionNoArg, "(screen)" },
352   { 0, 0, 0, 0 }
353 };
354
355 void
356 screenhack (Display *d, Window w)
357 {
358   dpy = d;
359   window = w;
360   init ();
361   if (delay2) usleep (delay2 * 2);
362   while (1)
363     {
364       rotate ();
365       screenhack_handle_events (d);
366       if (delay2) usleep (delay2);
367     }
368 }