From http://www.jwz.org/xscreensaver/xscreensaver-5.33.tar.gz
[xscreensaver] / OSX / jwxyz.m
index e96e03b183603db11761344de4a113852fea8dbd..d7847c8f497f3ca11a7e324c223443381b8171ee 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2010 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2015 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 <stdint.h>
 
 #import <stdlib.h>
 #import <stdint.h>
-#import <Cocoa/Cocoa.h>
+#import <wchar.h>
+
+#ifdef USE_IPHONE
+# import <UIKit/UIKit.h>
+# import <UIKit/UIScreen.h>
+# import <QuartzCore/QuartzCore.h>
+# 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
+
+# define NSFontTraitMask      UIFontDescriptorSymbolicTraits
+// The values for the flags for NSFontTraitMask and
+// UIFontDescriptorSymbolicTraits match up, not that it really matters here.
+# define NSBoldFontMask       UIFontDescriptorTraitBold
+# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
+# define NSItalicFontMask     UIFontDescriptorTraitItalic
+#else
+# import <Cocoa/Cocoa.h>
+#endif
+
+#import <CoreText/CTFont.h>
+#import <CoreText/CTLine.h>
+#import <CoreText/CTRun.h>
+
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
+#import "yarandom.h"
+#import "utf8wc.h"
+#import "xft.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
@@ -44,6 +78,7 @@ struct jwxyz_Drawable {
     struct {
       NSView *view;
       unsigned long background;
     struct {
       NSView *view;
       unsigned long background;
+      int last_mouse_x, last_mouse_y;
     } window;
     struct {
       int depth;
     } window;
     struct {
       int depth;
@@ -55,11 +90,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
@@ -68,7 +106,10 @@ struct jwxyz_Display {
 
 struct jwxyz_Screen {
   Display *dpy;
 
 struct jwxyz_Screen {
   Display *dpy;
+  CGBitmapInfo bitmap_info;
+  unsigned long black, white;
   Visual *visual;
   Visual *visual;
+  int screen_number;
 };
 
 struct jwxyz_GC {
 };
 
 struct jwxyz_GC {
@@ -78,31 +119,379 @@ struct jwxyz_GC {
 };
 
 struct jwxyz_Font {
 };
 
 struct jwxyz_Font {
+  Display *dpy;
   char *ps_name;
   NSFont *nsfont;
   float size;   // points
   char *ps_name;
   NSFont *nsfont;
   float size;   // points
+  char *xa_font;
 
   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
   // But we need the metrics on both of them, so they go here.
   XFontStruct metrics;
 };
 
 
   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
   // But we need the metrics on both of them, so they go here.
   XFontStruct metrics;
 };
 
+struct jwxyz_XFontSet {
+  XFontStruct *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
+}
+
+// 24/32bpp -> 32bpp image conversion.
+// Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
+// bits and an optional byte order swap.
+
+// This type encodes such a conversion.
+typedef unsigned convert_mode_t;
+
+// It's rotate, then swap.
+// A rotation here shifts bytes forward in memory. On x86/ARM, that's a left
+// rotate, and on PowerPC, a rightward rotation.
+static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3;
+static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
+
+
+// Converts an array of pixels ('src') from one format to another, placing the
+// result in 'dest', according to the pixel conversion mode 'mode'.
+static void
+convert_row (uint32_t *dest, const void *src, size_t count,
+             convert_mode_t mode, size_t src_bpp)
+{
+  Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
+
+  // This works OK iff src == dest or src and dest do not overlap.
+
+  if (!mode) {
+    if (src != dest)
+      memcpy (dest, src, count * 4);
+    return;
+  }
+
+  // This is correct, but not fast.
+  convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8;
+  convert_mode_t flip = mode & CONVERT_MODE_SWAP;
+
+  src_bpp /= 8;
+
+  uint32_t *dest_end = dest + count;
+  while (dest != dest_end) {
+    uint32_t x;
+
+    if (src_bpp == 4)
+      x = *(const uint32_t *)src;
+    else { // src_bpp == 3
+      const uint8_t *src8 = (const uint8_t *)src;
+      // __LITTLE/BIG_ENDIAN__ are defined by the compiler.
+# if defined __LITTLE_ENDIAN__
+      x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000;
+# elif defined __BIG_ENDIAN__
+      x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff;
+# else
+#  error "Can't determine system endianness."
+# endif
+    }
+
+    src = (const uint8_t *)src + src_bpp;
+
+    /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
+       for 32-bit integers, is:
+
+       (x << rot) | (x >> (32 - rot))
+
+       This works nearly everywhere. Compilers on x86 wil generally recognize
+       the idiom and convert it to a ROL instruction. But there's a problem
+       here: according to the C specification, bit shifts greater than or equal
+       to the length of the integer are undefined. And if rot = 0:
+       1. (x << 0) | (x >> (32 - 0))
+       2. (x << 0) | (x >> 32)
+       3. (x << 0) | (Undefined!)
+
+       Still, when the compiler converts this to a ROL on x86, everything works
+       as intended. But, there are two additional problems when Clang does
+       compile-time constant expression evaluation with the (x >> 32)
+       expression:
+       1. Instead of evaluating it to something reasonable (either 0, like a
+          human would intuitively expect, or x, like x86 would with SHR), Clang
+          seems to pull a value out of nowhere, like -1, or some other random
+          number.
+       2. Clang's warning for this, -Wshift-count-overflow, only works when the
+          shift count is a literal constant, as opposed to an arbitrary
+          expression that is optimized down to a constant.
+       Put together, this means that the assertions in jwxyz_make_display with
+       convert_px break with the above naive rotation, but only for a release
+       build.
+
+       http://blog.regehr.org/archives/1063
+       http://llvm.org/bugs/show_bug.cgi?id=17332
+       As described in those links, there is a solution here: Masking the
+       undefined shift with '& 31' as below makes the experesion well-defined
+       again. And LLVM is set to pick up on this safe version of the idiom and
+       use a rotation instruction on architectures (like x86) that support it,
+       just like it does with the unsafe version.
+
+       Too bad LLVM doesn't want to pick up on that particular optimization
+       here. Oh well. At least this code usually isn't critical w.r.t.
+       performance.
+     */
+
+# if defined __LITTLE_ENDIAN__
+    x = (x << rot) | (x >> ((32 - rot) & 31));
+# elif defined __BIG_ENDIAN__
+    x = (x >> rot) | (x << ((32 - rot) & 31));
+# endif
+
+    if (flip)
+      x = __builtin_bswap32(x); // LLVM/GCC built-in function.
+
+    *dest = x;
+    ++dest;
+  }
+}
+
+
+// Converts a single pixel.
+static uint32_t
+convert_px (uint32_t px, convert_mode_t mode)
+{
+  convert_row (&px, &px, 1, mode, 32);
+  return px;
+}
+
+
+// This returns the inverse conversion mode, such that:
+// pixel
+//   == convert_px(convert_px(pixel, mode), convert_mode_invert(mode))
+//   == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode)
+static convert_mode_t
+convert_mode_invert (convert_mode_t mode)
+{
+  // swap(0); rot(n) == rot(n); swap(0)
+  // swap(1); rot(n) == rot(-n); swap(1)
+  return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode;
+}
+
+
+// This combines two conversions into one, such that:
+// convert_px(convert_px(pixel, mode0), mode1)
+//   == convert_px(pixel, convert_mode_merge(mode0, mode1))
+static convert_mode_t
+convert_mode_merge (convert_mode_t m0, convert_mode_t m1)
+{
+  // rot(r0); swap(s0); rot(r1); swap(s1)
+  // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1)
+  // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1)
+  return
+    ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
+    ((m0 ^ m1) & CONVERT_MODE_SWAP);
+}
+
+
+// This returns a conversion mode that converts an arbitrary 32-bit format
+// specified by bitmap_info to RGBA.
+static convert_mode_t
+convert_mode_to_rgba (CGBitmapInfo bitmap_info)
+{
+  // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
+  // i.e. BGRA
+  // red   = 0x00FF0000;
+  // green = 0x0000FF00;
+  // blue  = 0x000000FF;
+
+  // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
+
+  CGImageAlphaInfo alpha_info =
+    (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
+
+  Assert (! (bitmap_info & kCGBitmapFloatComponents),
+          "kCGBitmapFloatComponents unsupported");
+  Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
+
+  convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
+                       alpha_info == kCGImageAlphaPremultipliedFirst ||
+                       alpha_info == kCGImageAlphaNoneSkipFirst ?
+                       3 : 0;
+
+  CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
+
+  Assert (byte_order == kCGBitmapByteOrder32Little ||
+          byte_order == kCGBitmapByteOrder32Big,
+          "byte order not supported");
+
+  convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
+                        CONVERT_MODE_SWAP : 0;
+  if (swap)
+    rot = CONVERT_MODE_ROTATE_MASK & -rot;
+  return swap | rot;
+}
+
+
+union color_bytes
+{
+  uint32_t pixel;
+  uint8_t bytes[4];
+};
+
+
+static uint32_t
+alloc_color (Display *dpy, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
+{
+  union color_bytes color;
+
+  /* Instead of (int)(c / 256.0), another possibility is
+     (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
+     uint8_t integer_math(uint16_t c) {
+       unsigned c0 = c + 128;
+       return (c0 - (c0 >> 8)) >> 8;
+     }
+   */
+
+  color.bytes[0] = r >> 8;
+  color.bytes[1] = g >> 8;
+  color.bytes[2] = b >> 8;
+  color.bytes[3] = a >> 8;
+
+  return
+    convert_px (color.pixel,
+      convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
+}
+
+
+static void
+query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
+{
+  union color_bytes color;
+  color.pixel = convert_px ((uint32_t)pixel,
+                            convert_mode_to_rgba (dpy->screen->bitmap_info));
+  for (unsigned i = 0; i != 4; ++i)
+    rgba[i] = color.bytes[i];
+}
+
+
+static void
+query_color_float (Display *dpy, unsigned long pixel, float *rgba)
+{
+  uint8_t rgba8[4];
+  query_color (dpy, pixel, rgba8);
+  for (unsigned i = 0; i != 4; ++i)
+    rgba[i] = rgba8[i] * (1.0f / 255.0f);
+}
+
+
+/* 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
+
+# ifdef USE_BACKBUFFER
+  d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
+# else
+  d->screen->bitmap_info = (kCGImageAlphaNoneSkipFirst |
+                            kCGBitmapByteOrder32Little);
+# endif
+  d->screen->black = alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
+  d->screen->white = alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+
+# if 0
+  // Tests for the image conversion modes.
+  {
+    const uint32_t key = 0x04030201;
+#  ifdef __LITTLE_ENDIAN__
+    assert (convert_px (key, 0) == key);
+    assert (convert_px (key, 1) == 0x03020104);
+    assert (convert_px (key, 3) == 0x01040302);
+    assert (convert_px (key, 4) == 0x01020304);
+    assert (convert_px (key, 5) == 0x04010203);
+#  endif
+    for (unsigned i = 0; i != 8; ++i) {
+      assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key);
+      assert (convert_mode_invert(convert_mode_invert(i)) == i);
+    }
+
+    for (unsigned i = 0; i != 8; ++i) {
+      for (unsigned j = 0; j != 8; ++j)
+        assert (convert_px(convert_px(key, i), j) ==
+                convert_px(key, convert_mode_merge(i, j)));
+    }
+  }
+# endif
+
   Visual *v = (Visual *) calloc (1, sizeof(Visual));
   v->class      = TrueColor;
   Visual *v = (Visual *) calloc (1, sizeof(Visual));
   v->class      = TrueColor;
-  v->red_mask   = 0x00FF0000;
-  v->green_mask = 0x0000FF00;
-  v->blue_mask  = 0x000000FF;
+  v->red_mask   = alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
+  v->green_mask = alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
+  v->blue_mask  = alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
+  CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask;
+  Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) &&
+          (byte_order == kCGBitmapByteOrder32Little ||
+           byte_order == kCGBitmapByteOrder32Big),
+          "invalid bits per channel");
   v->bits_per_rgb = 8;
   d->screen->visual = v;
   
   v->bits_per_rgb = 8;
   d->screen->visual = v;
   
@@ -111,32 +500,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;
   CFRetain (w->window.view);   // needed for garbage collection?
   w->window.view = view;
   CFRetain (w->window.view);   // needed for garbage collection?
-  w->window.background = BlackPixel(0,0);
+  w->window.background = BlackPixel(d,0);
 
   d->main_window = w;
 
 
   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);
 }
@@ -145,7 +550,7 @@ 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;
 }
 
@@ -167,15 +572,21 @@ invalidate_drawable_cache (Drawable d)
 /* 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;
@@ -186,10 +597,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.
     //
@@ -199,17 +619,36 @@ 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);
 }
 
 
 
   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.
+  // CGContextSynchronize is another possibility.
+  CGContextFlush(dpy->main_window->cgc);
+}
+
 jwxyz_sources_data *
 display_sources_data (Display *dpy)
 {
 jwxyz_sources_data *
 display_sources_data (Display *dpy)
 {
@@ -250,7 +689,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
@@ -265,36 +710,54 @@ XDisplayHeight (Display *dpy, int screen)
   return (int) dpy->main_window->frame.size.height;
 }
 
   return (int) dpy->main_window->frame.size.height;
 }
 
+unsigned long
+XBlackPixelOfScreen(Screen *screen)
+{
+  return screen->black;
+}
+
+unsigned long
+XWhitePixelOfScreen(Screen *screen)
+{
+  return screen->white;
+}
+
+unsigned long
+XCellsOfScreen(Screen *screen)
+{
+  Visual *v = screen->visual;
+  return v->red_mask | v->green_mask | v->blue_mask;
+}
+
 static void
 static void
-validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
+validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
+                BOOL alpha_allowed_p)
 {
   if (depth == 1)
     Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
   else if (!alpha_allowed_p)
 {
   if (depth == 1)
     Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
   else if (!alpha_allowed_p)
-    Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
+    Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
             "bogus color pixel");
 }
 
 
 static void
             "bogus color pixel");
 }
 
 
 static void
-set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
-           BOOL alpha_allowed_p, BOOL fill_p)
+set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
+           unsigned int depth, BOOL alpha_allowed_p, BOOL fill_p)
 {
 {
-  validate_pixel (argb, depth, alpha_allowed_p);
+  validate_pixel (dpy, argb, depth, alpha_allowed_p);
   if (depth == 1) {
     if (fill_p)
       CGContextSetGrayFillColor   (cgc, (argb ? 1.0 : 0.0), 1.0);
     else
       CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
   } else {
   if (depth == 1) {
     if (fill_p)
       CGContextSetGrayFillColor   (cgc, (argb ? 1.0 : 0.0), 1.0);
     else
       CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
   } else {
-    float a = ((argb >> 24) & 0xFF) / 255.0;
-    float r = ((argb >> 16) & 0xFF) / 255.0;
-    float g = ((argb >>  8) & 0xFF) / 255.0;
-    float b = ((argb      ) & 0xFF) / 255.0;
+    float rgba[4];
+    query_color_float (dpy, argb, rgba);
     if (fill_p)
     if (fill_p)
-      CGContextSetRGBFillColor   (cgc, r, g, b, a);
+      CGContextSetRGBFillColor   (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
     else
     else
-      CGContextSetRGBStrokeColor (cgc, r, g, b, a);
+      CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
   }
 }
 
   }
 }
 
@@ -349,33 +812,32 @@ 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.
  */
 static void
 
 
 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
  */
 static void
-push_color_gc (Drawable d, GC gc, unsigned long color, 
+push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
                BOOL antialias_p, Bool fill_p)
 {
   push_gc (d, gc);
 
   int depth = gc->depth;
   switch (gc->gcv.function) {
                BOOL antialias_p, Bool fill_p)
 {
   push_gc (d, gc);
 
   int depth = gc->depth;
   switch (gc->gcv.function) {
-    case GXset:   color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
-    case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
+    case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
+    case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
   }
 
   CGContextRef cgc = d->cgc;
   }
 
   CGContextRef cgc = d->cgc;
-
-  set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
+  set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
   CGContextSetShouldAntialias (cgc, antialias_p);
 }
 
   CGContextSetShouldAntialias (cgc, antialias_p);
 }
 
@@ -383,17 +845,17 @@ push_color_gc (Drawable d, GC gc, unsigned long color,
 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
  */
 static void
 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
  */
 static void
-push_fg_gc (Drawable d, GC gc, Bool fill_p)
+push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
 {
 {
-  push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
+  push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
 }
 
 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
  */
 static void
 }
 
 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
  */
 static void
-push_bg_gc (Drawable d, GC gc, Bool fill_p)
+push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
 {
 {
-  push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
+  push_color_gc (dpy, d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
 }
 
 
 }
 
 
@@ -408,6 +870,12 @@ push_bg_gc (Drawable d, GC gc, Bool fill_p)
  */
 #define XDRAWPOINTS_IMAGES
 
  */
 #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)
@@ -415,72 +883,128 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
   int i;
   CGRect wr = d->frame;
 
   int i;
   CGRect wr = d->frame;
 
-  push_fg_gc (d, gc, YES);
+# ifdef XDRAWPOINTS_CGDATA
 
 
-# ifdef XDRAWPOINTS_IMAGES
+#  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);
 
 
-  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);
+    Assert (data, "no bitmap data in Drawable");
 
 
-  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;
+    unsigned long argb = gc->gcv.foreground;
+    validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
+    if (gc->depth == 1)
+      argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,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 */
+  {
+    push_fg_gc (dpy, d, gc, YES);
+
+# ifdef XDRAWPOINTS_IMAGES
+
+    unsigned int argb = gc->gcv.foreground;
+    validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
+    if (gc->depth == 1)
+      argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,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. */
+                                    dpy->screen->bitmap_info,
+                                    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);
+    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;
   
   
-  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);
+    pop_gc (d, gc);
+  }
+
   invalidate_drawable_cache (d);
 
   return 0;
   invalidate_drawable_cache (d);
 
   return 0;
@@ -501,6 +1025,45 @@ 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, 
 int
 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
            int src_x, int src_y, 
@@ -522,9 +1085,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
-    set_color (dst->cgc, (gc->gcv.function == GXset
-                          ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
-                          : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+    set_color (dpy, dst->cgc,
+               (gc->gcv.function == GXset
+                ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
+                : (gc->depth == 1 ? 0 : BlackPixel(dpy,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;
@@ -558,8 +1122,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; \
@@ -580,19 +1142,148 @@ 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 (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;
   BOOL free_cgi_p = NO;
 
   
   NSObject *releaseme = 0;
   CGImageRef cgi;
   BOOL mask_p = NO;
   BOOL free_cgi_p = NO;
 
-  if (src->type == PIXMAP) {
+
+  /* If we're copying from a bitmap to a bitmap, and there's nothing funny
+     going on with clipping masks or depths or anything, optimize it by
+     just doing a memcpy instead of going through a CGI.
+   */
+  if (bitmap_context_p (src)) {
+
+    if (bitmap_context_p (dst) &&
+        gc->gcv.function == GXcopy &&
+        !gc->gcv.clip_mask &&
+        drawable_depth (src) == drawable_depth (dst)) {
+
+      Assert(!(int)src_frame.origin.x &&
+             !(int)src_frame.origin.y &&
+             !(int)dst_frame.origin.x &&
+             !(int)dst_frame.origin.y,
+             "unexpected non-zero origin");
+      
+      char *src_data = CGBitmapContextGetData(src->cgc);
+      char *dst_data = CGBitmapContextGetData(dst->cgc);
+      size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
+      size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
+      
+      // Int to float and back again. It's not very safe, but it seems to work.
+      int src_x0 = src_rect.origin.x;
+      int dst_x0 = dst_rect.origin.x;
+      
+      // Flip the Y-axis a second time.
+      int src_y0 = (src_frame.origin.y + src_frame.size.height -
+                    src_rect.size.height - src_rect.origin.y);
+      int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
+                    dst_rect.size.height - dst_rect.origin.y);
+      
+      unsigned width0  = (int) src_rect.size.width;
+      unsigned height0 = (int) src_rect.size.height;
+      
+      Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
+             (int)src_rect.size.height == (int)dst_rect.size.height,
+             "size mismatch");
+      {
+        char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
+        char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
+        size_t src_pitch0 = src_pitch;
+        size_t dst_pitch0 = dst_pitch;
+        size_t bytes = width0 * 4;
+
+        if (src == dst && dst_y0 > src_y0) {
+          // Copy upwards if the areas might overlap.
+          src_data0 += src_pitch0 * (height0 - 1);
+          dst_data0 += dst_pitch0 * (height0 - 1);
+          src_pitch0 = -src_pitch0;
+          dst_pitch0 = -dst_pitch0;
+        }
+      
+        size_t lines0 = height0;
+        while (lines0) {
+          // memcpy is an alias for memmove on OS X.
+          memmove(dst_data0, src_data0, bytes);
+          src_data0 += src_pitch0;
+          dst_data0 += dst_pitch0;
+          --lines0;
+        }
+      }
+
+      if (clipped) {
+        int orig_dst_x = orig_dst_rect.origin.x;
+        int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
+                          orig_dst_rect.origin.y - orig_dst_rect.size.height);
+        int orig_width  = orig_dst_rect.size.width;
+        int orig_height = orig_dst_rect.size.height;
+
+        Assert (orig_dst_x >= 0 &&
+                orig_dst_x + orig_width  <= (int) dst_frame.size.width &&
+                orig_dst_y >= 0 &&
+                orig_dst_y + orig_height <= (int) dst_frame.size.height,
+                "wrong dimensions");
+
+        if (orig_dst_y < dst_y0) {
+          fill_rect_memset (seek_xy (dst_data, dst_pitch,
+                                     orig_dst_x, orig_dst_y), dst_pitch,
+                            (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);
+        }
+      }
+
+      invalidate_drawable_cache (dst);
+      return 0;
+    }
+
 
     // If we are copying from a Pixmap to a Pixmap or Window, we must first
     // copy the bits to an intermediary CGImage object, then copy that to the
 
     // If we are copying from a Pixmap to a Pixmap or Window, we must first
     // copy the bits to an intermediary CGImage object, then copy that to the
@@ -625,9 +1316,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       free_cgi_p = YES;
     }
 
       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) */
     
     NSRect nsfrom;    // NSRect != CGRect on 10.4
   } else { /* (src->type == WINDOW) */
     
     NSRect nsfrom;    // NSRect != CGRect on 10.4
@@ -684,21 +1376,27 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
       CGDataProviderRelease (prov);
     }
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
       CGDataProviderRelease (prov);
     }
+
+# endif // !USE_BACKBUFFER
   }
 
   }
 
+  CGContextRef cgc = dst->cgc;
+
   if (mask_p) {                // src depth == 1
 
   if (mask_p) {                // src depth == 1
 
-    push_bg_gc (dst, gc, YES);
+    push_bg_gc (dpy, dst, gc, YES);
 
     // fill the destination rectangle with solid background...
 
     // 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 (dpy, 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);
 
@@ -713,17 +1411,22 @@ 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 (dpy, 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);
     }
 
     if (cgi) {
       // copy the CGImage onto the destination CGContext
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
     }
 
     if (cgi) {
       // copy the CGImage onto the destination CGContext
       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
-      CGContextDrawImage (dst->cgc, dst_rect, cgi);
+      CGContextDrawImage (cgc, dst_rect, cgi);
     } else {
       // No cgi means src == dst, and both are Windows.
     } 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;
       NSRect nsfrom;
       nsfrom.origin.x    = src_rect.origin.x;    // NSRect != CGRect on 10.4
       nsfrom.origin.y    = src_rect.origin.y;
@@ -733,12 +1436,14 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
       nsto.x             = dst_rect.origin.x;
       nsto.y             = dst_rect.origin.y;
       NSCopyBits (0, nsfrom, nsto);
       nsto.x             = dst_rect.origin.x;
       nsto.y             = dst_rect.origin.y;
       NSCopyBits (0, nsfrom, nsto);
+# endif // !USE_BACKBUFFER
     }
 
     pop_gc (dst, gc);
   }
 
   if (free_cgi_p) CGImageRelease (cgi);
     }
 
     pop_gc (dst, gc);
   }
 
   if (free_cgi_p) CGImageRelease (cgi);
+
   if (releaseme) [releaseme release];
   invalidate_drawable_cache (dst);
   return 0;
   if (releaseme) [releaseme release];
   invalidate_drawable_cache (dst);
   return 0;
@@ -779,15 +1484,16 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
   p.x = wr.origin.x + x1;
   p.y = wr.origin.y + wr.size.height - y1;
 
   p.x = wr.origin.x + x1;
   p.y = wr.origin.y + wr.size.height - y1;
 
-  push_fg_gc (d, gc, NO);
+  push_fg_gc (dpy, 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);
   invalidate_drawable_cache (d);
   return 0;
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -800,8 +1506,11 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
   int i;
   NSPoint p;
   CGRect wr = d->frame;
   int i;
   NSPoint p;
   CGRect wr = d->frame;
-  push_fg_gc (d, gc, NO);
-  set_line_mode (d->cgc, &gc->gcv);
+  push_fg_gc (dpy, d, gc, NO);
+
+  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.
@@ -812,8 +1521,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;
@@ -822,11 +1531,11 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
       p.x = wr.origin.x + points->x;
       p.y = wr.origin.y + wr.size.height - points->y;
     }
       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);
   invalidate_drawable_cache (d);
   return 0;
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -839,19 +1548,21 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
   int i;
   CGRect wr = d->frame;
 
   int i;
   CGRect wr = d->frame;
 
-  push_fg_gc (d, gc, NO);
-  set_line_mode (d->cgc, &gc->gcv);
-  CGContextBeginPath (d->cgc);
+  CGContextRef cgc = d->cgc;
+
+  push_fg_gc (dpy, d, gc, NO);
+  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);
   invalidate_drawable_cache (d);
   return 0;
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -861,7 +1572,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);
 }
@@ -869,8 +1580,8 @@ 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");
-  validate_pixel (pixel, 32, NO);
+  Assert (w && w->type == WINDOW, "not a window");
+  validate_pixel (dpy, pixel, 32, NO);
   w->window.background = pixel;
   return 0;
 }
   w->window.background = pixel;
   return 0;
 }
@@ -889,17 +1600,18 @@ draw_rect (Display *dpy, Drawable d, GC gc,
 
   if (gc) {
     if (foreground_p)
 
   if (gc) {
     if (foreground_p)
-      push_fg_gc (d, gc, fill_p);
+      push_fg_gc (dpy, d, gc, fill_p);
     else
     else
-      push_bg_gc (d, gc, fill_p);
+      push_bg_gc (dpy, 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)
   }
 
   if (gc)
@@ -929,14 +1641,15 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
 {
   CGRect wr = d->frame;
   int i;
 {
   CGRect wr = d->frame;
   int i;
-  push_fg_gc (d, gc, YES);
+  CGContextRef cgc = d->cgc;
+  push_fg_gc (dpy, d, gc, YES);
   for (i = 0; i < n; i++) {
     CGRect r;
     r.origin.x = wr.origin.x + rects->x;
     r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
     r.size.width = rects->width;
     r.size.height = rects->height;
   for (i = 0; i < n; i++) {
     CGRect r;
     r.origin.x = wr.origin.x + rects->x;
     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);
@@ -948,8 +1661,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 (dpy, 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;
 }
@@ -961,10 +1675,11 @@ XFillPolygon (Display *dpy, Drawable d, GC gc,
 {
   CGRect wr = d->frame;
   int i;
 {
   CGRect wr = d->frame;
   int i;
-  push_fg_gc (d, gc, YES);
-  CGContextBeginPath (d->cgc);
+  push_fg_gc (dpy, d, gc, YES);
+  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;
@@ -974,15 +1689,15 @@ 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);
   invalidate_drawable_cache (d);
   return 0;
   pop_gc (d, gc);
   invalidate_drawable_cache (d);
   return 0;
@@ -1012,27 +1727,28 @@ draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
   BOOL clockwise = angle2 < 0;
   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
   
   BOOL clockwise = angle2 < 0;
   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
   
-  push_fg_gc (d, gc, fill_p);
+  push_fg_gc (dpy, 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);
@@ -1082,12 +1798,12 @@ XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
 
 
 static void
 
 
 static void
-gcv_defaults (XGCValues *gcv, int depth)
+gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
 {
   memset (gcv, 0, sizeof(*gcv));
   gcv->function   = GXcopy;
 {
   memset (gcv, 0, sizeof(*gcv));
   gcv->function   = GXcopy;
-  gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
-  gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
+  gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
+  gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
   gcv->line_width = 1;
   gcv->cap_style  = CapNotLast;
   gcv->join_style = JoinMiter;
   gcv->line_width = 1;
   gcv->cap_style  = CapNotLast;
   gcv->join_style = JoinMiter;
@@ -1098,8 +1814,12 @@ gcv_defaults (XGCValues *gcv, int depth)
 }
 
 static void
 }
 
 static void
-set_gcv (GC gc, XGCValues *from, unsigned long mask)
+set_gcv (Display *dpy, 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;
@@ -1114,22 +1834,23 @@ set_gcv (GC gc, XGCValues *from, unsigned long mask)
   if (mask & GCClipMask)       XSetClipMask (0, gc, from->clip_mask);
   if (mask & GCFont)           XSetFont (0, gc, from->font);
 
   if (mask & GCClipMask)       XSetClipMask (0, gc, from->clip_mask);
   if (mask & GCFont)           XSetFont (0, gc, from->font);
 
-  if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
+  if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth,
                                            gc->gcv.alpha_allowed_p);
                                            gc->gcv.alpha_allowed_p);
-  if (mask & GCBackground) validate_pixel (from->background, gc->depth,
+  if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth,
                                            gc->gcv.alpha_allowed_p);
     
                                            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");
 }
 
 
 }
 
 
@@ -1143,15 +1864,15 @@ XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
     gc->depth = d->pixmap.depth;
   }
 
     gc->depth = d->pixmap.depth;
   }
 
-  gcv_defaults (&gc->gcv, gc->depth);
-  set_gcv (gc, xgcv, mask);
+  gcv_defaults (dpy, &gc->gcv, gc->depth);
+  set_gcv (dpy, gc, xgcv, mask);
   return gc;
 }
 
 int
 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
 {
   return gc;
 }
 
 int
 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
 {
-  set_gcv (gc, gcv, mask);
+  set_gcv (dpy, gc, gcv, mask);
   return 0;
 }
 
   return 0;
 }
 
@@ -1176,7 +1897,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;
@@ -1208,13 +1929,8 @@ XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
 Status
 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
 {
 Status
 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
 {
-  // store 32 bit ARGB in the pixel field.
-  // (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->pixel = alloc_color (dpy,
+                              color->red, color->green, color->blue, 0xFFFF);
   return 1;
 }
 
   return 1;
 }
 
@@ -1265,7 +1981,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")) {
@@ -1304,13 +2020,12 @@ XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
 int
 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
 {
 int
 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
 {
-  validate_pixel (color->pixel, 32, NO);
-  unsigned char r = ((color->pixel >> 16) & 0xFF);
-  unsigned char g = ((color->pixel >>  8) & 0xFF);
-  unsigned char b = ((color->pixel      ) & 0xFF);
-  color->red   = (r << 8) | r;
-  color->green = (g << 8) | g;
-  color->blue  = (b << 8) | b;
+  validate_pixel (dpy, color->pixel, 32, NO);
+  uint8_t rgba[4];
+  query_color(dpy, color->pixel, rgba);
+  color->red   = (rgba[0] << 8) | rgba[0];
+  color->green = (rgba[1] << 8) | rgba[1];
+  color->blue  = (rgba[2] << 8) | rgba[2];
   color->flags = DoRed|DoGreen|DoBlue;
   return 0;
 }
   color->flags = DoRed|DoGreen|DoBlue;
   return 0;
 }
@@ -1376,7 +2091,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;
 }
@@ -1394,13 +2109,13 @@ 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;
-  ximage->red_mask   = (depth == 1 ? 0 : 0x00FF0000);
-  ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
-  ximage->blue_mask  = (depth == 1 ? 0 : 0x000000FF);
+  ximage->red_mask   = (depth == 1 ? 0 : dpy->screen->visual->red_mask);
+  ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask);
+  ximage->blue_mask  = (depth == 1 ? 0 : dpy->screen->visual->blue_mask);
   ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
   ximage->bytes_per_line = bytes_per_line;
 
   ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
   ximage->bytes_per_line = bytes_per_line;
 
@@ -1411,8 +2126,13 @@ XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
 XImage *
 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
 {
 XImage *
 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
 {
-  XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
-                             w, h, from->bitmap_pad, 0);
+  XImage *to = (XImage *) malloc (sizeof(*to));
+  memcpy (to, from, sizeof(*from));
+  to->width = w;
+  to->height = h;
+  to->bytes_per_line = 0;
+  XInitImage (to);
+
   to->data = (char *) malloc (h * to->bytes_per_line);
 
   if (x >= from->width)
   to->data = (char *) malloc (h * to->bytes_per_line);
 
   if (x >= from->width)
@@ -1507,7 +2227,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++];
 }
 
@@ -1557,13 +2277,15 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
   if (w <= 0 || h <= 0)
     return 0;
 
   if (w <= 0 || h <= 0)
     return 0;
 
+  CGContextRef cgc = d->cgc;
+
   if (gc->gcv.function == GXset ||
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
   if (gc->gcv.function == GXset ||
       gc->gcv.function == GXclear) {
     // "set" and "clear" are dumb drawing modes that ignore the source
     // bits and just draw solid rectangles.
-    set_color (d->cgc, (gc->gcv.function == GXset
-                        ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
-                        : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
+    set_color (dpy, cgc, (gc->gcv.function == GXset
+                        ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
+                        : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
                gc->depth, gc->gcv.alpha_allowed_p, YES);
     draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
     return 0;
                gc->depth, gc->gcv.alpha_allowed_p, YES);
     draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
     return 0;
@@ -1592,17 +2314,14 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
     CGImageRef cgi = CGImageCreate (w, h,
                                     bpp/4, bpp, bpl,
                                     dpy->colorspace, 
     CGImageRef cgi = CGImageCreate (w, h,
                                     bpp/4, bpp, bpl,
                                     dpy->colorspace, 
-                                    /* Need this for XPMs to have the right
-                                       colors, e.g. the logo in "maze". */
-                                    (kCGImageAlphaNoneSkipFirst |
-                                     kCGBitmapByteOrder32Host),
+                                    dpy->screen->bitmap_info,
                                     prov, 
                                     NULL,  /* decode[] */
                                     NO, /* interpolate */
                                     kCGRenderingIntentDefault);
     CGDataProviderRelease (prov);
     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
                                     prov, 
                                     NULL,  /* decode[] */
                                     NO, /* interpolate */
                                     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)
@@ -1630,12 +2349,12 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
                                          prov,
                                          NULL,  /* decode[] */
                                          NO); /* interpolate */
                                          prov,
                                          NULL,  /* decode[] */
                                          NO); /* interpolate */
-    push_fg_gc (d, gc, YES);
+    push_fg_gc (dpy, 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 (dpy, 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);
@@ -1655,43 +2374,59 @@ 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, alpha_first_p;
+  size_t depth, ibpp, ibpl;
+  convert_mode_t mode;
+# ifndef USE_BACKBUFFER
   NSBitmapImageRep *bm = 0;
   NSBitmapImageRep *bm = 0;
+# endif
   
   Assert ((width  < 65535), "improbably large width");
   Assert ((height < 65535), "improbably large height");
   Assert ((x < 65535 && x > -65535), "improbably large x");
   Assert ((y < 65535 && y > -65535), "improbably large y");
 
   
   Assert ((width  < 65535), "improbably large width");
   Assert ((height < 65535), "improbably large height");
   Assert ((x < 65535 && x > -65535), "improbably large x");
   Assert ((y < 65535 && y > -65535), "improbably large y");
 
-  if (d->type == PIXMAP) {
-    depth = d->pixmap.depth;
-    alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
-    ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
-    ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
-    data = CGBitmapContextGetData (d->cgc);
-    Assert (data, "CGBitmapContextGetData failed");
-  } else {
-    // get the bits (desired sub-rectangle) out of the NSView
+  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);
+    mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
+    ibpp = CGBitmapContextGetBitsPerPixel (cgc);
+    ibpl = CGBitmapContextGetBytesPerRow (cgc);
+    data = CGBitmapContextGetData (cgc);
+    Assert (data, "CGBitmapContextGetData failed");
+
+# ifndef USE_BACKBUFFER
+  } else { /* (d->type == WINDOW) */
+
+    // get the bits (desired sub-rectangle) out of the NSView
     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;
     bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
     depth = 32;
     nsfrom.size.width = width;
     nsfrom.size.height = height;
     bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
     depth = 32;
-    alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
+    mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0;
     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");
+# 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);
   }
   
   // 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);
-  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;
@@ -1722,52 +2457,31 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
       iline += ibpl;
     }
   } else {
       iline += ibpl;
     }
   } else {
-    Assert (ibpp == 24 || ibpp == 32, "weird obpp");
     const unsigned char *iline = data;
     unsigned char *oline = (unsigned char *) image->data;
     const unsigned char *iline = data;
     unsigned char *oline = (unsigned char *) image->data;
+
+    mode = convert_mode_merge (mode,
+             convert_mode_invert (
+               convert_mode_to_rgba (dpy->screen->bitmap_info)));
+
     for (yy = 0; yy < height; yy++) {
 
     for (yy = 0; yy < height; yy++) {
 
-      const unsigned char *iline2 = iline;
-      unsigned char *oline2 = oline;
-
-      if (alpha_first_p)                       // 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;
-        }
-      else                                     // 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;
-        }
+      convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
 
       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.
  */
 /* Returns a transformation matrix to do rotation as per the provided
    EXIF "Orientation" value.
  */
@@ -1826,14 +2540,19 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
                                XRectangle *geom_ret, int exif_rotation)
 {
   CGImageRef cgi;
                                XRectangle *geom_ret, int exif_rotation)
 {
   CGImageRef cgi;
+# ifndef USE_IPHONE
   CGImageSourceRef cgsrc;
   CGImageSourceRef cgsrc;
+# endif // USE_IPHONE
   NSSize imgr;
 
   NSSize imgr;
 
+  CGContextRef cgc = d->cgc;
+
   if (nsimg_p) {
 
     NSImage *nsimg = (NSImage *) img_arg;
     imgr = [nsimg size];
 
   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...
     //
     // convert the NSImage to a CGImage via the toll-free-bridging 
     // of NSData and CFData...
     //
@@ -1843,6 +2562,9 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
     CFDataRef cfdata = (CFDataRef) nsdata;
     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
     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;
 
   } else {
     cgi = (CGImageRef) img_arg;
@@ -1879,26 +2601,28 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
   if (d->type == WINDOW)
     XClearWindow (dpy, d);
   else {
   if (d->type == WINDOW)
     XClearWindow (dpy, d);
   else {
-    set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
+    set_color (dpy, cgc, BlackPixel(dpy,0), 32, NO, YES);
     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
   }
 
   CGAffineTransform trans = 
     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
 
     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
   }
 
   CGAffineTransform trans = 
     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
 
-  CGContextSaveGState (d->cgc);
-  CGContextConcatCTM (d->cgc, 
+  CGContextSaveGState (cgc);
+  CGContextConcatCTM (cgc, 
                       CGAffineTransformMakeTranslation (dst.origin.x,
                                                         dst.origin.y));
                       CGAffineTransformMakeTranslation (dst.origin.x,
                                                         dst.origin.y));
-  CGContextConcatCTM (d->cgc, trans);
+  CGContextConcatCTM (cgc, trans);
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-  CGContextDrawImage (d->cgc, dst2, cgi);
-  CGContextRestoreGState (d->cgc);
+  CGContextDrawImage (cgc, dst2, cgi);
+  CGContextRestoreGState (cgc);
 
 
+# ifndef USE_IPHONE
   if (nsimg_p) {
     CFRelease (cgsrc);
     CGImageRelease (cgi);
   }
   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;
@@ -1911,6 +2635,7 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
 }
 
 
 }
 
 
+
 Pixmap
 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
                              const char *data,
 Pixmap
 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
                              const char *data,
@@ -1954,9 +2679,7 @@ XCreatePixmap (Display *dpy, Drawable d,
                                   8, /* bits per component */
                                   width * 4, /* bpl */
                                   dpy->colorspace,
                                   8, /* bits per component */
                                   width * 4, /* bpl */
                                   dpy->colorspace,
-                                  // Without this, it returns 0...
-                                  kCGImageAlphaNoneSkipFirst
-                                  );
+                                  dpy->screen->bitmap_info);
   Assert (p->cgc, "could not create CGBitmapContext");
   return p;
 }
   Assert (p->cgc, "could not create CGBitmapContext");
   return p;
 }
@@ -1965,7 +2688,7 @@ 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);
   if (p->pixmap.cgc_buffer)
   invalidate_drawable_cache (p);
   CGContextRelease (p->cgc);
   if (p->pixmap.cgc_buffer)
@@ -1996,15 +2719,24 @@ copy_pixmap (Display *dpy, Pixmap p)
                                    8, /* bits per component */
                                    width * 4, /* bpl */
                                    dpy->colorspace,
                                    8, /* bits per component */
                                    width * 4, /* bpl */
                                    dpy->colorspace,
-                                   // Without this, it returns 0...
-                                   kCGImageAlphaNoneSkipFirst
-                                   );
+                                   dpy->screen->bitmap_info);
   Assert (p2->cgc, "could not create CGBitmapContext");
 
   return p2;
 }
 
 
   Assert (p2->cgc, "could not create CGBitmapContext");
 
   return p2;
 }
 
 
+char *
+XGetAtomName (Display *dpy, Atom atom)
+{
+  if (atom == XA_FONT)
+    return strdup ("FONT");
+
+  // Note that atoms (that aren't predefined) are just char *.
+  return strdup ((char *) atom);
+}
+
+
 /* Font metric terminology, as used by X11:
 
    "lbearing" is the distance from the logical origin to the leftmost pixel.
 /* Font metric terminology, as used by X11:
 
    "lbearing" is the distance from the logical origin to the leftmost pixel.
@@ -2013,7 +2745,8 @@ copy_pixmap (Display *dpy, 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.
@@ -2024,6 +2757,81 @@ copy_pixmap (Display *dpy, Pixmap p)
    If "rbearing" is greater than "width", then this character overlaps the
    following character.  If smaller, then there is trailing blank space.
  */
    If "rbearing" is greater than "width", then this character overlaps the
    following character.  If smaller, then there is trailing blank space.
  */
+static void
+utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
+{
+  // Returns the metrics of the multi-character, single-line UTF8 string.
+
+  NSFont *nsfont = fid->nsfont;
+  Drawable d = XRootWindow (fid->dpy, 0);
+
+  CGContextRef cgc = d->cgc;
+  NSDictionary *attr =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+                    nsfont, NSFontAttributeName,
+                  nil];
+  NSAttributedString *astr = [[NSAttributedString alloc]
+                               initWithString:nsstr
+                                   attributes:attr];
+  CTLineRef ctline = CTLineCreateWithAttributedString (
+                       (__bridge CFAttributedStringRef) astr);
+  CGContextSetTextPosition (cgc, 0, 0);
+  CGContextSetShouldAntialias (cgc, True);  // #### Guess?
+
+  memset (cs, 0, sizeof(*cs));
+
+  // "CTRun represents set of consecutive glyphs sharing the same
+  // attributes and direction".
+  //
+  // We also get multiple runs any time font subsitution happens:
+  // E.g., if the current font is Verdana-Bold, a &larr; character
+  // in the NSString will actually be rendered in LucidaGrande-Bold.
+  //
+  int count = 0;
+  for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
+    CTRunRef run = (CTRunRef) runid;
+    CFRange r = { 0, };
+    CGRect bbox = CTRunGetImageBounds (run, cgc, r);
+    CGFloat ascent, descent, leading;
+    CGFloat advancement =
+      CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
+
+# 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
+
+    // Create the metrics for this run:
+    XCharStruct cc;
+    cc.ascent   = ceil  (bbox.origin.y + bbox.size.height);
+    cc.descent  = ceil (-bbox.origin.y);
+    cc.lbearing = floor (bbox.origin.x);
+    cc.rbearing = ceil  (bbox.origin.x + bbox.size.width);
+    cc.width    = floor (advancement + 0.5);
+
+    // Add those metrics into the cumulative metrics:
+    if (count == 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    = MAX (cs->width,      cs->width + cc.width);
+      }
+
+    // Why no y? What about vertical text?
+    // XCharStruct doesn't encapsulate that but XGlyphInfo does.
+
+    count++;
+  }
+
+  CFRelease (ctline);
+}
+
 
 
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
 
 
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
@@ -2032,12 +2840,12 @@ static void
 query_font (Font fid)
 {
   if (!fid || !fid->nsfont) {
 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;
@@ -2047,94 +2855,30 @@ 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->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->ascent            =  ceil ([fid->nsfont ascender]);
+  f->descent           = -floor ([fid->nsfont descender]);
 
 
-  min->width    = 255;  // set to smaller values in the loop
-  min->ascent   = 255;
-  min->descent  = 255;
-  min->lbearing = 255;
-  min->rbearing = 255;
+  min->width    = 32767;  // set to smaller values in the loop
+  min->ascent   = 32767;
+  min->descent  = 32767;
+  min->lbearing = 32767;
+  min->rbearing = 32767;
 
   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
 
   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
-  int i;
-
-  NSBezierPath *bpath = [NSBezierPath bezierPath];
-
-  for (i = first; i <= last; i++) {
-    unsigned char str[2];
-    str[0] = i;
-    str[1] = 0;
-
-    NSString *nsstr = [NSString stringWithCString:(char *) str
-                                         encoding:NSISOLatin1StringEncoding];
-
-    /* I can't believe we have to go through this bullshit just to
-       convert a 'char' to an NSGlyph!!
 
 
-       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".
-     */
-    NSGlyph glyph;
-    {
-      NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
-      [ts setFont:fid->nsfont];
-      NSLayoutManager *lm = [[NSLayoutManager alloc] init];
-
-      /* Without this, the layout manager ends up on a queue somewhere and is
-         referenced again after we return to the command loop.  Since we don't
-         use this layout manager again, by that time it may have been garbage
-         collected, and we crash.  Setting this seems to cause `lm' to no 
-         longer be referenced once we exit this block. */
-      [lm setBackgroundLayoutEnabled:NO];
-
-      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];
-    }
-
-    /* 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.
-     */
+  for (int i = first; i <= last; i++) {
     XCharStruct *cs = &f->per_char[i-first];
 
     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");
+    char s2[2];
+    s2[0] = i;
+    s2[1] = 0;
+    NSString *nsstr = [NSString stringWithCString:s2
+                               encoding:NSISOLatin1StringEncoding];
+    utf8_metrics (fid, nsstr, cs);
 
     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);
@@ -2148,19 +2892,16 @@ query_font (Font fid)
     min->lbearing = MIN (min->lbearing, cs->lbearing);
     min->rbearing = MIN (min->rbearing, cs->rbearing);
 
     min->lbearing = MIN (min->lbearing, cs->lbearing);
     min->rbearing = MIN (min->rbearing, cs->rbearing);
 
-# undef CEIL
-
-#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
   }
   }
-
 }
 
 
 }
 
 
@@ -2173,8 +2914,17 @@ XQueryFont (Display *dpy, Font fid)
   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
   *f = fid->metrics;
 
   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
   *f = fid->metrics;
 
+  // build XFontProps
+  f->n_properties = 1;
+  f->properties = malloc (sizeof(*f->properties) * f->n_properties);
+  f->properties[0].name = XA_FONT;
+  Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
+          "atoms probably needs a real implementation");
+  // If XInternAtom is ever implemented, use it here.
+  f->properties[0].card32 = (char *)fid->xa_font;
+
   // copy XCharStruct array
   // 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));
@@ -2199,6 +2949,7 @@ copy_font (Font fid)
 
   // copy the other pointers
   fid2->ps_name = strdup (fid->ps_name);
 
   // copy the other pointers
   fid2->ps_name = strdup (fid->ps_name);
+  fid2->xa_font = strdup (fid->xa_font);
 //  [fid2->nsfont retain];
   fid2->metrics.fid = fid2;
 
 //  [fid2->nsfont retain];
   fid2->metrics.fid = fid2;
 
@@ -2206,62 +2957,134 @@ copy_font (Font fid)
 }
 
 
 }
 
 
+static NSArray *
+font_family_members (NSString *family_name)
+{
+# ifndef USE_IPHONE
+  return [[NSFontManager sharedFontManager]
+          availableMembersOfFontFamily:family_name];
+# else
+  return [UIFont fontNamesForFamilyName:family_name];
+# endif
+}
+
+
+static NSString *
+default_font_family (NSFontTraitMask require)
+{
+  return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
+}
+
+
 static NSFont *
 static NSFont *
-try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
+try_font (NSFontTraitMask traits, NSFontTraitMask mask,
+          NSString *family_name, float size,
           char **name_ret)
 {
   Assert (size > 0, "zero font size");
           char **name_ret)
 {
   Assert (size > 0, "zero font size");
-  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];
-  if (f)
-    *name_ret = strdup(name);
-  return f;
+  NSArray *family_members = font_family_members (family_name);
+  if (!family_members.count)
+    family_members = font_family_members (default_font_family (traits));
+
+# ifndef USE_IPHONE
+  for (unsigned k = 0; k != family_members.count; ++k) {
+
+    NSArray *member = [family_members objectAtIndex:k];
+    NSFontTraitMask font_mask =
+    [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
+
+    if ((font_mask & mask) == traits) {
+
+      NSString *name = [member objectAtIndex:0];
+      NSFont *f = [NSFont fontWithName:name size:size];
+      if (!f)
+        break;
+
+      /* Don't use this font if it (probably) doesn't include ASCII characters.
+       */
+      NSStringEncoding enc = [f mostCompatibleStringEncoding];
+      if (! (enc == NSUTF8StringEncoding ||
+             enc == NSISOLatin1StringEncoding ||
+             enc == NSNonLossyASCIIStringEncoding ||
+             enc == NSISOLatin2StringEncoding ||
+             enc == NSUnicodeStringEncoding ||
+             enc == NSWindowsCP1250StringEncoding ||
+             enc == NSWindowsCP1252StringEncoding ||
+             enc == NSMacOSRomanStringEncoding)) {
+        // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
+        break;
+      }
+      // NSLog(@"using \"%@\": %d", name, enc);
+
+      // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
+      *name_ret = strdup (name.UTF8String);
+      return f;
+    }
+  }
+# else // USE_IPHONE
+
+  for (NSString *fn in family_members) {
+# define MATCH(X) \
+         ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
+         != NSNotFound)
+
+    // The magic invocation for getting font names is
+    // [[UIFontDescriptor
+    //   fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
+    //  symbolicTraits]
+    // ...but this only works on iOS 7 and later.
+    NSFontTraitMask font_mask = 0;
+    if (MATCH(@"Bold"))
+      font_mask |= NSBoldFontMask;
+    if (MATCH(@"Italic") || MATCH(@"Oblique"))
+      font_mask |= NSItalicFontMask;
+
+    if ((font_mask & mask) == traits) {
+
+      /* 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
+       )
+         break;
+
+      *name_ret = strdup (fn.UTF8String);
+      return [UIFont fontWithName:fn size:size];
+    }
+# undef MATCH
+  }
+
+# endif
+
+  return NULL;
 }
 
 static NSFont *
 }
 
 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, char **xa_font)
 {
   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
@@ -2270,6 +3093,7 @@ try_native_font (const char *name, char **name_ret, float *size_ret)
   if (f) {
     *name_ret = name2;
     *size_ret = size;
   if (f) {
     *name_ret = name2;
     *size_ret = size;
+    *xa_font = strdup (name); // Maybe this should be an XLFD?
     return f;
   } else {
     free (name2);
     return f;
   } else {
     free (name2);
@@ -2281,42 +3105,51 @@ try_native_font (const char *name, char **name_ret, float *size_ret)
 /* Returns a random font in the given size and face.
  */
 static NSFont *
 /* Returns a random font in the given size and face.
  */
 static NSFont *
-random_font (BOOL bold, BOOL ital, float size, char **name_ret)
+random_font (NSFontTraitMask traits, NSFontTraitMask mask,
+             float size, NSString **family_ret, char **name_ret)
 {
 {
-  NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
-                          (ital ? NSItalicFontMask : NSUnitalicFontMask));
-  NSArray *fonts = [[NSFontManager sharedFontManager]
-                     availableFontNamesWithTraits:mask];
-  if (!fonts) return 0;
 
 
-  int n = [fonts count];
+# ifndef USE_IPHONE
+  // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
+  // returns an empty list, at least on a system with default fonts only.
+  NSArray *families = [[NSFontManager sharedFontManager]
+                       availableFontFamilies];
+  if (!families) return 0;
+# else
+  NSArray *families = [UIFont familyNames];
+
+  // There are many dups in the families array -- uniquify it.
+  {
+    NSArray *sorted_families =
+    [families sortedArrayUsingSelector:@selector(compare:)];
+    NSMutableArray *new_families =
+    [NSMutableArray arrayWithCapacity:sorted_families.count];
+
+    NSString *prev_family = nil;
+    for (NSString *family in sorted_families) {
+      if ([family compare:prev_family])
+        [new_families addObject:family];
+    }
+
+    families = new_families;
+  }
+# endif // USE_IPHONE
+
+  long n = [families count];
   if (n <= 0) return 0;
 
   int j;
   for (j = 0; j < n; j++) {
     int i = random() % n;
   if (n <= 0) return 0;
 
   int j;
   for (j = 0; j < n; j++) {
     int i = random() % n;
-    NSString *name = [fonts objectAtIndex:i];
-    NSFont *f = [NSFont fontWithName:name size:size];
-    if (!f) continue;
-
-    /* Don't use this font if it (probably) doesn't include ASCII characters.
-     */
-    NSStringEncoding enc = [f mostCompatibleStringEncoding];
-    if (! (enc == NSUTF8StringEncoding ||
-           enc == NSISOLatin1StringEncoding ||
-           enc == NSNonLossyASCIIStringEncoding ||
-           enc == NSISOLatin2StringEncoding ||
-           enc == NSUnicodeStringEncoding ||
-           enc == NSWindowsCP1250StringEncoding ||
-           enc == NSWindowsCP1252StringEncoding ||
-           enc == NSMacOSRomanStringEncoding)) {
-      // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
-      continue;
+    NSString *family_name = [families objectAtIndex:i];
+
+    NSFont *result = try_font (traits, mask, family_name, size, name_ret);
+    if (result) {
+      [*family_ret release];
+      *family_ret = family_name;
+      [*family_ret retain];
+      return result;
     }
     }
-    // NSLog(@"using \"%@\": %d", name, enc);
-
-    *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
-    return f;
   }
 
   // None of the fonts support ASCII?
   }
 
   // None of the fonts support ASCII?
@@ -2324,87 +3157,169 @@ random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 }
 
 
 }
 
 
+// Fonts need this. XDisplayHeightMM and friends should probably be consistent
+// with this as well if they're ever implemented.
+static const unsigned dpi = 75;
+
+
+static const char *
+xlfd_field_end (const char *s)
+{
+  const char *s2 = strchr(s, '-');
+  if (!s2)
+    s2 = s + strlen(s);
+  return s2;
+}
+
+
+static size_t
+xlfd_next (const char **s, const char **s2)
+{
+  if (!**s2) {
+    *s = *s2;
+  } else {
+    Assert (**s2 == '-', "xlfd parse error");
+    *s = *s2 + 1;
+    *s2 = xlfd_field_end (*s);
+  }
+
+  return *s2 - *s;
+}
+
+
 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, char **xa_font)
 {
   NSFont *nsfont = 0;
 {
   NSFont *nsfont = 0;
-  BOOL bold  = NO;
-  BOOL ital  = NO;
-  BOOL fixed = NO;
-  BOOL serif = NO;
+  NSString *family_name = nil;
+  NSFontTraitMask require = 0, forbid = 0;
   BOOL rand  = NO;
   float size = 0;
   char *ps_name = 0;
 
   const char *s = (name ? name : "");
   BOOL rand  = NO;
   float size = 0;
   char *ps_name = 0;
 
   const char *s = (name ? name : "");
-  while (*s) {
-    while (*s && (*s == '*' || *s == '-'))
-      s++;
-    const char *s2 = s;
-    while (*s2 && (*s2 != '*' && *s2 != '-'))
-      s2++;
-    
-    int L = s2-s;
-    if (s == s2)
-      ;
+
+  size_t L = strlen (s);
 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
-    else if (CMP ("random"))   rand  = YES;
-    else if (CMP ("bold"))     bold  = YES;
-    else if (CMP ("i"))        ital  = YES;
-    else if (CMP ("o"))        ital  = YES;
-    else if (CMP ("courier"))  fixed = YES;
-    else if (CMP ("fixed"))    fixed = YES;
-    else if (CMP ("m"))        fixed = YES;
-    else if (CMP ("times"))    serif = YES;
-    else if (CMP ("6x10"))     fixed = YES, size = 8;
-    else if (CMP ("6x10bold")) fixed = YES, size = 8,  bold = YES;
-    else if (CMP ("9x15"))     fixed = YES, size = 12;
-    else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
-    else if (CMP ("vga"))      fixed = YES, size = 12;
-    else if (CMP ("console"))  fixed = YES, size = 12;
-    else if (CMP ("gallant"))  fixed = YES, size = 12;
-# undef CMP
-    else if (size == 0) {
-      int n = 0;
-      if (1 == sscanf (s, " %d ", &n))
-        size = n / 10.0;
+# define UNSPEC   (L == 0 || L == 1 && *s == '*')
+  if      (CMP ("6x10"))     size = 8,  require |= NSFixedPitchFontMask;
+  else if (CMP ("6x10bold")) size = 8,  require |= NSFixedPitchFontMask | NSBoldFontMask;
+  else if (CMP ("fixed"))    size = 12, require |= NSFixedPitchFontMask;
+  else if (CMP ("9x15"))     size = 12, require |= NSFixedPitchFontMask;
+  else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
+  else if (CMP ("vga"))      size = 12, require |= NSFixedPitchFontMask;
+  else if (CMP ("console"))  size = 12, require |= NSFixedPitchFontMask;
+  else if (CMP ("gallant"))  size = 12, require |= NSFixedPitchFontMask;
+  else {
+
+    // Incorrect fields are ignored.
+
+    if (*s == '-')
+      ++s;
+    const char *s2 = xlfd_field_end(s);
+
+    // Foundry (ignore)
+
+    L = xlfd_next (&s, &s2); // Family name
+    // This used to substitute Georgia for Times. Now it doesn't.
+    if (CMP ("random")) {
+      rand = YES;
+    } else if (CMP ("fixed")) {
+      require |= NSFixedPitchFontMask;
+      family_name = @"Courier";
+    } else if (!UNSPEC) {
+      family_name = [[[NSString alloc] initWithBytes:s
+                                              length:L
+                                            encoding:NSUTF8StringEncoding]
+                     autorelease];
     }
 
     }
 
-    s = s2;
+    L = xlfd_next (&s, &s2); // Weight name
+    if (CMP ("bold") || CMP ("demibold"))
+      require |= NSBoldFontMask;
+    else if (CMP ("medium") || CMP ("regular"))
+      forbid |= NSBoldFontMask;
+
+    L = xlfd_next (&s, &s2); // Slant
+    if (CMP ("i") || CMP ("o"))
+      require |= NSItalicFontMask;
+    else if (CMP ("r"))
+      forbid |= NSItalicFontMask;
+
+    xlfd_next (&s, &s2); // Set width name (ignore)
+    xlfd_next (&s, &s2); // Add style name (ignore)
+
+    xlfd_next (&s, &s2); // Pixel size (ignore)
+
+    xlfd_next (&s, &s2); // Point size
+    char *s3;
+    uintmax_t n = strtoumax(s, &s3, 10);
+    if (s2 == s3)
+      size = n / 10.0;
+
+    xlfd_next (&s, &s2); // Resolution X (ignore)
+    xlfd_next (&s, &s2); // Resolution Y (ignore)
+
+    xlfd_next (&s, &s2); // Spacing
+    if (CMP ("p"))
+      forbid |= NSFixedPitchFontMask;
+    else if (CMP ("m") || CMP ("c"))
+      require |= NSFixedPitchFontMask;
+
+    // Don't care about average_width or charset registry.
   }
   }
+# undef CMP
+# undef UNSPEC
+
+  if (!family_name && !rand)
+    family_name = default_font_family (require);
 
   if (size < 6 || size > 1000)
     size = 12;
 
 
   if (size < 6 || size > 1000)
     size = 12;
 
-  if (rand)
-    nsfont   = random_font (bold, ital, size, &ps_name);
+  size *= scale;
+
+  NSFontTraitMask mask = require | forbid;
+
+  if (rand) {
+    nsfont   = random_font (require, mask, size, &family_name, &ps_name);
+    [family_name autorelease];
+  }
 
   if (!nsfont)
 
   if (!nsfont)
-    nsfont   = try_font (fixed, bold, ital, serif, size, &ps_name);
+    nsfont   = try_font (require, mask, family_name, size, &ps_name);
 
   // if that didn't work, turn off attibutes until it does
   // (e.g., there is no "Monaco-Bold".)
   //
 
   // if that didn't work, turn off attibutes until it does
   // (e.g., there is no "Monaco-Bold".)
   //
-  if (!nsfont && serif) {
-    serif = NO;
-    nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+  if (!nsfont && (mask & NSItalicFontMask)) {
+    require &= ~NSItalicFontMask;
+    mask &= ~NSItalicFontMask;
+    nsfont = try_font (require, mask, family_name, size, &ps_name);
   }
   }
-  if (!nsfont && ital) {
-    ital = NO;
-    nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+  if (!nsfont && (mask & NSBoldFontMask)) {
+    require &= ~NSBoldFontMask;
+    mask &= ~NSBoldFontMask;
+    nsfont = try_font (require, mask, family_name, size, &ps_name);
   }
   }
-  if (!nsfont && bold) {
-    bold = NO;
-    nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
-  }
-  if (!nsfont && fixed) {
-    fixed = NO;
-    nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
+  if (!nsfont && (mask & NSFixedPitchFontMask)) {
+    require &= ~NSFixedPitchFontMask;
+    mask &= ~NSFixedPitchFontMask;
+    nsfont = try_font (require, mask, family_name, size, &ps_name);
   }
 
   if (nsfont) {
     *name_ret = ps_name;
     *size_ret = size;
   }
 
   if (nsfont) {
     *name_ret = ps_name;
     *size_ret = size;
+    float actual_size = size / scale;
+    asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
+             family_name.UTF8String,
+             (require & NSBoldFontMask) ? "bold" : "medium",
+             (require & NSItalicFontMask) ? 'o' : 'r',
+             (unsigned)(dpi * actual_size / 72.27 + 0.5),
+             (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
+             (require & NSFixedPitchFontMask) ? 'm' : 'p');
     return nsfont;
   } else {
     return 0;
     return nsfont;
   } else {
     return 0;
@@ -2417,7 +3332,23 @@ 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);
+  float scale = 1;
+
+# ifdef USE_IPHONE
+  /* Since iOS screens are physically smaller than desktop screens, scale up
+     the fonts to make them more readable.
+
+     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->dpy = dpy;
+  fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
+                                 &fid->xa_font);
 
   if (!fid->nsfont && name &&
       strchr (name, ' ') &&
 
   if (!fid->nsfont && name &&
       strchr (name, ' ') &&
@@ -2430,12 +3361,13 @@ XLoadFont (Display *dpy, const char *name)
   }
 
   if (! fid->nsfont)
   }
 
   if (! fid->nsfont)
-    fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
+    fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size,
+                                 &fid->xa_font);
 
   // We should never return NULL for XLFD fonts.
   if (!fid->nsfont) {
 
   // We should never return NULL for XLFD fonts.
   if (!fid->nsfont) {
-    NSLog(@"no NSFont for \"%s\"", name);
-    abort();
+    Assert (0, "no font");
+    return 0;
   }
   CFRetain (fid->nsfont);   // needed for garbage collection?
 
   }
   CFRetain (fid->nsfont);   // needed for garbage collection?
 
@@ -2486,8 +3418,10 @@ XFreeFontInfo (char **names, XFontStruct *info, int n)
   }
   if (info) {
     for (i = 0; i < n; i++)
   }
   if (info) {
     for (i = 0; i < n; i++)
-      if (info[i].per_char)
+      if (info[i].per_char) {
         free (info[i].per_char);
         free (info[i].per_char);
+        free (info[i].properties);
+      }
     free (info);
   }
   return 0;
     free (info);
   }
   return 0;
@@ -2514,28 +3448,132 @@ XSetFont (Display *dpy, GC gc, Font fid)
   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;
+  XFontStruct *f = XLoadQueryFont (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)
+{
+  XFreeFont (dpy, set->font);
+  free (set);
+}
+
+
+const char *
+jwxyz_nativeFontName (Font f, float *size)
+{
+  if (size) *size = f->size;
+  return f->ps_name;
+}
+
+
+void
+XFreeStringList (char **list)
+{
+  int i;
+  if (!list) return;
+  for (i = 0; list[i]; i++)
+    XFree (list[i]);
+  XFree (list);
+}
+
+
+// Returns the verbose Unicode name of this character, like "agrave" or
+// "daggerdouble".  Used by fontglide debugMetrics.
+//
+char *
+jwxyz_unicode_character_name (Font fid, unsigned long uc)
+{
+  char *ret = 0;
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+
+  CGGlyph cgglyph;
+  if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
+    NSString *name = (NSString *)
+      CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+                                   cgglyph);
+    ret = (name ? strdup ([name UTF8String]) : 0);
+  }
+
+  CFRelease (ctfont);
+  return ret;
+}
+
+
+// Given a UTF8 string, return an NSString.  Bogus UTF8 characters are ignored.
+// We have to do this because stringWithCString returns NULL if there are
+// any invalid characters at all.
+//
+static NSString *
+sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
+{
+  int out_len = in_len * 4;   // length of string might increase
+  char *s2 = (char *) malloc (out_len);
+  char *out = s2;
+  const char *in_end  = in  + in_len;
+  const char *out_end = out + out_len;
+  Bool latin1_p = True;
+
+  while (in < in_end)
+    {
+      unsigned long uc;
+      long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
+      long L2 = utf8_encode (uc, out, out_end - out);
+      in  += L1;
+      out += L2;
+      if (uc > 255) latin1_p = False;
+    }
+  *out = 0;
+  NSString *nsstr =
+    [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
+  free (s2);
+  if (latin1_pP) *latin1_pP = latin1_p;
+  return (nsstr ? nsstr : @"");
+}
+
+
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
               XCharStruct *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++) {
-    unsigned char c = (unsigned char) s[i];
-    if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
-      c = f->default_char;
-    const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
-    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;
-    }
-  }
+  // Unfortunately, adding XCharStructs together to get the extents for a
+  // string doesn't work: Cocoa uses non-integral character advancements, but
+  // XCharStruct.width is an integer. Plus that doesn't take into account
+  // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
+  // Zapfino.
+
+  NSString *nsstr = [[[NSString alloc] initWithBytes:s
+                                              length:length
+                                            encoding:NSISOLatin1StringEncoding]
+                     autorelease];
+  utf8_metrics (f->fid, nsstr, cs);
   *dir_ret = 0;
   *ascent_ret  = f->ascent;
   *descent_ret = f->descent;
   *dir_ret = 0;
   *ascent_ret  = f->ascent;
   *descent_ret = f->descent;
@@ -2552,88 +3590,151 @@ 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];
-    CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
+  Bool latin1_p = True;
+  int i, utf8_len = 0;
+  char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
+
+  for (i = 0; i < length; i++)
+    if (s[i].byte1 > 0) {
+      latin1_p = False;
+      break;
+    }
+
+  {
+    NSString *nsstr = [NSString stringWithCString:utf8
+                                encoding:NSUTF8StringEncoding];
+    utf8_metrics (f->fid, nsstr, cs);
   }
   }
-  CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
-  CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+
+  *dir_ret = 0;
+  *ascent_ret  = f->ascent;
+  *descent_ret = f->descent;
+  free (utf8);
+  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;
-    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);
-  }
+/* "Returns the distance in pixels in the primary draw direction from
+   the drawing origin to the origin of the next character to be drawn."
 
 
-  CGRect wr = d->frame;
+   "overall_ink_return is set to the bbox of the string's character ink."
 
 
-# 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)!
-   */
+   "The overall_ink_return for a nondescending, horizontally drawn Latin
+   character is conventionally entirely above the baseline; that is,
+   overall_ink_return.height <= -overall_ink_return.y."
 
 
-  push_fg_gc (d, gc, YES);
-  set_font (d->cgc, gc);
+     [So this means that y is the top of the ink, and height grows down:
+      For above-the-baseline characters, y is negative.]
 
 
-  CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
-  if (gc->gcv.antialias_p)
-    CGContextSetShouldAntialias (d->cgc, YES);
-  CGContextShowTextAtPoint (d->cgc,
-                            wr.origin.x + x,
-                            wr.origin.y + wr.size.height - y,
-                            str, len);
-  pop_gc (d, gc);
+   "The overall_ink_return for a nonkerned character is entirely at, and to
+   the right of, the origin; that is, overall_ink_return.x >= 0."
 
 
-# else /* !0 */
+     [So this means that x is the left of the ink, and width grows right.
+      For left-of-the-origin characters, x is negative.]
 
 
-  /* The Cocoa way...
+   "A character consisting of a single pixel at the origin would set
+   overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
+ */
+int
+Xutf8TextExtents (XFontSet set, const char *str, int len,
+                  XRectangle *overall_ink_return,
+                  XRectangle *overall_logical_return)
+{
+  Bool latin1_p;
+  NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
+  XCharStruct cs;
+
+  utf8_metrics (set->font->fid, nsstr, &cs);
+
+  /* "The overall_logical_return is the bounding box that provides minimum
+     spacing to other graphical features for the string. Other graphical
+     features, for example, a border surrounding the text, should not
+     intersect this rectangle."
+
+     So I think that means they're the same?  Or maybe "ink" is the bounding
+     box, and "logical" is the advancement?  But then why is the return value
+     the advancement?
    */
    */
+  if (overall_ink_return)
+    XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
+  if (overall_logical_return)
+    XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
+
+  return cs.width;
+}
+
+
+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));
-  float a = ((argb >> 24) & 0xFF) / 255.0;
-  float r = ((argb >> 16) & 0xFF) / 255.0;
-  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 rgba[4];
+  query_color_float (dpy, argb, rgba);
+  NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
+                                      green:rgba[1]
+                                       blue:rgba[2]
+                                      alpha:rgba[3]];
+
+  if (!gc->gcv.font) {
+    Assert (0, "no font");
+    return 1;
+  }
+
+  /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
+      NSFontAttributeName, and NSAttributedString are only present on iOS 6
+      and later.  We could resurrect the Quartz code from v5.29 and do a
+      runtime conditional on that, but that would be a pain in the ass.
+      Probably time to just make iOS 6 a requirement.
+   */
+
   NSDictionary *attr =
     [NSDictionary dictionaryWithObjectsAndKeys:
                     gc->gcv.font->nsfont, NSFontAttributeName,
                     fg, NSForegroundColorAttributeName,
                   nil];
   NSDictionary *attr =
     [NSDictionary dictionaryWithObjectsAndKeys:
                     gc->gcv.font->nsfont, NSFontAttributeName,
                     fg, NSForegroundColorAttributeName,
                   nil];
-  char *s2 = (char *) malloc (len + 1);
-  strncpy (s2, str, len);
-  s2[len] = 0;
-  NSString *nsstr = [NSString stringWithCString:s2
-                                         encoding:NSISOLatin1StringEncoding];
-  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];
 
 
-# endif  /* 0 */
+  // Don't understand why we have to do both set_color and
+  // NSForegroundColorAttributeName, but we do.
+  //
+  set_color (dpy, cgc, argb, 32, NO, YES);
+
+  NSAttributedString *astr = [[NSAttributedString alloc]
+                               initWithString:nsstr
+                                   attributes:attr];
+  CTLineRef dl = CTLineCreateWithAttributedString (
+                   (__bridge CFAttributedStringRef) astr);
+
+  // Not sure why this is necessary, but xoff is positive when the first
+  // character on the line has a negative lbearing.  Without this, the
+  // string is rendered with the first ink at 0 instead of at lbearing.
+  // I have not seen xoff be negative, so I'm not sure if that can happen.
+  //
+  // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
+  // a letter.
+  //
+  CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
+  Assert (xoff >= 0, "unexpected CTLineOffset");
+  x -= xoff;
+
+  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;
 
   invalidate_drawable_cache (d);
   return 0;
@@ -2644,21 +3745,67 @@ int
 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
              const char  *str, int len)
 {
 XDrawString (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, NO);
+  char *s2 = (char *) malloc (len + 1);
+  strncpy (s2, str, len);
+  s2[len] = 0;
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSISOLatin1StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
+  return ret;
+}
+
+
+int
+XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
+             const XChar2b *str, int len)
+{
+  char *s2 = XChar2b_to_utf8 (str, 0);   // already sanitized
+  NSString *nsstr =
+    [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
+  return ret;
+}
+
+
+void
+Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, 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 = sanitize_utf8 (str, len, 0);
+  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);
 }
 
 
 int
 XSetForeground (Display *dpy, GC gc, unsigned long fg)
 {
 }
 
 
 int
 XSetForeground (Display *dpy, GC gc, unsigned long fg)
 {
-  validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
+  validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p);
   gc->gcv.foreground = fg;
   return 0;
 }
   gc->gcv.foreground = fg;
   return 0;
 }
@@ -2667,7 +3814,7 @@ XSetForeground (Display *dpy, GC gc, unsigned long fg)
 int
 XSetBackground (Display *dpy, GC gc, unsigned long bg)
 {
 int
 XSetBackground (Display *dpy, GC gc, unsigned long bg)
 {
-  validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
+  validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p);
   gc->gcv.background = bg;
   return 0;
 }
   gc->gcv.background = bg;
   return 0;
 }
@@ -2731,7 +3878,8 @@ XSetClipMask (Display *dpy, GC gc, Pixmap m)
 
   gc->gcv.clip_mask = copy_pixmap (dpy, m);
   if (gc->gcv.clip_mask)
 
   gc->gcv.clip_mask = copy_pixmap (dpy, m);
   if (gc->gcv.clip_mask)
-    gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
+    gc->clip_mask =
+      CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
   else
     gc->clip_mask = 0;
 
   else
     gc->clip_mask = 0;
 
@@ -2752,7 +3900,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
@@ -2794,8 +3953,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;
@@ -2807,7 +3967,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
@@ -2842,6 +4011,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;
@@ -2862,11 +4032,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);
 }
 
 
 }
 
 
@@ -2900,7 +4084,7 @@ visual_depth (Screen *s, Visual *v)
 int
 visual_cells (Screen *s, Visual *v)
 {
 int
 visual_cells (Screen *s, Visual *v)
 {
-  return 0xFFFFFF;
+  return (int)(v->red_mask | v->green_mask | v->blue_mask);
 }
 
 int
 }
 
 int
@@ -2909,6 +4093,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)