From http://www.jwz.org/xscreensaver/xscreensaver-5.24.tar.gz
[xscreensaver] / OSX / jwxyz.m
index 604caf50dafb066ea1689f4172250918790aed2d..15d82135ade30bf2d4a10395924854db03ba365d 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2010 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-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
 
 #import <stdlib.h>
 #import <stdint.h>
-#import <Cocoa/Cocoa.h>
+#import <wchar.h>
+
+#ifdef USE_IPHONE
+# import <UIKit/UIKit.h>
+# import <UIKit/UIScreen.h>
+# import <QuartzCore/QuartzCore.h>
+# import <CoreText/CTFont.h>
+# define NSView  UIView
+# define NSRect  CGRect
+# define NSPoint CGPoint
+# define NSSize  CGSize
+# define NSColor UIColor
+# define NSImage UIImage
+# define NSEvent UIEvent
+# define NSFont  UIFont
+# define NSGlyph CGGlyph
+# define NSWindow UIWindow
+# define NSMakeSize   CGSizeMake
+# define NSBezierPath UIBezierPath
+#else
+# import <Cocoa/Cocoa.h>
+#endif
+
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
+#import "yarandom.h"
+
+# define USE_BACKBUFFER  /* must be in sync with XScreenSaverView.h */
 
 #undef  Assert
-#define Assert(C,S) do { \
-  if (!(C)) { \
-    NSLog(@"%s",S); \
-    abort(); \
-  }} while(0)
+#define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
 
 # undef MAX
 # undef MIN
@@ -44,6 +65,7 @@ struct jwxyz_Drawable {
     struct {
       NSView *view;
       unsigned long background;
+      int last_mouse_x, last_mouse_y;
     } window;
     struct {
       int depth;
@@ -57,9 +79,11 @@ struct jwxyz_Display {
   Screen *screen;
   struct jwxyz_sources_data *timers_data;
 
+# ifndef USE_IPHONE
   CGDirectDisplayID cgdpy;  /* ...of the one and only Window, main_window.
                                This can change if the window is dragged to
                                a different screen. */
+# endif
 
   CGColorSpaceRef colorspace;  /* Color space of this screen.  We tag all of
                                   our images with this to avoid translation
@@ -88,11 +112,38 @@ struct jwxyz_Font {
 };
 
 
+/* Instead of calling abort(), throw a real exception, so that
+   XScreenSaverView can catch it and display a dialog.
+ */
+void
+jwxyz_abort (const char *fmt, ...)
+{
+  char s[10240];
+  if (!fmt || !*fmt)
+    strcpy (s, "abort");
+  else
+    {
+      va_list args;
+      va_start (args, fmt);
+      vsprintf (s, fmt, args);
+      va_end (args);
+    }
+  [[NSException exceptionWithName: NSInternalInconsistencyException
+                reason: [NSString stringWithCString: s
+                                  encoding:NSUTF8StringEncoding]
+                userInfo: nil]
+    raise];
+  abort();  // not reached
+}
+
+
 Display *
-jwxyz_make_display (void *nsview_arg)
+jwxyz_make_display (void *nsview_arg, void *cgc_arg)
 {
+  CGContextRef cgc = (CGContextRef) cgc_arg;
   NSView *view = (NSView *) nsview_arg;
-  if (!view) abort();
+  Assert (view, "no view");
+  if (!view) return 0;
 
   Display *d = (Display *) calloc (1, sizeof(*d));
   d->screen = (Screen *) calloc (1, sizeof(Screen));
@@ -111,21 +162,22 @@ jwxyz_make_display (void *nsview_arg)
 
   Window w = (Window) calloc (1, sizeof(*w));
   w->type = WINDOW;
-  // kludge! this needs to be set late, so we do it in XClearWindow!
-  // w->cgc = [[[view window] graphicsContext] graphicsPort];
-  w->cgc = 0;
   w->window.view = view;
   CFRetain (w->window.view);   // needed for garbage collection?
   w->window.background = BlackPixel(0,0);
 
   d->main_window = w;
 
-  [view lockFocus];
-  w->cgc = [[[view window] graphicsContext] graphicsPort];
-  [view unlockFocus];
-
-  jwxyz_window_resized (d, w);
+# ifndef USE_IPHONE
+  if (! cgc) {
+    [view lockFocus];
+    cgc = [[[view window] graphicsContext] graphicsPort];
+    [view unlockFocus];
+    w->cgc = cgc;
+  }
+# endif
 
+  Assert (cgc, "no CGContext");
   return d;
 }
 
@@ -145,7 +197,7 @@ jwxyz_free_display (Display *dpy)
 void *
 jwxyz_window_view (Window w)
 {
-  Assert (w->type == WINDOW, "not a window");
+  Assert (w && w->type == WINDOW, "not a window");
   return w->window.view;
 }
 
@@ -167,15 +219,21 @@ invalidate_drawable_cache (Drawable d)
 /* Call this when the View changes size or position.
  */
 void
-jwxyz_window_resized (Display *dpy, Window w)
+jwxyz_window_resized (Display *dpy, Window w, 
+                      int new_x, int new_y, int new_width, int new_height,
+                      void *cgc_arg)
 {
-  Assert (w->type == WINDOW, "not a window");
-  NSRect r = [w->window.view frame];
-  w->frame.origin.x    = r.origin.x;   // NSRect -> CGRect
-  w->frame.origin.y    = r.origin.y;
-  w->frame.size.width  = r.size.width;
-  w->frame.size.height = r.size.height;
+  CGContextRef cgc = (CGContextRef) cgc_arg;
+  Assert (w && w->type == WINDOW, "not a window");
+  w->frame.origin.x    = new_x;
+  w->frame.origin.y    = new_y;
+  w->frame.size.width  = new_width;
+  w->frame.size.height = new_height;
+
+  if (cgc) w->cgc = cgc;
+  Assert (w->cgc, "no CGContext");
 
+# ifndef USE_IPHONE
   // Figure out which screen the window is currently on.
   {
     int wx, wy;
@@ -186,10 +244,19 @@ jwxyz_window_resized (Display *dpy, Window w)
     CGDisplayCount n;
     dpy->cgdpy = 0;
     CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+    // Auuugh!
+    if (! dpy->cgdpy) {
+      p.x = p.y = 0;
+      CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+    }
     Assert (dpy->cgdpy, "unable to find CGDisplay");
   }
+# endif // USE_IPHONE
+
+# ifndef USE_BACKBUFFER
+  // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
+  // then this one's faster.
 
-#if 0
   {
     // Figure out this screen's colorspace, and use that for every CGImage.
     //
@@ -199,17 +266,35 @@ jwxyz_window_resized (Display *dpy, Window w)
     dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
     Assert (dpy->colorspace, "unable to find colorspace");
   }
-# else
+# else  // USE_BACKBUFFER
 
   // WTF?  It's faster if we *do not* use the screen's colorspace!
   //
   dpy->colorspace = CGColorSpaceCreateDeviceRGB();
-# endif
+# endif // USE_BACKBUFFER
 
   invalidate_drawable_cache (w);
 }
 
 
+#ifdef USE_IPHONE
+void
+jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
+{
+  Assert (w && w->type == WINDOW, "not a window");
+  w->window.last_mouse_x = x;
+  w->window.last_mouse_y = y;
+}
+#endif // USE_IPHONE
+
+
+void
+jwxyz_flush_context (Display *dpy)
+{
+  // This is only used when USE_BACKBUFFER is off.
+  CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
+}
+
 jwxyz_sources_data *
 display_sources_data (Display *dpy)
 {
@@ -349,14 +434,14 @@ push_gc (Drawable d, GC gc)
     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
-    default: abort(); break;
+    default: Assert(0, "unknown gcv function"); break;
   }
 
   if (gc->gcv.clip_mask)
     set_clip_mask (d, gc);
 }
 
-#define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
+#define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
 
 
 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
@@ -374,7 +459,6 @@ push_color_gc (Drawable d, GC gc, unsigned long color,
   }
 
   CGContextRef cgc = d->cgc;
-
   set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
   CGContextSetShouldAntialias (cgc, antialias_p);
 }
@@ -408,6 +492,12 @@ push_bg_gc (Drawable d, GC gc, Bool fill_p)
  */
 #define XDRAWPOINTS_IMAGES
 
+/* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
+   the bitmap data directly is faster.  This only works on Pixmaps, though,
+   not Windows.  (Fortunately, on iOS, the Window is really a Pixmap.)
+ */
+#define XDRAWPOINTS_CGDATA
+
 int
 XDrawPoints (Display *dpy, Drawable d, GC gc, 
              XPoint *points, int count, int mode)
@@ -417,68 +507,125 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
 
   push_fg_gc (d, gc, YES);
 
-# ifdef XDRAWPOINTS_IMAGES
+# ifdef XDRAWPOINTS_CGDATA
 
-  unsigned int argb = gc->gcv.foreground;
-  validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
-  if (gc->depth == 1)
-    argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
-
-  CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
-  CGImageRef cgi = CGImageCreate (1, 1,
-                                  8, 32, 4,
-                                  dpy->colorspace, 
-                                  /* Host-ordered, since we're using the
-                                     address of an int as the color data. */
-                                  (kCGImageAlphaNoneSkipFirst | 
-                                   kCGBitmapByteOrder32Host),
-                                  prov, 
-                                  NULL,  /* decode[] */
-                                  NO, /* interpolate */
-                                  kCGRenderingIntentDefault);
-  CGDataProviderRelease (prov);
+#  ifdef USE_BACKBUFFER
+  if (1)  // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+#  else
+  if (d->type == PIXMAP)
+#  endif
+  {
+    CGContextRef cgc = d->cgc;
+    void *data = CGBitmapContextGetData (cgc);
+    size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
+    size_t w = CGBitmapContextGetWidth (cgc);
+    size_t h = CGBitmapContextGetHeight (cgc);
 
-  CGContextRef cgc = d->cgc;
-  CGRect rect;
-  rect.size.width = rect.size.height = 1;
-  for (i = 0; i < count; i++) {
-    if (i > 0 && mode == CoordModePrevious) {
-      rect.origin.x += points->x;
-      rect.origin.x -= points->y;
+    Assert (data, "no bitmap data in Drawable");
+
+    unsigned int argb = gc->gcv.foreground;
+    validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
+    if (gc->depth == 1)
+      argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
+
+    CGFloat x0 = wr.origin.x;
+    CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
+
+    // It's uglier, but faster, to hoist the conditional out of the loop.
+    if (mode == CoordModePrevious) {
+      CGFloat x = x0, y = y0;
+      for (i = 0; i < count; i++, points++) {
+        x += points->x;
+        y += points->y;
+
+        if (x >= 0 && x < w && y >= 0 && y < h) {
+          unsigned int *p = (unsigned int *)
+            ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+          *p = argb;
+        }
+      }
     } else {
-      rect.origin.x = wr.origin.x + points->x;
-      rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+      for (i = 0; i < count; i++, points++) {
+        CGFloat x = x0 + points->x;
+        CGFloat y = y0 + points->y;
+
+        if (x >= 0 && x < w && y >= 0 && y < h) {
+          unsigned int *p = (unsigned int *)
+            ((char *) data + (size_t) y * bpr + (size_t) x * 4);
+          *p = argb;
+        }
+      }
     }
 
-    //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-    CGContextDrawImage (cgc, rect, cgi);
-    points++;
-  }
+  } else       /* d->type == WINDOW */
+
+# endif /* XDRAWPOINTS_CGDATA */
+  {
 
-  CGImageRelease (cgi);
+# ifdef XDRAWPOINTS_IMAGES
+
+    unsigned int argb = gc->gcv.foreground;
+    validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
+    if (gc->depth == 1)
+      argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
+
+    CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
+                                                           NULL);
+    CGImageRef cgi = CGImageCreate (1, 1,
+                                    8, 32, 4,
+                                    dpy->colorspace, 
+                                    /* Host-ordered, since we're using the
+                                       address of an int as the color data. */
+                                    (kCGImageAlphaNoneSkipFirst | 
+                                     kCGBitmapByteOrder32Host),
+                                    prov, 
+                                    NULL,  /* decode[] */
+                                    NO, /* interpolate */
+                                    kCGRenderingIntentDefault);
+    CGDataProviderRelease (prov);
+
+    CGContextRef cgc = d->cgc;
+    CGRect rect;
+    rect.size.width = rect.size.height = 1;
+    for (i = 0; i < count; i++) {
+      if (i > 0 && mode == CoordModePrevious) {
+        rect.origin.x += points->x;
+        rect.origin.x -= points->y;
+      } else {
+        rect.origin.x = wr.origin.x + points->x;
+        rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+      }
+
+      //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
+      CGContextDrawImage (cgc, rect, cgi);
+      points++;
+    }
+
+    CGImageRelease (cgi);
 
 # else /* ! XDRAWPOINTS_IMAGES */
 
-  CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
-  CGRect *r = rects;
+    CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
+    CGRect *r = rects;
   
-  for (i = 0; i < count; i++) {
-    r->size.width = r->size.height = 1;
-    if (i > 0 && mode == CoordModePrevious) {
-      r->origin.x = r[-1].origin.x + points->x;
-      r->origin.y = r[-1].origin.x - points->y;
-    } else {
-      r->origin.x = wr.origin.x + points->x;
-      r->origin.y = wr.origin.y + wr.size.height - points->y;
+    for (i = 0; i < count; i++) {
+      r->size.width = r->size.height = 1;
+      if (i > 0 && mode == CoordModePrevious) {
+        r->origin.x = r[-1].origin.x + points->x;
+        r->origin.y = r[-1].origin.x - points->y;
+      } else {
+        r->origin.x = wr.origin.x + points->x;
+        r->origin.y = wr.origin.y + wr.size.height - points->y;
+      }
+      points++;
+      r++;
     }
-    points++;
-    r++;
-  }
 
-  CGContextFillRects (d->cgc, rects, count);
-  free (rects);
+    CGContextFillRects (d->cgc, rects, count);
+    free (rects);
 
 # endif /* ! XDRAWPOINTS_IMAGES */
+  }
 
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
@@ -501,6 +648,45 @@ static void draw_rect (Display *, Drawable, GC,
                        int x, int y, unsigned int width, unsigned int height, 
                        BOOL foreground_p, BOOL fill_p);
 
+static Bool
+bitmap_context_p (Drawable d)
+{
+# ifdef USE_BACKBUFFER
+  return True;
+# else
+  // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+  return d->type == PIXMAP;
+# endif
+}
+
+static void
+fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
+                  size_t fill_width, size_t fill_height)
+{
+  Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
+  while (fill_height) {
+    // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
+    wmemset (dst, fill_data, fill_width);
+    --fill_height;
+    dst = (char *) dst + dst_pitch;
+  }
+}
+
+static void *
+seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
+{
+  return (char *)dst + dst_pitch * y + x * 4;
+}
+
+static unsigned int
+drawable_depth (Drawable d)
+{
+  return (d->type == WINDOW
+          ? visual_depth (NULL, NULL)
+          : d->pixmap.depth);
+}
+
+
 int
 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
            int src_x, int src_y, 
@@ -522,9 +708,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
-    set_color (dst->cgc, (gc->gcv.function == GXset
-                          ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
-                          : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+    set_color (dst->cgc,
+               (gc->gcv.function == GXset
+                ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
+                : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
                gc->depth, gc->gcv.alpha_allowed_p, YES);
     draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
     return 0;
@@ -558,8 +745,6 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
   
   // Clip rects to frames...
   //
-//  CGRect orig_src_rect = src_rect;
-  CGRect orig_dst_rect = dst_rect;
 
 # define CLIP(THIS,THAT,VAL,SIZE) do { \
   float off = THIS##_rect.origin.VAL; \
@@ -580,19 +765,148 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
 
   CLIP (dst, src, x, width);
   CLIP (dst, src, y, height);
+
+  // Not actually the original dst_rect, just the one before it's clipped to
+  // the src_frame.
+  CGRect orig_dst_rect = dst_rect;
+
   CLIP (src, dst, x, width);
   CLIP (src, dst, y, height);
 # undef CLIP
 
-  if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
+  if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
     return 0;
+
+  // Sort-of-special case where no pixels can be grabbed from the source,
+  // and the whole destination is filled with the background color.
+  if (src_rect.size.width < 0 || src_rect.size.height < 0) {
+    
+    Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
+           (int)src_rect.size.height == (int)dst_rect.size.height,
+           "size mismatch");
+    
+    src_rect.size.width  = 0;
+    src_rect.size.height = 0;
+    dst_rect.size.width  = 0;
+    dst_rect.size.height = 0;
+  }
   
   NSObject *releaseme = 0;
   CGImageRef cgi;
   BOOL mask_p = NO;
   BOOL free_cgi_p = NO;
 
-  if (src->type == PIXMAP) {
+
+  /* If we're copying from a bitmap to a bitmap, and there's nothing funny
+     going on with clipping masks or depths or anything, optimize it by
+     just doing a memcpy instead of going through a CGI.
+   */
+  if (bitmap_context_p (src)) {
+
+    if (bitmap_context_p (dst) &&
+        gc->gcv.function == GXcopy &&
+        !gc->gcv.clip_mask &&
+        drawable_depth (src) == drawable_depth (dst)) {
+
+      Assert(!(int)src_frame.origin.x &&
+             !(int)src_frame.origin.y &&
+             !(int)dst_frame.origin.x &&
+             !(int)dst_frame.origin.y,
+             "unexpected non-zero origin");
+      
+      char *src_data = CGBitmapContextGetData(src->cgc);
+      char *dst_data = CGBitmapContextGetData(dst->cgc);
+      size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
+      size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
+      
+      // Int to float and back again. It's not very safe, but it seems to work.
+      int src_x0 = src_rect.origin.x;
+      int dst_x0 = dst_rect.origin.x;
+      
+      // Flip the Y-axis a second time.
+      int src_y0 = (src_frame.origin.y + src_frame.size.height -
+                    src_rect.size.height - src_rect.origin.y);
+      int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
+                    dst_rect.size.height - dst_rect.origin.y);
+      
+      unsigned width0  = (int) src_rect.size.width;
+      unsigned height0 = (int) src_rect.size.height;
+      
+      Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
+             (int)src_rect.size.height == (int)dst_rect.size.height,
+             "size mismatch");
+      {
+        char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
+        char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
+        size_t src_pitch0 = src_pitch;
+        size_t dst_pitch0 = dst_pitch;
+        size_t bytes = width0 * 4;
+
+        if (src == dst && dst_y0 > src_y0) {
+          // Copy upwards if the areas might overlap.
+          src_data0 += src_pitch0 * (height0 - 1);
+          dst_data0 += dst_pitch0 * (height0 - 1);
+          src_pitch0 = -src_pitch0;
+          dst_pitch0 = -dst_pitch0;
+        }
+      
+        size_t lines0 = height0;
+        while (lines0) {
+          // memcpy is an alias for memmove on OS X.
+          memmove(dst_data0, src_data0, bytes);
+          src_data0 += src_pitch0;
+          dst_data0 += dst_pitch0;
+          --lines0;
+        }
+      }
+
+      if (clipped) {
+        int orig_dst_x = orig_dst_rect.origin.x;
+        int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
+                          orig_dst_rect.origin.y - orig_dst_rect.size.height);
+        int orig_width  = orig_dst_rect.size.width;
+        int orig_height = orig_dst_rect.size.height;
+
+        Assert (orig_dst_x >= 0 &&
+                orig_dst_x + orig_width  <= (int) dst_frame.size.width &&
+                orig_dst_y >= 0 &&
+                orig_dst_y + orig_height <= (int) dst_frame.size.height,
+                "wrong dimensions");
+
+        if (orig_dst_y < dst_y0) {
+          fill_rect_memset (seek_xy (dst_data, dst_pitch,
+                                     orig_dst_x, orig_dst_y), dst_pitch,
+                            gc->gcv.background, orig_width,
+                            dst_y0 - orig_dst_y);
+        }
+
+        if (orig_dst_y + orig_height > dst_y0 + height0) {
+          fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
+                                     dst_y0 + height0),
+                            dst_pitch,
+                            gc->gcv.background, orig_width,
+                            orig_dst_y + orig_height - dst_y0 - height0);
+        }
+
+        if (orig_dst_x < dst_x0) {
+          fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
+                            dst_pitch, gc->gcv.background,
+                            dst_x0 - orig_dst_x, height0);
+        }
+
+        if (dst_x0 + width0 < orig_dst_x + orig_width) {
+          fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
+                                     dst_y0),
+                            dst_pitch, gc->gcv.background,
+                            orig_dst_x + orig_width - dst_x0 - width0,
+                            height0);
+        }
+      }
+
+      invalidate_drawable_cache (dst);
+      return 0;
+    }
+
 
     // If we are copying from a Pixmap to a Pixmap or Window, we must first
     // copy the bits to an intermediary CGImage object, then copy that to the
@@ -625,9 +939,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       free_cgi_p = YES;
     }
 
-    if (src->pixmap.depth == 1)
+  if (src->type == PIXMAP && src->pixmap.depth == 1)
       mask_p = YES;
 
+# ifndef USE_BACKBUFFER
   } else { /* (src->type == WINDOW) */
     
     NSRect nsfrom;    // NSRect != CGRect on 10.4
@@ -684,21 +999,27 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
       CGDataProviderRelease (prov);
     }
+
+# endif // !USE_BACKBUFFER
   }
 
+  CGContextRef cgc = dst->cgc;
+
   if (mask_p) {                // src depth == 1
 
     push_bg_gc (dst, gc, YES);
 
     // fill the destination rectangle with solid background...
-    CGContextFillRect (dst->cgc, orig_dst_rect);
+    CGContextFillRect (cgc, orig_dst_rect);
+
+    Assert (cgc, "no CGC with 1-bit XCopyArea");
 
     // then fill in a solid rectangle of the fg color, using the image as an
     // alpha mask.  (the image has only values of BlackPixel or WhitePixel.)
-    set_color (dst->cgc, gc->gcv.foreground, gc->depth, 
+    set_color (cgc, gc->gcv.foreground, gc->depth, 
                gc->gcv.alpha_allowed_p, YES);
-    CGContextClipToMask (dst->cgc, dst_rect, cgi);
-    CGContextFillRect (dst->cgc, dst_rect);
+    CGContextClipToMask (cgc, dst_rect, cgi);
+    CGContextFillRect (cgc, dst_rect);
 
     pop_gc (dst, gc);
 
@@ -713,17 +1034,22 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
     // being copied.
     //
     if (clipped) {
-      set_color (dst->cgc, gc->gcv.background, gc->depth, 
+      set_color (cgc, gc->gcv.background, gc->depth, 
                  gc->gcv.alpha_allowed_p, YES);
-      CGContextFillRect (dst->cgc, orig_dst_rect);
+      CGContextFillRect (cgc, orig_dst_rect);
     }
 
     if (cgi) {
       // copy the CGImage onto the destination CGContext
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
-      CGContextDrawImage (dst->cgc, dst_rect, cgi);
+      CGContextDrawImage (cgc, dst_rect, cgi);
     } else {
       // No cgi means src == dst, and both are Windows.
+
+# ifdef USE_BACKBUFFER
+      Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
+      return 0;
+# else // !USE_BACKBUFFER
       NSRect nsfrom;
       nsfrom.origin.x    = src_rect.origin.x;    // NSRect != CGRect on 10.4
       nsfrom.origin.y    = src_rect.origin.y;
@@ -733,12 +1059,14 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       nsto.x             = dst_rect.origin.x;
       nsto.y             = dst_rect.origin.y;
       NSCopyBits (0, nsfrom, nsto);
+# endif // !USE_BACKBUFFER
     }
 
     pop_gc (dst, gc);
   }
 
   if (free_cgi_p) CGImageRelease (cgi);
+
   if (releaseme) [releaseme release];
   invalidate_drawable_cache (dst);
   return 0;
@@ -781,13 +1109,14 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
 
   push_fg_gc (d, gc, NO);
 
-  set_line_mode (d->cgc, &gc->gcv);
-  CGContextBeginPath (d->cgc);
-  CGContextMoveToPoint (d->cgc, p.x, p.y);
+  CGContextRef cgc = d->cgc;
+  set_line_mode (cgc, &gc->gcv);
+  CGContextBeginPath (cgc);
+  CGContextMoveToPoint (cgc, p.x, p.y);
   p.x = wr.origin.x + x2;
   p.y = wr.origin.y + wr.size.height - y2;
-  CGContextAddLineToPoint (d->cgc, p.x, p.y);
-  CGContextStrokePath (d->cgc);
+  CGContextAddLineToPoint (cgc, p.x, p.y);
+  CGContextStrokePath (cgc);
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -801,7 +1130,10 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
   NSPoint p;
   CGRect wr = d->frame;
   push_fg_gc (d, gc, NO);
-  set_line_mode (d->cgc, &gc->gcv);
+
+  CGContextRef cgc = d->cgc;
+
+  set_line_mode (cgc, &gc->gcv);
   
   // if the first and last points coincide, use closepath to get
   // the proper line-joining.
@@ -812,8 +1144,8 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
   p.x = wr.origin.x + points->x;
   p.y = wr.origin.y + wr.size.height - points->y;
   points++;
-  CGContextBeginPath (d->cgc);
-  CGContextMoveToPoint (d->cgc, p.x, p.y);
+  CGContextBeginPath (cgc);
+  CGContextMoveToPoint (cgc, p.x, p.y);
   for (i = 1; i < count; i++) {
     if (mode == CoordModePrevious) {
       p.x += points->x;
@@ -822,11 +1154,11 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
       p.x = wr.origin.x + points->x;
       p.y = wr.origin.y + wr.size.height - points->y;
     }
-    CGContextAddLineToPoint (d->cgc, p.x, p.y);
+    CGContextAddLineToPoint (cgc, p.x, p.y);
     points++;
   }
-  if (closed_p) CGContextClosePath (d->cgc);
-  CGContextStrokePath (d->cgc);
+  if (closed_p) CGContextClosePath (cgc);
+  CGContextStrokePath (cgc);
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -839,19 +1171,21 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
   int i;
   CGRect wr = d->frame;
 
+  CGContextRef cgc = d->cgc;
+
   push_fg_gc (d, gc, NO);
-  set_line_mode (d->cgc, &gc->gcv);
-  CGContextBeginPath (d->cgc);
+  set_line_mode (cgc, &gc->gcv);
+  CGContextBeginPath (cgc);
   for (i = 0; i < count; i++) {
-    CGContextMoveToPoint    (d->cgc, 
+    CGContextMoveToPoint    (cgc, 
                              wr.origin.x + segments->x1,
                              wr.origin.y + wr.size.height - segments->y1);
-    CGContextAddLineToPoint (d->cgc,
+    CGContextAddLineToPoint (cgc,
                              wr.origin.x + segments->x2,
                              wr.origin.y + wr.size.height - segments->y2);
     segments++;
   }
-  CGContextStrokePath (d->cgc);
+  CGContextStrokePath (cgc);
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -861,7 +1195,7 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
 int
 XClearWindow (Display *dpy, Window win)
 {
-  Assert (win->type == WINDOW, "not a window");
+  Assert (win && win->type == WINDOW, "not a window");
   CGRect wr = win->frame;
   return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
 }
@@ -869,7 +1203,7 @@ XClearWindow (Display *dpy, Window win)
 int
 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
 {
-  Assert (w->type == WINDOW, "not a window");
+  Assert (w && w->type == WINDOW, "not a window");
   validate_pixel (pixel, 32, NO);
   w->window.background = pixel;
   return 0;
@@ -894,12 +1228,13 @@ draw_rect (Display *dpy, Drawable d, GC gc,
       push_bg_gc (d, gc, fill_p);
   }
 
+  CGContextRef cgc = d->cgc;
   if (fill_p)
-    CGContextFillRect (d->cgc, r);
+    CGContextFillRect (cgc, r);
   else {
     if (gc)
-      set_line_mode (d->cgc, &gc->gcv);
-    CGContextStrokeRect (d->cgc, r);
+      set_line_mode (cgc, &gc->gcv);
+    CGContextStrokeRect (cgc, r);
   }
 
   if (gc)
@@ -929,6 +1264,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
 {
   CGRect wr = d->frame;
   int i;
+  CGContextRef cgc = d->cgc;
   push_fg_gc (d, gc, YES);
   for (i = 0; i < n; i++) {
     CGRect r;
@@ -936,7 +1272,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
     r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
     r.size.width = rects->width;
     r.size.height = rects->height;
-    CGContextFillRect (d->cgc, r);
+    CGContextFillRect (cgc, r);
     rects++;
   }
   pop_gc (d, gc);
@@ -948,8 +1284,9 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
 int
 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
 {
-  Assert (win->type == WINDOW, "not a window");
-  set_color (win->cgc, win->window.background, 32, NO, YES);
+  Assert (win && win->type == WINDOW, "not a window");
+  CGContextRef cgc = win->cgc;
+  set_color (cgc, win->window.background, 32, NO, YES);
   draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
   return 0;
 }
@@ -962,9 +1299,10 @@ XFillPolygon (Display *dpy, Drawable d, GC gc,
   CGRect wr = d->frame;
   int i;
   push_fg_gc (d, gc, YES);
-  CGContextBeginPath (d->cgc);
+  CGContextRef cgc = d->cgc;
+  CGContextBeginPath (cgc);
+  float x = 0, y = 0;
   for (i = 0; i < npoints; i++) {
-    float x, y;
     if (i > 0 && mode == CoordModePrevious) {
       x += points[i].x;
       y -= points[i].y;
@@ -974,15 +1312,15 @@ XFillPolygon (Display *dpy, Drawable d, GC gc,
     }
         
     if (i == 0)
-      CGContextMoveToPoint (d->cgc, x, y);
+      CGContextMoveToPoint (cgc, x, y);
     else
-      CGContextAddLineToPoint (d->cgc, x, y);
+      CGContextAddLineToPoint (cgc, x, y);
   }
-  CGContextClosePath (d->cgc);
+  CGContextClosePath (cgc);
   if (gc->gcv.fill_rule == EvenOddRule)
-    CGContextEOFillPath (d->cgc);
+    CGContextEOFillPath (cgc);
   else
-    CGContextFillPath (d->cgc);
+    CGContextFillPath (cgc);
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -1014,25 +1352,26 @@ draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
   
   push_fg_gc (d, gc, fill_p);
 
-  CGContextBeginPath (d->cgc);
+  CGContextRef cgc = d->cgc;
+  CGContextBeginPath (cgc);
   
-  CGContextSaveGState(d->cgc);
-  CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
-  CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
+  CGContextSaveGState(cgc);
+  CGContextTranslateCTM (cgc, ctr.x, ctr.y);
+  CGContextScaleCTM (cgc, width/2.0, height/2.0);
   if (fill_p)
-    CGContextMoveToPoint (d->cgc, 0, 0);
+    CGContextMoveToPoint (cgc, 0, 0);
 
-  CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
-  CGContextRestoreGState (d->cgc);  // restore before stroke, for line width
+  CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
+  CGContextRestoreGState (cgc);  // restore before stroke, for line width
 
   if (closed_p)
-    CGContextClosePath (d->cgc); // for proper line joining
+    CGContextClosePath (cgc); // for proper line joining
   
   if (fill_p) {
-    CGContextFillPath (d->cgc);
+    CGContextFillPath (cgc);
   } else {
-    set_line_mode (d->cgc, &gc->gcv);
-    CGContextStrokePath (d->cgc);
+    set_line_mode (cgc, &gc->gcv);
+    CGContextStrokePath (cgc);
   }
 
   pop_gc (d, gc);
@@ -1100,6 +1439,10 @@ gcv_defaults (XGCValues *gcv, int depth)
 static void
 set_gcv (GC gc, XGCValues *from, unsigned long mask)
 {
+  if (! mask) return;
+  Assert (gc && from, "no gc");
+  if (!gc || !from) return;
+
   if (mask & GCFunction)       gc->gcv.function        = from->function;
   if (mask & GCForeground)     gc->gcv.foreground      = from->foreground;
   if (mask & GCBackground)     gc->gcv.background      = from->background;
@@ -1119,17 +1462,18 @@ set_gcv (GC gc, XGCValues *from, unsigned long mask)
   if (mask & GCBackground) validate_pixel (from->background, gc->depth,
                                            gc->gcv.alpha_allowed_p);
     
-  if (mask & GCLineStyle)      abort();
-  if (mask & GCPlaneMask)      abort();
-  if (mask & GCFillStyle)      abort();
-  if (mask & GCTile)           abort();
-  if (mask & GCStipple)                abort();
-  if (mask & GCTileStipXOrigin)        abort();
-  if (mask & GCTileStipYOrigin)        abort();
-  if (mask & GCGraphicsExposures) abort();
-  if (mask & GCDashOffset)     abort();
-  if (mask & GCDashList)       abort();
-  if (mask & GCArcMode)                abort();
+  Assert ((! (mask & (GCLineStyle |
+                      GCPlaneMask |
+                      GCFillStyle |
+                      GCTile |
+                      GCStipple |
+                      GCTileStipXOrigin |
+                      GCTileStipYOrigin |
+                      GCGraphicsExposures |
+                      GCDashOffset |
+                      GCDashList |
+                      GCArcMode))),
+          "unimplemented gcvalues mask");
 }
 
 
@@ -1176,7 +1520,7 @@ XFreeGC (Display *dpy, GC gc)
 Status
 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
 {
-  Assert (w->type == WINDOW, "not a window");
+  Assert (w && w->type == WINDOW, "not a window");
   memset (xgwa, 0, sizeof(*xgwa));
   xgwa->x      = w->frame.origin.x;
   xgwa->y      = w->frame.origin.y;
@@ -1265,7 +1609,7 @@ XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
     g = (hex[spec[3]] << 4) | hex[spec[4]];
     b = (hex[spec[5]] << 4) | hex[spec[6]];
   } else if (!strcasecmp(spec,"black")) {
-    r = g = b = 0;
+//  r = g = b = 0;
   } else if (!strcasecmp(spec,"white")) {
     r = g = b = 255;
   } else if (!strcasecmp(spec,"red")) {
@@ -1376,7 +1720,7 @@ XInitImage (XImage *ximage)
     ximage->f.put_pixel = ximage_putpixel_32;
     ximage->f.get_pixel = ximage_getpixel_32;
   } else {
-    abort();
+    Assert (0, "unknown depth");
   }
   return 1;
 }
@@ -1557,11 +1901,13 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
   if (w <= 0 || h <= 0)
     return 0;
 
+  CGContextRef cgc = d->cgc;
+
   if (gc->gcv.function == GXset ||
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
-    set_color (d->cgc, (gc->gcv.function == GXset
+    set_color (cgc, (gc->gcv.function == GXset
                         ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
                         : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
                gc->depth, gc->gcv.alpha_allowed_p, YES);
@@ -1602,7 +1948,7 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
                                     kCGRenderingIntentDefault);
     CGDataProviderRelease (prov);
     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-    CGContextDrawImage (d->cgc, r, cgi);
+    CGContextDrawImage (cgc, r, cgi);
     CGImageRelease (cgi);
 
   } else {   // (bpp == 1)
@@ -1632,10 +1978,10 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
                                          NO); /* interpolate */
     push_fg_gc (d, gc, YES);
 
-    CGContextFillRect (d->cgc, r);                     // foreground color
-    CGContextClipToMask (d->cgc, r, mask);
-    set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
-    CGContextFillRect (d->cgc, r);                     // background color
+    CGContextFillRect (cgc, r);                                // foreground color
+    CGContextClipToMask (cgc, r, mask);
+    set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
+    CGContextFillRect (cgc, r);                                // background color
     pop_gc (d, gc);
 
     free (flipped);
@@ -1655,35 +2001,52 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
            unsigned long plane_mask, int format)
 {
   const unsigned char *data = 0;
-  int depth, ibpp, ibpl, alpha_first_p;
+  int depth, ibpp, ibpl;
+  enum { RGBA, ARGB, BGRA } src_format; // As bytes.
+# ifndef USE_BACKBUFFER
   NSBitmapImageRep *bm = 0;
+# endif
   
   Assert ((width  < 65535), "improbably large width");
   Assert ((height < 65535), "improbably large height");
   Assert ((x < 65535 && x > -65535), "improbably large x");
   Assert ((y < 65535 && y > -65535), "improbably large y");
 
-  if (d->type == PIXMAP) {
-    depth = d->pixmap.depth;
-    alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
-    ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
-    ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
-    data = CGBitmapContextGetData (d->cgc);
+  CGContextRef cgc = d->cgc;
+
+#ifndef USE_BACKBUFFER
+  // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+  if (d->type == PIXMAP)
+# endif
+  {
+    depth = (d->type == PIXMAP
+             ? d->pixmap.depth
+             : 32);
+    // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
+    src_format = BGRA; // #### Should this be ARGB on PPC?
+    ibpp = CGBitmapContextGetBitsPerPixel (cgc);
+    ibpl = CGBitmapContextGetBytesPerRow (cgc);
+    data = CGBitmapContextGetData (cgc);
     Assert (data, "CGBitmapContextGetData failed");
-  } else {
+
+# ifndef USE_BACKBUFFER
+  } else { /* (d->type == WINDOW) */
+
     // get the bits (desired sub-rectangle) out of the NSView
     NSRect nsfrom;
     nsfrom.origin.x = x;
-    nsfrom.origin.y = y;
+//  nsfrom.origin.y = y;
+    nsfrom.origin.y = d->frame.size.height - height - y;
     nsfrom.size.width = width;
     nsfrom.size.height = height;
     bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
     depth = 32;
-    alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
+    src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
     ibpp = [bm bitsPerPixel];
     ibpl = [bm bytesPerRow];
     data = [bm bitmapData];
     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
+# endif // !USE_BACKBUFFER
   }
   
   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
@@ -1730,7 +2093,8 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
       const unsigned char *iline2 = iline;
       unsigned char *oline2 = oline;
 
-      if (alpha_first_p)                       // ARGB
+      switch (src_format) {
+      case ARGB:
         for (xx = 0; xx < width; xx++) {
           unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
           unsigned char r = *iline2++;
@@ -1743,7 +2107,8 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
           *((uint32_t *) oline2) = pixel;
           oline2 += 4;
         }
-      else                                     // RGBA
+        break;
+      case RGBA:
         for (xx = 0; xx < width; xx++) {
           unsigned char r = *iline2++;
           unsigned char g = *iline2++;
@@ -1756,18 +2121,40 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
           *((uint32_t *) oline2) = pixel;
           oline2 += 4;
         }
+        break;
+      case BGRA:
+        for (xx = 0; xx < width; xx++) {
+          unsigned char b = *iline2++;
+          unsigned char g = *iline2++;
+          unsigned char r = *iline2++;
+          unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
+          uint32_t pixel = ((a << 24) |
+                            (r << 16) |
+                            (g <<  8) |
+                            (b <<  0));
+          *((uint32_t *) oline2) = pixel;
+          oline2 += 4;
+        }
+        break;
+      default:
+        abort();
+        break;
+      }
 
       oline += obpl;
       iline += ibpl;
     }
   }
 
+# ifndef USE_BACKBUFFER
   if (bm) [bm release];
+# endif
 
   return image;
 }
 
 
+
 /* Returns a transformation matrix to do rotation as per the provided
    EXIF "Orientation" value.
  */
@@ -1826,14 +2213,19 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
                                XRectangle *geom_ret, int exif_rotation)
 {
   CGImageRef cgi;
+# ifndef USE_IPHONE
   CGImageSourceRef cgsrc;
+# endif // USE_IPHONE
   NSSize imgr;
 
+  CGContextRef cgc = d->cgc;
+
   if (nsimg_p) {
 
     NSImage *nsimg = (NSImage *) img_arg;
     imgr = [nsimg size];
 
+# ifndef USE_IPHONE
     // convert the NSImage to a CGImage via the toll-free-bridging 
     // of NSData and CFData...
     //
@@ -1843,6 +2235,9 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
     CFDataRef cfdata = (CFDataRef) nsdata;
     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
+# else  // USE_IPHONE
+    cgi = nsimg.CGImage;
+# endif // USE_IPHONE
 
   } else {
     cgi = (CGImageRef) img_arg;
@@ -1879,26 +2274,28 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
   if (d->type == WINDOW)
     XClearWindow (dpy, d);
   else {
-    set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
+    set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
   }
 
   CGAffineTransform trans = 
     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
 
-  CGContextSaveGState (d->cgc);
-  CGContextConcatCTM (d->cgc, 
+  CGContextSaveGState (cgc);
+  CGContextConcatCTM (cgc, 
                       CGAffineTransformMakeTranslation (dst.origin.x,
                                                         dst.origin.y));
-  CGContextConcatCTM (d->cgc, trans);
+  CGContextConcatCTM (cgc, trans);
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-  CGContextDrawImage (d->cgc, dst2, cgi);
-  CGContextRestoreGState (d->cgc);
+  CGContextDrawImage (cgc, dst2, cgi);
+  CGContextRestoreGState (cgc);
 
+# ifndef USE_IPHONE
   if (nsimg_p) {
     CFRelease (cgsrc);
     CGImageRelease (cgi);
   }
+# endif // USE_IPHONE
 
   if (geom_ret) {
     geom_ret->x = dst.origin.x;
@@ -1911,6 +2308,7 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
 }
 
 
+
 Pixmap
 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
                              const char *data,
@@ -1955,7 +2353,8 @@ XCreatePixmap (Display *dpy, Drawable d,
                                   width * 4, /* bpl */
                                   dpy->colorspace,
                                   // Without this, it returns 0...
-                                  kCGImageAlphaNoneSkipFirst
+                                  (kCGImageAlphaNoneSkipFirst |
+                                   kCGBitmapByteOrder32Host)
                                   );
   Assert (p->cgc, "could not create CGBitmapContext");
   return p;
@@ -1965,7 +2364,7 @@ XCreatePixmap (Display *dpy, Drawable d,
 int
 XFreePixmap (Display *d, Pixmap p)
 {
-  Assert (p->type == PIXMAP, "not a pixmap");
+  Assert (p && p->type == PIXMAP, "not a pixmap");
   invalidate_drawable_cache (p);
   CGContextRelease (p->cgc);
   if (p->pixmap.cgc_buffer)
@@ -1997,7 +2396,8 @@ copy_pixmap (Display *dpy, Pixmap p)
                                    width * 4, /* bpl */
                                    dpy->colorspace,
                                    // Without this, it returns 0...
-                                   kCGImageAlphaNoneSkipFirst
+                                   (kCGImageAlphaNoneSkipFirst |
+                                    kCGBitmapByteOrder32Host)
                                    );
   Assert (p2->cgc, "could not create CGBitmapContext");
 
@@ -2032,12 +2432,12 @@ static void
 query_font (Font fid)
 {
   if (!fid || !fid->nsfont) {
-    NSLog(@"no NSFont in fid");
-    abort();
+    Assert (0, "no NSFont in fid");
+    return;
   }
   if (![fid->nsfont fontName]) {
-    NSLog(@"broken NSFont in fid");
-    abort();
+    Assert(0, @"broken NSFont in fid");
+    return;
   }
 
   int first = 32;
@@ -2047,7 +2447,7 @@ query_font (Font fid)
   XCharStruct *min = &f->min_bounds;
   XCharStruct *max = &f->max_bounds;
 
-#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
+#define CEIL(F)  ((F) < 0 ? floor(F) : ceil(F))
 
   f->fid               = fid;
   f->min_char_or_byte2 = first;
@@ -2065,7 +2465,15 @@ query_font (Font fid)
   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
   int i;
 
+# ifndef USE_IPHONE
   NSBezierPath *bpath = [NSBezierPath bezierPath];
+# else  // USE_IPHONE
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+# endif // USE_IPHONE
 
   for (i = first; i <= last; i++) {
     unsigned char str[2];
@@ -2074,6 +2482,10 @@ query_font (Font fid)
 
     NSString *nsstr = [NSString stringWithCString:(char *) str
                                          encoding:NSISOLatin1StringEncoding];
+    NSPoint advancement = { 0, };
+    NSRect bbox = {{ 0, }, };
+
+# ifndef USE_IPHONE
 
     /* I can't believe we have to go through this bullshit just to
        convert a 'char' to an NSGlyph!!
@@ -2111,8 +2523,6 @@ query_font (Font fid)
        [NSString sizeWithAttributes] both return an advancement-sized
        rectangle, not a rectangle completely enclosing the glyph's ink.
      */
-    NSPoint advancement;
-    NSRect bbox;
     advancement.x = advancement.y = 0;
     [bpath removeAllPoints];
     [bpath moveToPoint:advancement];
@@ -2120,6 +2530,59 @@ query_font (Font fid)
     advancement = [bpath currentPoint];
     bbox = [bpath bounds];
 
+# else  // USE_IPHONE
+
+    /* There is no way to get "lbearing", "rbearing" or "descent" out of
+       NSFont.  'sizeWithFont' gives us "width" and "height" only.
+       Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
+       width of the character and the ascent of the font.
+
+       Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
+       the CoreText library, but there's no non-CoreText way to turn a
+       unichar into a CGGlyph.
+     */
+    UniChar uchar = [nsstr characterAtIndex: 0];
+    CGGlyph cgglyph = 0;
+
+    if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
+      {
+        bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
+                                                kCTFontDefaultOrientation,
+                                                &cgglyph, NULL, 1);
+        CGSize adv = { 0, };
+        CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
+                                    &cgglyph, &adv, 1);
+        advancement.x = adv.width;
+        advancement.y = adv.height;
+
+        /* A bug that existed was that the GL FPS display was truncating
+           characters slightly: commas looked like periods.
+
+           At one point, I believed the bounding box was being rounded
+           wrong and we needed to add padding to it here.
+
+           I think what was actually going on was, I was computing rbearing
+           wrong.  Also there was an off-by-one error in texfont.c, displaying
+           too little of the bitmap.
+
+           Adding arbitrarily large padding to the bbox is fine in fontglide
+           and FPS display, but screws up BSOD. Increasing bbox width makes
+           inverted text print too wide; decreasing origin makes characters
+           clip.
+
+           I think that all 3 states are correct now with the new lbearing
+           computation plus the texfont fix.
+         */
+#  if 0
+        double kludge = 2;
+        bbox.origin.x    -= kludge;
+        bbox.origin.y    -= kludge;
+        bbox.size.width  += kludge;
+        bbox.size.height += kludge;
+#  endif
+      }
+# endif // USE_IPHONE
+
     /* Now that we know the advancement and bounding box, we can compute
        the lbearing and rbearing.
      */
@@ -2127,12 +2590,13 @@ query_font (Font fid)
 
     cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
     cs->descent  = CEIL(-bbox.origin.y);
-    cs->lbearing = CEIL (bbox.origin.x);
-    cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
+    cs->lbearing = floor (bbox.origin.x);
+//  cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
+    cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
     cs->width    = CEIL (advancement.x);
 
-    Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
-            "bbox w wrong");
+//  Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
+//          "bbox w wrong");
     Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
             "bbox h wrong");
 
@@ -2152,15 +2616,18 @@ query_font (Font fid)
 
 #if 0
     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
-                    " bb=%3d x %3d @ %3d %3d  adv=%3d %3d\n",
+                    " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n",
             i, i, cs->width, cs->lbearing, cs->rbearing, 
             cs->ascent, cs->descent,
-            (int) bbox.size.width, (int) bbox.size.height,
-            (int) bbox.origin.x, (int) bbox.origin.y,
-            (int) advancement.x, (int) advancement.y);
+            bbox.size.width, bbox.size.height,
+            bbox.origin.x, bbox.origin.y,
+            advancement.x, advancement.y);
 #endif
   }
 
+# ifdef USE_IPHONE
+  CFRelease (ctfont);
+# endif
 }
 
 
@@ -2253,15 +2720,20 @@ try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
 }
 
 static NSFont *
-try_native_font (const char *name, char **name_ret, float *size_ret)
+try_native_font (const char *name, float scale,
+                 char **name_ret, float *size_ret)
 {
   if (!name) return 0;
   const char *spc = strrchr (name, ' ');
   if (!spc) return 0;
-  int size = 0;
-  if (1 != sscanf (spc, " %d ", &size)) return 0;
+  int dsize = 0;
+  if (1 != sscanf (spc, " %d ", &dsize)) return 0;
+  float size = dsize;
+
   if (size <= 4) return 0;
 
+  size *= scale;
+
   char *name2 = strdup (name);
   name2[strlen(name2) - strlen(spc)] = 0;
   NSString *nsname = [NSString stringWithCString:name2
@@ -2283,6 +2755,7 @@ try_native_font (const char *name, char **name_ret, float *size_ret)
 static NSFont *
 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 {
+# ifndef USE_IPHONE
   NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
                           (ital ? NSItalicFontMask : NSUnitalicFontMask));
   NSArray *fonts = [[NSFontManager sharedFontManager]
@@ -2321,11 +2794,70 @@ random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 
   // None of the fonts support ASCII?
   return 0;
+
+# else  // USE_IPHONE
+
+  NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
+  NSArray *families = [UIFont familyNames];
+  NSMutableDictionary *famdict = [NSMutableDictionary 
+                                   dictionaryWithCapacity:100];
+  NSObject *y = [NSNumber numberWithBool:YES];
+  for (NSString *name in families) {
+    // There are many dups in the families array -- uniquify it.
+    [famdict setValue:y forKey:name];
+  }
+
+  for (NSString *name in famdict) {
+    for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
+
+# define MATCH(X) \
+         ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
+         != NSNotFound)
+
+      BOOL bb = MATCH(@"Bold");
+      BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
+
+      if (!bold != !bb) continue;
+      if (!ital != !ii) continue;
+
+      /* Check if it can do ASCII.  No good way to accomplish this!
+         These are fonts present in iPhone Simulator as of June 2012
+         that don't include ASCII.
+       */
+      if (MATCH(@"AppleGothic") ||     // Korean
+          MATCH(@"Dingbats") ||                // Dingbats
+          MATCH(@"Emoji") ||           // Emoticons
+          MATCH(@"Geeza") ||           // Arabic
+          MATCH(@"Hebrew") ||          // Hebrew
+          MATCH(@"HiraKaku") ||                // Japanese
+          MATCH(@"HiraMin") ||         // Japanese
+          MATCH(@"Kailasa") ||         // Tibetan
+          MATCH(@"Ornaments") ||       // Dingbats
+          MATCH(@"STHeiti")            // Chinese
+         )
+        continue;
+
+      [fonts addObject:fn];
+# undef MATCH
+    }
+  }
+
+  if (! [fonts count]) return 0;       // Nothing suitable?
+
+  int i = random() % [fonts count];
+  NSString *name = [fonts objectAtIndex:i];
+  UIFont *ff = [UIFont fontWithName:name size:size];
+  *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
+
+  return ff;
+
+# endif // USE_IPHONE
 }
 
 
 static NSFont *
-try_xlfd_font (const char *name, char **name_ret, float *size_ret)
+try_xlfd_font (const char *name, float scale,
+               char **name_ret, float *size_ret)
 {
   NSFont *nsfont = 0;
   BOOL bold  = NO;
@@ -2376,6 +2908,8 @@ try_xlfd_font (const char *name, char **name_ret, float *size_ret)
   if (size < 6 || size > 1000)
     size = 12;
 
+  size *= scale;
+
   if (rand)
     nsfont   = random_font (bold, ital, size, &ps_name);
 
@@ -2417,12 +2951,32 @@ XLoadFont (Display *dpy, const char *name)
 {
   Font fid = (Font) calloc (1, sizeof(*fid));
 
-  fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
+  float scale = 1;
+
+# ifdef USE_IPHONE
+  // Scale up fonts on Retina displays.
+  scale = dpy->main_window->window.view.contentScaleFactor;
+# endif
+
+  fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
+
+  if (!fid->nsfont && name &&
+      strchr (name, ' ') &&
+      !strchr (name, '*')) {
+    // If name contains a space but no stars, it is a native font spec --
+    // return NULL so that we know it really didn't exist.  Else, it is an
+    //  XLFD font, so keep trying.
+    XUnloadFont (dpy, fid);
+    return 0;
+  }
+
   if (! fid->nsfont)
-    fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
+    fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
+
+  // We should never return NULL for XLFD fonts.
   if (!fid->nsfont) {
-    NSLog(@"no NSFont for \"%s\"", name);
-    abort();
+    Assert (0, "no font");
+    return 0;
   }
   CFRetain (fid->nsfont);   // needed for garbage collection?
 
@@ -2438,14 +2992,17 @@ XFontStruct *
 XLoadQueryFont (Display *dpy, const char *name)
 {
   Font fid = XLoadFont (dpy, name);
+  if (!fid) return 0;
   return XQueryFont (dpy, fid);
 }
 
 int
 XUnloadFont (Display *dpy, Font fid)
 {
-  free (fid->ps_name);
-  free (fid->metrics.per_char);
+  if (fid->ps_name)
+    free (fid->ps_name);
+  if (fid->metrics.per_char)
+    free (fid->metrics.per_char);
 
   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
@@ -2537,11 +3094,11 @@ XTextWidth (XFontStruct *f, const char *s, int length)
 
 
 static void
-set_font (CGContextRef cgc, GC gc)
+set_font (Display *dpy, CGContextRef cgc, GC gc)
 {
   Font font = gc->gcv.font;
   if (! font) {
-    font = XLoadFont (0, 0);
+    font = XLoadFont (dpy, 0);
     gc->gcv.font = font;
     [gc->gcv.font->nsfont retain];
     CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
@@ -2577,13 +3134,14 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
      But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
    */
 
+  CGContextRef cgc = d->cgc;
   push_fg_gc (d, gc, YES);
-  set_font (d->cgc, gc);
+  set_font (dpy, cgc, gc);
 
-  CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
+  CGContextSetTextDrawingMode (cgc, kCGTextFill);
   if (gc->gcv.antialias_p)
-    CGContextSetShouldAntialias (d->cgc, YES);
-  CGContextShowTextAtPoint (d->cgc,
+    CGContextSetShouldAntialias (cgc, YES);
+  CGContextShowTextAtPoint (cgc,
                             wr.origin.x + x,
                             wr.origin.y + wr.size.height - y,
                             str, len);
@@ -2715,7 +3273,8 @@ XSetClipMask (Display *dpy, GC gc, Pixmap m)
 
   gc->gcv.clip_mask = copy_pixmap (dpy, m);
   if (gc->gcv.clip_mask)
-    gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
+    gc->clip_mask =
+      CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
   else
     gc->clip_mask = 0;
 
@@ -2736,7 +3295,18 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
                int *root_x_ret, int *root_y_ret, 
                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
 {
-  Assert (w->type == WINDOW, "not a window");
+  Assert (w && w->type == WINDOW, "not a window");
+
+# ifdef USE_IPHONE
+  int x = w->window.last_mouse_x;
+  int y = w->window.last_mouse_y;
+  if (root_x_ret) *root_x_ret = x;
+  if (root_y_ret) *root_y_ret = y;
+  if (win_x_ret)  *win_x_ret  = x;
+  if (win_y_ret)  *win_y_ret  = y;
+
+# else  // !USE_IPHONE
+
   NSWindow *nsw = [w->window.view window];
   NSPoint wpos;
   // get bottom left of window on screen, from bottom left
@@ -2760,8 +3330,13 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
   NSScreen *screen = (screens && [screens count] > 0
                       ? [screens objectAtIndex:0]
                       : [NSScreen mainScreen]);
+#ifdef USE_IPHONE
+  double s = w->window.view.contentScaleFactor;
+#else
+  int s = 1;
+#endif
   NSRect srect = [screen frame];
-  vpos.y = srect.size.height - vpos.y;
+  vpos.y = (s * srect.size.height) - vpos.y;
   
   // get the mouse position on window, from bottom left
   NSEvent *e = [NSApp currentEvent];
@@ -2778,8 +3353,9 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
   if (root_y_ret) *root_y_ret = (int) p.y;
   if (win_x_ret)  *win_x_ret  = (int) (p.x - vpos.x);
   if (win_y_ret)  *win_y_ret  = (int) (p.y - vpos.y);
+# endif // !USE_IPHONE
   
-  if (mask_ret)   *mask_ret   = 0;  // ####
+  if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
   if (root_ret)   *root_ret   = 0;
   if (child_ret)  *child_ret  = 0;
   return True;
@@ -2791,7 +3367,16 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
                        int *dest_x_ret, int *dest_y_ret,
                        Window *child_ret)
 {
-  Assert (w->type == WINDOW, "not a window");
+  Assert (w && w->type == WINDOW, "not a window");
+
+# ifdef USE_IPHONE
+
+  NSPoint p;
+  p.x = src_x;
+  p.y = src_y;
+
+# else  // !USE_IPHONE
+
   NSWindow *nsw = [w->window.view window];
   NSPoint wpos;
   // get bottom left of window on screen, from bottom left
@@ -2815,8 +3400,13 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
   NSScreen *screen = (screens && [screens count] > 0
                       ? [screens objectAtIndex:0]
                       : [NSScreen mainScreen]);
+# ifdef USE_IPHONE
+  double s = w->window.view.contentScaleFactor;
+# else
+  int s = 1;
+# endif
   NSRect srect = [screen frame];
-  vpos.y = srect.size.height - vpos.y;
+  vpos.y = (s * srect.size.height) - vpos.y;
   
   // point starts out relative to top left of view
   NSPoint p;
@@ -2826,6 +3416,7 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
   // get point relative to top left of screen
   p.x += vpos.x;
   p.y += vpos.y;
+# endif // !USE_IPHONE
 
   *dest_x_ret = p.x;
   *dest_y_ret = p.y;
@@ -2846,11 +3437,25 @@ XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
                XComposeStatus *xc)
 {
   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
-  char c = (char) ks;    // could be smarter about modifiers here...
+  char c = 0;
+  // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
+  if ((unsigned int) ks <= 255)
+    c = (char) ks;
+
+  // Put control characters in the string.  Not meta.
+  if (e->state & ControlMask) {
+    if (c >= 'a' && c <= 'z')    // Upcase control.
+      c -= 'a'-'A';
+    if (c >= '@' && c <= '_')    // Shift to control page.
+      c -= '@';
+    if (c == ' ')               // C-SPC is NULL.
+      c = 0;
+  }
+
   if (k_ret) *k_ret = ks;
   if (size > 0) buf[0] = c;
   if (size > 1) buf[1] = 0;
-  return 0;
+  return (size > 0 ? 1 : 0);
 }