http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / distort.c
index a18df51bc598dd5948957b6fa4538b8ce8543293..54a12fa9d3e6623a10ff7bf91f914f31b47c4605 100644 (file)
@@ -1,5 +1,5 @@
 /* -*- mode: C; tab-width: 4 -*-
- * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
+ * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998, 2002, 2003
  * Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  *     -more distortion matrices (fortunately, I'm out of ideas :)
  * Stuff that would be cool but probably too much of a resource hog:
  *     -some kind of interpolation to avoid jaggies
+ *    -large speed values leaves the image distorted
  * program idea borrowed from a screensaver on a non-*NIX OS,
+ *
+ * 28 Sep 1999 Jonas Munsin (jmunsin@iki.fi)
+ *    Added about 10x faster algortim for 8, 16 and 32 bpp (modifies pixels
+ *    directly avoiding costly XPutPixle(XGetPixel()) calls, inspired by
+ *    xwhirl made by horvai@clipper.ens.fr (Peter Horvai) and the XFree86
+ *    Xlib sources.
+ *    This piece of code is really horrible, but it works, and at the moment
+ *    I don't have time or inspiration to fix something that works (knock
+ *    on wood).
+ * 08 Oct 1999 Jonas Munsin (jmunsin@iki.fi)
+ *     Corrected several bugs causing references beyond allocated memory.
  */
 
 #include <math.h>
 #include "screenhack.h"
 #include <X11/Xutil.h>
+#include <X11/Xmd.h>
 
 #ifdef HAVE_XSHM_EXTENSION
 # include "xshm.h"
-static Bool use_shm;
+static Bool use_shm = False;
 static XShmSegmentInfo shm_info;
 #endif /* HAVE_XSHM_EXTENSION */
 
@@ -41,21 +54,35 @@ struct coo {
 };
 static struct coo xy_coo[10];
 
-static int delay, radius, speed, number, blackhole, vortex, magnify;
+static int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow;
 static XWindowAttributes xgwa;
 static GC gc;
 static Window g_window;
-static Display *g_dpy; 
+static Display *g_dpy;
+static unsigned long black_pixel;
 
 static XImage *orig_map, *buffer_map;
+static unsigned long *buffer_map_cache;
 
 static int ***from;
 static int ****from_array;
+static int *fast_from = NULL;
 static void (*effect) (int) = NULL;
 static void move_lense(int);
 static void swamp_thing(int);
 static void new_rnd_coo(int);
 static void init_round_lense(void);
+static void (*draw) (int) = NULL;
+static void reflect_draw(int);
+static void plain_draw(int);
+
+static void (*draw_routine)(XImage *, XImage *, int, int, int *) = NULL;
+static void fast_draw_8(XImage *, XImage *, int, int, int *);
+static void fast_draw_16(XImage *, XImage *, int, int, int *);
+static void fast_draw_32(XImage *, XImage *, int, int, int *);
+static void generic_draw(XImage *, XImage *, int, int, int *);
+static int bpp_size = 0;
+
 
 static void init_distort(Display *dpy, Window window) 
 {
@@ -66,10 +93,10 @@ static void init_distort(Display *dpy, Window window)
        g_window=window;
        g_dpy=dpy;
 
-       delay = get_integer_resource ("delay", "Integer");
-       radius = get_integer_resource ("radius", "Integer");
-       speed = get_integer_resource ("speed", "Integer");
-       number = get_integer_resource ("number", "Integer");
+       delay = get_integer_resource("delay", "Integer");
+       radius = get_integer_resource("radius", "Integer");
+       speed = get_integer_resource("speed", "Integer");
+       number = get_integer_resource("number", "Integer");
 
 #ifdef HAVE_XSHM_EXTENSION
        use_shm = get_boolean_resource("useSHM", "Boolean");
@@ -78,14 +105,18 @@ static void init_distort(Display *dpy, Window window)
        blackhole = get_boolean_resource("blackhole", "Boolean");
        vortex = get_boolean_resource("vortex", "Boolean");
        magnify = get_boolean_resource("magnify", "Boolean");
+       reflect = get_boolean_resource("reflect", "Boolean");
+       slow = get_boolean_resource("slow", "Boolean");
        
-       if (get_boolean_resource ("swamp", "Boolean"))
+       if (get_boolean_resource("swamp", "Boolean"))
                effect = &swamp_thing;
-       if (get_boolean_resource ("bounce", "Boolean"))
+       if (get_boolean_resource("bounce", "Boolean"))
                effect = &move_lense;
 
+       XGetWindowAttributes (dpy, window, &xgwa);
+
        if (effect == NULL && radius == 0 && speed == 0 && number == 0
-               && !blackhole && !vortex && !magnify) {
+               && !blackhole && !vortex && !magnify && !reflect) {
 /* if no cmdline options are given, randomly choose one of:
  * -radius 50 -number 4 -speed 1 -bounce
  * -radius 50 -number 4 -speed 1 -blackhole
@@ -97,14 +128,19 @@ static void init_distort(Display *dpy, Window window)
  * -radius 100 -number 1 -speed 2 -vortex
  * -radius 100 -number 1 -speed 2 -vortex -magnify
  * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
- * -radius 50 -number 4 -speed 2 -swamp
- * -radius 50 -number 4 -speed 2 -swamp -blackhole
- * -radius 50 -number 4 -speed 2 -swamp -vortex
- * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
- * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
+ * -radius 80 -number 1 -speed 2 -reflect
+ * -radius 50 -number 3 -speed 2 -reflect
+ * jwz: not these
+ *   -radius 50 -number 4 -speed 2 -swamp
+ *   -radius 50 -number 4 -speed 2 -swamp -blackhole
+ *   -radius 50 -number 4 -speed 2 -swamp -vortex
+ *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
+ *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
  */
                
-               i = (random() % 15);
+               i = (random() % 12 /* 17 */);
+
+               draw = &plain_draw;
 
                switch (i) {
                        case 0:
@@ -137,25 +173,53 @@ static void init_distort(Display *dpy, Window window)
                        case 9:
                                radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
                                effect=&move_lense;break;
+
                        case 10:
+                               radius=80;number=1;speed=2;reflect=1;
+                               draw = &reflect_draw;effect = &move_lense;break;
+                       case 11:
+                               radius=50;number=4;speed=2;reflect=1;
+                               draw = &reflect_draw;effect = &move_lense;break;
+
+#if 0 /* jwz: not these */
+                       case 12:
                                radius=50;number=4;speed=2;
                                effect=&swamp_thing;break;
-                       case 11:
+                       case 13:
                                radius=50;number=4;speed=2;blackhole=1;
                                effect=&swamp_thing;break;
-                       case 12:
+                       case 14:
                                radius=50;number=4;speed=2;vortex=1;
                                effect=&swamp_thing;break;
-                       case 13:
+                       case 15:
                                radius=50;number=4;speed=2;vortex=1;magnify=1;
                                effect=&swamp_thing;break;
-                       case 14: default:
+                       case 16:
                                radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
                                effect=&swamp_thing;break;
+#endif
+
+            default:
+                abort(); break;
                }
 
+        /* but if the window is small, reduce default radius */
+        if (xgwa.width < radius * 8)
+          radius = xgwa.width/8;
        }
 
+    /* never allow the radius to be too close to the min window dimension
+     */
+    if (radius >= xgwa.width  * 0.45) radius = xgwa.width  * 0.45;
+    if (radius >= xgwa.height * 0.45) radius = xgwa.height * 0.45;
+
+
+    /* -swamp mode consumes vast amounts of memory, proportional to radius --
+       so throttle radius to a small-ish value (60 => ~30MB.)
+     */
+    if (effect == &swamp_thing && radius > 60)
+      radius = 60;
+
        if (delay < 0)
                delay = 0;
        if (radius <= 0)
@@ -168,8 +232,14 @@ static void init_distort(Display *dpy, Window window)
                number=1;
        if (effect == NULL)
                effect = &move_lense;
+       if (reflect) {
+               draw = &reflect_draw;
+               effect = &move_lense;
+       }
+       if (draw == NULL)
+               draw = &plain_draw;
 
-       XGetWindowAttributes (dpy, window, &xgwa);
+       black_pixel = BlackPixelOfScreen( xgwa.screen );
 
        gcv.function = GXcopy;
        gcv.subwindow_mode = IncludeInferiors;
@@ -178,11 +248,17 @@ static void init_distort(Display *dpy, Window window)
                gcflags |= GCSubwindowMode;
        gc = XCreateGC (dpy, window, gcflags, &gcv);
 
-       grab_screen_image (xgwa.screen, window);
+    load_random_image (xgwa.screen, window, window, NULL);
 
        buffer_map = 0;
        orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
                                                 ~0L, ZPixmap);
+       buffer_map_cache = malloc(sizeof(unsigned long)*(2*radius+speed+2)*(2*radius+speed+2));
+
+       if (buffer_map_cache == NULL) {
+               perror("distort");
+               exit(EXIT_FAILURE);
+       }
 
 # ifdef HAVE_XSHM_EXTENSION
 
@@ -207,6 +283,31 @@ static void init_distort(Display *dpy, Window window)
                  calloc(buffer_map->height, buffer_map->bytes_per_line);
        }
 
+       if ((buffer_map->byte_order == orig_map->byte_order)
+                       && (buffer_map->depth == orig_map->depth)
+                       && (buffer_map->format == ZPixmap)
+                       && (orig_map->format == ZPixmap)
+                       && !slow) {
+               switch (orig_map->bits_per_pixel) {
+                       case 32:
+                               draw_routine = &fast_draw_32;
+                               bpp_size = sizeof(CARD32);
+                               break;
+                       case 16:
+                               draw_routine = &fast_draw_16;
+                               bpp_size = sizeof(CARD16);
+                               break;
+                       case 8:
+                               draw_routine = &fast_draw_8;
+                               bpp_size = sizeof(CARD8);
+                               break;
+                       default:
+                               draw_routine = &generic_draw;
+                               break;
+               }
+       } else {
+               draw_routine = &generic_draw;
+       }
        init_round_lense();
 
        for (i = 0; i < number; i++) {
@@ -219,6 +320,7 @@ static void init_distort(Display *dpy, Window window)
                xy_coo[i].xmove = speed + (i%2)*2*(-speed);
                xy_coo[i].ymove = speed + (i%2)*2*(-speed);
        }
+
 }
 
 /* example: initializes a "see-trough" matrix */
@@ -233,6 +335,26 @@ static void init_distort(Display *dpy, Window window)
        } 
 }
 */
+static void convert(void) {
+       int *p;
+       int i, j;
+       fast_from = calloc(1, sizeof(int)*((buffer_map->bytes_per_line/bpp_size)*(2*radius+speed+2) + 2*radius+speed+2));
+       if (fast_from == NULL) {
+               perror("distort");
+               exit(EXIT_FAILURE);
+       }
+       p = fast_from;
+       for (i = 0; i < 2*radius+speed+2; i++) {
+               for (j = 0; j < 2*radius+speed+2; j++) {
+                       *(p + i + j*buffer_map->bytes_per_line/bpp_size)
+                               = from[i][j][0] + xgwa.width*from[i][j][1];
+                       if (*(p + i + j*buffer_map->bytes_per_line/bpp_size) < 0
+                                       || *(p + i + j*buffer_map->bytes_per_line/bpp_size) >= orig_map->height*orig_map->width) {
+                               *(p + i + j*buffer_map->bytes_per_line/bpp_size) = 0;
+                       }
+               }
+       }
+}
 
 /* makes a lense with the Radius=loop and centred in
  * the point (radius, radius)
@@ -242,10 +364,13 @@ static void make_round_lense(int radius, int loop)
        int i, j;
 
        for (i = 0; i < 2*radius+speed+2; i++) {
-               for(j = 0; j < 2*radius+speed+2; j++) {
+               for(j = 0; j < ((0 == bpp_size) ? (2*radius+speed+2) : (buffer_map->bytes_per_line/bpp_size)); j++) {
                        double r, d;
                        r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
-                       d=r/loop;
+                       if (loop == 0)
+                         d=0.0;
+                       else
+                         d=r/loop;
 
                        if (r < loop-1) {
 
@@ -255,14 +380,19 @@ static void make_round_lense(int radius, int loop)
                 * (with permission) from the whirl plugin for gimp,
                 * Copyright (C) 1996 Federico Mena Quintero
                 */
-               /* 2.5 is just a constant used because it looks good :) */
-                                       angle = 2.5*(1-r/loop)*(1-r/loop);
-
-                                       from[i][j][0] = radius + cos(angle -
-                                               atan2(radius-j,-(radius-i)))*r;
-                                       from[i][j][1] = radius + sin(angle -
-                                               atan2(radius-j,-(radius-i)))*r;
-
+               /* 5 is just a constant used because it looks good :) */
+                                       angle = 5*(1-d)*(1-d);
+
+        /* Avoid atan2: DOMAIN error message */
+                                       if ((radius-j) == 0.0 && (radius-i) == 0.0) {
+                                               from[i][j][0] = radius + cos(angle)*r;
+                                               from[i][j][1] = radius + sin(angle)*r;
+                                       } else {
+                                               from[i][j][0] = radius +
+                                                       cos(angle - atan2(radius-j, -(radius-i)))*r;
+                                               from[i][j][1] = radius +
+                                                       sin(angle - atan2(radius-j, -(radius-i)))*r;
+                                       }
                                        if (magnify) {
                                                r = sin(d*M_PI_2);
                                                if (blackhole && r != 0) /* blackhole effect */
@@ -277,7 +407,7 @@ static void make_round_lense(int radius, int loop)
         * distortion, a negative value sucks everything into a black hole
         */
                                /*      r = r*r; */
-                                       if (blackhole) /* blackhole effect */
+                                       if (blackhole && r != 0) /* blackhole effect */
                                                r = 1/r;
                                                                        /* bubble effect (and blackhole) */
                                        from[i][j][0] = radius + (i-radius)*r;
@@ -289,28 +419,38 @@ static void make_round_lense(int radius, int loop)
                        }
                }
        }
+
+       /* this is really just a quick hack to keep both the compability mode with all depths and still
+        * allow the custom optimized draw routines with the minimum amount of work */
+       if (0 != bpp_size) {
+               convert();
+       }
 }
 
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE -1
+#endif
+
 static void allocate_lense(void)
 {
        int i, j;
+       int s = ((0 != bpp_size) ? (buffer_map->bytes_per_line/bpp_size) : (2*radius+speed+2));
        /* maybe this should be redone so that from[][][] is in one block;
         * then pointers could be used instead of arrays in some places (and
         * maybe give a speedup - maybe also consume less memory)
         */
-
-       from = (int ***)malloc((2*radius+speed+2) * sizeof(int **));
+       from = (int ***)malloc(s*sizeof(int **));
        if (from == NULL) {
                perror("distort");
                exit(EXIT_FAILURE);
        }
-       for (i = 0; i < 2*radius+speed+2; i++) {
+       for (i = 0; i < s; i++) {
                from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
                if (from[i] == NULL) {
                        perror("distort");
                        exit(EXIT_FAILURE);
                }
-               for (j = 0; j < 2*radius+speed+2; j++) {
+               for (j = 0; j < s; j++) {
                        from[i][j] = (int *)malloc(2 * sizeof(int));
                        if (from[i][j] == NULL) {
                                perror("distort");
@@ -341,21 +481,126 @@ static void init_round_lense(void)
        }
 }
 
+/* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties
+ * of the src and dest XImages must hold (otherwise the generic, slooow, method provided
+ * by X is to be used):
+ *     src->byte_order == dest->byte_order
+ *     src->format == ZPixmap && dest->format == ZPixmap
+ *     src->depth == dest->depth == the depth the function in question asumes
+ * x and y is the coordinates in src from where to cut out the image from,
+ * distort_matrix is a precalculated array of how to distort the matrix
+ */
+
+static void fast_draw_8(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
+       CARD8 *u = (CARD8 *)dest->data;
+       CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8);
+
+       while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height
+                               *dest->bytes_per_line/sizeof(CARD8))) {
+               *u++ = t[*distort_matrix++];
+       }
+}
+
+static void fast_draw_16(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
+       CARD16 *u = (CARD16 *)dest->data;
+       CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16);
+
+       while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height
+                               *dest->bytes_per_line/sizeof(CARD16))) {
+               *u++ = t[*distort_matrix++];
+       }
+}
+
+static void fast_draw_32(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
+       CARD32 *u = (CARD32 *)dest->data;
+       CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32);
+
+       while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height
+                               *dest->bytes_per_line/sizeof(CARD32))) {
+               *u++ = t[*distort_matrix++];
+       }
+}
+
+static void generic_draw(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
+       int i, j;
+       for (i = 0; i < dest->width; i++)
+               for (j = 0; j < dest->height; j++)
+                       if (from[i][j][0] + x >= 0 &&
+                                       from[i][j][0] + x < src->width &&
+                                       from[i][j][1] + y >= 0 &&
+                                       from[i][j][1] + y < src->height)
+                               XPutPixel(dest, i, j,
+                                               XGetPixel(src,
+                                                       from[i][j][0] + x,
+                                                       from[i][j][1] + y));
+}
 
 /* generate an XImage of from[][][] and draw it on the screen */
-void draw(int k)
+static void plain_draw(int k)
+{
+       if (xy_coo[k].x+2*radius+speed+2 > orig_map->width ||
+                       xy_coo[k].y+2*radius+speed+2 > orig_map->height)
+               return;
+
+       draw_routine(orig_map, buffer_map, xy_coo[k].x, xy_coo[k].y, fast_from);
+
+# ifdef HAVE_XSHM_EXTENSION
+       if (use_shm)
+               XShmPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
+                               2*radius+speed+2, 2*radius+speed+2, False);
+       else
+
+       if (!use_shm)
+# endif
+               XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
+                               2*radius+speed+2, 2*radius+speed+2);
+
+}
+
+
+/* generate an XImage from the reflect algoritm submitted by
+ * Randy Zack <randy@acucorp.com>
+ * draw really got too big and ugly so I split it up
+ * it should be possible to use the from[][] to speed it up
+ * (once I figure out the algorithm used :)
+ */
+static void reflect_draw(int k)
 {
        int i, j;
+       int     cx, cy;
+       int     ly, lysq, lx, ny, dist, rsq = radius * radius;
+
+       cx = cy = radius;
+       if (xy_coo[k].ymove > 0)
+               cy += speed;
+       if (xy_coo[k].xmove > 0)
+               cx += speed;
+
        for(i = 0 ; i < 2*radius+speed+2; i++) {
+               ly = i - cy;
+               lysq = ly * ly;
+               ny = xy_coo[k].y + i;
+               if (ny >= orig_map->height) ny = orig_map->height-1;
                for(j = 0 ; j < 2*radius+speed+2 ; j++) {
-                       if (xy_coo[k].x+from[i][j][0] >= 0 &&
-                                       xy_coo[k].x+from[i][j][0] < xgwa.width &&
-                                       xy_coo[k].y+from[i][j][1] >= 0 &&
-                                       xy_coo[k].y+from[i][j][1] < xgwa.height)
-                               XPutPixel(buffer_map, i, j,
-                                               XGetPixel(orig_map,
-                                                       xy_coo[k].x+from[i][j][0],
-                                                       xy_coo[k].y+from[i][j][1]));
+                       lx = j - cx;
+                       dist = lx * lx + lysq;
+                       if (dist > rsq ||
+                               ly < -radius || ly > radius ||
+                               lx < -radius || lx > radius)
+                               XPutPixel( buffer_map, j, i,
+                                                  XGetPixel( orig_map, xy_coo[k].x + j, ny ));
+                       else if (dist == 0)
+                               XPutPixel( buffer_map, j, i, black_pixel );
+                       else {
+                               int     x = xy_coo[k].x + cx + (lx * rsq / dist);
+                               int     y = xy_coo[k].y + cy + (ly * rsq / dist);
+                               if (x < 0 || x >= xgwa.width ||
+                                       y < 0 || y >= xgwa.height)
+                                       XPutPixel( buffer_map, j, i, black_pixel );
+                               else
+                                       XPutPixel( buffer_map, j, i,
+                                                          XGetPixel( orig_map, x, y ));
+                       }
                }
        }
 
@@ -391,11 +636,11 @@ static void move_lense(int k)
 {
        int i;
 
-       if (xy_coo[k].x + 4*radius/2 >= xgwa.width)
+       if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
                xy_coo[k].xmove = -abs(xy_coo[k].xmove);
        if (xy_coo[k].x <= speed) 
                xy_coo[k].xmove = abs(xy_coo[k].xmove);
-       if (xy_coo[k].y + 4*radius/2 >= xgwa.height)
+       if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
                xy_coo[k].ymove = -abs(xy_coo[k].ymove);
        if (xy_coo[k].y <= speed)
                xy_coo[k].ymove = abs(xy_coo[k].ymove);
@@ -403,10 +648,11 @@ static void move_lense(int k)
        xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
        xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
 
+       /* bounce against othe lenses */
        for (i = 0; i < number; i++) {
                if ((i != k)
                
-/* This commented test is for rectangular lenses (not presently used) and
+/* This commented test is for rectangular lenses (not currently used) and
  * the one used is for circular ones
                && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
                && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
@@ -428,7 +674,7 @@ static void move_lense(int k)
 }
 
 /* make xy_coo[k] grow/shrink */
-void swamp_thing(int k)
+static void swamp_thing(int k)
 {
        if (xy_coo[k].r >= radius)
                xy_coo[k].r_change = -abs(xy_coo[k].r_change);
@@ -463,7 +709,7 @@ char *defaults [] = {
        "*visualID:                     Best",
 #endif
 
-       "*delay:                        10000",
+       "*delay:                        1000",
        "*radius:                       0",
        "*speed:                        0",
        "*number:                       0",
@@ -471,6 +717,7 @@ char *defaults [] = {
        "*magnify:                      False",
        "*swamp:                        False",
        "*bounce:                       False",
+       "*reflect:                      False",
        "*blackhole:            False",
 #ifdef HAVE_XSHM_EXTENSION
        "*useSHM:                       False",         /* xshm turns out not to help. */
@@ -485,9 +732,11 @@ XrmOptionDescRec options [] = {
        { "-number",    ".number",      XrmoptionSepArg, 0 },
        { "-swamp",     ".swamp",       XrmoptionNoArg, "True" },
        { "-bounce",    ".bounce",      XrmoptionNoArg, "True" },
+       { "-reflect",   ".reflect",     XrmoptionNoArg, "True" },
        { "-vortex",    ".vortex",      XrmoptionNoArg, "True" },
        { "-magnify",   ".magnify",     XrmoptionNoArg, "True" },
        { "-blackhole", ".blackhole",   XrmoptionNoArg, "True" },
+       { "-slow",      ".slow",        XrmoptionNoArg, "True" },
 #ifdef HAVE_XSHM_EXTENSION
        { "-shm",               ".useSHM",      XrmoptionNoArg, "True" },
        { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
@@ -507,7 +756,8 @@ void screenhack(Display *dpy, Window window)
                        draw(k);
                }
 
-               XSync(dpy, True);
+               XSync(dpy, False);
+        screenhack_handle_events (dpy);
                if (delay) usleep(delay);
        }