From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[xscreensaver] / OSX / XScreenSaverView.m.orig
diff --git a/OSX/XScreenSaverView.m.orig b/OSX/XScreenSaverView.m.orig
deleted file mode 100644 (file)
index ee8f3e6..0000000
+++ /dev/null
@@ -1,1804 +0,0 @@
-/* xscreensaver, Copyright (c) 2006-2013 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.
- */
-
-/* This is a subclass of Apple's ScreenSaverView that knows how to run
-   xscreensaver programs without X11 via the dark magic of the "jwxyz"
-   library.  In xscreensaver terminology, this is the replacement for
-   the "screenhack.c" module.
- */
-
-#import <QuartzCore/QuartzCore.h>
-#import <zlib.h>
-#import "XScreenSaverView.h"
-#import "XScreenSaverConfigSheet.h"
-#import "screenhackI.h"
-#import "xlockmoreI.h"
-#import "jwxyz-timers.h"
-
-
-/* Garbage collection only exists if we are being compiled against the 
-   10.6 SDK or newer, not if we are building against the 10.4 SDK.
- */
-#ifndef  MAC_OS_X_VERSION_10_6
-# define MAC_OS_X_VERSION_10_6 1060  /* undefined in 10.4 SDK, grr */
-#endif
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6  /* 10.6 SDK */
-# import <objc/objc-auto.h>
-# define DO_GC_HACKERY
-#endif
-
-extern struct xscreensaver_function_table *xscreensaver_function_table;
-
-/* Global variables used by the screen savers
- */
-const char *progname;
-const char *progclass;
-int mono_p = 0;
-
-
-# ifdef USE_IPHONE
-
-extern NSDictionary *make_function_table_dict(void);  // ios-function-table.m
-
-/* Stub definition of the superclass, for iPhone.
- */
-@implementation ScreenSaverView
-{
-  NSTimeInterval anim_interval;
-  Bool animating_p;
-  NSTimer *anim_timer;
-}
-
-- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {
-  self = [super initWithFrame:frame];
-  if (! self) return 0;
-  anim_interval = 1.0/30;
-  return self;
-}
-- (NSTimeInterval)animationTimeInterval { return anim_interval; }
-- (void)setAnimationTimeInterval:(NSTimeInterval)i { anim_interval = i; }
-- (BOOL)hasConfigureSheet { return NO; }
-- (NSWindow *)configureSheet { return nil; }
-- (NSView *)configureView { return nil; }
-- (BOOL)isPreview { return NO; }
-- (BOOL)isAnimating { return animating_p; }
-- (void)animateOneFrame { }
-
-- (void)startAnimation {
-  if (animating_p) return;
-  animating_p = YES;
-  anim_timer = [NSTimer scheduledTimerWithTimeInterval: anim_interval
-                        target:self
-                        selector:@selector(animateOneFrame)
-                        userInfo:nil
-                        repeats:YES];
-}
-
-- (void)stopAnimation {
-  if (anim_timer) {
-    [anim_timer invalidate];
-    anim_timer = 0;
-  }
-  animating_p = NO;
-}
-@end
-
-# endif // !USE_IPHONE
-
-
-
-@interface XScreenSaverView (Private)
-- (void) stopAndClose:(Bool)relaunch;
-@end
-
-@implementation XScreenSaverView
-
-// Given a lower-cased saver name, returns the function table for it.
-// If no name, guess the name from the class's bundle name.
-//
-- (struct xscreensaver_function_table *) findFunctionTable:(NSString *)name
-{
-  NSBundle *nsb = [NSBundle bundleForClass:[self class]];
-  NSAssert1 (nsb, @"no bundle for class %@", [self class]);
-
-  NSString *path = [nsb bundlePath];
-  CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
-                                               (CFStringRef) path,
-                                               kCFURLPOSIXPathStyle,
-                                               true);
-  CFBundleRef cfb = CFBundleCreate (kCFAllocatorDefault, url);
-  CFRelease (url);
-  NSAssert1 (cfb, @"no CFBundle for \"%@\"", path);
-  // #### Analyze says "Potential leak of an object stored into cfb"
-  
-  if (! name)
-    name = [[path lastPathComponent] stringByDeletingPathExtension];
-
-  name = [[name lowercaseString]
-           stringByReplacingOccurrencesOfString:@" "
-           withString:@""];
-
-# ifndef USE_IPHONE
-  // CFBundleGetDataPointerForName doesn't work in "Archive" builds.
-  // I'm guessing that symbol-stripping is mandatory.  Fuck.
-  NSString *table_name = [name stringByAppendingString:
-                                 @"_xscreensaver_function_table"];
-  void *addr = CFBundleGetDataPointerForName (cfb, (CFStringRef) table_name);
-  CFRelease (cfb);
-
-  if (! addr)
-    NSLog (@"no symbol \"%@\" for \"%@\"", table_name, path);
-
-# else  // USE_IPHONE
-  // Remember: any time you add a new saver to the iOS app,
-  // manually run "make ios-function-table.m"!
-  if (! function_tables)
-    function_tables = [make_function_table_dict() retain];
-  NSValue *v = [function_tables objectForKey: name];
-  void *addr = v ? [v pointerValue] : 0;
-# endif // USE_IPHONE
-
-  return (struct xscreensaver_function_table *) addr;
-}
-
-
-// Add the "Contents/Resources/" subdirectory of this screen saver's .bundle
-// to $PATH for the benefit of savers that include helper shell scripts.
-//
-- (void) setShellPath
-{
-  NSBundle *nsb = [NSBundle bundleForClass:[self class]];
-  NSAssert1 (nsb, @"no bundle for class %@", [self class]);
-  
-  NSString *nsdir = [nsb resourcePath];
-  NSAssert1 (nsdir, @"no resourcePath for class %@", [self class]);
-  const char *dir = [nsdir cStringUsingEncoding:NSUTF8StringEncoding];
-  const char *opath = getenv ("PATH");
-  if (!opath) opath = "/bin"; // $PATH is unset when running under Shark!
-  char *npath = (char *) malloc (strlen (opath) + strlen (dir) + 30);
-  strcpy (npath, "PATH=");
-  strcat (npath, dir);
-  strcat (npath, ":");
-  strcat (npath, opath);
-  if (putenv (npath)) {
-    perror ("putenv");
-    NSAssert1 (0, @"putenv \"%s\" failed", npath);
-  }
-
-  /* Don't free (npath) -- MacOS's putenv() does not copy it. */
-}
-
-
-// set an $XSCREENSAVER_CLASSPATH variable so that included shell scripts
-// (e.g., "xscreensaver-text") know how to look up resources.
-//
-- (void) setResourcesEnv:(NSString *) name
-{
-  NSBundle *nsb = [NSBundle bundleForClass:[self class]];
-  NSAssert1 (nsb, @"no bundle for class %@", [self class]);
-  
-  const char *s = [name cStringUsingEncoding:NSUTF8StringEncoding];
-  char *env = (char *) malloc (strlen (s) + 40);
-  strcpy (env, "XSCREENSAVER_CLASSPATH=");
-  strcat (env, s);
-  if (putenv (env)) {
-    perror ("putenv");
-    NSAssert1 (0, @"putenv \"%s\" failed", env);
-  }
-  /* Don't free (env) -- MacOS's putenv() does not copy it. */
-}
-
-
-static void
-add_default_options (const XrmOptionDescRec *opts,
-                     const char * const *defs,
-                     XrmOptionDescRec **opts_ret,
-                     const char ***defs_ret)
-{
-  /* These aren't "real" command-line options (there are no actual command-line
-     options in the Cocoa version); but this is the somewhat kludgey way that
-     the <xscreensaver-text /> and <xscreensaver-image /> tags in the
-     ../hacks/config/\*.xml files communicate with the preferences database.
-  */
-  static const XrmOptionDescRec default_options [] = {
-    { "-text-mode",              ".textMode",          XrmoptionSepArg, 0 },
-    { "-text-literal",           ".textLiteral",       XrmoptionSepArg, 0 },
-    { "-text-file",              ".textFile",          XrmoptionSepArg, 0 },
-    { "-text-url",               ".textURL",           XrmoptionSepArg, 0 },
-    { "-text-program",           ".textProgram",       XrmoptionSepArg, 0 },
-    { "-grab-desktop",           ".grabDesktopImages", XrmoptionNoArg, "True" },
-    { "-no-grab-desktop",        ".grabDesktopImages", XrmoptionNoArg, "False"},
-    { "-choose-random-images",   ".chooseRandomImages",XrmoptionNoArg, "True" },
-    { "-no-choose-random-images",".chooseRandomImages",XrmoptionNoArg, "False"},
-    { "-image-directory",        ".imageDirectory",    XrmoptionSepArg, 0 },
-    { "-fps",                    ".doFPS",             XrmoptionNoArg, "True" },
-    { "-no-fps",                 ".doFPS",             XrmoptionNoArg, "False"},
-    { "-foreground",             ".foreground",        XrmoptionSepArg, 0 },
-    { "-fg",                     ".foreground",        XrmoptionSepArg, 0 },
-    { "-background",             ".background",        XrmoptionSepArg, 0 },
-    { "-bg",                     ".background",        XrmoptionSepArg, 0 },
-    { 0, 0, 0, 0 }
-  };
-  static const char *default_defaults [] = {
-    ".doFPS:              False",
-    ".doubleBuffer:       True",
-    ".multiSample:        False",
-# ifndef USE_IPHONE
-    ".textMode:           date",
-# else
-    ".textMode:           url",
-# endif
- // ".textLiteral:        ",
- // ".textFile:           ",
-    ".textURL:            http://en.wikipedia.org/w/index.php?title=Special:NewPages&feed=rss",
- // ".textProgram:        ",
-    ".grabDesktopImages:  yes",
-# ifndef USE_IPHONE
-    ".chooseRandomImages: no",
-# else
-    ".chooseRandomImages: yes",
-# endif
-    ".imageDirectory:     ~/Pictures",
-    ".relaunchDelay:      2",
-    0
-  };
-
-  int count = 0, i, j;
-  for (i = 0; default_options[i].option; i++)
-    count++;
-  for (i = 0; opts[i].option; i++)
-    count++;
-
-  XrmOptionDescRec *opts2 = (XrmOptionDescRec *) 
-    calloc (count + 1, sizeof (*opts2));
-
-  i = 0;
-  j = 0;
-  while (default_options[j].option) {
-    opts2[i] = default_options[j];
-    i++, j++;
-  }
-  j = 0;
-  while (opts[j].option) {
-    opts2[i] = opts[j];
-    i++, j++;
-  }
-
-  *opts_ret = opts2;
-
-
-  /* now the defaults
-   */
-  count = 0;
-  for (i = 0; default_defaults[i]; i++)
-    count++;
-  for (i = 0; defs[i]; i++)
-    count++;
-
-  const char **defs2 = (const char **) calloc (count + 1, sizeof (*defs2));
-
-  i = 0;
-  j = 0;
-  while (default_defaults[j]) {
-    defs2[i] = default_defaults[j];
-    i++, j++;
-  }
-  j = 0;
-  while (defs[j]) {
-    defs2[i] = defs[j];
-    i++, j++;
-  }
-
-  *defs_ret = defs2;
-}
-
-
-#ifdef USE_IPHONE
-/* Returns the current time in seconds as a double.
- */
-static double
-double_time (void)
-{
-  struct timeval now;
-# ifdef GETTIMEOFDAY_TWO_ARGS
-  struct timezone tzp;
-  gettimeofday(&now, &tzp);
-# else
-  gettimeofday(&now);
-# endif
-
-  return (now.tv_sec + ((double) now.tv_usec * 0.000001));
-}
-#endif // USE_IPHONE
-
-
-- (id) initWithFrame:(NSRect)frame
-           saverName:(NSString *)saverName
-           isPreview:(BOOL)isPreview
-{
-# ifdef USE_IPHONE
-  initial_bounds = frame.size;
-  rot_current_size = frame.size;       // needs to be early, because
-  rot_from = rot_current_size;         // [self setFrame] is called by
-  rot_to = rot_current_size;           // [super initWithFrame].
-  rotation_ratio = -1;
-# endif
-
-  if (! (self = [super initWithFrame:frame isPreview:isPreview]))
-    return 0;
-  
-  xsft = [self findFunctionTable: saverName];
-  if (! xsft) {
-    [self release];
-    return 0;
-  }
-
-  [self setShellPath];
-
-# ifdef USE_IPHONE
-  [self setMultipleTouchEnabled:YES];
-  orientation = UIDeviceOrientationUnknown;
-  [self didRotate:nil];
-# endif // USE_IPHONE
-
-  setup_p = YES;
-  if (xsft->setup_cb)
-    xsft->setup_cb (xsft, xsft->setup_arg);
-
-  /* The plist files for these preferences show up in
-     $HOME/Library/Preferences/ByHost/ in a file named like
-     "org.jwz.xscreensaver.<SAVERNAME>.<NUMBERS>.plist"
-   */
-  NSString *name = [NSString stringWithCString:xsft->progclass
-                             encoding:NSISOLatin1StringEncoding];
-  name = [@"org.jwz.xscreensaver." stringByAppendingString:name];
-  [self setResourcesEnv:name];
-
-  
-  XrmOptionDescRec *opts = 0;
-  const char **defs = 0;
-  add_default_options (xsft->options, xsft->defaults, &opts, &defs);
-  prefsReader = [[PrefsReader alloc]
-                  initWithName:name xrmKeys:opts defaults:defs];
-  free (defs);
-  // free (opts);  // bah, we need these! #### leak!
-  xsft->options = opts;
-  
-  progname = progclass = xsft->progclass;
-
-  next_frame_time = 0;
-  
-# ifdef USE_BACKBUFFER
-  [self createBackbuffer];
-  [self initLayer];
-# endif
-
-# ifdef USE_IPHONE
-  // So we can tell when we're docked.
-  [UIDevice currentDevice].batteryMonitoringEnabled = YES;
-# endif // USE_IPHONE
-
-  return self;
-}
-
-- (void) initLayer
-{
-# ifndef USE_IPHONE
-  [self setLayer: [CALayer layer]];
-  self.layer.delegate = self;
-  self.layer.opaque = YES;
-  [self setWantsLayer: YES];
-# endif
-}
-
-
-- (id) initWithFrame:(NSRect)frame isPreview:(BOOL)p
-{
-  return [self initWithFrame:frame saverName:0 isPreview:p];
-}
-
-
-- (void) dealloc
-{
-  NSAssert(![self isAnimating], @"still animating");
-  NSAssert(!xdata, @"xdata not yet freed");
-  if (xdpy)
-    jwxyz_free_display (xdpy);
-
-# ifdef USE_BACKBUFFER
-  if (backbuffer)
-    CGContextRelease (backbuffer);
-# endif
-
-  [prefsReader release];
-
-  // xsft
-  // fpst
-
-  [super dealloc];
-}
-
-- (PrefsReader *) prefsReader
-{
-  return prefsReader;
-}
-
-
-#ifdef USE_IPHONE
-- (void) lockFocus { }
-- (void) unlockFocus { }
-#endif // USE_IPHONE
-
-
-
-# ifdef USE_IPHONE
-/* A few seconds after the saver launches, we store the "wasRunning"
-   preference.  This is so that if the saver is crashing at startup,
-   we don't launch it again next time, getting stuck in a crash loop.
- */
-- (void) allSystemsGo: (NSTimer *) timer
-{
-  NSAssert (timer == crash_timer, @"crash timer screwed up");
-  crash_timer = 0;
-
-  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
-  [prefs setBool:YES forKey:@"wasRunning"];
-  [prefs synchronize];
-}
-#endif // USE_IPHONE
-
-
-- (void) startAnimation
-{
-  NSAssert(![self isAnimating], @"already animating");
-  NSAssert(!initted_p && !xdata, @"already initialized");
-  [super startAnimation];
-  /* We can't draw on the window from this method, so we actually do the
-     initialization of the screen saver (xsft->init_cb) in the first call
-     to animateOneFrame() instead.
-   */
-
-# ifdef USE_IPHONE
-  if (crash_timer)
-    [crash_timer invalidate];
-
-  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
-  [prefs removeObjectForKey:@"wasRunning"];
-  [prefs synchronize];
-
-  crash_timer = [NSTimer scheduledTimerWithTimeInterval: 5
-                         target:self
-                         selector:@selector(allSystemsGo:)
-                         userInfo:nil
-                         repeats:NO];
-
-# endif // USE_IPHONE
-
-  // Never automatically turn the screen off if we are docked,
-  // and an animation is running.
-  //
-# ifdef USE_IPHONE
-  [UIApplication sharedApplication].idleTimerDisabled =
-    ([UIDevice currentDevice].batteryState != UIDeviceBatteryStateUnplugged);
-# endif
-}
-
-
-- (void)stopAnimation
-{
-  NSAssert([self isAnimating], @"not animating");
-
-  if (initted_p) {
-
-    [self lockFocus];       // in case something tries to draw from here
-    [self prepareContext];
-
-    /* I considered just not even calling the free callback at all...
-       But webcollage-cocoa needs it, to kill the inferior webcollage
-       processes (since the screen saver framework never generates a
-       SIGPIPE for them...)  Instead, I turned off the free call in
-       xlockmore.c, which is where all of the bogus calls are anyway.
-     */
-    xsft->free_cb (xdpy, xwindow, xdata);
-    [self unlockFocus];
-
-//  setup_p = NO; // #### wait, do we need this?
-    initted_p = NO;
-    xdata = 0;
-  }
-
-# ifdef USE_IPHONE
-  if (crash_timer)
-    [crash_timer invalidate];
-  crash_timer = 0;
-  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
-  [prefs removeObjectForKey:@"wasRunning"];
-  [prefs synchronize];
-# endif // USE_IPHONE
-
-  [super stopAnimation];
-
-  // When an animation is no longer running (e.g., looking at the list)
-  // then it's ok to power off the screen when docked.
-  //
-# ifdef USE_IPHONE
-  [UIApplication sharedApplication].idleTimerDisabled = NO;
-# endif
-}
-
-
-/* Hook for the XScreenSaverGLView subclass
- */
-- (void) prepareContext
-{
-}
-
-/* Hook for the XScreenSaverGLView subclass
- */
-- (void) resizeContext
-{
-}
-
-
-static void
-screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
-{
-  fps_compute (fpst, 0, -1);
-  fps_draw (fpst);
-}
-
-
-#ifdef USE_IPHONE
-
-/* On iPhones with Retina displays, we can draw the savers in "real"
-   pixels, and that works great.  The 320x480 "point" screen is really
-   a 640x960 *pixel* screen.  However, Retina iPads have 768x1024
-   point screens which are 1536x2048 pixels, and apparently that's
-   enough pixels that copying those bits to the screen is slow.  Like,
-   drops us from 15fps to 7fps.  So, on Retina iPads, we don't draw in
-   real pixels.  This will probably make the savers look better
-   anyway, since that's a higher resolution than most desktop monitors
-   have even today.  (This is only true for X11 programs, not GL 
-   programs.  Those are fine at full rez.)
-
-   This method is overridden in XScreenSaverGLView, since this kludge
-   isn't necessary for GL programs, being resolution independent by
-   nature.
- */
-- (CGFloat) hackedContentScaleFactor
-{
-  GLfloat s = [self contentScaleFactor];
-  if (initial_bounds.width  >= 1024 ||
-      initial_bounds.height >= 1024)
-    s = 1;
-  return s;
-}
-
-
-static GLfloat _global_rot_current_angle_kludge;
-
-double current_device_rotation (void)
-{
-  return -_global_rot_current_angle_kludge;
-}
-
-
-- (void) hackRotation
-{
-  if (rotation_ratio >= 0) {   // in the midst of a rotation animation
-
-#   define CLAMP180(N) while (N < 0) N += 360; while (N > 180) N -= 360
-    GLfloat f = angle_from;
-    GLfloat t = angle_to;
-    CLAMP180(f);
-    CLAMP180(t);
-    GLfloat dist = -(t-f);
-    CLAMP180(dist);
-
-    // Intermediate angle.
-    rot_current_angle = f - rotation_ratio * dist;
-
-    // Intermediate frame size.
-    rot_current_size.width = rot_from.width + 
-      rotation_ratio * (rot_to.width - rot_from.width);
-    rot_current_size.height = rot_from.height + 
-      rotation_ratio * (rot_to.height - rot_from.height);
-
-    // Tick animation.  Complete rotation in 1/6th sec.
-    double now = double_time();
-    double duration = 1/6.0;
-    rotation_ratio = 1 - ((rot_start_time + duration - now) / duration);
-
-    if (rotation_ratio > 1) {  // Done animating.
-      orientation = new_orientation;
-      rot_current_angle = angle_to;
-      rot_current_size = rot_to;
-      rotation_ratio = -1;
-
-      // Check orientation again in case we rotated again while rotating:
-      // this is a no-op if nothing has changed.
-      [self didRotate:nil];
-    }
-  } else {                     // Not animating a rotation.
-    rot_current_angle = angle_to;
-    rot_current_size = rot_to;
-  }
-
-  CLAMP180(rot_current_angle);
-  _global_rot_current_angle_kludge = rot_current_angle;
-
-#   undef CLAMP180
-
-  double s = [self hackedContentScaleFactor];
-  if (!ignore_rotation_p &&
-      /* rotation_ratio && */
-      ((int) backbuffer_size.width  != (int) (s * rot_current_size.width) ||
-       (int) backbuffer_size.height != (int) (s * rot_current_size.height)))
-    [self resize_x11];
-}
-
-
-- (void)alertView:(UIAlertView *)av clickedButtonAtIndex:(NSInteger)i
-{
-  if (i == 0) exit (-1);       // Cancel
-  [self stopAndClose:NO];      // Keep going
-}
-
-- (void) handleException: (NSException *)e
-{
-  NSLog (@"Caught exception: %@", e);
-  [[[UIAlertView alloc] initWithTitle:
-                          [NSString stringWithFormat: @"%s crashed!",
-                                    xsft->progclass]
-                        message:
-                          [NSString stringWithFormat:
-                                      @"The error message was:"
-                                    "\n\n%@\n\n"
-                                    "If it keeps crashing, try "
-                                    "resetting its options.",
-                                    e]
-                        delegate: self
-                        cancelButtonTitle: @"Exit"
-                        otherButtonTitles: @"Keep going", nil]
-    show];
-  [self stopAnimation];
-}
-
-#endif // USE_IPHONE
-
-
-#ifdef USE_BACKBUFFER
-
-/* Create a bitmap context into which we render everything.
-   If the desired size has changed, re-created it.
- */
-- (void) createBackbuffer
-{
-# ifdef USE_IPHONE
-  double s = [self hackedContentScaleFactor];
-  CGSize rotsize = ignore_rotation_p ? initial_bounds : rot_current_size;
-  int new_w = s * rotsize.width;
-  int new_h = s * rotsize.height;
-# else
-  int new_w = [self bounds].size.width;
-  int new_h = [self bounds].size.height;
-# endif
-
-  if (backbuffer &&
-      backbuffer_size.width  == new_w &&
-      backbuffer_size.height == new_h)
-    return;
-
-  CGSize osize = backbuffer_size;
-  CGContextRef ob = backbuffer;
-
-  backbuffer_size.width  = new_w;
-  backbuffer_size.height = new_h;
-
-  CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
-  backbuffer = CGBitmapContextCreate (NULL,
-                                      backbuffer_size.width,
-                                      backbuffer_size.height,
-                                      8, 
-                                      backbuffer_size.width * 4,
-                                      cs,
-                                      // kCGImageAlphaPremultipliedLast
-                                      (kCGImageAlphaNoneSkipFirst |
-                                       kCGBitmapByteOrder32Host)
-                                      );
-  CGColorSpaceRelease (cs);
-  NSAssert (backbuffer, @"unable to allocate back buffer");
-
-  // Clear it.
-  CGRect r;
-  r.origin.x = r.origin.y = 0;
-  r.size = backbuffer_size;
-  CGContextSetGrayFillColor (backbuffer, 0, 1);
-  CGContextFillRect (backbuffer, r);
-
-  if (ob) {
-    // Restore old bits, as much as possible, to the X11 upper left origin.
-    CGRect rect;
-    rect.origin.x = 0;
-    rect.origin.y = (backbuffer_size.height - osize.height);
-    rect.size  = osize;
-    CGImageRef img = CGBitmapContextCreateImage (ob);
-    CGContextDrawImage (backbuffer, rect, img);
-    CGImageRelease (img);
-    CGContextRelease (ob);
-  }
-}
-
-#endif // USE_BACKBUFFER
-
-
-/* Inform X11 that the size of our window has changed.
- */
-- (void) resize_x11
-{
-  if (!xwindow) return;  // early
-
-# ifdef USE_BACKBUFFER
-  [self createBackbuffer];
-  jwxyz_window_resized (xdpy, xwindow,
-                        0, 0,
-                        backbuffer_size.width, backbuffer_size.height,
-                        backbuffer);
-# else   // !USE_BACKBUFFER
-  NSRect r = [self frame];             // ignoring rotation is closer
-  r.size = [self bounds].size;         // to what XGetGeometry expects.
-  jwxyz_window_resized (xdpy, xwindow,
-                        r.origin.x, r.origin.y,
-                        r.size.width, r.size.height,
-                        0);
-# endif  // !USE_BACKBUFFER
-
-  // Next time render_x11 is called, run the saver's reshape_cb.
-  resized_p = YES;
-}
-
-
-- (void) render_x11
-{
-# ifdef USE_IPHONE
-  @try {
-
-  if (orientation == UIDeviceOrientationUnknown)
-    [self didRotate:nil];
-  [self hackRotation];
-# endif
-
-  if (!initted_p) {
-
-    if (! xdpy) {
-# ifdef USE_BACKBUFFER
-      NSAssert (backbuffer, @"no back buffer");
-      xdpy = jwxyz_make_display (self, backbuffer);
-# else
-      xdpy = jwxyz_make_display (self, 0);
-# endif
-      xwindow = XRootWindow (xdpy, 0);
-
-# ifdef USE_IPHONE
-      /* Some X11 hacks (fluidballs) want to ignore all rotation events. */
-      ignore_rotation_p =
-        get_boolean_resource (xdpy, "ignoreRotation", "IgnoreRotation");
-# endif // USE_IPHONE
-
-      [self resize_x11];
-    }
-
-    if (!setup_p) {
-      setup_p = YES;
-      if (xsft->setup_cb)
-        xsft->setup_cb (xsft, xsft->setup_arg);
-    }
-    initted_p = YES;
-    resized_p = NO;
-    NSAssert(!xdata, @"xdata already initialized");
-
-
-# undef ya_rand_init
-    ya_rand_init (0);
-    
-    XSetWindowBackground (xdpy, xwindow,
-                          get_pixel_resource (xdpy, 0,
-                                              "background", "Background"));
-    XClearWindow (xdpy, xwindow);
-    
-# ifndef USE_IPHONE
-    [[self window] setAcceptsMouseMovedEvents:YES];
-# endif
-
-    /* In MacOS 10.5, this enables "QuartzGL", meaning that the Quartz
-       drawing primitives will run on the GPU instead of the CPU.
-       It seems like it might make things worse rather than better,
-       though...  Plus it makes us binary-incompatible with 10.4.
-
-# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
-    [[self window] setPreferredBackingLocation:
-                     NSWindowBackingLocationVideoMemory];
-# endif
-     */
-
-    /* Kludge: even though the init_cb functions are declared to take 2 args,
-      actually call them with 3, for the benefit of xlockmore_init() and
-      xlockmore_setup().
-      */
-    void *(*init_cb) (Display *, Window, void *) = 
-      (void *(*) (Display *, Window, void *)) xsft->init_cb;
-    
-    xdata = init_cb (xdpy, xwindow, xsft->setup_arg);
-
-    if (get_boolean_resource (xdpy, "doFPS", "DoFPS")) {
-      fpst = fps_init (xdpy, xwindow);
-      if (! xsft->fps_cb) xsft->fps_cb = screenhack_do_fps;
-    }
-  }
-
-
-  /* I don't understand why we have to do this *every frame*, but we do,
-     or else the cursor comes back on.
-   */
-# ifndef USE_IPHONE
-  if (![self isPreview])
-    [NSCursor setHiddenUntilMouseMoves:YES];
-# endif
-
-
-  if (fpst)
-    {
-      /* This is just a guess, but the -fps code wants to know how long
-         we were sleeping between frames.
-       */
-      long usecs = 1000000 * [self animationTimeInterval];
-      usecs -= 200;  // caller apparently sleeps for slightly less sometimes...
-      if (usecs < 0) usecs = 0;
-      fps_slept (fpst, usecs);
-    }
-
-
-  /* It turns out that [ScreenSaverView setAnimationTimeInterval] does nothing.
-     This is bad, because some of the screen hacks want to delay for long 
-     periods (like 5 seconds or a minute!) between frames, and running them
-     all at 60 FPS is no good.
-  
-     So, we don't use setAnimationTimeInterval, and just let the framework call
-     us whenever.  But, we only invoke the screen hack's "draw frame" method
-     when enough time has expired.
-  
-     This means two extra calls to gettimeofday() per frame.  For fast-cycling
-     screen savers, that might actually slow them down.  Oh well.
-
-     #### Also, we do not run the draw callback faster than the system's
-          animationTimeInterval, so if any savers are pickier about timing
-          than that, this may slow them down too much.  If that's a problem,
-          then we could call draw_cb in a loop here (with usleep) until the
-          next call would put us past animationTimeInterval...  But a better
-          approach would probably be to just change the saver to not do that.
-   */
-  struct timeval tv;
-  gettimeofday (&tv, 0);
-  double now = tv.tv_sec + (tv.tv_usec / 1000000.0);
-  if (now < next_frame_time) return;
-  
-  [self prepareContext];
-
-  if (resized_p) {
-    // We do this here instead of in setFrame so that all the
-    // Xlib drawing takes place under the animation timer.
-    [self resizeContext];
-    NSRect r;
-# ifndef USE_BACKBUFFER
-    r = [self bounds];
-# else  // USE_BACKBUFFER
-    r.origin.x = 0;
-    r.origin.y = 0;
-    r.size.width  = backbuffer_size.width;
-    r.size.height = backbuffer_size.height;
-# endif // USE_BACKBUFFER
-
-    xsft->reshape_cb (xdpy, xwindow, xdata, r.size.width, r.size.height);
-    resized_p = NO;
-  }
-
-  // Run any XtAppAddInput callbacks now.
-  // (Note that XtAppAddTimeOut callbacks have already been run by
-  // the Cocoa event loop.)
-  //
-  jwxyz_sources_run (display_sources_data (xdpy));
-
-
-  // And finally:
-  //
-# ifndef USE_IPHONE
-  NSDisableScreenUpdates();
-# endif
-  unsigned long delay = xsft->draw_cb (xdpy, xwindow, xdata);
-  if (fpst) xsft->fps_cb (xdpy, xwindow, fpst, xdata);
-# ifndef USE_IPHONE
-  NSEnableScreenUpdates();
-# endif
-
-  gettimeofday (&tv, 0);
-  now = tv.tv_sec + (tv.tv_usec / 1000000.0);
-  next_frame_time = now + (delay / 1000000.0);
-
-# ifdef USE_IPHONE     // Allow savers on the iPhone to run full-tilt.
-  if (delay < [self animationTimeInterval])
-    [self setAnimationTimeInterval:(delay / 1000000.0)];
-# endif
-
-# ifdef DO_GC_HACKERY
-  /* Current theory is that the 10.6 garbage collector sucks in the
-     following way:
-
-     It only does a collection when a threshold of outstanding
-     collectable allocations has been surpassed.  However, CoreGraphics
-     creates lots of small collectable allocations that contain pointers
-     to very large non-collectable allocations: a small CG object that's
-     collectable referencing large malloc'd allocations (non-collectable)
-     containing bitmap data.  So the large allocation doesn't get freed
-     until GC collects the small allocation, which triggers its finalizer
-     to run which frees the large allocation.  So GC is deciding that it
-     doesn't really need to run, even though the process has gotten
-     enormous.  GC eventually runs once pageouts have happened, but by
-     then it's too late, and the machine's resident set has been
-     sodomized.
-
-     So, we force an exhaustive garbage collection in this process
-     approximately every 5 seconds whether the system thinks it needs 
-     one or not.
-  */
-  {
-    static int tick = 0;
-    if (++tick > 5*30) {
-      tick = 0;
-      objc_collect (OBJC_EXHAUSTIVE_COLLECTION);
-    }
-  }
-# endif // DO_GC_HACKERY
-
-# ifdef USE_IPHONE
-  }
-  @catch (NSException *e) {
-    [self handleException: e];
-  }
-# endif // USE_IPHONE
-}
-
-
-/* drawRect always does nothing, and animateOneFrame renders bits to the
-   screen.  This is (now) true of both X11 and GL on both MacOS and iOS.
- */
-
-- (void)drawRect:(NSRect)rect
-{
-  if (xwindow)    // clear to the X window's bg color, not necessarily black.
-    XClearWindow (xdpy, xwindow);
-  else
-    [super drawRect:rect];    // early: black.
-}
-
-
-#ifndef USE_BACKBUFFER
-
-- (void) animateOneFrame
-{
-  [self render_x11];
-}
-
-#else  // USE_BACKBUFFER
-
-- (void) animateOneFrame
-{
-  // Render X11 into the backing store bitmap...
-
-  NSAssert (backbuffer, @"no back buffer");
-
-# ifdef USE_IPHONE
-  UIGraphicsPushContext (backbuffer);
-# endif
-
-  [self render_x11];
-
-# ifdef USE_IPHONE
-  UIGraphicsPopContext();
-# endif
-
-# ifdef USE_IPHONE
-  // Then compute the transformations for rotation.
-  double hs = [self hackedContentScaleFactor];
-  double s = [self contentScaleFactor];
-
-  // The rotation origin for layer.affineTransform is in the center already.
-  CGAffineTransform t = ignore_rotation_p ?
-    CGAffineTransformIdentity :
-    CGAffineTransformMakeRotation (rot_current_angle / (180.0 / M_PI));
-
-  CGFloat f = s / hs;
-  self.layer.affineTransform = CGAffineTransformScale(t, f, f);
-
-  CGRect bounds;
-  bounds.origin.x = 0;
-  bounds.origin.y = 0;
-  bounds.size.width = backbuffer_size.width / s;
-  bounds.size.height = backbuffer_size.height / s;
-  self.layer.bounds = bounds;
-# endif // USE_IPHONE
-  [self.layer setNeedsDisplay];
-}
-
-- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
-{
-  // This "isn't safe" if NULL is passed to CGBitmapCreateContext before iOS 4.
-  char *dest_data = (char *)CGBitmapContextGetData (ctx);
-
-  // The CGContext here is normally upside-down on iOS.
-  if (dest_data &&
-      CGBitmapContextGetBitmapInfo (ctx) ==
-        (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
-#ifdef USE_IPHONE
-      && CGContextGetCTM (ctx).d < 0
-#endif
-      )
-  {
-    size_t dest_height = CGBitmapContextGetHeight (ctx);
-    size_t dest_bpr = CGBitmapContextGetBytesPerRow (ctx);
-    size_t src_height = CGBitmapContextGetHeight (backbuffer);
-    size_t src_bpr = CGBitmapContextGetBytesPerRow (backbuffer);
-    char *src_data = (char *)CGBitmapContextGetData (backbuffer);
-
-    size_t height = src_height < dest_height ? src_height : dest_height;
-    
-    if (src_bpr == dest_bpr) {
-      // iPad 1: 4.0 ms, iPad 2: 6.7 ms
-      memcpy (dest_data, src_data, src_bpr * height);
-    } else {
-      // iPad 1: 4.6 ms, iPad 2: 7.2 ms
-      size_t bpr = src_bpr < dest_bpr ? src_bpr : dest_bpr;
-      while (height) {
-        memcpy (dest_data, src_data, bpr);
-        --height;
-        src_data += src_bpr;
-        dest_data += dest_bpr;
-      }
-    }
-  } else {
-
-    // iPad 1: 9.6 ms, iPad 2: 12.1 ms
-
-#ifdef USE_IPHONE
-    CGContextScaleCTM (ctx, 1, -1);
-    CGFloat s = [self contentScaleFactor];
-    CGFloat hs = [self hackedContentScaleFactor];
-    CGContextTranslateCTM (ctx, 0, -backbuffer_size.height * hs / s);
-#endif
-    
-    CGImageRef img = CGBitmapContextCreateImage (backbuffer);
-    CGContextDrawImage (ctx, self.layer.bounds, img);
-    CGImageRelease (img);
-  }
-}
-
-#endif // !USE_BACKBUFFER
-
-
-
-- (void) setFrame:(NSRect) newRect
-{
-  [super setFrame:newRect];
-
-  if (xwindow)     // inform Xlib that the window has changed now.
-    [self resize_x11];
-}
-
-
-# ifndef USE_IPHONE  // Doesn't exist on iOS
-- (void) setFrameSize:(NSSize) newSize
-{
-  [super setFrameSize:newSize];
-  if (xwindow)
-    [self resize_x11];
-}
-# endif // !USE_IPHONE
-
-
-+(BOOL) performGammaFade
-{
-  return YES;
-}
-
-- (BOOL) hasConfigureSheet
-{
-  return YES;
-}
-
-+ (NSString *) decompressXML: (NSData *)data
-{
-  if (! data) return 0;
-  BOOL compressed_p = !!strncmp ((const char *) data.bytes, "<?xml", 5);
-
-  // If it's not already XML, decompress it.
-  NSAssert (compressed_p, @"xml isn't compressed");
-  if (compressed_p) {
-    NSMutableData *data2 = 0;
-    int ret = -1;
-    z_stream zs;
-    memset (&zs, 0, sizeof(zs));
-    ret = inflateInit2 (&zs, 16 + MAX_WBITS);
-    if (ret == Z_OK) {
-      UInt32 usize = * (UInt32 *) (data.bytes + data.length - 4);
-      data2 = [NSMutableData dataWithLength: usize];
-      zs.next_in   = (Bytef *) data.bytes;
-      zs.avail_in  = data.length;
-      zs.next_out  = (Bytef *) data2.bytes;
-      zs.avail_out = data2.length;
-      ret = inflate (&zs, Z_FINISH);
-      inflateEnd (&zs);
-    }
-    if (ret == Z_OK || ret == Z_STREAM_END)
-      data = data2;
-    else
-      NSAssert2 (0, @"gunzip error: %d: %s",
-                 ret, (zs.msg ? zs.msg : "<null>"));
-  }
-
-  return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
-}
-
-
-#ifndef USE_IPHONE
-- (NSWindow *) configureSheet
-#else
-- (UIViewController *) configureView
-#endif
-{
-  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
-  NSString *file = [NSString stringWithCString:xsft->progclass
-                                      encoding:NSISOLatin1StringEncoding];
-  file = [file lowercaseString];
-  NSString *path = [bundle pathForResource:file ofType:@"xml"];
-  if (!path) {
-    NSLog (@"%@.xml does not exist in the application bundle: %@/",
-           file, [bundle resourcePath]);
-    return nil;
-  }
-  
-# ifdef USE_IPHONE
-  UIViewController *sheet;
-# else  // !USE_IPHONE
-  NSWindow *sheet;
-# endif // !USE_IPHONE
-
-  NSData *xmld = [NSData dataWithContentsOfFile:path];
-  NSString *xml = [[self class] decompressXML: xmld];
-  sheet = [[XScreenSaverConfigSheet alloc]
-            initWithXML:[xml dataUsingEncoding:NSUTF8StringEncoding]
-                options:xsft->options
-             controller:[prefsReader userDefaultsController]
-               defaults:[prefsReader defaultOptions]];
-
-  // #### am I expected to retain this, or not? wtf.
-  //      I thought not, but if I don't do this, we (sometimes) crash.
-  // #### Analyze says "potential leak of an object stored into sheet"
-  [sheet retain];
-
-  return sheet;
-}
-
-
-- (NSUserDefaultsController *) userDefaultsController
-{
-  return [prefsReader userDefaultsController];
-}
-
-
-/* Announce our willingness to accept keyboard input.
-*/
-- (BOOL)acceptsFirstResponder
-{
-  return YES;
-}
-
-
-#ifndef USE_IPHONE
-
-/* Convert an NSEvent into an XEvent, and pass it along.
-   Returns YES if it was handled.
- */
-- (BOOL) doEvent: (NSEvent *) e
-            type: (int) type
-{
-  if (![self isPreview] ||     // no event handling if actually screen-saving!
-      ![self isAnimating] ||
-      !initted_p)
-    return NO;
-
-  XEvent xe;
-  memset (&xe, 0, sizeof(xe));
-  
-  int state = 0;
-  
-  int flags = [e modifierFlags];
-  if (flags & NSAlphaShiftKeyMask) state |= LockMask;
-  if (flags & NSShiftKeyMask)      state |= ShiftMask;
-  if (flags & NSControlKeyMask)    state |= ControlMask;
-  if (flags & NSAlternateKeyMask)  state |= Mod1Mask;
-  if (flags & NSCommandKeyMask)    state |= Mod2Mask;
-  
-  NSPoint p = [[[e window] contentView] convertPoint:[e locationInWindow]
-                                            toView:self];
-# ifdef USE_IPHONE
-  double s = [self hackedContentScaleFactor];
-# else
-  int s = 1;
-# endif
-  int x = s * p.x;
-  int y = s * ([self bounds].size.height - p.y);
-
-  xe.xany.type = type;
-  switch (type) {
-    case ButtonPress:
-    case ButtonRelease:
-      xe.xbutton.x = x;
-      xe.xbutton.y = y;
-      xe.xbutton.state = state;
-      if ([e type] == NSScrollWheel)
-        xe.xbutton.button = ([e deltaY] > 0 ? Button4 :
-                             [e deltaY] < 0 ? Button5 :
-                             [e deltaX] > 0 ? Button6 :
-                             [e deltaX] < 0 ? Button7 :
-                             0);
-      else
-        xe.xbutton.button = [e buttonNumber] + 1;
-      break;
-    case MotionNotify:
-      xe.xmotion.x = x;
-      xe.xmotion.y = y;
-      xe.xmotion.state = state;
-      break;
-    case KeyPress:
-    case KeyRelease:
-      {
-        NSString *ns = (([e type] == NSFlagsChanged) ? 0 :
-                        [e charactersIgnoringModifiers]);
-        KeySym k = 0;
-
-        if (!ns || [ns length] == 0)                   // dead key
-          {
-            // Cocoa hides the difference between left and right keys.
-            // Also we only get KeyPress events for these, no KeyRelease
-            // (unless we hack the mod state manually.  Bleh.)
-            //
-            if      (flags & NSAlphaShiftKeyMask)   k = XK_Caps_Lock;
-            else if (flags & NSShiftKeyMask)        k = XK_Shift_L;
-            else if (flags & NSControlKeyMask)      k = XK_Control_L;
-            else if (flags & NSAlternateKeyMask)    k = XK_Alt_L;
-            else if (flags & NSCommandKeyMask)      k = XK_Meta_L;
-          }
-        else if ([ns length] == 1)                     // real key
-          {
-            switch ([ns characterAtIndex:0]) {
-            case NSLeftArrowFunctionKey:  k = XK_Left;      break;
-            case NSRightArrowFunctionKey: k = XK_Right;     break;
-            case NSUpArrowFunctionKey:    k = XK_Up;        break;
-            case NSDownArrowFunctionKey:  k = XK_Down;      break;
-            case NSPageUpFunctionKey:     k = XK_Page_Up;   break;
-            case NSPageDownFunctionKey:   k = XK_Page_Down; break;
-            case NSHomeFunctionKey:       k = XK_Home;      break;
-            case NSPrevFunctionKey:       k = XK_Prior;     break;
-            case NSNextFunctionKey:       k = XK_Next;      break;
-            case NSBeginFunctionKey:      k = XK_Begin;     break;
-            case NSEndFunctionKey:        k = XK_End;       break;
-            default:
-              {
-                const char *s =
-                  [ns cStringUsingEncoding:NSISOLatin1StringEncoding];
-                k = (s && *s ? *s : 0);
-              }
-              break;
-            }
-          }
-
-        if (! k) return YES;   // E.g., "KeyRelease XK_Shift_L"
-
-        xe.xkey.keycode = k;
-        xe.xkey.state = state;
-        break;
-      }
-    default:
-      NSAssert1 (0, @"unknown X11 event type: %d", type);
-      break;
-  }
-
-  [self lockFocus];
-  [self prepareContext];
-  BOOL result = xsft->event_cb (xdpy, xwindow, xdata, &xe);
-  [self unlockFocus];
-  return result;
-}
-
-
-- (void) mouseDown: (NSEvent *) e
-{
-  if (! [self doEvent:e type:ButtonPress])
-    [super mouseDown:e];
-}
-
-- (void) mouseUp: (NSEvent *) e
-{
-  if (! [self doEvent:e type:ButtonRelease])
-    [super mouseUp:e];
-}
-
-- (void) otherMouseDown: (NSEvent *) e
-{
-  if (! [self doEvent:e type:ButtonPress])
-    [super otherMouseDown:e];
-}
-
-- (void) otherMouseUp: (NSEvent *) e
-{
-  if (! [self doEvent:e type:ButtonRelease])
-    [super otherMouseUp:e];
-}
-
-- (void) mouseMoved: (NSEvent *) e
-{
-  if (! [self doEvent:e type:MotionNotify])
-    [super mouseMoved:e];
-}
-
-- (void) mouseDragged: (NSEvent *) e
-{
-  if (! [self doEvent:e type:MotionNotify])
-    [super mouseDragged:e];
-}
-
-- (void) otherMouseDragged: (NSEvent *) e
-{
-  if (! [self doEvent:e type:MotionNotify])
-    [super otherMouseDragged:e];
-}
-
-- (void) scrollWheel: (NSEvent *) e
-{
-  if (! [self doEvent:e type:ButtonPress])
-    [super scrollWheel:e];
-}
-
-- (void) keyDown: (NSEvent *) e
-{
-  if (! [self doEvent:e type:KeyPress])
-    [super keyDown:e];
-}
-
-- (void) keyUp: (NSEvent *) e
-{
-  if (! [self doEvent:e type:KeyRelease])
-    [super keyUp:e];
-}
-
-- (void) flagsChanged: (NSEvent *) e
-{
-  if (! [self doEvent:e type:KeyPress])
-    [super flagsChanged:e];
-}
-
-#else  // USE_IPHONE
-
-
-- (void) stopAndClose:(Bool)relaunch_p
-{
-  if ([self isAnimating])
-    [self stopAnimation];
-
-  /* Need to make the SaverListController be the firstResponder again
-     so that it can continue to receive its own shake events.  I
-     suppose that this abstraction-breakage means that I'm adding
-     XScreenSaverView to the UINavigationController wrong...
-   */
-  UIViewController *v = [[self window] rootViewController];
-  if ([v isKindOfClass: [UINavigationController class]]) {
-    UINavigationController *n = (UINavigationController *) v;
-    [[n topViewController] becomeFirstResponder];
-  }
-
-  UIView *fader = [self superview];  // the "backgroundView" view is our parent
-
-  if (relaunch_p) {   // Fake a shake on the SaverListController.
-    // Why is [self window] sometimes null here?
-    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
-    UIViewController *v = [w rootViewController];
-    if ([v isKindOfClass: [UINavigationController class]]) {
-      UINavigationController *n = (UINavigationController *) v;
-      [[n topViewController] motionEnded: UIEventSubtypeMotionShake
-                               withEvent: nil];
-    }
-  } else {     // Not launching another, animate our return to the list.
-    [UIView animateWithDuration: 0.5
-            animations:^{ fader.alpha = 0.0; }
-            completion:^(BOOL finished) {
-               [fader removeFromSuperview];
-               fader.alpha = 1.0;
-            }];
-  }
-}
-
-
-/* Called after the device's orientation has changed.
-
-   Note: we could include a subclass of UIViewController which
-   contains a shouldAutorotateToInterfaceOrientation method that
-   returns YES, in which case Core Animation would auto-rotate our
-   View for us in response to rotation events... but, that interacts
-   badly with the EAGLContext -- if you introduce Core Animation into
-   the path, the OpenGL pipeline probably falls back on software
-   rendering and performance goes to hell.  Also, the scaling and
-   rotation that Core Animation does interacts incorrectly with the GL
-   context anyway.
-
-   So, we have to hack the rotation animation manually, in the GL world.
-
-   Possibly XScreenSaverView should use Core Animation, and 
-   XScreenSaverGLView should override that.
-*/
-- (void)didRotate:(NSNotification *)notification
-{
-  UIDeviceOrientation current = [[UIDevice currentDevice] orientation];
-
-  /* If the simulator starts up in the rotated position, sometimes
-     the UIDevice says we're in Portrait when we're not -- but it
-     turns out that the UINavigationController knows what's up!
-     So get it from there.
-   */
-  if (current == UIDeviceOrientationUnknown) {
-    switch ([[[self window] rootViewController] interfaceOrientation]) {
-    case UIInterfaceOrientationPortrait:
-      current = UIDeviceOrientationPortrait;
-      break;
-    case UIInterfaceOrientationPortraitUpsideDown:
-      current = UIDeviceOrientationPortraitUpsideDown;
-      break;
-    case UIInterfaceOrientationLandscapeLeft:          // It's opposite day
-      current = UIDeviceOrientationLandscapeRight;
-      break;
-    case UIInterfaceOrientationLandscapeRight:
-      current = UIDeviceOrientationLandscapeLeft;
-      break;
-    default:
-      break;
-    }
-  }
-
-  /* On the iPad (but not iPhone 3GS, or the simulator) sometimes we get
-     an orientation change event with an unknown orientation.  Those seem
-     to always be immediately followed by another orientation change with
-     a *real* orientation change, so let's try just ignoring those bogus
-     ones and hoping that the real one comes in shortly...
-   */
-  if (current == UIDeviceOrientationUnknown)
-    return;
-
-  if (rotation_ratio >= 0) return;     // in the midst of rotation animation
-  if (orientation == current) return;  // no change
-
-  // When transitioning to FaceUp or FaceDown, pretend there was no change.
-  if (current == UIDeviceOrientationFaceUp ||
-      current == UIDeviceOrientationFaceDown)
-    return;
-
-  new_orientation = current;           // current animation target
-  rotation_ratio = 0;                  // start animating
-  rot_start_time = double_time();
-
-  switch (orientation) {
-  case UIDeviceOrientationLandscapeLeft:      angle_from = 90;  break;
-  case UIDeviceOrientationLandscapeRight:     angle_from = 270; break;
-  case UIDeviceOrientationPortraitUpsideDown: angle_from = 180; break;
-  default:                                    angle_from = 0;   break;
-  }
-
-  switch (new_orientation) {
-  case UIDeviceOrientationLandscapeLeft:      angle_to = 90;  break;
-  case UIDeviceOrientationLandscapeRight:     angle_to = 270; break;
-  case UIDeviceOrientationPortraitUpsideDown: angle_to = 180; break;
-  default:                                    angle_to = 0;   break;
-  }
-
-  switch (orientation) {
-  case UIDeviceOrientationLandscapeRight:      // from landscape
-  case UIDeviceOrientationLandscapeLeft:
-    rot_from.width  = initial_bounds.height;
-    rot_from.height = initial_bounds.width;
-    break;
-  default:                                     // from portrait
-    rot_from.width  = initial_bounds.width;
-    rot_from.height = initial_bounds.height;
-    break;
-  }
-
-  switch (new_orientation) {
-  case UIDeviceOrientationLandscapeRight:      // to landscape
-  case UIDeviceOrientationLandscapeLeft:
-    rot_to.width  = initial_bounds.height;
-    rot_to.height = initial_bounds.width;
-    break;
-  default:                                     // to portrait
-    rot_to.width  = initial_bounds.width;
-    rot_to.height = initial_bounds.height;
-    break;
-  }
-
- if (! initted_p) {
-   // If we've done a rotation but the saver hasn't been initialized yet,
-   // don't bother going through an X11 resize, but just do it now.
-   rot_start_time = 0;  // dawn of time
-   [self hackRotation];
- }
-}
-
-
-/* I believe we can't use UIGestureRecognizer for tracking touches
-   because UIPanGestureRecognizer doesn't give us enough detail in its
-   callbacks.
-
-   Currently we don't handle multi-touches (just the first touch) but
-   I'm leaving this comment here for future reference:
-
-   In the simulator, multi-touch sequences look like this:
-
-     touchesBegan [touchA, touchB]
-     touchesEnd [touchA, touchB]
-
-   But on real devices, sometimes you get that, but sometimes you get:
-
-     touchesBegan [touchA, touchB]
-     touchesEnd [touchB]
-     touchesEnd [touchA]
-
-   Or even
-
-     touchesBegan [touchA]
-     touchesBegan [touchB]
-     touchesEnd [touchA]
-     touchesEnd [touchB]
-
-   So the only way to properly detect a "pinch" gesture is to remember
-   the start-point of each touch as it comes in; and the end-point of
-   each touch as those come in; and only process the gesture once the
-   number of touchEnds matches the number of touchBegins.
- */
-
-- (void) rotateMouse:(int)rot x:(int*)x y:(int *)y w:(int)w h:(int)h
-{
-  // This is a no-op unless contentScaleFactor != hackedContentScaleFactor.
-  // Currently, this is the iPad Retina only.
-  CGRect frame = [self bounds];                // Scale.
-  double s = [self hackedContentScaleFactor];
-  *x *= (backbuffer_size.width  / frame.size.width)  / s;
-  *y *= (backbuffer_size.height / frame.size.height) / s;
-}
-
-
-#if 0  // AudioToolbox/AudioToolbox.h
-- (void) beep
-{
-  // There's no way to play a standard system alert sound!
-  // We'd have to include our own WAV for that.  Eh, fuck it.
-  AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
-# if TARGET_IPHONE_SIMULATOR
-  NSLog(@"BEEP");  // The sim doesn't vibrate.
-# endif
-}
-#endif
-
-
-/* We distinguish between taps and drags.
-   - Drags (down, motion, up) are sent to the saver to handle.
-   - Single-taps exit the saver.
-   This means a saver cannot respond to a single-tap.  Only a few try to.
- */
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-{
-  // If they are trying to pinch, just do nothing.
-  if ([[event allTouches] count] > 1)
-    return;
-
-  tap_time = 0;
-
-  if (xsft->event_cb && xwindow) {
-    double s = [self hackedContentScaleFactor];
-    XEvent xe;
-    memset (&xe, 0, sizeof(xe));
-    int i = 0;
-    // #### 'frame' here or 'bounds'?
-    int w = s * [self frame].size.width;
-    int h = s * [self frame].size.height;
-    for (UITouch *touch in touches) {
-      CGPoint p = [touch locationInView:self];
-      xe.xany.type = ButtonPress;
-      xe.xbutton.button = i + 1;
-      xe.xbutton.button = i + 1;
-      xe.xbutton.x      = s * p.x;
-      xe.xbutton.y      = s * p.y;
-      [self rotateMouse: rot_current_angle
-            x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h];
-      jwxyz_mouse_moved (xdpy, xwindow, xe.xbutton.x, xe.xbutton.y);
-
-      // Ignore return code: don't care whether the hack handled it.
-      xsft->event_cb (xdpy, xwindow, xdata, &xe);
-
-      // Remember when/where this was, to determine tap versus drag or hold.
-      tap_time = double_time();
-      tap_point = p;
-
-      i++;
-      break;  // No pinches: only look at the first touch.
-    }
-  }
-}
-
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
-{
-  // If they are trying to pinch, just do nothing.
-  if ([[event allTouches] count] > 1)
-    return;
-
-  if (xsft->event_cb && xwindow) {
-    double s = [self hackedContentScaleFactor];
-    XEvent xe;
-    memset (&xe, 0, sizeof(xe));
-    int i = 0;
-    // #### 'frame' here or 'bounds'?
-    int w = s * [self frame].size.width;
-    int h = s * [self frame].size.height;
-    for (UITouch *touch in touches) {
-      CGPoint p = [touch locationInView:self];
-
-      // If the ButtonRelease came less than half a second after ButtonPress,
-      // and didn't move far, then this was a tap, not a drag or a hold.
-      // Interpret it as "exit".
-      //
-      double dist = sqrt (((p.x - tap_point.x) * (p.x - tap_point.x)) +
-                          ((p.y - tap_point.y) * (p.y - tap_point.y)));
-      if (tap_time + 0.5 >= double_time() && dist < 20) {
-        [self stopAndClose:NO];
-        return;
-      }
-
-      xe.xany.type      = ButtonRelease;
-      xe.xbutton.button = i + 1;
-      xe.xbutton.x      = s * p.x;
-      xe.xbutton.y      = s * p.y;
-      [self rotateMouse: rot_current_angle
-            x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h];
-      jwxyz_mouse_moved (xdpy, xwindow, xe.xbutton.x, xe.xbutton.y);
-      xsft->event_cb (xdpy, xwindow, xdata, &xe);
-      i++;
-      break;  // No pinches: only look at the first touch.
-    }
-  }
-}
-
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-{
-  // If they are trying to pinch, just do nothing.
-  if ([[event allTouches] count] > 1)
-    return;
-
-  if (xsft->event_cb && xwindow) {
-    double s = [self hackedContentScaleFactor];
-    XEvent xe;
-    memset (&xe, 0, sizeof(xe));
-    int i = 0;
-    // #### 'frame' here or 'bounds'?
-    int w = s * [self frame].size.width;
-    int h = s * [self frame].size.height;
-    for (UITouch *touch in touches) {
-      CGPoint p = [touch locationInView:self];
-      xe.xany.type      = MotionNotify;
-      xe.xmotion.x      = s * p.x;
-      xe.xmotion.y      = s * p.y;
-      [self rotateMouse: rot_current_angle
-            x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h];
-      jwxyz_mouse_moved (xdpy, xwindow, xe.xmotion.x, xe.xmotion.y);
-      xsft->event_cb (xdpy, xwindow, xdata, &xe);
-      i++;
-      break;  // No pinches: only look at the first touch.
-    }
-  }
-}
-
-
-/* We need this to respond to "shake" gestures
- */
-- (BOOL)canBecomeFirstResponder
-{
-  return YES;
-}
-
-- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
-{
-}
-
-
-- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
-{
-}
-
-/* Shake means exit and launch a new saver.
- */
-- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
-{
-  [self stopAndClose:YES];
-}
-
-
-- (void)setScreenLocked:(BOOL)locked
-{
-  if (screenLocked == locked) return;
-  screenLocked = locked;
-  if (locked) {
-    if ([self isAnimating])
-      [self stopAnimation];
-  } else {
-    if (! [self isAnimating])
-      [self startAnimation];
-  }
-}
-
-
-#endif // USE_IPHONE
-
-
-@end
-
-/* Utility functions...
- */
-
-static PrefsReader *
-get_prefsReader (Display *dpy)
-{
-  XScreenSaverView *view = jwxyz_window_view (XRootWindow (dpy, 0));
-  if (!view) return 0;
-  return [view prefsReader];
-}
-
-
-char *
-get_string_resource (Display *dpy, char *name, char *class)
-{
-  return [get_prefsReader(dpy) getStringResource:name];
-}
-
-Bool
-get_boolean_resource (Display *dpy, char *name, char *class)
-{
-  return [get_prefsReader(dpy) getBooleanResource:name];
-}
-
-int
-get_integer_resource (Display *dpy, char *name, char *class)
-{
-  return [get_prefsReader(dpy) getIntegerResource:name];
-}
-
-double
-get_float_resource (Display *dpy, char *name, char *class)
-{
-  return [get_prefsReader(dpy) getFloatResource:name];
-}