From http://www.jwz.org/xscreensaver/xscreensaver-5.32.tar.gz
[xscreensaver] / OSX / jwxyz.m
index 559133804a75f9b3d4ea280f2d05eba526d3a82d..720569573fd5385dff404fad7ae5287438c8f882 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2014 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
  *
  * 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 <stdlib.h>
-#import <Cocoa/Cocoa.h>
+#import <stdint.h>
+#import <wchar.h>
+
+#ifdef USE_IPHONE
+# import <UIKit/UIKit.h>
+# import <UIKit/UIScreen.h>
+# import <QuartzCore/QuartzCore.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
+# define colorWithDeviceRed colorWithRed
+#else
+# import <Cocoa/Cocoa.h>
+#endif
+
+#import <CoreText/CTFont.h>
+#import <CoreText/CTLine.h>
+
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
+#import "yarandom.h"
+#import "utf8wc.h"
+
+# define USE_BACKBUFFER  /* must be in sync with XScreenSaverView.h */
 
 #undef  Assert
 
 #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
 
 # undef MAX
 # undef MIN
 struct jwxyz_Drawable {
   enum { WINDOW, PIXMAP } type;
   CGContextRef cgc;
 struct jwxyz_Drawable {
   enum { WINDOW, PIXMAP } type;
   CGContextRef cgc;
+  CGImageRef cgi;
   CGRect frame;
   union {
     struct {
       NSView *view;
       unsigned long background;
   CGRect frame;
   union {
     struct {
       NSView *view;
       unsigned long background;
+      int last_mouse_x, last_mouse_y;
     } window;
     struct {
       int depth;
     } window;
     struct {
       int depth;
+      void *cgc_buffer;                // the bits to which CGContextRef renders
     } pixmap;
   };
 };
     } pixmap;
   };
 };
@@ -52,11 +81,14 @@ struct jwxyz_Drawable {
 struct jwxyz_Display {
   Window main_window;
   Screen *screen;
 struct jwxyz_Display {
   Window main_window;
   Screen *screen;
+  int screen_count;
   struct jwxyz_sources_data *timers_data;
 
   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. */
   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
 
   CGColorSpaceRef colorspace;  /* Color space of this screen.  We tag all of
                                   our images with this to avoid translation
@@ -66,6 +98,7 @@ struct jwxyz_Display {
 struct jwxyz_Screen {
   Display *dpy;
   Visual *visual;
 struct jwxyz_Screen {
   Display *dpy;
   Visual *visual;
+  int screen_number;
 };
 
 struct jwxyz_GC {
 };
 
 struct jwxyz_GC {
@@ -84,17 +117,86 @@ struct jwxyz_Font {
   XFontStruct metrics;
 };
 
   XFontStruct metrics;
 };
 
+struct jwxyz_XFontSet {
+  Font 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
+}
+
+
+/* We keep a list of all of the Displays that have been created and not
+   yet freed so that they can have sensible display numbers.  If three
+   displays are created (0, 1, 2) and then #1 is closed, then the fourth
+   display will be given the now-unused display number 1. (Everything in
+   here assumes a 1:1 Display/Screen mapping.)
+
+   The size of this array is the most number of live displays at one time.
+   So if it's 20, then we'll blow up if the system has 19 monitors and also
+   has System Preferences open (the small preview window).
+
+   Note that xlockmore-style savers tend to allocate big structures, so
+   setting this to 1000 will waste a few megabytes.  Also some of them assume
+   that the number of screens never changes, so dynamically expanding this
+   array won't work.
+ */
+# ifndef USE_IPHONE
+static Display *jwxyz_live_displays[20] = { 0, };
+# endif
+
 
 Display *
 
 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;
   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));
   d->screen->dpy = d;
   
 
   Display *d = (Display *) calloc (1, sizeof(*d));
   d->screen = (Screen *) calloc (1, sizeof(Screen));
   d->screen->dpy = d;
   
+  d->screen_count = 1;
+  d->screen->screen_number = 0;
+# ifndef USE_IPHONE
+  {
+    // Find the first empty slot in live_displays and plug us in.
+    int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
+    int i;
+    for (i = 0; i < size; i++) {
+      if (! jwxyz_live_displays[i])
+        break;
+    }
+    if (i >= size) abort();
+    jwxyz_live_displays[i] = d;
+    d->screen_count = size;
+    d->screen->screen_number = i;
+  }
+# endif // !USE_IPHONE
+
   Visual *v = (Visual *) calloc (1, sizeof(Visual));
   v->class      = TrueColor;
   v->red_mask   = 0x00FF0000;
   Visual *v = (Visual *) calloc (1, sizeof(Visual));
   v->class      = TrueColor;
   v->red_mask   = 0x00FF0000;
@@ -108,31 +210,48 @@ jwxyz_make_display (void *nsview_arg)
 
   Window w = (Window) calloc (1, sizeof(*w));
   w->type = WINDOW;
 
   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;
   w->window.view = view;
+  CFRetain (w->window.view);   // needed for garbage collection?
   w->window.background = BlackPixel(0,0);
 
   d->main_window = w;
 
   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;
 }
 
 void
 jwxyz_free_display (Display *dpy)
 {
   return d;
 }
 
 void
 jwxyz_free_display (Display *dpy)
 {
-  jwxyz_XtRemoveInput_all (dpy);
-  // #### jwxyz_XtRemoveTimeOut_all ();
+  jwxyz_sources_free (dpy->timers_data);
   
   
+# ifndef USE_IPHONE
+  {
+    // Find us in live_displays and clear that slot.
+    int size = ScreenCount(dpy);
+    int i;
+    for (i = 0; i < size; i++) {
+      if (dpy == jwxyz_live_displays[i]) {
+        jwxyz_live_displays[i] = 0;
+        break;
+      }
+    }
+    if (i >= size) abort();
+  }
+# endif // !USE_IPHONE
+
   free (dpy->screen->visual);
   free (dpy->screen);
   free (dpy->screen->visual);
   free (dpy->screen);
+  CFRelease (dpy->main_window->window.view);
   free (dpy->main_window);
   free (dpy);
 }
   free (dpy->main_window);
   free (dpy);
 }
@@ -141,22 +260,43 @@ jwxyz_free_display (Display *dpy)
 void *
 jwxyz_window_view (Window w)
 {
 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;
 }
 
   return w->window.view;
 }
 
+
+/* Call this after any modification to the bits on a Pixmap or Window.
+   Most Pixmaps are used frequently as sources and infrequently as
+   destinations, so it pays to cache the data as a CGImage as needed.
+ */
+static void
+invalidate_drawable_cache (Drawable d)
+{
+  if (d && d->cgi) {
+    CGImageRelease (d->cgi);
+    d->cgi = 0;
+  }
+}
+
+
 /* Call this when the View changes size or position.
  */
 void
 /* 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;
   // Figure out which screen the window is currently on.
   {
     int wx, wy;
@@ -167,10 +307,19 @@ jwxyz_window_resized (Display *dpy, Window w)
     CGDisplayCount n;
     dpy->cgdpy = 0;
     CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
     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");
   }
     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.
     //
   {
     // Figure out this screen's colorspace, and use that for every CGImage.
     //
@@ -180,15 +329,35 @@ jwxyz_window_resized (Display *dpy, Window w)
     dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
     Assert (dpy->colorspace, "unable to find colorspace");
   }
     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();
 
   // 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)
 {
 jwxyz_sources_data *
 display_sources_data (Display *dpy)
 {
@@ -229,7 +398,13 @@ XDisplayNumberOfScreen (Screen *s)
 int
 XScreenNumberOfScreen (Screen *s)
 {
 int
 XScreenNumberOfScreen (Screen *s)
 {
-  return 0;
+  return s->screen_number;
+}
+
+int
+jwxyz_ScreenCount (Display *dpy)
+{
+  return dpy->screen_count;
 }
 
 int
 }
 
 int
@@ -244,7 +419,6 @@ XDisplayHeight (Display *dpy, int screen)
   return (int) dpy->main_window->frame.size.height;
 }
 
   return (int) dpy->main_window->frame.size.height;
 }
 
-
 static void
 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
 {
 static void
 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
 {
@@ -329,14 +503,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;
     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);
 }
 
   }
 
   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.
 
 
 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
@@ -354,7 +528,6 @@ push_color_gc (Drawable d, GC gc, unsigned long color,
   }
 
   CGContextRef cgc = d->cgc;
   }
 
   CGContextRef cgc = d->cgc;
-
   set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
   CGContextSetShouldAntialias (cgc, antialias_p);
 }
   set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
   CGContextSetShouldAntialias (cgc, antialias_p);
 }
@@ -382,12 +555,18 @@ push_bg_gc (Drawable d, GC gc, Bool fill_p)
 
    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
    with repeated calls to CGContextDrawImage than it is to make a single
 
    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
    with repeated calls to CGContextDrawImage than it is to make a single
-   call to CGContextFillRects()!
+   call to CGContextFillRects() with a list of 1x1 rectangles!
 
    I still wouldn't call it *fast*, however...
  */
 #define XDRAWPOINTS_IMAGES
 
 
    I still wouldn't call it *fast*, however...
  */
 #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)
 int
 XDrawPoints (Display *dpy, Drawable d, GC gc, 
              XPoint *points, int count, int mode)
@@ -395,69 +574,130 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
   int i;
   CGRect wr = d->frame;
 
   int i;
   CGRect wr = d->frame;
 
-# ifdef XDRAWPOINTS_IMAGES
+  push_fg_gc (d, gc, YES);
 
 
-  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 XDRAWPOINTS_CGDATA
 
 
-  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;
+#  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);
+
+    Assert (data, "no bitmap data in Drawable");
+
+    unsigned long 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 = (unsigned int) argb;
+        }
+      }
     } else {
     } 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 = (unsigned int) 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 */
 
 
 # else /* ! XDRAWPOINTS_IMAGES */
 
-  CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
-  CGRect *r = rects;
+    CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
+    CGRect *r = rects;
   
   
-  push_fg_gc (d, gc, YES);
-  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 */
 
 # endif /* ! XDRAWPOINTS_IMAGES */
+  }
+
+  pop_gc (d, gc);
+  invalidate_drawable_cache (d);
 
   return 0;
 }
 
   return 0;
 }
@@ -477,12 +717,52 @@ static void draw_rect (Display *, Drawable, GC,
                        int x, int y, unsigned int width, unsigned int height, 
                        BOOL foreground_p, BOOL fill_p);
 
                        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, 
            unsigned int width, unsigned int height, 
            int dst_x, int dst_y)
 {
 int
 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
            int src_x, int src_y, 
            unsigned int width, unsigned int height, 
            int dst_x, int dst_y)
 {
+  Assert (gc, "no GC");
   Assert ((width  < 65535), "improbably large width");
   Assert ((height < 65535), "improbably large height");
   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
   Assert ((width  < 65535), "improbably large width");
   Assert ((height < 65535), "improbably large height");
   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
@@ -493,13 +773,14 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
   if (width == 0 || height == 0)
     return 0;
 
   if (width == 0 || height == 0)
     return 0;
 
-  if (gc && (gc->gcv.function == GXset ||
-             gc->gcv.function == GXclear)) {
+  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" 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;
                gc->depth, gc->gcv.alpha_allowed_p, YES);
     draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
     return 0;
@@ -533,8 +814,6 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
   
   // Clip rects to frames...
   //
   
   // 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; \
 
 # define CLIP(THIS,THAT,VAL,SIZE) do { \
   float off = THIS##_rect.origin.VAL; \
@@ -555,33 +834,166 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
 
   CLIP (dst, src, x, width);
   CLIP (dst, src, y, height);
 
   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
 
   CLIP (src, dst, x, width);
   CLIP (src, dst, y, height);
 # undef CLIP
 
-#if 0
-  Assert (src_rect.size.width  == dst_rect.size.width, "width out of sync");
-  Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
-  Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
-  Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
-  Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
-  Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
-  Assert (src_rect.origin.x  + src_rect.size.width <=
-          src_frame.origin.x + src_frame.size.width, "clip failed src_width");
-#endif
-  
-  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;
     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;
   
   NSObject *releaseme = 0;
   CGImageRef cgi;
   BOOL mask_p = NO;
+  BOOL free_cgi_p = NO;
+
+
+  /* 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,
+                            (uint32_t) 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,
+                            (uint32_t) 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, (uint32_t) 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, (uint32_t) gc->gcv.background,
+                            orig_dst_x + orig_width - dst_x0 - width0,
+                            height0);
+        }
+      }
 
 
-  if (src->type == PIXMAP) {
+      invalidate_drawable_cache (dst);
+      return 0;
+    }
 
 
-    // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
-    // but it presumably shares the data pointer instead of copying it.
-    cgi = CGBitmapContextCreateImage (src->cgc);
+
+    // 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
+    // destination drawable's CGContext.
+    //
+    // (It doesn't seem to be possible to use NSCopyBits() to optimize the
+    // case of copying from a Pixmap back to itself, but I don't think that
+    // happens very often anyway.)
+    //
+    // First we get a CGImage out of the pixmap CGContext -- it's the whole
+    // pixmap, but it presumably shares the data pointer instead of copying
+    // it.  We then cache that CGImage it inside the Pixmap object.  Note:
+    // invalidate_drawable_cache() must be called to discard this any time a
+    // modification is made to the pixmap, or we'll end up re-using old bits.
+    //
+    if (!src->cgi)
+      src->cgi = CGBitmapContextCreateImage (src->cgc);
+    cgi = src->cgi;
 
     // if doing a sub-rect, trim it down.
     if (src_rect.origin.x    != src_frame.origin.x   ||
 
     // if doing a sub-rect, trim it down.
     if (src_rect.origin.x    != src_frame.origin.x   ||
@@ -592,97 +1004,91 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       src_rect.origin.y = (src_frame.size.height -
                            src_rect.size.height - src_rect.origin.y);
       // This does not copy image data, so it should be fast.
       src_rect.origin.y = (src_frame.size.height -
                            src_rect.size.height - src_rect.origin.y);
       // This does not copy image data, so it should be fast.
-      CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
-      CGImageRelease (cgi);
-      cgi = cgi2;
+      cgi = CGImageCreateWithImageInRect (cgi, src_rect);
+      free_cgi_p = YES;
     }
 
     }
 
-    if (src->pixmap.depth == 1)
+  if (src->type == PIXMAP && src->pixmap.depth == 1)
       mask_p = YES;
 
       mask_p = YES;
 
+# ifndef USE_BACKBUFFER
   } else { /* (src->type == WINDOW) */
     
   } else { /* (src->type == WINDOW) */
     
-    NSRect nsfrom;
+    NSRect nsfrom;    // NSRect != CGRect on 10.4
     nsfrom.origin.x    = src_rect.origin.x;
     nsfrom.origin.y    = src_rect.origin.y;
     nsfrom.size.width  = src_rect.size.width;
     nsfrom.size.height = src_rect.size.height;
 
     nsfrom.origin.x    = src_rect.origin.x;
     nsfrom.origin.y    = src_rect.origin.y;
     nsfrom.size.width  = src_rect.size.width;
     nsfrom.size.height = src_rect.size.height;
 
-#if 1
-    // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
-    //
-    NSBitmapImageRep *bm = [NSBitmapImageRep alloc];
-    [bm initWithFocusedViewRect:nsfrom];
-    unsigned char *data = [bm bitmapData];
-    int bps = [bm bitsPerSample];
-    int bpp = [bm bitsPerPixel];
-    int bpl = [bm bytesPerRow];
-    releaseme = bm;
-#endif
+    if (src == dst) {
 
 
-#if 0
-    // QuickDraw way (doesn't work, need NSQuickDrawView)
-    PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
-    char **data = GetPortPixMap (pix);
-    int bps = 8;
-    int bpp = 32;
-    int bpl = GetPixRowBytes (pix) & 0x3FFF;
-#endif
+      // If we are copying from a window to itself, we can use NSCopyBits()
+      // without first copying the rectangle to an intermediary CGImage.
+      // This is ~28% faster (but I *expected* it to be twice as fast...)
+      // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
+      //
+      cgi = 0;
 
 
-#if 0
-    // get the bits (desired sub-rectangle) out of the raw frame buffer.
-    // (This renders wrong, and appears to be even slower anyway.)
-    //
-    int window_x, window_y;
-    XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
-    window_x += nsfrom.origin.x;
-    window_y += (dst->frame.size.height
-                 - (nsfrom.origin.y + nsfrom.size.height));
-
-    unsigned char *data = (unsigned char *) 
-      CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
-    int bps = CGDisplayBitsPerSample (dpy->cgdpy);
-    int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
-    int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
+    } else {
 
 
-#endif
+      // If we are copying from a Window to a Pixmap, we must first copy
+      // the bits to an intermediary CGImage object, then copy that to the
+      // Pixmap's CGContext.
+      //
+      NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
+                               initWithFocusedViewRect:nsfrom];
+      unsigned char *data = [bm bitmapData];
+      int bps = [bm bitsPerSample];
+      int bpp = [bm bitsPerPixel];
+      int bpl = [bm bytesPerRow];
+      releaseme = bm;
+
+      // create a CGImage from those bits.
+      // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
+      // but that method didn't exist in 10.4.)
+
+      CGDataProviderRef prov =
+        CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
+                                      NULL);
+      cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
+                           bps, bpp, bpl,
+                           dpy->colorspace, 
+                           /* Use whatever default bit ordering we got from
+                              initWithFocusedViewRect.  I would have assumed
+                              that it was (kCGImageAlphaNoneSkipFirst |
+                              kCGBitmapByteOrder32Host), but on Intel,
+                              it's not!
+                           */
+                           0,
+                           prov, 
+                           NULL,  /* decode[] */
+                           NO, /* interpolate */
+                           kCGRenderingIntentDefault);
+      free_cgi_p = YES;
+      //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
+      CGDataProviderRelease (prov);
+    }
 
 
-    // create a CGImage from those bits
-
-    CGDataProviderRef prov =
-      CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
-                                    NULL);
-    cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
-                         bps, bpp, bpl,
-                         dpy->colorspace, 
-                         /* Use whatever default bit ordering we got from
-                            initWithFocusedViewRect.  I would have assumed
-                            that it was (kCGImageAlphaNoneSkipFirst |
-                            kCGBitmapByteOrder32Host), but on Intel,
-                            it's not!
-                          */
-                         0,
-                         prov, 
-                         NULL,  /* decode[] */
-                         NO, /* interpolate */
-                         kCGRenderingIntentDefault);
-    //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...
   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.)
 
     // 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);
                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);
 
 
     pop_gc (dst, gc);
 
@@ -697,20 +1103,41 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
     // being copied.
     //
     if (clipped) {
     // 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);
                  gc->gcv.alpha_allowed_p, YES);
-      CGContextFillRect (dst->cgc, orig_dst_rect);
+      CGContextFillRect (cgc, orig_dst_rect);
     }
 
     }
 
-    // copy the CGImage onto the destination CGContext
-    //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-    CGContextDrawImage (dst->cgc, dst_rect, cgi);
+    if (cgi) {
+      // copy the CGImage onto the destination CGContext
+      //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
+      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;
+      nsfrom.size.width  = src_rect.size.width;
+      nsfrom.size.height = src_rect.size.height;
+      NSPoint nsto;
+      nsto.x             = dst_rect.origin.x;
+      nsto.y             = dst_rect.origin.y;
+      NSCopyBits (0, nsfrom, nsto);
+# endif // !USE_BACKBUFFER
+    }
 
     pop_gc (dst, gc);
   }
 
     pop_gc (dst, gc);
   }
-  
-  CGImageRelease (cgi);
+
+  if (free_cgi_p) CGImageRelease (cgi);
+
   if (releaseme) [releaseme release];
   if (releaseme) [releaseme release];
+  invalidate_drawable_cache (dst);
   return 0;
 }
 
   return 0;
 }
 
@@ -751,14 +1178,16 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
 
   push_fg_gc (d, gc, NO);
 
 
   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;
   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);
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -770,7 +1199,10 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
   NSPoint p;
   CGRect wr = d->frame;
   push_fg_gc (d, gc, NO);
   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.
   
   // if the first and last points coincide, use closepath to get
   // the proper line-joining.
@@ -781,8 +1213,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++;
   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;
   for (i = 1; i < count; i++) {
     if (mode == CoordModePrevious) {
       p.x += points->x;
@@ -791,12 +1223,13 @@ 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;
     }
       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++;
   }
     points++;
   }
-  if (closed_p) CGContextClosePath (d->cgc);
-  CGContextStrokePath (d->cgc);
+  if (closed_p) CGContextClosePath (cgc);
+  CGContextStrokePath (cgc);
   pop_gc (d, gc);
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -807,20 +1240,23 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
   int i;
   CGRect wr = d->frame;
 
   int i;
   CGRect wr = d->frame;
 
+  CGContextRef cgc = d->cgc;
+
   push_fg_gc (d, gc, NO);
   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++) {
   for (i = 0; i < count; i++) {
-    CGContextMoveToPoint    (d->cgc, 
+    CGContextMoveToPoint    (cgc, 
                              wr.origin.x + segments->x1,
                              wr.origin.y + wr.size.height - segments->y1);
                              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++;
   }
                              wr.origin.x + segments->x2,
                              wr.origin.y + wr.size.height - segments->y2);
     segments++;
   }
-  CGContextStrokePath (d->cgc);
+  CGContextStrokePath (cgc);
   pop_gc (d, gc);
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -828,7 +1264,7 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
 int
 XClearWindow (Display *dpy, Window win)
 {
 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);
 }
   CGRect wr = win->frame;
   return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
 }
@@ -836,7 +1272,7 @@ XClearWindow (Display *dpy, Window win)
 int
 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
 {
 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;
   validate_pixel (pixel, 32, NO);
   w->window.background = pixel;
   return 0;
@@ -861,16 +1297,18 @@ draw_rect (Display *dpy, Drawable d, GC gc,
       push_bg_gc (d, gc, fill_p);
   }
 
       push_bg_gc (d, gc, fill_p);
   }
 
+  CGContextRef cgc = d->cgc;
   if (fill_p)
   if (fill_p)
-    CGContextFillRect (d->cgc, r);
+    CGContextFillRect (cgc, r);
   else {
     if (gc)
   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)
     pop_gc (d, gc);
   }
 
   if (gc)
     pop_gc (d, gc);
+  invalidate_drawable_cache (d);
 }
 
 
 }
 
 
@@ -895,6 +1333,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
 {
   CGRect wr = d->frame;
   int i;
 {
   CGRect wr = d->frame;
   int i;
+  CGContextRef cgc = d->cgc;
   push_fg_gc (d, gc, YES);
   for (i = 0; i < n; i++) {
     CGRect r;
   push_fg_gc (d, gc, YES);
   for (i = 0; i < n; i++) {
     CGRect r;
@@ -902,10 +1341,11 @@ 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;
     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);
     rects++;
   }
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -913,8 +1353,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)
 {
 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;
 }
   draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
   return 0;
 }
@@ -927,9 +1368,10 @@ XFillPolygon (Display *dpy, Drawable d, GC gc,
   CGRect wr = d->frame;
   int i;
   push_fg_gc (d, gc, YES);
   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++) {
   for (i = 0; i < npoints; i++) {
-    float x, y;
     if (i > 0 && mode == CoordModePrevious) {
       x += points[i].x;
       y -= points[i].y;
     if (i > 0 && mode == CoordModePrevious) {
       x += points[i].x;
       y -= points[i].y;
@@ -939,16 +1381,17 @@ XFillPolygon (Display *dpy, Drawable d, GC gc,
     }
         
     if (i == 0)
     }
         
     if (i == 0)
-      CGContextMoveToPoint (d->cgc, x, y);
+      CGContextMoveToPoint (cgc, x, y);
     else
     else
-      CGContextAddLineToPoint (d->cgc, x, y);
+      CGContextAddLineToPoint (cgc, x, y);
   }
   }
-  CGContextClosePath (d->cgc);
+  CGContextClosePath (cgc);
   if (gc->gcv.fill_rule == EvenOddRule)
   if (gc->gcv.fill_rule == EvenOddRule)
-    CGContextEOFillPath (d->cgc);
+    CGContextEOFillPath (cgc);
   else
   else
-    CGContextFillPath (d->cgc);
+    CGContextFillPath (cgc);
   pop_gc (d, gc);
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -978,28 +1421,30 @@ draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
   
   push_fg_gc (d, gc, fill_p);
 
   
   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)
   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)
 
   if (closed_p)
-    CGContextClosePath (d->cgc); // for proper line joining
+    CGContextClosePath (cgc); // for proper line joining
   
   if (fill_p) {
   
   if (fill_p) {
-    CGContextFillPath (d->cgc);
+    CGContextFillPath (cgc);
   } else {
   } else {
-    set_line_mode (d->cgc, &gc->gcv);
-    CGContextStrokePath (d->cgc);
+    set_line_mode (cgc, &gc->gcv);
+    CGContextStrokePath (cgc);
   }
 
   pop_gc (d, gc);
   }
 
   pop_gc (d, gc);
+  invalidate_drawable_cache (d);
   return 0;
 }
 
   return 0;
 }
 
@@ -1063,6 +1508,10 @@ gcv_defaults (XGCValues *gcv, int depth)
 static void
 set_gcv (GC gc, XGCValues *from, unsigned long mask)
 {
 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;
   if (mask & GCFunction)       gc->gcv.function        = from->function;
   if (mask & GCForeground)     gc->gcv.foreground      = from->foreground;
   if (mask & GCBackground)     gc->gcv.background      = from->background;
@@ -1082,17 +1531,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 & 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");
 }
 
 
 }
 
 
@@ -1139,7 +1589,7 @@ XFreeGC (Display *dpy, GC gc)
 Status
 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
 {
 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;
   memset (xgwa, 0, sizeof(*xgwa));
   xgwa->x      = w->frame.origin.x;
   xgwa->y      = w->frame.origin.y;
@@ -1172,7 +1622,9 @@ Status
 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
 {
   // store 32 bit ARGB in the pixel field.
 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
 {
   // store 32 bit ARGB in the pixel field.
-  color->pixel = ((                       0xFF  << 24) |
+  // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
+  color->pixel = (uint32_t)
+                 ((                       0xFF  << 24) |
                   (((color->red   >> 8) & 0xFF) << 16) |
                   (((color->green >> 8) & 0xFF) <<  8) |
                   (((color->blue  >> 8) & 0xFF)      ));
                   (((color->red   >> 8) & 0xFF) << 16) |
                   (((color->green >> 8) & 0xFF) <<  8) |
                   (((color->blue  >> 8) & 0xFF)      ));
@@ -1226,7 +1678,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")) {
     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")) {
   } else if (!strcasecmp(spec,"white")) {
     r = g = b = 255;
   } else if (!strcasecmp(spec,"red")) {
@@ -1306,17 +1758,18 @@ ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
 static unsigned long
 ximage_getpixel_32 (XImage *ximage, int x, int y)
 {
 static unsigned long
 ximage_getpixel_32 (XImage *ximage, int x, int y)
 {
-  return *((unsigned long *) ximage->data +
-           (y * (ximage->bytes_per_line >> 2)) +
-           x);
+  return ((unsigned long)
+          *((uint32_t *) ximage->data +
+            (y * (ximage->bytes_per_line >> 2)) +
+            x));
 }
 
 static int
 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
 {
 }
 
 static int
 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
 {
-  *((unsigned long *) ximage->data +
+  *((uint32_t *) ximage->data +
     (y * (ximage->bytes_per_line >> 2)) +
     (y * (ximage->bytes_per_line >> 2)) +
-    x) = pixel;
+    x) = (uint32_t) pixel;
   return 0;
 }
 
   return 0;
 }
 
@@ -1336,7 +1789,7 @@ XInitImage (XImage *ximage)
     ximage->f.put_pixel = ximage_putpixel_32;
     ximage->f.get_pixel = ximage_getpixel_32;
   } else {
     ximage->f.put_pixel = ximage_putpixel_32;
     ximage->f.get_pixel = ximage_getpixel_32;
   } else {
-    abort();
+    Assert (0, "unknown depth");
   }
   return 1;
 }
   }
   return 1;
 }
@@ -1354,7 +1807,7 @@ XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
   ximage->format = format;
   ximage->data = data;
   ximage->bitmap_unit = 8;
   ximage->format = format;
   ximage->data = data;
   ximage->bitmap_unit = 8;
-  ximage->byte_order = MSBFirst;
+  ximage->byte_order = LSBFirst;
   ximage->bitmap_bit_order = ximage->byte_order;
   ximage->bitmap_pad = bitmap_pad;
   ximage->depth = depth;
   ximage->bitmap_bit_order = ximage->byte_order;
   ximage->bitmap_pad = bitmap_pad;
   ximage->depth = depth;
@@ -1467,7 +1920,7 @@ flipbits (unsigned const char *in, unsigned char *out, int length)
     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
   };
     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
   };
-  while (--length > 0)
+  while (length-- > 0)
     *out++ = table[*in++];
 }
 
     *out++ = table[*in++];
 }
 
@@ -1479,6 +1932,14 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
 {
   CGRect wr = d->frame;
 
 {
   CGRect wr = d->frame;
 
+  Assert (gc, "no GC");
+  Assert ((w < 65535), "improbably large width");
+  Assert ((h < 65535), "improbably large height");
+  Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
+  Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
+  Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
+  Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
+
   // Clip width and height to the bounds of the Drawable
   //
   if (dest_x + w > wr.size.width) {
   // Clip width and height to the bounds of the Drawable
   //
   if (dest_x + w > wr.size.width) {
@@ -1509,11 +1970,13 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
   if (w <= 0 || h <= 0)
     return 0;
 
   if (w <= 0 || h <= 0)
     return 0;
 
-  if (gc && (gc->gcv.function == GXset ||
-             gc->gcv.function == GXclear)) {
+  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" 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);
                         ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
                         : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
                gc->depth, gc->gcv.alpha_allowed_p, YES);
@@ -1554,7 +2017,7 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
                                     kCGRenderingIntentDefault);
     CGDataProviderRelease (prov);
     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
                                     kCGRenderingIntentDefault);
     CGDataProviderRelease (prov);
     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-    CGContextDrawImage (d->cgc, r, cgi);
+    CGContextDrawImage (cgc, r, cgi);
     CGImageRelease (cgi);
 
   } else {   // (bpp == 1)
     CGImageRelease (cgi);
 
   } else {   // (bpp == 1)
@@ -1584,10 +2047,10 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
                                          NO); /* interpolate */
     push_fg_gc (d, gc, YES);
 
                                          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);
     pop_gc (d, gc);
 
     free (flipped);
@@ -1595,6 +2058,8 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
     CGImageRelease (mask);
   }
 
     CGImageRelease (mask);
   }
 
+  invalidate_drawable_cache (d);
+
   return 0;
 }
 
   return 0;
 }
 
@@ -1605,36 +2070,60 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
            unsigned long plane_mask, int format)
 {
   const unsigned char *data = 0;
            unsigned long plane_mask, int format)
 {
   const unsigned char *data = 0;
-  int depth, ibpp, ibpl;
+  size_t depth, ibpp, ibpl;
+  enum { RGBA, ARGB, BGRA } src_format; // As bytes.
+# ifndef USE_BACKBUFFER
   NSBitmapImageRep *bm = 0;
   NSBitmapImageRep *bm = 0;
+# endif
   
   
-  if (d->type == PIXMAP) {
-    depth = d->pixmap.depth;
-    ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
-    ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
-    data = CGBitmapContextGetData (d->cgc);
+  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");
+
+  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");
     Assert (data, "CGBitmapContextGetData failed");
-  } else {
+
+# ifndef USE_BACKBUFFER
+  } else { /* (d->type == WINDOW) */
+
     // get the bits (desired sub-rectangle) out of the NSView
     // get the bits (desired sub-rectangle) out of the NSView
-    bm = [NSBitmapImageRep alloc];
     NSRect nsfrom;
     nsfrom.origin.x = x;
     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;
     nsfrom.size.width = width;
     nsfrom.size.height = height;
-    [bm initWithFocusedViewRect:nsfrom];
+    bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
     depth = 32;
     depth = 32;
+    src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
     ibpp = [bm bitsPerPixel];
     ibpl = [bm bytesPerRow];
     data = [bm bitmapData];
     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
     ibpp = [bm bitsPerPixel];
     ibpl = [bm bytesPerRow];
     data = [bm bitmapData];
     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
-
-    data += (y * ibpl) + (x * (ibpp/8));
+# endif // !USE_BACKBUFFER
   }
   
   }
   
+  // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
+  data += (y * ibpl) + (x * (ibpp/8));
+  
   format = (depth == 1 ? XYPixmap : ZPixmap);
   format = (depth == 1 ? XYPixmap : ZPixmap);
-  XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
-                                0, 0);
+  XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
+                                format, 0, 0, width, height, 0, 0);
   image->data = (char *) malloc (height * image->bytes_per_line);
   
   int obpl = image->bytes_per_line;
   image->data = (char *) malloc (height * image->bytes_per_line);
   
   int obpl = image->bytes_per_line;
@@ -1642,19 +2131,23 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
   /* both PPC and Intel use word-ordered ARGB frame buffers, which
      means that on Intel it is BGRA when viewed by bytes (And BGR
      when using 24bpp packing).
   /* both PPC and Intel use word-ordered ARGB frame buffers, which
      means that on Intel it is BGRA when viewed by bytes (And BGR
      when using 24bpp packing).
+
+     BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
+     The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
+     indicator of this latest kink.
    */
   int xx, yy;
   if (depth == 1) {
     const unsigned char *iline = data;
    */
   int xx, yy;
   if (depth == 1) {
     const unsigned char *iline = data;
-    for (yy = y; yy < y+height; yy++) {
+    for (yy = 0; yy < height; yy++) {
 
       const unsigned char *iline2 = iline;
 
       const unsigned char *iline2 = iline;
-      for (xx = x; xx < x+width; xx++) {
+      for (xx = 0; xx < width; xx++) {
 
 
-        iline2++;                     // ignore b or a
-        iline2++;                     // ignore g or r
-        unsigned char r = *iline2++;  //        r or g
-        if (ibpp == 32) iline2++;     // ignore a or b
+        iline2++;                     // ignore R  or  A  or  A  or  B
+        iline2++;                     // ignore G  or  B  or  R  or  G
+        unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
+        if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
 
         XPutPixel (image, xx, yy, (r ? 1 : 0));
       }
 
         XPutPixel (image, xx, yy, (r ? 1 : 0));
       }
@@ -1664,76 +2157,214 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
     Assert (ibpp == 24 || ibpp == 32, "weird obpp");
     const unsigned char *iline = data;
     unsigned char *oline = (unsigned char *) image->data;
     Assert (ibpp == 24 || ibpp == 32, "weird obpp");
     const unsigned char *iline = data;
     unsigned char *oline = (unsigned char *) image->data;
-    oline += (y * obpl);
-    for (yy = y; yy < y+height; yy++) {
+    for (yy = 0; yy < height; yy++) {
 
       const unsigned char *iline2 = iline;
       unsigned char *oline2 = oline;
 
       const unsigned char *iline2 = iline;
       unsigned char *oline2 = oline;
-      for (xx = x; xx < x+width; xx++) {
-
-        unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
-        unsigned char r = *iline2++;
-        unsigned char g = *iline2++;
-        unsigned char b = *iline2++;
-        unsigned long pixel = ((a << 24) |
-                               (r << 16) |
-                               (g <<  8) |
-                               (b <<  0));
-        *((unsigned int *) oline2) = pixel;
-        oline2 += 4;
-     }
+
+      switch (src_format) {
+      case ARGB:
+        for (xx = 0; xx < width; xx++) {
+          unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
+          unsigned char r = *iline2++;
+          unsigned char g = *iline2++;
+          unsigned char b = *iline2++;
+          uint32_t pixel = ((a << 24) |
+                            (r << 16) |
+                            (g <<  8) |
+                            (b <<  0));
+          *((uint32_t *) oline2) = pixel;
+          oline2 += 4;
+        }
+        break;
+      case RGBA:
+        for (xx = 0; xx < width; xx++) {
+          unsigned char r = *iline2++;
+          unsigned char g = *iline2++;
+          unsigned char b = *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;
+      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;
     }
   }
 
       oline += obpl;
       iline += ibpl;
     }
   }
 
+# ifndef USE_BACKBUFFER
   if (bm) [bm release];
   if (bm) [bm release];
+# endif
 
   return image;
 }
 
 
   return image;
 }
 
+
+
+/* Returns a transformation matrix to do rotation as per the provided
+   EXIF "Orientation" value.
+ */
+static CGAffineTransform
+exif_rotate (int rot, CGSize rect)
+{
+  CGAffineTransform trans = CGAffineTransformIdentity;
+  switch (rot) {
+  case 2:              // flip horizontal
+    trans = CGAffineTransformMakeTranslation (rect.width, 0);
+    trans = CGAffineTransformScale (trans, -1, 1);
+    break;
+
+  case 3:              // rotate 180
+    trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
+    trans = CGAffineTransformRotate (trans, M_PI);
+    break;
+
+  case 4:              // flip vertical
+    trans = CGAffineTransformMakeTranslation (0, rect.height);
+    trans = CGAffineTransformScale (trans, 1, -1);
+    break;
+
+  case 5:              // transpose (UL-to-LR axis)
+    trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
+    trans = CGAffineTransformScale (trans, -1, 1);
+    trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
+    break;
+
+  case 6:              // rotate 90
+    trans = CGAffineTransformMakeTranslation (0, rect.width);
+    trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
+    break;
+
+  case 7:              // transverse (UR-to-LL axis)
+    trans = CGAffineTransformMakeScale (-1, 1);
+    trans = CGAffineTransformRotate (trans, M_PI / 2);
+    break;
+
+  case 8:              // rotate 270
+    trans = CGAffineTransformMakeTranslation (rect.height, 0);
+    trans = CGAffineTransformRotate (trans, M_PI / 2);
+    break;
+
+  default: 
+    break;
+  }
+
+  return trans;
+}
+
+
 void
 void
-jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
-                    XRectangle *geom_ret)
+jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
+                                Bool nsimg_p, void *img_arg,
+                               XRectangle *geom_ret, int exif_rotation)
 {
 {
-  NSImage *nsimg = (NSImage *) nsimg_arg;
+  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...
+    //
+    NSData *nsdata = [NSBitmapImageRep
+                       TIFFRepresentationOfImageRepsInArray:
+                         [nsimg representations]];
+    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;
+    imgr.width  = CGImageGetWidth (cgi);
+    imgr.height = CGImageGetHeight (cgi);
+  }
+
+  Bool rot_p = (exif_rotation >= 5);
+
+  if (rot_p)
+    imgr = NSMakeSize (imgr.height, imgr.width);
 
 
-  // convert the NSImage to a CGImage via the toll-free-bridging 
-  // of NSData and CFData...
-  //
-  NSData *nsdata = [NSBitmapImageRep
-                        TIFFRepresentationOfImageRepsInArray:
-                          [nsimg representations]];
-  CFDataRef cfdata = (CFDataRef) nsdata;
-  CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
-  CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
-
-  NSSize imgr = [nsimg size];
   CGRect winr = d->frame;
   float rw = winr.size.width  / imgr.width;
   float rh = winr.size.height / imgr.height;
   float r = (rw < rh ? rw : rh);
 
   CGRect winr = d->frame;
   float rw = winr.size.width  / imgr.width;
   float rh = winr.size.height / imgr.height;
   float r = (rw < rh ? rw : rh);
 
-  CGRect dst;
+  CGRect dst, dst2;
   dst.size.width  = imgr.width  * r;
   dst.size.height = imgr.height * r;
   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
   dst.origin.y = (winr.size.height - dst.size.height) / 2;
 
   dst.size.width  = imgr.width  * r;
   dst.size.height = imgr.height * r;
   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
   dst.origin.y = (winr.size.height - dst.size.height) / 2;
 
+  dst2.origin.x = dst2.origin.y = 0;
+  if (rot_p) {
+    dst2.size.width = dst.size.height; 
+    dst2.size.height = dst.size.width;
+  } else {
+    dst2.size = dst.size;
+  }
+
   // Clear the part not covered by the image to background or black.
   //
   if (d->type == WINDOW)
     XClearWindow (dpy, d);
   else {
   // Clear the part not covered by the image to background or black.
   //
   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);
   }
 
     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 (cgc);
+  CGContextConcatCTM (cgc, 
+                      CGAffineTransformMakeTranslation (dst.origin.x,
+                                                        dst.origin.y));
+  CGContextConcatCTM (cgc, trans);
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-  CGContextDrawImage (d->cgc, dst, cgi);
+  CGContextDrawImage (cgc, dst2, cgi);
+  CGContextRestoreGState (cgc);
 
 
-  CFRelease (cgsrc);
-  CGImageRelease (cgi);
+# ifndef USE_IPHONE
+  if (nsimg_p) {
+    CFRelease (cgsrc);
+    CGImageRelease (cgi);
+  }
+# endif // USE_IPHONE
 
   if (geom_ret) {
     geom_ret->x = dst.origin.x;
 
   if (geom_ret) {
     geom_ret->x = dst.origin.x;
@@ -1741,9 +2372,12 @@ jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
     geom_ret->width  = dst.size.width;
     geom_ret->height = dst.size.height;
   }
     geom_ret->width  = dst.size.width;
     geom_ret->height = dst.size.height;
   }
+
+  invalidate_drawable_cache (d);
 }
 
 
 }
 
 
+
 Pixmap
 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
                              const char *data,
 Pixmap
 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
                              const char *data,
@@ -1777,9 +2411,10 @@ XCreatePixmap (Display *dpy, Drawable d,
   p->frame.size.width  = width;
   p->frame.size.height = height;
   p->pixmap.depth      = depth;
   p->frame.size.width  = width;
   p->frame.size.height = height;
   p->pixmap.depth      = depth;
+  p->pixmap.cgc_buffer = data;
   
   /* Quartz doesn't have a 1bpp image type.
   
   /* Quartz doesn't have a 1bpp image type.
-     We used to use 8bpp gray images instead of 1bpp, but some Mac video
+     Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
      don't support that!  So we always use 32bpp, regardless of depth. */
 
   p->cgc = CGBitmapContextCreate (data, width, height,
      don't support that!  So we always use 32bpp, regardless of depth. */
 
   p->cgc = CGBitmapContextCreate (data, width, height,
@@ -1787,7 +2422,8 @@ XCreatePixmap (Display *dpy, Drawable d,
                                   width * 4, /* bpl */
                                   dpy->colorspace,
                                   // Without this, it returns 0...
                                   width * 4, /* bpl */
                                   dpy->colorspace,
                                   // Without this, it returns 0...
-                                  kCGImageAlphaNoneSkipFirst
+                                  (kCGImageAlphaNoneSkipFirst |
+                                   kCGBitmapByteOrder32Host)
                                   );
   Assert (p->cgc, "could not create CGBitmapContext");
   return p;
                                   );
   Assert (p->cgc, "could not create CGBitmapContext");
   return p;
@@ -1797,21 +2433,43 @@ XCreatePixmap (Display *dpy, Drawable d,
 int
 XFreePixmap (Display *d, Pixmap p)
 {
 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);
   CGContextRelease (p->cgc);
+  if (p->pixmap.cgc_buffer)
+    free (p->pixmap.cgc_buffer);
   free (p);
   return 0;
 }
 
 
 static Pixmap
   free (p);
   return 0;
 }
 
 
 static Pixmap
-copy_pixmap (Pixmap p)
+copy_pixmap (Display *dpy, Pixmap p)
 {
   if (!p) return 0;
   Assert (p->type == PIXMAP, "not a pixmap");
 {
   if (!p) return 0;
   Assert (p->type == PIXMAP, "not a pixmap");
+
+  int width  = p->frame.size.width;
+  int height = p->frame.size.height;
+  char *data = (char *) malloc (width * height * 4);
+  if (! data) return 0;
+
+  memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
+
   Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
   *p2 = *p;
   Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
   *p2 = *p;
-  CGContextRetain (p2->cgc);   // #### is this ok? need to copy it instead?
+  p2->cgi = 0;
+  p2->pixmap.cgc_buffer = data;
+  p2->cgc = CGBitmapContextCreate (data, width, height,
+                                   8, /* bits per component */
+                                   width * 4, /* bpl */
+                                   dpy->colorspace,
+                                   // Without this, it returns 0...
+                                   (kCGImageAlphaNoneSkipFirst |
+                                    kCGBitmapByteOrder32Host)
+                                   );
+  Assert (p2->cgc, "could not create CGBitmapContext");
+
   return p2;
 }
 
   return p2;
 }
 
@@ -1824,7 +2482,8 @@ copy_pixmap (Pixmap p)
    "rbearing" is the distance from the logical origin to the rightmost pixel.
 
    "descent" is the distance from the logical origin to the bottommost pixel.
    "rbearing" is the distance from the logical origin to the rightmost pixel.
 
    "descent" is the distance from the logical origin to the bottommost pixel.
-   For characters with descenders, it is negative.
+   For characters with descenders, it is positive.  For superscripts, it
+   is negative.
 
    "ascent" is the distance from the logical origin to the topmost pixel.
    It is the number of pixels above the baseline.
 
    "ascent" is the distance from the logical origin to the topmost pixel.
    It is the number of pixels above the baseline.
@@ -1837,18 +2496,58 @@ copy_pixmap (Pixmap p)
  */
 
 
  */
 
 
+static void
+glyph_metrics (CTFontRef ctfont, CGGlyph cgglyph, XCharStruct *cs)
+{
+  CGRect bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
+                                                 kCTFontDefaultOrientation,
+                                                 &cgglyph, NULL, 1);
+  CGSize advancement;
+  CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
+                              &cgglyph, &advancement, 1);
+
+# ifndef USE_IPHONE
+  // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
+  bbox.origin.x    -= 2.0/3.0;
+  bbox.size.width  += 4.0/3.0;
+  bbox.size.height += 1.0/2.0;
+# endif
+
+//#define CEIL(F)  ((F) < 0 ? floor(F) : ceil(F))
+//#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
+#define CEIL(F)  ceil(F)
+#define FLOOR(F) floor(F)
+
+  /* Now that we know the advancement and bounding box, we can compute
+     the lbearing and rbearing.
+   */
+//cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
+  cs->ascent   = CEIL (bbox.origin.y + bbox.size.height);
+  cs->descent  = CEIL(-bbox.origin.y);
+  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->width    = FLOOR (advancement.width + 0.5);
+
+  //  Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
+  //          "bbox w wrong");
+  //  Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
+  //          "bbox h wrong");
+}
+
+
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
 //
 static void
 query_font (Font fid)
 {
   if (!fid || !fid->nsfont) {
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
 //
 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]) {
   }
   if (![fid->nsfont fontName]) {
-    NSLog(@"broken NSFont in fid");
-    abort();
+    Assert(0, @"broken NSFont in fid");
+    return;
   }
 
   int first = 32;
   }
 
   int first = 32;
@@ -1858,14 +2557,12 @@ query_font (Font fid)
   XCharStruct *min = &f->min_bounds;
   XCharStruct *max = &f->max_bounds;
 
   XCharStruct *min = &f->min_bounds;
   XCharStruct *max = &f->max_bounds;
 
-#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
-
   f->fid               = fid;
   f->min_char_or_byte2 = first;
   f->max_char_or_byte2 = last;
   f->default_char      = 'M';
   f->ascent            =  CEIL ([fid->nsfont ascender]);
   f->fid               = fid;
   f->min_char_or_byte2 = first;
   f->max_char_or_byte2 = last;
   f->default_char      = 'M';
   f->ascent            =  CEIL ([fid->nsfont ascender]);
-  f->descent           = -CEIL ([fid->nsfont descender]);
+  f->descent           = -FLOOR ([fid->nsfont descender]);
 
   min->width    = 255;  // set to smaller values in the loop
   min->ascent   = 255;
 
   min->width    = 255;  // set to smaller values in the loop
   min->ascent   = 255;
@@ -1874,70 +2571,48 @@ query_font (Font fid)
   min->rbearing = 255;
 
   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
   min->rbearing = 255;
 
   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
-  int i;
+  UniChar i;
 
 
-  NSBezierPath *bpath = [NSBezierPath bezierPath];
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
 
   for (i = first; i <= last; i++) {
 
   for (i = first; i <= last; i++) {
-    unsigned char str[2];
-    str[0] = i;
-    str[1] = 0;
-
-    NSString *nsstr = [NSString stringWithCString:(char *) str
-                                         encoding:NSISOLatin1StringEncoding];
+    XCharStruct *cs = &f->per_char[i-first];
 
 
-    /* I can't believe we have to go through this bullshit just to
-       convert a 'char' to an NSGlyph!!
+    /* 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.
 
 
-       You might think that we could do
-          NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
-       but that doesn't work; my guess is that glyphWithName expects
-       full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
+       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.
      */
      */
-    NSGlyph glyph;
-    {
-      NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
-      [ts setFont:fid->nsfont];
-      NSLayoutManager *lm = [[NSLayoutManager alloc] init];
-      NSTextContainer *tc = [[NSTextContainer alloc] init];
-      [lm addTextContainer:tc];
-      [tc release];    // lm retains tc
-      [ts addLayoutManager:lm];
-      [lm release];    // ts retains lm
-      glyph = [lm glyphAtIndex:0];
-      [ts release];
-    }
+    CGGlyph cgglyph = 0;
 
 
-    /* Compute the bounding box and advancement by converting the glyph
-       to a bezier path.  There appears to be *no other way* to find out
-       the bounding box of a character: [NSFont boundingRectForGlyph] and
-       [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];
-    [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
-    advancement = [bpath currentPoint];
-    bbox = [bpath bounds];
-
-    /* Now that we know the advancement and bounding box, we can compute
-       the lbearing and rbearing.
-     */
-    XCharStruct *cs = &f->per_char[i-first];
-
-    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->width    = CEIL (advancement.x);
-
-    Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
-            "bbox w wrong");
-    Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
-            "bbox h wrong");
+    if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
+      glyph_metrics (ctfont, cgglyph, cs);
+    else
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (cs, 0, sizeof(*cs));
+
+# if 0
+    if (i == 224) {  // Latin1 == "agrave", MacRoman == "daggerdouble".
+      NSString *glyph_name = (NSString *)
+        CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+                                     cgglyph);
+      Assert ([glyph_name isEqualToString:@"agrave"], @"wrong encoding");
+    }
+    if (i == 250) {  // Latin1 == "uacute", MacRoman == "dotaccent".
+      NSString *glyph_name = (NSString *)
+        CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+                                     cgglyph);
+      Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
+    }
+# endif // 0
 
     max->width    = MAX (max->width,    cs->width);
     max->ascent   = MAX (max->ascent,   cs->ascent);
 
     max->width    = MAX (max->width,    cs->width);
     max->ascent   = MAX (max->ascent,   cs->ascent);
@@ -1952,18 +2627,20 @@ query_font (Font fid)
     min->rbearing = MIN (min->rbearing, cs->rbearing);
 
 # undef CEIL
     min->rbearing = MIN (min->rbearing, cs->rbearing);
 
 # undef CEIL
+# undef FLOOR
 
 
-#if 0
+# if 0
     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
     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,
             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);
-#endif
+            bbox.size.width, bbox.size.height,
+            bbox.origin.x, bbox.origin.y,
+            advancement.width, advancement.height);
+# endif // 0
   }
 
   }
 
+  CFRelease (ctfont);
 }
 
 
 }
 
 
@@ -1977,7 +2654,7 @@ XQueryFont (Display *dpy, Font fid)
   *f = fid->metrics;
 
   // copy XCharStruct array
   *f = fid->metrics;
 
   // copy XCharStruct array
-  int size = f->max_char_or_byte2 - f->min_char_or_byte2;
+  int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
   memcpy (f->per_char, fid->metrics.per_char,
           size * sizeof (XCharStruct));
   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
   memcpy (f->per_char, fid->metrics.per_char,
           size * sizeof (XCharStruct));
@@ -2014,36 +2691,62 @@ try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
           char **name_ret)
 {
   Assert (size > 0, "zero font size");
           char **name_ret)
 {
   Assert (size > 0, "zero font size");
-  const char *prefix = (fixed ? "Monaco" : (serif ? "Times" : "Helvetica"));
-  const char *suffix = (bold && ital
-                        ? (serif ? "-BoldItalic" : "-BoldOblique")
-                        : (bold ? "-Bold" :
-                           ital ? (serif ? "-Italic" : "-Oblique") : ""));
-  char *name = (char *) malloc (strlen(prefix) + strlen(suffix) + 1);
-  strcpy (name, prefix);
-  strcat (name, suffix);
+  const char *name;
+
+  if (fixed) {
+    // 
+    // "Monaco" only exists in plain.
+    // "LucidaSansTypewriterStd" gets an AGL bad value error.
+    // 
+    if (bold && ital) name = "Courier-BoldOblique";
+    else if (bold)    name = "Courier-Bold";
+    else if (ital)    name = "Courier-Oblique";
+    else              name = "Courier";
+
+  } else if (serif) {
+    // 
+    // "Georgia" looks better than "Times".
+    // 
+    if (bold && ital) name = "Georgia-BoldItalic";
+    else if (bold)    name = "Georgia-Bold";
+    else if (ital)    name = "Georgia-Italic";
+    else              name = "Georgia";
+
+  } else {
+    // 
+    // "Geneva" only exists in plain.
+    // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
+    // "Verdana" renders smoother than "Helvetica" for some reason.
+    // 
+    if (bold && ital) name = "Verdana-BoldItalic";
+    else if (bold)    name = "Verdana-Bold";
+    else if (ital)    name = "Verdana-Italic";
+    else              name = "Verdana";
+  }
 
   NSString *nsname = [NSString stringWithCString:name
                                         encoding:NSUTF8StringEncoding];
   NSFont *f = [NSFont fontWithName:nsname size:size];
 
   NSString *nsname = [NSString stringWithCString:name
                                         encoding:NSUTF8StringEncoding];
   NSFont *f = [NSFont fontWithName:nsname size:size];
-  if (f) {
-    *name_ret = name;
-  } else {
-    free (name);
-  }
+  if (f)
+    *name_ret = strdup(name);
   return f;
 }
 
 static NSFont *
   return f;
 }
 
 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;
 {
   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;
 
   if (size <= 4) return 0;
 
+  size *= scale;
+
   char *name2 = strdup (name);
   name2[strlen(name2) - strlen(spc)] = 0;
   NSString *nsname = [NSString stringWithCString:name2
   char *name2 = strdup (name);
   name2[strlen(name2) - strlen(spc)] = 0;
   NSString *nsname = [NSString stringWithCString:name2
@@ -2065,8 +2768,9 @@ 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)
 {
 static NSFont *
 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 {
-  NSFontTraitMask mask = ((bold ? NSUnboldFontMask   : NSBoldFontMask) |
-                          (ital ? NSUnitalicFontMask : NSItalicFontMask));
+# ifndef USE_IPHONE
+  NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
+                          (ital ? NSItalicFontMask : NSUnitalicFontMask));
   NSArray *fonts = [[NSFontManager sharedFontManager]
                      availableFontNamesWithTraits:mask];
   if (!fonts) return 0;
   NSArray *fonts = [[NSFontManager sharedFontManager]
                      availableFontNamesWithTraits:mask];
   if (!fonts) return 0;
@@ -2103,11 +2807,70 @@ random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 
   // None of the fonts support ASCII?
   return 0;
 
   // 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 *
 }
 
 
 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;
 {
   NSFont *nsfont = 0;
   BOOL bold  = NO;
@@ -2126,7 +2889,7 @@ try_xlfd_font (const char *name, char **name_ret, float *size_ret)
     while (*s2 && (*s2 != '*' && *s2 != '-'))
       s2++;
     
     while (*s2 && (*s2 != '*' && *s2 != '-'))
       s2++;
     
-    int L = s2-s;
+    unsigned long L = s2-s;
     if (s == s2)
       ;
 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
     if (s == s2)
       ;
 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
@@ -2158,6 +2921,8 @@ try_xlfd_font (const char *name, char **name_ret, float *size_ret)
   if (size < 6 || size > 1000)
     size = 12;
 
   if (size < 6 || size > 1000)
     size = 12;
 
+  size *= scale;
+
   if (rand)
     nsfont   = random_font (bold, ital, size, &ps_name);
 
   if (rand)
     nsfont   = random_font (bold, ital, size, &ps_name);
 
@@ -2199,53 +2964,47 @@ XLoadFont (Display *dpy, const char *name)
 {
   Font fid = (Font) calloc (1, sizeof(*fid));
 
 {
   Font fid = (Font) calloc (1, sizeof(*fid));
 
-  fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
-  if (! fid->nsfont)
-    fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
-  if (!fid->nsfont) {
-    NSLog(@"no NSFont for \"%s\"", name);
-    abort();
-  }
-
-  //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
+  float scale = 1;
 
 
-  query_font (fid);
+# ifdef USE_IPHONE
+  /* Since iOS screens are physically smaller than desktop screens, scale up
+     the fonts to make them more readable.
 
 
-  return fid;
-}
+     Note that X11 apps on iOS also have the backbuffer sized in points
+     instead of pixels, resulting in an effective X11 screen size of 768x1024
+     or so, even if the display has significantly higher resolution.  That is
+     unrelated to this hack, which is really about DPI.
+   */
+  scale = 2;
+# endif
 
 
+  fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
 
 
-/* This translates the NSFont into the numbers that aglUseFont() wants.
- */
-int
-jwxyz_font_info (Font f, int *size_ret, int *face_ret)
-{
-  char *name = strdup (f->ps_name);
-  char *dash = strchr (name, '-');
-  int flags = 0;
-  int size = f->size;
-  if (dash) {
-    // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
-    if (strcasestr (dash, "bold"))    flags |= 1;
-    if (strcasestr (dash, "italic"))  flags |= 2;
-    if (strcasestr (dash, "oblique")) flags |= 2;
-    *dash = 0;
+  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;
   }
   }
-  NSString *nname = [NSString stringWithCString:name
-                                       encoding:NSUTF8StringEncoding];
-  ATSFontFamilyRef id =
-    ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
 
 
+  if (! fid->nsfont)
+    fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
 
 
-  // WTF?  aglUseFont gets a BadValue if size is small!!
-  if (size < 9) size = 9;
+  // We should never return NULL for XLFD fonts.
+  if (!fid->nsfont) {
+    Assert (0, "no font");
+    return 0;
+  }
+  CFRetain (fid->nsfont);   // needed for garbage collection?
 
 
-  //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
-  Assert (id >= 0, "no ATS font family");
+  //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
+
+  query_font (fid);
 
 
-  *size_ret = size;
-  *face_ret = flags;
-  return id;
+  return fid;
 }
 
 
 }
 
 
@@ -2253,14 +3012,17 @@ XFontStruct *
 XLoadQueryFont (Display *dpy, const char *name)
 {
   Font fid = XLoadFont (dpy, name);
 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)
 {
   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
 
   // #### 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
@@ -2268,6 +3030,7 @@ XUnloadFont (Display *dpy, Font fid)
   //      They're probably not very big...
   //
   //  [fid->nsfont release];
   //      They're probably not very big...
   //
   //  [fid->nsfont release];
+  //  CFRelease (fid->nsfont);
 
   free (fid);
   return 0;
 
   free (fid);
   return 0;
@@ -2308,17 +3071,61 @@ XSetFont (Display *dpy, GC gc, Font fid)
     XUnloadFont (dpy, gc->gcv.font);
   gc->gcv.font = copy_font (fid);
   [gc->gcv.font->nsfont retain];
     XUnloadFont (dpy, gc->gcv.font);
   gc->gcv.font = copy_font (fid);
   [gc->gcv.font->nsfont retain];
+  CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
   return 0;
 }
 
   return 0;
 }
 
+
+XFontSet
+XCreateFontSet (Display *dpy, char *name, 
+                char ***missing_charset_list_return,
+                int *missing_charset_count_return,
+                char **def_string_return)
+{
+  char *name2 = strdup (name);
+  char *s = strchr (name, ",");
+  if (s) *s = 0;
+  XFontSet set = 0;
+  Font f = XLoadFont (dpy, name2);
+  if (f)
+    {
+      set = (XFontSet) calloc (1, sizeof(*set));
+      set->font = f;
+    }
+  free (name2);
+  if (missing_charset_list_return)  *missing_charset_list_return = 0;
+  if (missing_charset_count_return) *missing_charset_count_return = 0;
+  if (def_string_return) *def_string_return = 0;
+  return set;
+}
+
+
+void
+XFreeFontSet (Display *dpy, XFontSet set)
+{
+  XUnloadFont (dpy, set->font);
+  free (set);
+}
+
+
+void
+XFreeStringList (char **list)
+{
+  int i;
+  if (!list) return;
+  for (i = 0; list[i]; i++)
+    XFree (list[i]);
+  XFree (list);
+}
+
+
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
               XCharStruct *cs)
 {
   memset (cs, 0, sizeof(*cs));
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
               XCharStruct *cs)
 {
   memset (cs, 0, sizeof(*cs));
-  int i;
-  for (i = 0; i < length; i++) {
+  for (int i = 0; i < length; i++) {
     unsigned char c = (unsigned char) s[i];
     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
       c = f->default_char;
     unsigned char c = (unsigned char) s[i];
     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
       c = f->default_char;
@@ -2349,62 +3156,121 @@ XTextWidth (XFontStruct *f, const char *s, int length)
 }
 
 
 }
 
 
-static void
-set_font (CGContextRef cgc, GC gc)
+int
+XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
+                int *dir_ret, int *ascent_ret, int *descent_ret,
+                XCharStruct *cs)
 {
 {
-  Font font = gc->gcv.font;
-  if (! font) {
-    font = XLoadFont (0, 0);
-    gc->gcv.font = font;
-    [gc->gcv.font->nsfont retain];
+  Font fid = f->fid;
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+
+  int utf8_len = 0;
+  char *utf8 = XChar2b_to_utf8 (s, &utf8_len);
+  NSString *nsstr = [NSString stringWithCString:utf8
+                                       encoding:NSUTF8StringEncoding];
+  NSUInteger L = [nsstr length];
+
+  for (int i = 0; i < L; i++) {
+    unichar c = [nsstr characterAtIndex:i];
+    XCharStruct cc;
+    CGGlyph cgglyph = 0;
+
+    if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
+      glyph_metrics (ctfont, cgglyph, &cc);
+    else
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (&cc, 0, sizeof(cc));
+
+    if (i == 0) {
+      *cs = cc;
+    } else {
+      cs->ascent   = MAX (cs->ascent,   cc.ascent);
+      cs->descent  = MAX (cs->descent,  cc.descent);
+      cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
+      cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
+      cs->width   += cc.width;
+    }
   }
   }
-  CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
-  CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+  *dir_ret = 0;
+  *ascent_ret  = f->ascent;
+  *descent_ret = f->descent;
+
+  free (utf8);
+  CFRelease (ctfont);
+
+  return 0;
 }
 
 
 }
 
 
-static int
-draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
-             const char  *str, int len, BOOL clear_background_p)
-{
-  if (clear_background_p) {
-    int ascent, descent, dir;
+int
+Xutf8TextExtents (XFontSet set, const char *str, int num_bytes,
+                  XRectangle *overall_ink_return,
+                  XRectangle *overall_logical_return)
+{
+  Font fid = set->font;
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+
+  NSString *nsstr = [NSString stringWithCString:str
+                                       encoding:NSUTF8StringEncoding];
+  NSUInteger L = [nsstr length];
+
+  XRectangle ink = { 0, };
+  XRectangle logical = { 0, };
+
+  logical.height = fid->metrics.ascent;
+
+  for (int i = 0; i < L; i++) {
+    unichar c = [nsstr characterAtIndex:i];
     XCharStruct cs;
     XCharStruct cs;
-    XTextExtents (&gc->gcv.font->metrics, str, len,
-                  &dir, &ascent, &descent, &cs);
-    draw_rect (dpy, d, gc,
-               x + MIN (0, cs.lbearing),
-               y - MAX (0, ascent),
-               MAX (MAX (0, cs.rbearing) -
-                    MIN (0, cs.lbearing),
-                    cs.width),
-               MAX (0, ascent) + MAX (0, descent),
-               NO, YES);
-  }
+    CGGlyph cgglyph = 0;
 
 
-  CGRect wr = d->frame;
+    if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
+      glyph_metrics (ctfont, cgglyph, &cs);
+    else
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (&cs, 0, sizeof(cs));
 
 
-# if 1
-  /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
-     But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
-   */
+    logical.width += cs.width;
 
 
-  push_fg_gc (d, gc, YES);
-  set_font (d->cgc, gc);
+    ink.height = MAX(ink.height, cs.ascent);
+    ink.y      = MIN(ink.y,      cs.descent);
 
 
-  CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
-  if (! gc->gcv.antialias_p)
-    CGContextSetShouldAntialias (d->cgc, YES);  // always antialias text
-  CGContextShowTextAtPoint (d->cgc,
-                            wr.origin.x + x,
-                            wr.origin.y + wr.size.height - y,
-                            str, len);
-  pop_gc (d, gc);
+    if (i == 0)
+      ink.x = cs.lbearing;
+
+    if (i == L-1) {
+      ink.width += cs.rbearing;
+    } else {
+      ink.width += cs.width;
+    }
+  }
+    
+  CFRelease (ctfont);
 
 
-#else /* !0 */
+  if (overall_ink_return)
+    *overall_ink_return = ink;
+  if (overall_logical_return)
+    *overall_logical_return = logical;
+  return 0;
+}
 
 
-  /* The Cocoa way...
-   */
+
+static int
+draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
+             NSString *nsstr)
+{
+  if (! nsstr) return 1;
+
+  CGRect wr = d->frame;
+  CGContextRef cgc = d->cgc;
 
   unsigned long argb = gc->gcv.foreground;
   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
 
   unsigned long argb = gc->gcv.foreground;
   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
@@ -2413,40 +3279,107 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
   float g = ((argb >>  8) & 0xFF) / 255.0;
   float b = ((argb      ) & 0xFF) / 255.0;
   NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
   float g = ((argb >>  8) & 0xFF) / 255.0;
   float b = ((argb      ) & 0xFF) / 255.0;
   NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
+
+  if (!gc->gcv.font) {
+    Assert (0, "no font");
+    return 1;
+  }
+
   NSDictionary *attr =
     [NSDictionary dictionaryWithObjectsAndKeys:
                     gc->gcv.font->nsfont, NSFontAttributeName,
                     fg, NSForegroundColorAttributeName,
                   nil];
   NSDictionary *attr =
     [NSDictionary dictionaryWithObjectsAndKeys:
                     gc->gcv.font->nsfont, NSFontAttributeName,
                     fg, NSForegroundColorAttributeName,
                   nil];
+
+  // Don't understand why we have to do both set_color and
+  // NSForegroundColorAttributeName, but we do.
+  //
+  set_color (cgc, argb, 32, NO, YES);
+
+  NSAttributedString *astr = [[NSAttributedString alloc]
+                               initWithString:nsstr
+                                   attributes:attr];
+  CTLineRef dl = CTLineCreateWithAttributedString (
+                   (__bridge CFAttributedStringRef) astr);
+  CGContextSetTextPosition (cgc,
+                            wr.origin.x + x,
+                            wr.origin.y + wr.size.height - y);
+  CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
+  CTLineDraw (dl, cgc);
+  CFRelease (dl);
+
+  invalidate_drawable_cache (d);
+  return 0;
+}
+
+
+int
+XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
+             const char  *str, int len)
+{
   char *s2 = (char *) malloc (len + 1);
   strncpy (s2, str, len);
   s2[len] = 0;
   NSString *nsstr = [NSString stringWithCString:s2
   char *s2 = (char *) malloc (len + 1);
   strncpy (s2, str, len);
   s2[len] = 0;
   NSString *nsstr = [NSString stringWithCString:s2
-                                         encoding:NSISOLatin1StringEncoding];
+                                       encoding:NSISOLatin1StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
   free (s2);
   free (s2);
-  NSPoint pos;
-  pos.x = wr.origin.x + x;
-  pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
-  [nsstr drawAtPoint:pos withAttributes:attr];
+  return ret;
+}
 
 
-#endif  /* 0 */
 
 
-  return 0;
+int
+XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
+             const XChar2b *str, int len)
+{
+  char *s2 = XChar2b_to_utf8 (str, 0);
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSUTF8StringEncoding];
+  if (! nsstr)
+    /* If the C string has invalid UTF8 bytes in it, the result is
+       "undefined", which turns out to mean "return a null string"
+       instead of just omitting the bogus characters. Greaaat.
+       So try it again as Latin1, I guess. */
+    nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
+  return ret;
 }
 
 
 }
 
 
-int
-XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
-             const char  *str, int len)
+void
+Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
+                 int x, int y, const char *str, int len)
 {
 {
-  return draw_string (dpy, d, gc, x, y, str, len, NO);
+  char *s2 = (char *) malloc (len + 1);
+  strncpy (s2, str, len);
+  s2[len] = 0;
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSUTF8StringEncoding];
+  if (! nsstr)
+    nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
+  draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
 }
 
 }
 
+
 int
 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
                   const char *str, int len)
 {
 int
 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
                   const char *str, int len)
 {
-  return draw_string (dpy, d, gc, x, y, str, len, YES);
+  int ascent, descent, dir;
+  XCharStruct cs;
+  XTextExtents (&gc->gcv.font->metrics, str, len,
+                &dir, &ascent, &descent, &cs);
+  draw_rect (dpy, d, gc,
+             x + MIN (0, cs.lbearing),
+             y - MAX (0, ascent),
+             MAX (MAX (0, cs.rbearing) -
+                  MIN (0, cs.lbearing),
+                  cs.width),
+             MAX (0, ascent) + MAX (0, descent),
+             NO, YES);
+  return XDrawString (dpy, d, gc, x, y, str, len);
 }
 
 
 }
 
 
@@ -2524,9 +3457,10 @@ XSetClipMask (Display *dpy, GC gc, Pixmap m)
     CGImageRelease (gc->clip_mask);
   }
 
     CGImageRelease (gc->clip_mask);
   }
 
-  gc->gcv.clip_mask = copy_pixmap (m);
+  gc->gcv.clip_mask = copy_pixmap (dpy, m);
   if (gc->gcv.clip_mask)
   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;
 
   else
     gc->clip_mask = 0;
 
@@ -2547,7 +3481,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)
 {
                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
   NSWindow *nsw = [w->window.view window];
   NSPoint wpos;
   // get bottom left of window on screen, from bottom left
@@ -2589,8 +3534,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);
   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;
   if (root_ret)   *root_ret   = 0;
   if (child_ret)  *child_ret  = 0;
   return True;
@@ -2602,7 +3548,16 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
                        int *dest_x_ret, int *dest_y_ret,
                        Window *child_ret)
 {
                        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
   NSWindow *nsw = [w->window.view window];
   NSPoint wpos;
   // get bottom left of window on screen, from bottom left
@@ -2637,6 +3592,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;
   // 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;
 
   *dest_x_ret = p.x;
   *dest_y_ret = p.y;
@@ -2657,11 +3613,25 @@ XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
                XComposeStatus *xc)
 {
   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
                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;
   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);
 }
 
 
 }
 
 
@@ -2704,6 +3674,25 @@ visual_class (Screen *s, Visual *v)
   return TrueColor;
 }
 
   return TrueColor;
 }
 
+int
+get_bits_per_pixel (Display *dpy, int depth)
+{
+  Assert (depth == 32 || depth == 1, "unexpected depth");
+  return depth;
+}
+
+int
+screen_number (Screen *screen)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  int i;
+  for (i = 0; i < ScreenCount (dpy); i++)
+    if (ScreenOfDisplay (dpy, i) == screen)
+      return i;
+  abort ();
+  return 0;
+}
+
 // declared in utils/grabclient.h
 Bool
 use_subwindow_mode_p (Screen *screen, Window window)
 // declared in utils/grabclient.h
 Bool
 use_subwindow_mode_p (Screen *screen, Window window)