From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / OSX / jwxyz.m
1 /* xscreensaver, Copyright (c) 1991-2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* JWXYZ Is Not Xlib.
13
14    But it's a bunch of function definitions that bear some resemblance to
15    Xlib and that do Cocoa-ish things that bear some resemblance to the
16    things that Xlib might have done.
17  */
18
19 #import <stdlib.h>
20 #import <stdint.h>
21 #import <wchar.h>
22
23 #ifdef USE_IPHONE
24 # import <UIKit/UIKit.h>
25 # import <UIKit/UIScreen.h>
26 # import <QuartzCore/QuartzCore.h>
27 # define NSView  UIView
28 # define NSRect  CGRect
29 # define NSPoint CGPoint
30 # define NSSize  CGSize
31 # define NSColor UIColor
32 # define NSImage UIImage
33 # define NSEvent UIEvent
34 # define NSFont  UIFont
35 # define NSGlyph CGGlyph
36 # define NSWindow UIWindow
37 # define NSMakeSize   CGSizeMake
38 # define NSBezierPath UIBezierPath
39 # define colorWithDeviceRed colorWithRed
40 #else
41 # import <Cocoa/Cocoa.h>
42 #endif
43
44 #import <CoreText/CTFont.h>
45 #import <CoreText/CTLine.h>
46
47 #import "jwxyz.h"
48 #import "jwxyz-timers.h"
49 #import "yarandom.h"
50
51 # define USE_BACKBUFFER  /* must be in sync with XScreenSaverView.h */
52
53 #undef  Assert
54 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
55
56 # undef MAX
57 # undef MIN
58 # define MAX(a,b) ((a)>(b)?(a):(b))
59 # define MIN(a,b) ((a)<(b)?(a):(b))
60
61
62 struct jwxyz_Drawable {
63   enum { WINDOW, PIXMAP } type;
64   CGContextRef cgc;
65   CGImageRef cgi;
66   CGRect frame;
67   union {
68     struct {
69       NSView *view;
70       unsigned long background;
71       int last_mouse_x, last_mouse_y;
72     } window;
73     struct {
74       int depth;
75       void *cgc_buffer;         // the bits to which CGContextRef renders
76     } pixmap;
77   };
78 };
79
80 struct jwxyz_Display {
81   Window main_window;
82   Screen *screen;
83   int screen_count;
84   struct jwxyz_sources_data *timers_data;
85
86 # ifndef USE_IPHONE
87   CGDirectDisplayID cgdpy;  /* ...of the one and only Window, main_window.
88                                This can change if the window is dragged to
89                                a different screen. */
90 # endif
91
92   CGColorSpaceRef colorspace;  /* Color space of this screen.  We tag all of
93                                   our images with this to avoid translation
94                                   when rendering. */
95 };
96
97 struct jwxyz_Screen {
98   Display *dpy;
99   Visual *visual;
100   int screen_number;
101 };
102
103 struct jwxyz_GC {
104   XGCValues gcv;
105   unsigned int depth;
106   CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
107 };
108
109 struct jwxyz_Font {
110   char *ps_name;
111   NSFont *nsfont;
112   float size;   // points
113
114   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
115   // But we need the metrics on both of them, so they go here.
116   XFontStruct metrics;
117 };
118
119
120 /* Instead of calling abort(), throw a real exception, so that
121    XScreenSaverView can catch it and display a dialog.
122  */
123 void
124 jwxyz_abort (const char *fmt, ...)
125 {
126   char s[10240];
127   if (!fmt || !*fmt)
128     strcpy (s, "abort");
129   else
130     {
131       va_list args;
132       va_start (args, fmt);
133       vsprintf (s, fmt, args);
134       va_end (args);
135     }
136   [[NSException exceptionWithName: NSInternalInconsistencyException
137                 reason: [NSString stringWithCString: s
138                                   encoding:NSUTF8StringEncoding]
139                 userInfo: nil]
140     raise];
141   abort();  // not reached
142 }
143
144
145 /* We keep a list of all of the Displays that have been created and not
146    yet freed so that they can have sensible display numbers.  If three
147    displays are created (0, 1, 2) and then #1 is closed, then the fourth
148    display will be given the now-unused display number 1. (Everything in
149    here assumes a 1:1 Display/Screen mapping.)
150
151    The size of this array is the most number of live displays at one time.
152    So if it's 20, then we'll blow up if the system has 19 monitors and also
153    has System Preferences open (the small preview window).
154
155    Note that xlockmore-style savers tend to allocate big structures, so
156    setting this to 1000 will waste a few megabytes.  Also some of them assume
157    that the number of screens never changes, so dynamically expanding this
158    array won't work.
159  */
160 # ifndef USE_IPHONE
161 static Display *jwxyz_live_displays[20] = { 0, };
162 # endif
163
164
165 Display *
166 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
167 {
168   CGContextRef cgc = (CGContextRef) cgc_arg;
169   NSView *view = (NSView *) nsview_arg;
170   Assert (view, "no view");
171   if (!view) return 0;
172
173   Display *d = (Display *) calloc (1, sizeof(*d));
174   d->screen = (Screen *) calloc (1, sizeof(Screen));
175   d->screen->dpy = d;
176   
177   d->screen_count = 1;
178   d->screen->screen_number = 0;
179 # ifndef USE_IPHONE
180   {
181     // Find the first empty slot in live_displays and plug us in.
182     int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
183     int i;
184     for (i = 0; i < size; i++) {
185       if (! jwxyz_live_displays[i])
186         break;
187     }
188     if (i >= size) abort();
189     jwxyz_live_displays[i] = d;
190     d->screen_count = size;
191     d->screen->screen_number = i;
192   }
193 # endif // !USE_IPHONE
194
195   Visual *v = (Visual *) calloc (1, sizeof(Visual));
196   v->class      = TrueColor;
197   v->red_mask   = 0x00FF0000;
198   v->green_mask = 0x0000FF00;
199   v->blue_mask  = 0x000000FF;
200   v->bits_per_rgb = 8;
201   d->screen->visual = v;
202   
203   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
204
205
206   Window w = (Window) calloc (1, sizeof(*w));
207   w->type = WINDOW;
208   w->window.view = view;
209   CFRetain (w->window.view);   // needed for garbage collection?
210   w->window.background = BlackPixel(0,0);
211
212   d->main_window = w;
213
214 # ifndef USE_IPHONE
215   if (! cgc) {
216     [view lockFocus];
217     cgc = [[[view window] graphicsContext] graphicsPort];
218     [view unlockFocus];
219     w->cgc = cgc;
220   }
221 # endif
222
223   Assert (cgc, "no CGContext");
224   return d;
225 }
226
227 void
228 jwxyz_free_display (Display *dpy)
229 {
230   jwxyz_sources_free (dpy->timers_data);
231   
232 # ifndef USE_IPHONE
233   {
234     // Find us in live_displays and clear that slot.
235     int size = ScreenCount(dpy);
236     int i;
237     for (i = 0; i < size; i++) {
238       if (dpy == jwxyz_live_displays[i]) {
239         jwxyz_live_displays[i] = 0;
240         break;
241       }
242     }
243     if (i >= size) abort();
244   }
245 # endif // !USE_IPHONE
246
247   free (dpy->screen->visual);
248   free (dpy->screen);
249   CFRelease (dpy->main_window->window.view);
250   free (dpy->main_window);
251   free (dpy);
252 }
253
254
255 void *
256 jwxyz_window_view (Window w)
257 {
258   Assert (w && w->type == WINDOW, "not a window");
259   return w->window.view;
260 }
261
262
263 /* Call this after any modification to the bits on a Pixmap or Window.
264    Most Pixmaps are used frequently as sources and infrequently as
265    destinations, so it pays to cache the data as a CGImage as needed.
266  */
267 static void
268 invalidate_drawable_cache (Drawable d)
269 {
270   if (d && d->cgi) {
271     CGImageRelease (d->cgi);
272     d->cgi = 0;
273   }
274 }
275
276
277 /* Call this when the View changes size or position.
278  */
279 void
280 jwxyz_window_resized (Display *dpy, Window w, 
281                       int new_x, int new_y, int new_width, int new_height,
282                       void *cgc_arg)
283 {
284   CGContextRef cgc = (CGContextRef) cgc_arg;
285   Assert (w && w->type == WINDOW, "not a window");
286   w->frame.origin.x    = new_x;
287   w->frame.origin.y    = new_y;
288   w->frame.size.width  = new_width;
289   w->frame.size.height = new_height;
290
291   if (cgc) w->cgc = cgc;
292   Assert (w->cgc, "no CGContext");
293
294 # ifndef USE_IPHONE
295   // Figure out which screen the window is currently on.
296   {
297     int wx, wy;
298     XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
299     CGPoint p;
300     p.x = wx;
301     p.y = wy;
302     CGDisplayCount n;
303     dpy->cgdpy = 0;
304     CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
305     // Auuugh!
306     if (! dpy->cgdpy) {
307       p.x = p.y = 0;
308       CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
309     }
310     Assert (dpy->cgdpy, "unable to find CGDisplay");
311   }
312 # endif // USE_IPHONE
313
314 # ifndef USE_BACKBUFFER
315   // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
316   // then this one's faster.
317
318   {
319     // Figure out this screen's colorspace, and use that for every CGImage.
320     //
321     CMProfileRef profile = 0;
322     CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
323     Assert (profile, "unable to find colorspace profile");
324     dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
325     Assert (dpy->colorspace, "unable to find colorspace");
326   }
327 # else  // USE_BACKBUFFER
328
329   // WTF?  It's faster if we *do not* use the screen's colorspace!
330   //
331   dpy->colorspace = CGColorSpaceCreateDeviceRGB();
332 # endif // USE_BACKBUFFER
333
334   invalidate_drawable_cache (w);
335 }
336
337
338 #ifdef USE_IPHONE
339 void
340 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
341 {
342   Assert (w && w->type == WINDOW, "not a window");
343   w->window.last_mouse_x = x;
344   w->window.last_mouse_y = y;
345 }
346 #endif // USE_IPHONE
347
348
349 void
350 jwxyz_flush_context (Display *dpy)
351 {
352   // This is only used when USE_BACKBUFFER is off.
353   CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
354 }
355
356 jwxyz_sources_data *
357 display_sources_data (Display *dpy)
358 {
359   return dpy->timers_data;
360 }
361
362
363 Window
364 XRootWindow (Display *dpy, int screen)
365 {
366   return dpy->main_window;
367 }
368
369 Screen *
370 XDefaultScreenOfDisplay (Display *dpy)
371 {
372   return dpy->screen;
373 }
374
375 Visual *
376 XDefaultVisualOfScreen (Screen *screen)
377 {
378   return screen->visual;
379 }
380
381 Display *
382 XDisplayOfScreen (Screen *s)
383 {
384   return s->dpy;
385 }
386
387 int
388 XDisplayNumberOfScreen (Screen *s)
389 {
390   return 0;
391 }
392
393 int
394 XScreenNumberOfScreen (Screen *s)
395 {
396   return s->screen_number;
397 }
398
399 int
400 jwxyz_ScreenCount (Display *dpy)
401 {
402   return dpy->screen_count;
403 }
404
405 int
406 XDisplayWidth (Display *dpy, int screen)
407 {
408   return (int) dpy->main_window->frame.size.width;
409 }
410
411 int
412 XDisplayHeight (Display *dpy, int screen)
413 {
414   return (int) dpy->main_window->frame.size.height;
415 }
416
417 static void
418 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
419 {
420   if (depth == 1)
421     Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
422   else if (!alpha_allowed_p)
423     Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
424             "bogus color pixel");
425 }
426
427
428 static void
429 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
430            BOOL alpha_allowed_p, BOOL fill_p)
431 {
432   validate_pixel (argb, depth, alpha_allowed_p);
433   if (depth == 1) {
434     if (fill_p)
435       CGContextSetGrayFillColor   (cgc, (argb ? 1.0 : 0.0), 1.0);
436     else
437       CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
438   } else {
439     float a = ((argb >> 24) & 0xFF) / 255.0;
440     float r = ((argb >> 16) & 0xFF) / 255.0;
441     float g = ((argb >>  8) & 0xFF) / 255.0;
442     float b = ((argb      ) & 0xFF) / 255.0;
443     if (fill_p)
444       CGContextSetRGBFillColor   (cgc, r, g, b, a);
445     else
446       CGContextSetRGBStrokeColor (cgc, r, g, b, a);
447   }
448 }
449
450 static void
451 set_line_mode (CGContextRef cgc, XGCValues *gcv)
452 {
453   CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
454   CGContextSetLineJoin  (cgc,
455                          gcv->join_style == JoinMiter ? kCGLineJoinMiter :
456                          gcv->join_style == JoinRound ? kCGLineJoinRound :
457                          kCGLineJoinBevel);
458   CGContextSetLineCap   (cgc, 
459                          gcv->cap_style == CapNotLast ? kCGLineCapButt  :
460                          gcv->cap_style == CapButt    ? kCGLineCapButt  :
461                          gcv->cap_style == CapRound   ? kCGLineCapRound :
462                          kCGLineCapSquare);
463 }
464
465 static void
466 set_clip_mask (Drawable d, GC gc)
467 {
468   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
469
470   Pixmap p = gc->gcv.clip_mask;
471   if (!p) return;
472   Assert (p->type == PIXMAP, "not a pixmap");
473
474   CGRect wr = d->frame;
475   CGRect to;
476   to.origin.x    = wr.origin.x + gc->gcv.clip_x_origin;
477   to.origin.y    = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
478                     - p->frame.size.height;
479   to.size.width  = p->frame.size.width;
480   to.size.height = p->frame.size.height;
481
482   CGContextClipToMask (d->cgc, to, gc->clip_mask);
483 }
484
485
486 /* Pushes a GC context; sets BlendMode and ClipMask.
487  */
488 static void
489 push_gc (Drawable d, GC gc)
490 {
491   CGContextRef cgc = d->cgc;
492   CGContextSaveGState (cgc);
493
494   switch (gc->gcv.function) {
495     case GXset:
496     case GXclear:
497     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
498     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
499     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
500     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
501     default: Assert(0, "unknown gcv function"); break;
502   }
503
504   if (gc->gcv.clip_mask)
505     set_clip_mask (d, gc);
506 }
507
508 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
509
510
511 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
512  */
513 static void
514 push_color_gc (Drawable d, GC gc, unsigned long color, 
515                BOOL antialias_p, Bool fill_p)
516 {
517   push_gc (d, gc);
518
519   int depth = gc->depth;
520   switch (gc->gcv.function) {
521     case GXset:   color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
522     case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
523   }
524
525   CGContextRef cgc = d->cgc;
526   set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
527   CGContextSetShouldAntialias (cgc, antialias_p);
528 }
529
530
531 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
532  */
533 static void
534 push_fg_gc (Drawable d, GC gc, Bool fill_p)
535 {
536   push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
537 }
538
539 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
540  */
541 static void
542 push_bg_gc (Drawable d, GC gc, Bool fill_p)
543 {
544   push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
545 }
546
547
548
549 /* You've got to be fucking kidding me!
550
551    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
552    with repeated calls to CGContextDrawImage than it is to make a single
553    call to CGContextFillRects() with a list of 1x1 rectangles!
554
555    I still wouldn't call it *fast*, however...
556  */
557 #define XDRAWPOINTS_IMAGES
558
559 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
560    the bitmap data directly is faster.  This only works on Pixmaps, though,
561    not Windows.  (Fortunately, on iOS, the Window is really a Pixmap.)
562  */
563 #define XDRAWPOINTS_CGDATA
564
565 int
566 XDrawPoints (Display *dpy, Drawable d, GC gc, 
567              XPoint *points, int count, int mode)
568 {
569   int i;
570   CGRect wr = d->frame;
571
572   push_fg_gc (d, gc, YES);
573
574 # ifdef XDRAWPOINTS_CGDATA
575
576 #  ifdef USE_BACKBUFFER
577   if (1)  // Because of the backbuffer, all iPhone Windows work like Pixmaps.
578 #  else
579   if (d->type == PIXMAP)
580 #  endif
581   {
582     CGContextRef cgc = d->cgc;
583     void *data = CGBitmapContextGetData (cgc);
584     size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
585     size_t w = CGBitmapContextGetWidth (cgc);
586     size_t h = CGBitmapContextGetHeight (cgc);
587
588     Assert (data, "no bitmap data in Drawable");
589
590     unsigned long argb = gc->gcv.foreground;
591     validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
592     if (gc->depth == 1)
593       argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
594
595     CGFloat x0 = wr.origin.x;
596     CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
597
598     // It's uglier, but faster, to hoist the conditional out of the loop.
599     if (mode == CoordModePrevious) {
600       CGFloat x = x0, y = y0;
601       for (i = 0; i < count; i++, points++) {
602         x += points->x;
603         y += points->y;
604
605         if (x >= 0 && x < w && y >= 0 && y < h) {
606           unsigned int *p = (unsigned int *)
607             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
608           *p = (unsigned int) argb;
609         }
610       }
611     } else {
612       for (i = 0; i < count; i++, points++) {
613         CGFloat x = x0 + points->x;
614         CGFloat y = y0 + points->y;
615
616         if (x >= 0 && x < w && y >= 0 && y < h) {
617           unsigned int *p = (unsigned int *)
618             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
619           *p = (unsigned int) argb;
620         }
621       }
622     }
623
624   } else        /* d->type == WINDOW */
625
626 # endif /* XDRAWPOINTS_CGDATA */
627   {
628
629 # ifdef XDRAWPOINTS_IMAGES
630
631     unsigned int argb = gc->gcv.foreground;
632     validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
633     if (gc->depth == 1)
634       argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
635
636     CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
637                                                            NULL);
638     CGImageRef cgi = CGImageCreate (1, 1,
639                                     8, 32, 4,
640                                     dpy->colorspace, 
641                                     /* Host-ordered, since we're using the
642                                        address of an int as the color data. */
643                                     (kCGImageAlphaNoneSkipFirst | 
644                                      kCGBitmapByteOrder32Host),
645                                     prov, 
646                                     NULL,  /* decode[] */
647                                     NO, /* interpolate */
648                                     kCGRenderingIntentDefault);
649     CGDataProviderRelease (prov);
650
651     CGContextRef cgc = d->cgc;
652     CGRect rect;
653     rect.size.width = rect.size.height = 1;
654     for (i = 0; i < count; i++) {
655       if (i > 0 && mode == CoordModePrevious) {
656         rect.origin.x += points->x;
657         rect.origin.x -= points->y;
658       } else {
659         rect.origin.x = wr.origin.x + points->x;
660         rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
661       }
662
663       //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
664       CGContextDrawImage (cgc, rect, cgi);
665       points++;
666     }
667
668     CGImageRelease (cgi);
669
670 # else /* ! XDRAWPOINTS_IMAGES */
671
672     CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
673     CGRect *r = rects;
674   
675     for (i = 0; i < count; i++) {
676       r->size.width = r->size.height = 1;
677       if (i > 0 && mode == CoordModePrevious) {
678         r->origin.x = r[-1].origin.x + points->x;
679         r->origin.y = r[-1].origin.x - points->y;
680       } else {
681         r->origin.x = wr.origin.x + points->x;
682         r->origin.y = wr.origin.y + wr.size.height - points->y;
683       }
684       points++;
685       r++;
686     }
687
688     CGContextFillRects (d->cgc, rects, count);
689     free (rects);
690
691 # endif /* ! XDRAWPOINTS_IMAGES */
692   }
693
694   pop_gc (d, gc);
695   invalidate_drawable_cache (d);
696
697   return 0;
698 }
699
700
701 int
702 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
703 {
704   XPoint p;
705   p.x = x;
706   p.y = y;
707   return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
708 }
709
710
711 static void draw_rect (Display *, Drawable, GC, 
712                        int x, int y, unsigned int width, unsigned int height, 
713                        BOOL foreground_p, BOOL fill_p);
714
715 static Bool
716 bitmap_context_p (Drawable d)
717 {
718 # ifdef USE_BACKBUFFER
719   return True;
720 # else
721   // Because of the backbuffer, all iPhone Windows work like Pixmaps.
722   return d->type == PIXMAP;
723 # endif
724 }
725
726 static void
727 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
728                   size_t fill_width, size_t fill_height)
729 {
730   Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
731   while (fill_height) {
732     // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
733     wmemset (dst, fill_data, fill_width);
734     --fill_height;
735     dst = (char *) dst + dst_pitch;
736   }
737 }
738
739 static void *
740 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
741 {
742   return (char *)dst + dst_pitch * y + x * 4;
743 }
744
745 static unsigned int
746 drawable_depth (Drawable d)
747 {
748   return (d->type == WINDOW
749           ? visual_depth (NULL, NULL)
750           : d->pixmap.depth);
751 }
752
753
754 int
755 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
756            int src_x, int src_y, 
757            unsigned int width, unsigned int height, 
758            int dst_x, int dst_y)
759 {
760   Assert (gc, "no GC");
761   Assert ((width  < 65535), "improbably large width");
762   Assert ((height < 65535), "improbably large height");
763   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
764   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
765   Assert ((dst_x  < 65535 && dst_x  > -65535), "improbably large dst_x");
766   Assert ((dst_y  < 65535 && dst_y  > -65535), "improbably large dst_y");
767
768   if (width == 0 || height == 0)
769     return 0;
770
771   if (gc->gcv.function == GXset ||
772       gc->gcv.function == GXclear) {
773     // "set" and "clear" are dumb drawing modes that ignore the source
774     // bits and just draw solid rectangles.
775     set_color (dst->cgc,
776                (gc->gcv.function == GXset
777                 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
778                 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
779                gc->depth, gc->gcv.alpha_allowed_p, YES);
780     draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
781     return 0;
782   }
783
784   CGRect src_frame, dst_frame;   // Sizes and origins of the two drawables
785   CGRect src_rect,  dst_rect;    // The two rects to draw, clipped to the
786                                  //  bounds of their drawables.
787   BOOL clipped = NO;             // Whether we did any clipping of the rects.
788
789   src_frame = src->frame;
790   dst_frame = dst->frame;
791   
792   // Initialize src_rect...
793   //
794   src_rect.origin.x    = src_frame.origin.x + src_x;
795   src_rect.origin.y    = src_frame.origin.y + src_frame.size.height
796                           - height - src_y;
797   if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
798   src_rect.size.width  = width;
799   src_rect.size.height = height;
800   
801   // Initialize dst_rect...
802   //
803   dst_rect.origin.x    = dst_frame.origin.x + dst_x;
804   dst_rect.origin.y    = dst_frame.origin.y + dst_frame.size.height
805                           - height - dst_y;
806   if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
807   dst_rect.size.width  = width;
808   dst_rect.size.height = height;
809   
810   // Clip rects to frames...
811   //
812
813 # define CLIP(THIS,THAT,VAL,SIZE) do { \
814   float off = THIS##_rect.origin.VAL; \
815   if (off < 0) { \
816     clipped = YES; \
817     THIS##_rect.size.SIZE  += off; \
818     THAT##_rect.size.SIZE  += off; \
819     THIS##_rect.origin.VAL -= off; \
820     THAT##_rect.origin.VAL -= off; \
821   } \
822   off = (( THIS##_rect.origin.VAL +  THIS##_rect.size.SIZE) - \
823          (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
824   if (off > 0) { \
825     clipped = YES; \
826     THIS##_rect.size.SIZE  -= off; \
827     THAT##_rect.size.SIZE  -= off; \
828   }} while(0)
829
830   CLIP (dst, src, x, width);
831   CLIP (dst, src, y, height);
832
833   // Not actually the original dst_rect, just the one before it's clipped to
834   // the src_frame.
835   CGRect orig_dst_rect = dst_rect;
836
837   CLIP (src, dst, x, width);
838   CLIP (src, dst, y, height);
839 # undef CLIP
840
841   if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
842     return 0;
843
844   // Sort-of-special case where no pixels can be grabbed from the source,
845   // and the whole destination is filled with the background color.
846   if (src_rect.size.width < 0 || src_rect.size.height < 0) {
847     
848     Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
849            (int)src_rect.size.height == (int)dst_rect.size.height,
850            "size mismatch");
851     
852     src_rect.size.width  = 0;
853     src_rect.size.height = 0;
854     dst_rect.size.width  = 0;
855     dst_rect.size.height = 0;
856   }
857   
858   NSObject *releaseme = 0;
859   CGImageRef cgi;
860   BOOL mask_p = NO;
861   BOOL free_cgi_p = NO;
862
863
864   /* If we're copying from a bitmap to a bitmap, and there's nothing funny
865      going on with clipping masks or depths or anything, optimize it by
866      just doing a memcpy instead of going through a CGI.
867    */
868   if (bitmap_context_p (src)) {
869
870     if (bitmap_context_p (dst) &&
871         gc->gcv.function == GXcopy &&
872         !gc->gcv.clip_mask &&
873         drawable_depth (src) == drawable_depth (dst)) {
874
875       Assert(!(int)src_frame.origin.x &&
876              !(int)src_frame.origin.y &&
877              !(int)dst_frame.origin.x &&
878              !(int)dst_frame.origin.y,
879              "unexpected non-zero origin");
880       
881       char *src_data = CGBitmapContextGetData(src->cgc);
882       char *dst_data = CGBitmapContextGetData(dst->cgc);
883       size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
884       size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
885       
886       // Int to float and back again. It's not very safe, but it seems to work.
887       int src_x0 = src_rect.origin.x;
888       int dst_x0 = dst_rect.origin.x;
889       
890       // Flip the Y-axis a second time.
891       int src_y0 = (src_frame.origin.y + src_frame.size.height -
892                     src_rect.size.height - src_rect.origin.y);
893       int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
894                     dst_rect.size.height - dst_rect.origin.y);
895       
896       unsigned width0  = (int) src_rect.size.width;
897       unsigned height0 = (int) src_rect.size.height;
898       
899       Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
900              (int)src_rect.size.height == (int)dst_rect.size.height,
901              "size mismatch");
902       {
903         char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
904         char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
905         size_t src_pitch0 = src_pitch;
906         size_t dst_pitch0 = dst_pitch;
907         size_t bytes = width0 * 4;
908
909         if (src == dst && dst_y0 > src_y0) {
910           // Copy upwards if the areas might overlap.
911           src_data0 += src_pitch0 * (height0 - 1);
912           dst_data0 += dst_pitch0 * (height0 - 1);
913           src_pitch0 = -src_pitch0;
914           dst_pitch0 = -dst_pitch0;
915         }
916       
917         size_t lines0 = height0;
918         while (lines0) {
919           // memcpy is an alias for memmove on OS X.
920           memmove(dst_data0, src_data0, bytes);
921           src_data0 += src_pitch0;
922           dst_data0 += dst_pitch0;
923           --lines0;
924         }
925       }
926
927       if (clipped) {
928         int orig_dst_x = orig_dst_rect.origin.x;
929         int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
930                           orig_dst_rect.origin.y - orig_dst_rect.size.height);
931         int orig_width  = orig_dst_rect.size.width;
932         int orig_height = orig_dst_rect.size.height;
933
934         Assert (orig_dst_x >= 0 &&
935                 orig_dst_x + orig_width  <= (int) dst_frame.size.width &&
936                 orig_dst_y >= 0 &&
937                 orig_dst_y + orig_height <= (int) dst_frame.size.height,
938                 "wrong dimensions");
939
940         if (orig_dst_y < dst_y0) {
941           fill_rect_memset (seek_xy (dst_data, dst_pitch,
942                                      orig_dst_x, orig_dst_y), dst_pitch,
943                             (uint32_t) gc->gcv.background, orig_width,
944                             dst_y0 - orig_dst_y);
945         }
946
947         if (orig_dst_y + orig_height > dst_y0 + height0) {
948           fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
949                                      dst_y0 + height0),
950                             dst_pitch,
951                             (uint32_t) gc->gcv.background, orig_width,
952                             orig_dst_y + orig_height - dst_y0 - height0);
953         }
954
955         if (orig_dst_x < dst_x0) {
956           fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
957                             dst_pitch, (uint32_t) gc->gcv.background,
958                             dst_x0 - orig_dst_x, height0);
959         }
960
961         if (dst_x0 + width0 < orig_dst_x + orig_width) {
962           fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
963                                      dst_y0),
964                             dst_pitch, (uint32_t) gc->gcv.background,
965                             orig_dst_x + orig_width - dst_x0 - width0,
966                             height0);
967         }
968       }
969
970       invalidate_drawable_cache (dst);
971       return 0;
972     }
973
974
975     // If we are copying from a Pixmap to a Pixmap or Window, we must first
976     // copy the bits to an intermediary CGImage object, then copy that to the
977     // destination drawable's CGContext.
978     //
979     // (It doesn't seem to be possible to use NSCopyBits() to optimize the
980     // case of copying from a Pixmap back to itself, but I don't think that
981     // happens very often anyway.)
982     //
983     // First we get a CGImage out of the pixmap CGContext -- it's the whole
984     // pixmap, but it presumably shares the data pointer instead of copying
985     // it.  We then cache that CGImage it inside the Pixmap object.  Note:
986     // invalidate_drawable_cache() must be called to discard this any time a
987     // modification is made to the pixmap, or we'll end up re-using old bits.
988     //
989     if (!src->cgi)
990       src->cgi = CGBitmapContextCreateImage (src->cgc);
991     cgi = src->cgi;
992
993     // if doing a sub-rect, trim it down.
994     if (src_rect.origin.x    != src_frame.origin.x   ||
995         src_rect.origin.y    != src_frame.origin.y   ||
996         src_rect.size.width  != src_frame.size.width ||
997         src_rect.size.height != src_frame.size.height) {
998       // #### I don't understand why this is needed...
999       src_rect.origin.y = (src_frame.size.height -
1000                            src_rect.size.height - src_rect.origin.y);
1001       // This does not copy image data, so it should be fast.
1002       cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1003       free_cgi_p = YES;
1004     }
1005
1006   if (src->type == PIXMAP && src->pixmap.depth == 1)
1007       mask_p = YES;
1008
1009 # ifndef USE_BACKBUFFER
1010   } else { /* (src->type == WINDOW) */
1011     
1012     NSRect nsfrom;    // NSRect != CGRect on 10.4
1013     nsfrom.origin.x    = src_rect.origin.x;
1014     nsfrom.origin.y    = src_rect.origin.y;
1015     nsfrom.size.width  = src_rect.size.width;
1016     nsfrom.size.height = src_rect.size.height;
1017
1018     if (src == dst) {
1019
1020       // If we are copying from a window to itself, we can use NSCopyBits()
1021       // without first copying the rectangle to an intermediary CGImage.
1022       // This is ~28% faster (but I *expected* it to be twice as fast...)
1023       // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1024       //
1025       cgi = 0;
1026
1027     } else {
1028
1029       // If we are copying from a Window to a Pixmap, we must first copy
1030       // the bits to an intermediary CGImage object, then copy that to the
1031       // Pixmap's CGContext.
1032       //
1033       NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1034                                initWithFocusedViewRect:nsfrom];
1035       unsigned char *data = [bm bitmapData];
1036       int bps = [bm bitsPerSample];
1037       int bpp = [bm bitsPerPixel];
1038       int bpl = [bm bytesPerRow];
1039       releaseme = bm;
1040
1041       // create a CGImage from those bits.
1042       // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1043       // but that method didn't exist in 10.4.)
1044
1045       CGDataProviderRef prov =
1046         CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1047                                       NULL);
1048       cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1049                            bps, bpp, bpl,
1050                            dpy->colorspace, 
1051                            /* Use whatever default bit ordering we got from
1052                               initWithFocusedViewRect.  I would have assumed
1053                               that it was (kCGImageAlphaNoneSkipFirst |
1054                               kCGBitmapByteOrder32Host), but on Intel,
1055                               it's not!
1056                            */
1057                            0,
1058                            prov, 
1059                            NULL,  /* decode[] */
1060                            NO, /* interpolate */
1061                            kCGRenderingIntentDefault);
1062       free_cgi_p = YES;
1063       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1064       CGDataProviderRelease (prov);
1065     }
1066
1067 # endif // !USE_BACKBUFFER
1068   }
1069
1070   CGContextRef cgc = dst->cgc;
1071
1072   if (mask_p) {         // src depth == 1
1073
1074     push_bg_gc (dst, gc, YES);
1075
1076     // fill the destination rectangle with solid background...
1077     CGContextFillRect (cgc, orig_dst_rect);
1078
1079     Assert (cgc, "no CGC with 1-bit XCopyArea");
1080
1081     // then fill in a solid rectangle of the fg color, using the image as an
1082     // alpha mask.  (the image has only values of BlackPixel or WhitePixel.)
1083     set_color (cgc, gc->gcv.foreground, gc->depth, 
1084                gc->gcv.alpha_allowed_p, YES);
1085     CGContextClipToMask (cgc, dst_rect, cgi);
1086     CGContextFillRect (cgc, dst_rect);
1087
1088     pop_gc (dst, gc);
1089
1090   } else {              // src depth > 1
1091
1092     push_gc (dst, gc);
1093
1094     // If either the src or dst rects did not lie within their drawables,
1095     // then we have adjusted both the src and dst rects to account for 
1096     // the clipping; that means we need to first clear to the background,
1097     // so that clipped bits end up in the bg color instead of simply not
1098     // being copied.
1099     //
1100     if (clipped) {
1101       set_color (cgc, gc->gcv.background, gc->depth, 
1102                  gc->gcv.alpha_allowed_p, YES);
1103       CGContextFillRect (cgc, orig_dst_rect);
1104     }
1105
1106     if (cgi) {
1107       // copy the CGImage onto the destination CGContext
1108       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1109       CGContextDrawImage (cgc, dst_rect, cgi);
1110     } else {
1111       // No cgi means src == dst, and both are Windows.
1112
1113 # ifdef USE_BACKBUFFER
1114       Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1115       return 0;
1116 # else // !USE_BACKBUFFER
1117       NSRect nsfrom;
1118       nsfrom.origin.x    = src_rect.origin.x;    // NSRect != CGRect on 10.4
1119       nsfrom.origin.y    = src_rect.origin.y;
1120       nsfrom.size.width  = src_rect.size.width;
1121       nsfrom.size.height = src_rect.size.height;
1122       NSPoint nsto;
1123       nsto.x             = dst_rect.origin.x;
1124       nsto.y             = dst_rect.origin.y;
1125       NSCopyBits (0, nsfrom, nsto);
1126 # endif // !USE_BACKBUFFER
1127     }
1128
1129     pop_gc (dst, gc);
1130   }
1131
1132   if (free_cgi_p) CGImageRelease (cgi);
1133
1134   if (releaseme) [releaseme release];
1135   invalidate_drawable_cache (dst);
1136   return 0;
1137 }
1138
1139
1140 int
1141 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1142             int src_x, int src_y,
1143             unsigned width, int height,
1144             int dest_x, int dest_y, unsigned long plane)
1145 {
1146   Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1147   
1148   // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1149   // not to white/black.
1150   return XCopyArea (dpy, src, dest, gc,
1151                     src_x, src_y, width, height, dest_x, dest_y);
1152 }
1153
1154
1155 int
1156 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1157 {
1158   // when drawing a zero-length line, obey line-width and cap-style.
1159   if (x1 == x2 && y1 == y2) {
1160     int w = gc->gcv.line_width;
1161     x1 -= w/2;
1162     y1 -= w/2;
1163     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1164       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1165     else
1166       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1167   }
1168   
1169   CGRect wr = d->frame;
1170   NSPoint p;
1171   p.x = wr.origin.x + x1;
1172   p.y = wr.origin.y + wr.size.height - y1;
1173
1174   push_fg_gc (d, gc, NO);
1175
1176   CGContextRef cgc = d->cgc;
1177   set_line_mode (cgc, &gc->gcv);
1178   CGContextBeginPath (cgc);
1179   CGContextMoveToPoint (cgc, p.x, p.y);
1180   p.x = wr.origin.x + x2;
1181   p.y = wr.origin.y + wr.size.height - y2;
1182   CGContextAddLineToPoint (cgc, p.x, p.y);
1183   CGContextStrokePath (cgc);
1184   pop_gc (d, gc);
1185   invalidate_drawable_cache (d);
1186   return 0;
1187 }
1188
1189 int
1190 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1191             int mode)
1192 {
1193   int i;
1194   NSPoint p;
1195   CGRect wr = d->frame;
1196   push_fg_gc (d, gc, NO);
1197
1198   CGContextRef cgc = d->cgc;
1199
1200   set_line_mode (cgc, &gc->gcv);
1201   
1202   // if the first and last points coincide, use closepath to get
1203   // the proper line-joining.
1204   BOOL closed_p = (points[0].x == points[count-1].x &&
1205                    points[0].y == points[count-1].y);
1206   if (closed_p) count--;
1207   
1208   p.x = wr.origin.x + points->x;
1209   p.y = wr.origin.y + wr.size.height - points->y;
1210   points++;
1211   CGContextBeginPath (cgc);
1212   CGContextMoveToPoint (cgc, p.x, p.y);
1213   for (i = 1; i < count; i++) {
1214     if (mode == CoordModePrevious) {
1215       p.x += points->x;
1216       p.y -= points->y;
1217     } else {
1218       p.x = wr.origin.x + points->x;
1219       p.y = wr.origin.y + wr.size.height - points->y;
1220     }
1221     CGContextAddLineToPoint (cgc, p.x, p.y);
1222     points++;
1223   }
1224   if (closed_p) CGContextClosePath (cgc);
1225   CGContextStrokePath (cgc);
1226   pop_gc (d, gc);
1227   invalidate_drawable_cache (d);
1228   return 0;
1229 }
1230
1231
1232 int
1233 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1234 {
1235   int i;
1236   CGRect wr = d->frame;
1237
1238   CGContextRef cgc = d->cgc;
1239
1240   push_fg_gc (d, gc, NO);
1241   set_line_mode (cgc, &gc->gcv);
1242   CGContextBeginPath (cgc);
1243   for (i = 0; i < count; i++) {
1244     CGContextMoveToPoint    (cgc, 
1245                              wr.origin.x + segments->x1,
1246                              wr.origin.y + wr.size.height - segments->y1);
1247     CGContextAddLineToPoint (cgc,
1248                              wr.origin.x + segments->x2,
1249                              wr.origin.y + wr.size.height - segments->y2);
1250     segments++;
1251   }
1252   CGContextStrokePath (cgc);
1253   pop_gc (d, gc);
1254   invalidate_drawable_cache (d);
1255   return 0;
1256 }
1257
1258
1259 int
1260 XClearWindow (Display *dpy, Window win)
1261 {
1262   Assert (win && win->type == WINDOW, "not a window");
1263   CGRect wr = win->frame;
1264   return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1265 }
1266
1267 int
1268 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1269 {
1270   Assert (w && w->type == WINDOW, "not a window");
1271   validate_pixel (pixel, 32, NO);
1272   w->window.background = pixel;
1273   return 0;
1274 }
1275
1276 static void
1277 draw_rect (Display *dpy, Drawable d, GC gc, 
1278            int x, int y, unsigned int width, unsigned int height, 
1279            BOOL foreground_p, BOOL fill_p)
1280 {
1281   CGRect wr = d->frame;
1282   CGRect r;
1283   r.origin.x = wr.origin.x + x;
1284   r.origin.y = wr.origin.y + wr.size.height - y - height;
1285   r.size.width = width;
1286   r.size.height = height;
1287
1288   if (gc) {
1289     if (foreground_p)
1290       push_fg_gc (d, gc, fill_p);
1291     else
1292       push_bg_gc (d, gc, fill_p);
1293   }
1294
1295   CGContextRef cgc = d->cgc;
1296   if (fill_p)
1297     CGContextFillRect (cgc, r);
1298   else {
1299     if (gc)
1300       set_line_mode (cgc, &gc->gcv);
1301     CGContextStrokeRect (cgc, r);
1302   }
1303
1304   if (gc)
1305     pop_gc (d, gc);
1306   invalidate_drawable_cache (d);
1307 }
1308
1309
1310 int
1311 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
1312                 unsigned int width, unsigned int height)
1313 {
1314   draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1315   return 0;
1316 }
1317
1318 int
1319 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
1320                 unsigned int width, unsigned int height)
1321 {
1322   draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1323   return 0;
1324 }
1325
1326 int
1327 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1328 {
1329   CGRect wr = d->frame;
1330   int i;
1331   CGContextRef cgc = d->cgc;
1332   push_fg_gc (d, gc, YES);
1333   for (i = 0; i < n; i++) {
1334     CGRect r;
1335     r.origin.x = wr.origin.x + rects->x;
1336     r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1337     r.size.width = rects->width;
1338     r.size.height = rects->height;
1339     CGContextFillRect (cgc, r);
1340     rects++;
1341   }
1342   pop_gc (d, gc);
1343   invalidate_drawable_cache (d);
1344   return 0;
1345 }
1346
1347
1348 int
1349 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1350 {
1351   Assert (win && win->type == WINDOW, "not a window");
1352   CGContextRef cgc = win->cgc;
1353   set_color (cgc, win->window.background, 32, NO, YES);
1354   draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1355   return 0;
1356 }
1357
1358
1359 int
1360 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1361               XPoint *points, int npoints, int shape, int mode)
1362 {
1363   CGRect wr = d->frame;
1364   int i;
1365   push_fg_gc (d, gc, YES);
1366   CGContextRef cgc = d->cgc;
1367   CGContextBeginPath (cgc);
1368   float x = 0, y = 0;
1369   for (i = 0; i < npoints; i++) {
1370     if (i > 0 && mode == CoordModePrevious) {
1371       x += points[i].x;
1372       y -= points[i].y;
1373     } else {
1374       x = wr.origin.x + points[i].x;
1375       y = wr.origin.y + wr.size.height - points[i].y;
1376     }
1377         
1378     if (i == 0)
1379       CGContextMoveToPoint (cgc, x, y);
1380     else
1381       CGContextAddLineToPoint (cgc, x, y);
1382   }
1383   CGContextClosePath (cgc);
1384   if (gc->gcv.fill_rule == EvenOddRule)
1385     CGContextEOFillPath (cgc);
1386   else
1387     CGContextFillPath (cgc);
1388   pop_gc (d, gc);
1389   invalidate_drawable_cache (d);
1390   return 0;
1391 }
1392
1393 #define radians(DEG) ((DEG) * M_PI / 180.0)
1394 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1395
1396 static int
1397 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, 
1398           unsigned int width, unsigned int height, int angle1, int angle2,
1399           BOOL fill_p)
1400 {
1401   CGRect wr = d->frame;
1402   CGRect bound;
1403   bound.origin.x = wr.origin.x + x;
1404   bound.origin.y = wr.origin.y + wr.size.height - y - height;
1405   bound.size.width = width;
1406   bound.size.height = height;
1407   
1408   CGPoint ctr;
1409   ctr.x = bound.origin.x + bound.size.width /2;
1410   ctr.y = bound.origin.y + bound.size.height/2;
1411   
1412   float r1 = radians (angle1/64.0);
1413   float r2 = radians (angle2/64.0) + r1;
1414   BOOL clockwise = angle2 < 0;
1415   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1416   
1417   push_fg_gc (d, gc, fill_p);
1418
1419   CGContextRef cgc = d->cgc;
1420   CGContextBeginPath (cgc);
1421   
1422   CGContextSaveGState(cgc);
1423   CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1424   CGContextScaleCTM (cgc, width/2.0, height/2.0);
1425   if (fill_p)
1426     CGContextMoveToPoint (cgc, 0, 0);
1427
1428   CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1429   CGContextRestoreGState (cgc);  // restore before stroke, for line width
1430
1431   if (closed_p)
1432     CGContextClosePath (cgc); // for proper line joining
1433   
1434   if (fill_p) {
1435     CGContextFillPath (cgc);
1436   } else {
1437     set_line_mode (cgc, &gc->gcv);
1438     CGContextStrokePath (cgc);
1439   }
1440
1441   pop_gc (d, gc);
1442   invalidate_drawable_cache (d);
1443   return 0;
1444 }
1445
1446 int
1447 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y, 
1448           unsigned int width, unsigned int height, int angle1, int angle2)
1449 {
1450   return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1451 }
1452
1453 int
1454 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y, 
1455           unsigned int width, unsigned int height, int angle1, int angle2)
1456 {
1457   return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1458 }
1459
1460 int
1461 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1462 {
1463   int i;
1464   for (i = 0; i < narcs; i++)
1465     draw_arc (dpy, d, gc, 
1466               arcs[i].x, arcs[i].y, 
1467               arcs[i].width, arcs[i].height, 
1468               arcs[i].angle1, arcs[i].angle2,
1469               NO);
1470   return 0;
1471 }
1472
1473 int
1474 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1475 {
1476   int i;
1477   for (i = 0; i < narcs; i++)
1478     draw_arc (dpy, d, gc, 
1479               arcs[i].x, arcs[i].y, 
1480               arcs[i].width, arcs[i].height, 
1481               arcs[i].angle1, arcs[i].angle2,
1482               YES);
1483   return 0;
1484 }
1485
1486
1487 static void
1488 gcv_defaults (XGCValues *gcv, int depth)
1489 {
1490   memset (gcv, 0, sizeof(*gcv));
1491   gcv->function   = GXcopy;
1492   gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1493   gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1494   gcv->line_width = 1;
1495   gcv->cap_style  = CapNotLast;
1496   gcv->join_style = JoinMiter;
1497   gcv->fill_rule  = EvenOddRule;
1498
1499   gcv->alpha_allowed_p = NO;
1500   gcv->antialias_p     = YES;
1501 }
1502
1503 static void
1504 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1505 {
1506   if (! mask) return;
1507   Assert (gc && from, "no gc");
1508   if (!gc || !from) return;
1509
1510   if (mask & GCFunction)        gc->gcv.function        = from->function;
1511   if (mask & GCForeground)      gc->gcv.foreground      = from->foreground;
1512   if (mask & GCBackground)      gc->gcv.background      = from->background;
1513   if (mask & GCLineWidth)       gc->gcv.line_width      = from->line_width;
1514   if (mask & GCCapStyle)        gc->gcv.cap_style       = from->cap_style;
1515   if (mask & GCJoinStyle)       gc->gcv.join_style      = from->join_style;
1516   if (mask & GCFillRule)        gc->gcv.fill_rule       = from->fill_rule;
1517   if (mask & GCClipXOrigin)     gc->gcv.clip_x_origin   = from->clip_x_origin;
1518   if (mask & GCClipYOrigin)     gc->gcv.clip_y_origin   = from->clip_y_origin;
1519   if (mask & GCSubwindowMode)   gc->gcv.subwindow_mode  = from->subwindow_mode;
1520   
1521   if (mask & GCClipMask)        XSetClipMask (0, gc, from->clip_mask);
1522   if (mask & GCFont)            XSetFont (0, gc, from->font);
1523
1524   if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1525                                            gc->gcv.alpha_allowed_p);
1526   if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1527                                            gc->gcv.alpha_allowed_p);
1528     
1529   Assert ((! (mask & (GCLineStyle |
1530                       GCPlaneMask |
1531                       GCFillStyle |
1532                       GCTile |
1533                       GCStipple |
1534                       GCTileStipXOrigin |
1535                       GCTileStipYOrigin |
1536                       GCGraphicsExposures |
1537                       GCDashOffset |
1538                       GCDashList |
1539                       GCArcMode))),
1540           "unimplemented gcvalues mask");
1541 }
1542
1543
1544 GC
1545 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1546 {
1547   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1548   if (d->type == WINDOW) {
1549     gc->depth = 32;
1550   } else { /* (d->type == PIXMAP) */
1551     gc->depth = d->pixmap.depth;
1552   }
1553
1554   gcv_defaults (&gc->gcv, gc->depth);
1555   set_gcv (gc, xgcv, mask);
1556   return gc;
1557 }
1558
1559 int
1560 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1561 {
1562   set_gcv (gc, gcv, mask);
1563   return 0;
1564 }
1565
1566
1567 int
1568 XFreeGC (Display *dpy, GC gc)
1569 {
1570   if (gc->gcv.font)
1571     XUnloadFont (dpy, gc->gcv.font);
1572
1573   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1574
1575   if (gc->gcv.clip_mask) {
1576     XFreePixmap (dpy, gc->gcv.clip_mask);
1577     CGImageRelease (gc->clip_mask);
1578   }
1579   free (gc);
1580   return 0;
1581 }
1582
1583
1584 Status
1585 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1586 {
1587   Assert (w && w->type == WINDOW, "not a window");
1588   memset (xgwa, 0, sizeof(*xgwa));
1589   xgwa->x      = w->frame.origin.x;
1590   xgwa->y      = w->frame.origin.y;
1591   xgwa->width  = w->frame.size.width;
1592   xgwa->height = w->frame.size.height;
1593   xgwa->depth  = 32;
1594   xgwa->screen = dpy->screen;
1595   xgwa->visual = dpy->screen->visual;
1596   return 0;
1597 }
1598
1599 Status
1600 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1601               int *x_ret, int *y_ret, 
1602               unsigned int *w_ret, unsigned int *h_ret,
1603               unsigned int *bw_ret, unsigned int *d_ret)
1604 {
1605   *x_ret    = d->frame.origin.x;
1606   *y_ret    = d->frame.origin.y;
1607   *w_ret    = d->frame.size.width;
1608   *h_ret    = d->frame.size.height;
1609   *d_ret    = (d->type == WINDOW ? 32 : d->pixmap.depth);
1610   *root_ret = RootWindow (dpy, 0);
1611   *bw_ret   = 0;
1612   return True;
1613 }
1614
1615
1616 Status
1617 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1618 {
1619   // store 32 bit ARGB in the pixel field.
1620   // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1621   color->pixel = (uint32_t)
1622                  ((                       0xFF  << 24) |
1623                   (((color->red   >> 8) & 0xFF) << 16) |
1624                   (((color->green >> 8) & 0xFF) <<  8) |
1625                   (((color->blue  >> 8) & 0xFF)      ));
1626   return 1;
1627 }
1628
1629 Status
1630 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1631                   unsigned long *pmret, unsigned int npl,
1632                   unsigned long *pxret, unsigned int npx)
1633 {
1634   return 0;
1635 }
1636
1637 int
1638 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1639 {
1640   Assert(0, "XStoreColors called");
1641   return 0;
1642 }
1643
1644 int
1645 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1646 {
1647   Assert(0, "XStoreColor called");
1648   return 0;
1649 }
1650
1651 int
1652 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1653              unsigned long planes)
1654 {
1655   return 0;
1656 }
1657
1658 Status
1659 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1660 {
1661   unsigned char r=0, g=0, b=0;
1662   if (*spec == '#' && strlen(spec) == 7) {
1663     static unsigned const char hex[] = {   // yeah yeah, shoot me.
1664       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1665       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1666       0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1667       0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1668       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1669       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1670       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1671       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1672     r = (hex[spec[1]] << 4) | hex[spec[2]];
1673     g = (hex[spec[3]] << 4) | hex[spec[4]];
1674     b = (hex[spec[5]] << 4) | hex[spec[6]];
1675   } else if (!strcasecmp(spec,"black")) {
1676 //  r = g = b = 0;
1677   } else if (!strcasecmp(spec,"white")) {
1678     r = g = b = 255;
1679   } else if (!strcasecmp(spec,"red")) {
1680     r = 255;
1681   } else if (!strcasecmp(spec,"green")) {
1682     g = 255;
1683   } else if (!strcasecmp(spec,"blue")) {
1684     b = 255;
1685   } else if (!strcasecmp(spec,"cyan")) {
1686     g = b = 255;
1687   } else if (!strcasecmp(spec,"magenta")) {
1688     r = b = 255;
1689   } else if (!strcasecmp(spec,"yellow")) {
1690     r = g = 255;
1691   } else {
1692     return 0;
1693   }
1694   
1695   ret->red   = (r << 8) | r;
1696   ret->green = (g << 8) | g;
1697   ret->blue  = (b << 8) | b;
1698   ret->flags = DoRed|DoGreen|DoBlue;
1699   return 1;
1700 }
1701
1702 Status
1703 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1704                   XColor *screen_ret, XColor *exact_ret)
1705 {
1706   if (! XParseColor (dpy, cmap, name, screen_ret))
1707     return False;
1708   *exact_ret = *screen_ret;
1709   return XAllocColor (dpy, cmap, screen_ret);
1710 }
1711
1712 int
1713 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1714 {
1715   validate_pixel (color->pixel, 32, NO);
1716   unsigned char r = ((color->pixel >> 16) & 0xFF);
1717   unsigned char g = ((color->pixel >>  8) & 0xFF);
1718   unsigned char b = ((color->pixel      ) & 0xFF);
1719   color->red   = (r << 8) | r;
1720   color->green = (g << 8) | g;
1721   color->blue  = (b << 8) | b;
1722   color->flags = DoRed|DoGreen|DoBlue;
1723   return 0;
1724 }
1725
1726 int
1727 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1728 {
1729   int i;
1730   for (i = 0; i < n; i++)
1731     XQueryColor (dpy, cmap, &c[i]);
1732   return 0;
1733 }
1734
1735
1736 static unsigned long
1737 ximage_getpixel_1 (XImage *ximage, int x, int y)
1738 {
1739   return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1740 }
1741
1742 static int
1743 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1744 {
1745   if (pixel)
1746     ximage->data [y * ximage->bytes_per_line + (x>>3)] |=  (1 << (x & 7));
1747   else
1748     ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1749
1750   return 0;
1751 }
1752
1753 static unsigned long
1754 ximage_getpixel_32 (XImage *ximage, int x, int y)
1755 {
1756   return ((unsigned long)
1757           *((uint32_t *) ximage->data +
1758             (y * (ximage->bytes_per_line >> 2)) +
1759             x));
1760 }
1761
1762 static int
1763 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1764 {
1765   *((uint32_t *) ximage->data +
1766     (y * (ximage->bytes_per_line >> 2)) +
1767     x) = (uint32_t) pixel;
1768   return 0;
1769 }
1770
1771
1772 Status
1773 XInitImage (XImage *ximage)
1774 {
1775   if (!ximage->bytes_per_line)
1776     ximage->bytes_per_line = (ximage->depth == 1
1777                               ? (ximage->width + 7) / 8
1778                               : ximage->width * 4);
1779
1780   if (ximage->depth == 1) {
1781     ximage->f.put_pixel = ximage_putpixel_1;
1782     ximage->f.get_pixel = ximage_getpixel_1;
1783   } else if (ximage->depth == 32 || ximage->depth == 24) {
1784     ximage->f.put_pixel = ximage_putpixel_32;
1785     ximage->f.get_pixel = ximage_getpixel_32;
1786   } else {
1787     Assert (0, "unknown depth");
1788   }
1789   return 1;
1790 }
1791
1792
1793 XImage *
1794 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1795               int format, int offset, char *data,
1796               unsigned int width, unsigned int height,
1797               int bitmap_pad, int bytes_per_line)
1798 {
1799   XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1800   ximage->width = width;
1801   ximage->height = height;
1802   ximage->format = format;
1803   ximage->data = data;
1804   ximage->bitmap_unit = 8;
1805   ximage->byte_order = LSBFirst;
1806   ximage->bitmap_bit_order = ximage->byte_order;
1807   ximage->bitmap_pad = bitmap_pad;
1808   ximage->depth = depth;
1809   ximage->red_mask   = (depth == 1 ? 0 : 0x00FF0000);
1810   ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1811   ximage->blue_mask  = (depth == 1 ? 0 : 0x000000FF);
1812   ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1813   ximage->bytes_per_line = bytes_per_line;
1814
1815   XInitImage (ximage);
1816   return ximage;
1817 }
1818
1819 XImage *
1820 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1821 {
1822   XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1823                              w, h, from->bitmap_pad, 0);
1824   to->data = (char *) malloc (h * to->bytes_per_line);
1825
1826   if (x >= from->width)
1827     w = 0;
1828   else if (x+w > from->width)
1829     w = from->width - x;
1830
1831   if (y >= from->height)
1832     h = 0;
1833   else if (y+h > from->height)
1834     h = from->height - y;
1835
1836   int tx, ty;
1837   for (ty = 0; ty < h; ty++)
1838     for (tx = 0; tx < w; tx++)
1839       XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1840   return to;
1841 }
1842
1843
1844 XPixmapFormatValues *
1845 XListPixmapFormats (Display *dpy, int *n_ret)
1846 {
1847   XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1848   ret[0].depth = 32;
1849   ret[0].bits_per_pixel = 32;
1850   ret[0].scanline_pad = 8;
1851   ret[1].depth = 1;
1852   ret[1].bits_per_pixel = 1;
1853   ret[1].scanline_pad = 8;
1854   *n_ret = 2;
1855   return ret;
1856 }
1857
1858
1859 unsigned long
1860 XGetPixel (XImage *ximage, int x, int y)
1861 {
1862   return ximage->f.get_pixel (ximage, x, y);
1863 }
1864
1865
1866 int
1867 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1868 {
1869   return ximage->f.put_pixel (ximage, x, y, pixel);
1870 }
1871
1872 int
1873 XDestroyImage (XImage *ximage)
1874 {
1875   if (ximage->data) free (ximage->data);
1876   free (ximage);
1877   return 0;
1878 }
1879
1880
1881 static void
1882 flipbits (unsigned const char *in, unsigned char *out, int length)
1883 {
1884   static const unsigned char table[256] = {
1885     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1886     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1887     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1888     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1889     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1890     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1891     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1892     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1893     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1894     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1895     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1896     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1897     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1898     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1899     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1900     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1901     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1902     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1903     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1904     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1905     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1906     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1907     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1908     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1909     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1910     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1911     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 
1912     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1913     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1914     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1915     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1916     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1917   };
1918   while (length-- > 0)
1919     *out++ = table[*in++];
1920 }
1921
1922
1923 int
1924 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1925            int src_x, int src_y, int dest_x, int dest_y,
1926            unsigned int w, unsigned int h)
1927 {
1928   CGRect wr = d->frame;
1929
1930   Assert (gc, "no GC");
1931   Assert ((w < 65535), "improbably large width");
1932   Assert ((h < 65535), "improbably large height");
1933   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1934   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1935   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1936   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1937
1938   // Clip width and height to the bounds of the Drawable
1939   //
1940   if (dest_x + w > wr.size.width) {
1941     if (dest_x > wr.size.width)
1942       return 0;
1943     w = wr.size.width - dest_x;
1944   }
1945   if (dest_y + h > wr.size.height) {
1946     if (dest_y > wr.size.height)
1947       return 0;
1948     h = wr.size.height - dest_y;
1949   }
1950   if (w <= 0 || h <= 0)
1951     return 0;
1952
1953   // Clip width and height to the bounds of the XImage
1954   //
1955   if (src_x + w > ximage->width) {
1956     if (src_x > ximage->width)
1957       return 0;
1958     w = ximage->width - src_x;
1959   }
1960   if (src_y + h > ximage->height) {
1961     if (src_y > ximage->height)
1962       return 0;
1963     h = ximage->height - src_y;
1964   }
1965   if (w <= 0 || h <= 0)
1966     return 0;
1967
1968   CGContextRef cgc = d->cgc;
1969
1970   if (gc->gcv.function == GXset ||
1971       gc->gcv.function == GXclear) {
1972     // "set" and "clear" are dumb drawing modes that ignore the source
1973     // bits and just draw solid rectangles.
1974     set_color (cgc, (gc->gcv.function == GXset
1975                         ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1976                         : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1977                gc->depth, gc->gcv.alpha_allowed_p, YES);
1978     draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1979     return 0;
1980   }
1981
1982   int bpl = ximage->bytes_per_line;
1983   int bpp = ximage->bits_per_pixel;
1984   int bsize = bpl * h;
1985   char *data = ximage->data;
1986
1987   CGRect r;
1988   r.origin.x = wr.origin.x + dest_x;
1989   r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1990   r.size.width = w;
1991   r.size.height = h;
1992
1993   if (bpp == 32) {
1994
1995     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1996        to create a CGImage from a sub-rectagle of the XImage.
1997      */
1998     data += (src_y * bpl) + (src_x * 4);
1999     CGDataProviderRef prov = 
2000       CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2001
2002     CGImageRef cgi = CGImageCreate (w, h,
2003                                     bpp/4, bpp, bpl,
2004                                     dpy->colorspace, 
2005                                     /* Need this for XPMs to have the right
2006                                        colors, e.g. the logo in "maze". */
2007                                     (kCGImageAlphaNoneSkipFirst |
2008                                      kCGBitmapByteOrder32Host),
2009                                     prov, 
2010                                     NULL,  /* decode[] */
2011                                     NO, /* interpolate */
2012                                     kCGRenderingIntentDefault);
2013     CGDataProviderRelease (prov);
2014     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2015     CGContextDrawImage (cgc, r, cgi);
2016     CGImageRelease (cgi);
2017
2018   } else {   // (bpp == 1)
2019
2020     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2021
2022        #### However, the bit order within a byte in a 1bpp XImage is
2023             the wrong way around from what Quartz expects, so first we
2024             have to copy the data to reverse it.  Shit!  Maybe it
2025             would be worthwhile to go through the hacks and #ifdef
2026             each one that diddles 1bpp XImage->data directly...
2027      */
2028     Assert ((src_x % 8) == 0,
2029             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2030
2031     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
2032     unsigned char *flipped = (unsigned char *) malloc (bsize);
2033
2034     flipbits ((unsigned char *) data, flipped, bsize);
2035
2036     CGDataProviderRef prov = 
2037       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2038     CGImageRef mask = CGImageMaskCreate (w, h, 
2039                                          1, bpp, bpl,
2040                                          prov,
2041                                          NULL,  /* decode[] */
2042                                          NO); /* interpolate */
2043     push_fg_gc (d, gc, YES);
2044
2045     CGContextFillRect (cgc, r);                         // foreground color
2046     CGContextClipToMask (cgc, r, mask);
2047     set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
2048     CGContextFillRect (cgc, r);                         // background color
2049     pop_gc (d, gc);
2050
2051     free (flipped);
2052     CGDataProviderRelease (prov);
2053     CGImageRelease (mask);
2054   }
2055
2056   invalidate_drawable_cache (d);
2057
2058   return 0;
2059 }
2060
2061
2062 XImage *
2063 XGetImage (Display *dpy, Drawable d, int x, int y,
2064            unsigned int width, unsigned int height,
2065            unsigned long plane_mask, int format)
2066 {
2067   const unsigned char *data = 0;
2068   size_t depth, ibpp, ibpl;
2069   enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2070 # ifndef USE_BACKBUFFER
2071   NSBitmapImageRep *bm = 0;
2072 # endif
2073   
2074   Assert ((width  < 65535), "improbably large width");
2075   Assert ((height < 65535), "improbably large height");
2076   Assert ((x < 65535 && x > -65535), "improbably large x");
2077   Assert ((y < 65535 && y > -65535), "improbably large y");
2078
2079   CGContextRef cgc = d->cgc;
2080
2081 #ifndef USE_BACKBUFFER
2082   // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2083   if (d->type == PIXMAP)
2084 # endif
2085   {
2086     depth = (d->type == PIXMAP
2087              ? d->pixmap.depth
2088              : 32);
2089     // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2090     src_format = BGRA; // #### Should this be ARGB on PPC?
2091     ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2092     ibpl = CGBitmapContextGetBytesPerRow (cgc);
2093     data = CGBitmapContextGetData (cgc);
2094     Assert (data, "CGBitmapContextGetData failed");
2095
2096 # ifndef USE_BACKBUFFER
2097   } else { /* (d->type == WINDOW) */
2098
2099     // get the bits (desired sub-rectangle) out of the NSView
2100     NSRect nsfrom;
2101     nsfrom.origin.x = x;
2102 //  nsfrom.origin.y = y;
2103     nsfrom.origin.y = d->frame.size.height - height - y;
2104     nsfrom.size.width = width;
2105     nsfrom.size.height = height;
2106     bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2107     depth = 32;
2108     src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2109     ibpp = [bm bitsPerPixel];
2110     ibpl = [bm bytesPerRow];
2111     data = [bm bitmapData];
2112     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2113 # endif // !USE_BACKBUFFER
2114   }
2115   
2116   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
2117   data += (y * ibpl) + (x * (ibpp/8));
2118   
2119   format = (depth == 1 ? XYPixmap : ZPixmap);
2120   XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2121                                 format, 0, 0, width, height, 0, 0);
2122   image->data = (char *) malloc (height * image->bytes_per_line);
2123   
2124   int obpl = image->bytes_per_line;
2125   
2126   /* both PPC and Intel use word-ordered ARGB frame buffers, which
2127      means that on Intel it is BGRA when viewed by bytes (And BGR
2128      when using 24bpp packing).
2129
2130      BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2131      The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2132      indicator of this latest kink.
2133    */
2134   int xx, yy;
2135   if (depth == 1) {
2136     const unsigned char *iline = data;
2137     for (yy = 0; yy < height; yy++) {
2138
2139       const unsigned char *iline2 = iline;
2140       for (xx = 0; xx < width; xx++) {
2141
2142         iline2++;                     // ignore R  or  A  or  A  or  B
2143         iline2++;                     // ignore G  or  B  or  R  or  G
2144         unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
2145         if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
2146
2147         XPutPixel (image, xx, yy, (r ? 1 : 0));
2148       }
2149       iline += ibpl;
2150     }
2151   } else {
2152     Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2153     const unsigned char *iline = data;
2154     unsigned char *oline = (unsigned char *) image->data;
2155     for (yy = 0; yy < height; yy++) {
2156
2157       const unsigned char *iline2 = iline;
2158       unsigned char *oline2 = oline;
2159
2160       switch (src_format) {
2161       case ARGB:
2162         for (xx = 0; xx < width; xx++) {
2163           unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2164           unsigned char r = *iline2++;
2165           unsigned char g = *iline2++;
2166           unsigned char b = *iline2++;
2167           uint32_t pixel = ((a << 24) |
2168                             (r << 16) |
2169                             (g <<  8) |
2170                             (b <<  0));
2171           *((uint32_t *) oline2) = pixel;
2172           oline2 += 4;
2173         }
2174         break;
2175       case RGBA:
2176         for (xx = 0; xx < width; xx++) {
2177           unsigned char r = *iline2++;
2178           unsigned char g = *iline2++;
2179           unsigned char b = *iline2++;
2180           unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2181           uint32_t pixel = ((a << 24) |
2182                             (r << 16) |
2183                             (g <<  8) |
2184                             (b <<  0));
2185           *((uint32_t *) oline2) = pixel;
2186           oline2 += 4;
2187         }
2188         break;
2189       case BGRA:
2190         for (xx = 0; xx < width; xx++) {
2191           unsigned char b = *iline2++;
2192           unsigned char g = *iline2++;
2193           unsigned char r = *iline2++;
2194           unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2195           uint32_t pixel = ((a << 24) |
2196                             (r << 16) |
2197                             (g <<  8) |
2198                             (b <<  0));
2199           *((uint32_t *) oline2) = pixel;
2200           oline2 += 4;
2201         }
2202         break;
2203       default:
2204         abort();
2205         break;
2206       }
2207
2208       oline += obpl;
2209       iline += ibpl;
2210     }
2211   }
2212
2213 # ifndef USE_BACKBUFFER
2214   if (bm) [bm release];
2215 # endif
2216
2217   return image;
2218 }
2219
2220
2221
2222 /* Returns a transformation matrix to do rotation as per the provided
2223    EXIF "Orientation" value.
2224  */
2225 static CGAffineTransform
2226 exif_rotate (int rot, CGSize rect)
2227 {
2228   CGAffineTransform trans = CGAffineTransformIdentity;
2229   switch (rot) {
2230   case 2:               // flip horizontal
2231     trans = CGAffineTransformMakeTranslation (rect.width, 0);
2232     trans = CGAffineTransformScale (trans, -1, 1);
2233     break;
2234
2235   case 3:               // rotate 180
2236     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2237     trans = CGAffineTransformRotate (trans, M_PI);
2238     break;
2239
2240   case 4:               // flip vertical
2241     trans = CGAffineTransformMakeTranslation (0, rect.height);
2242     trans = CGAffineTransformScale (trans, 1, -1);
2243     break;
2244
2245   case 5:               // transpose (UL-to-LR axis)
2246     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2247     trans = CGAffineTransformScale (trans, -1, 1);
2248     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2249     break;
2250
2251   case 6:               // rotate 90
2252     trans = CGAffineTransformMakeTranslation (0, rect.width);
2253     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2254     break;
2255
2256   case 7:               // transverse (UR-to-LL axis)
2257     trans = CGAffineTransformMakeScale (-1, 1);
2258     trans = CGAffineTransformRotate (trans, M_PI / 2);
2259     break;
2260
2261   case 8:               // rotate 270
2262     trans = CGAffineTransformMakeTranslation (rect.height, 0);
2263     trans = CGAffineTransformRotate (trans, M_PI / 2);
2264     break;
2265
2266   default: 
2267     break;
2268   }
2269
2270   return trans;
2271 }
2272
2273
2274 void
2275 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
2276                                 Bool nsimg_p, void *img_arg,
2277                                XRectangle *geom_ret, int exif_rotation)
2278 {
2279   CGImageRef cgi;
2280 # ifndef USE_IPHONE
2281   CGImageSourceRef cgsrc;
2282 # endif // USE_IPHONE
2283   NSSize imgr;
2284
2285   CGContextRef cgc = d->cgc;
2286
2287   if (nsimg_p) {
2288
2289     NSImage *nsimg = (NSImage *) img_arg;
2290     imgr = [nsimg size];
2291
2292 # ifndef USE_IPHONE
2293     // convert the NSImage to a CGImage via the toll-free-bridging 
2294     // of NSData and CFData...
2295     //
2296     NSData *nsdata = [NSBitmapImageRep
2297                        TIFFRepresentationOfImageRepsInArray:
2298                          [nsimg representations]];
2299     CFDataRef cfdata = (CFDataRef) nsdata;
2300     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2301     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2302 # else  // USE_IPHONE
2303     cgi = nsimg.CGImage;
2304 # endif // USE_IPHONE
2305
2306   } else {
2307     cgi = (CGImageRef) img_arg;
2308     imgr.width  = CGImageGetWidth (cgi);
2309     imgr.height = CGImageGetHeight (cgi);
2310   }
2311
2312   Bool rot_p = (exif_rotation >= 5);
2313
2314   if (rot_p)
2315     imgr = NSMakeSize (imgr.height, imgr.width);
2316
2317   CGRect winr = d->frame;
2318   float rw = winr.size.width  / imgr.width;
2319   float rh = winr.size.height / imgr.height;
2320   float r = (rw < rh ? rw : rh);
2321
2322   CGRect dst, dst2;
2323   dst.size.width  = imgr.width  * r;
2324   dst.size.height = imgr.height * r;
2325   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
2326   dst.origin.y = (winr.size.height - dst.size.height) / 2;
2327
2328   dst2.origin.x = dst2.origin.y = 0;
2329   if (rot_p) {
2330     dst2.size.width = dst.size.height; 
2331     dst2.size.height = dst.size.width;
2332   } else {
2333     dst2.size = dst.size;
2334   }
2335
2336   // Clear the part not covered by the image to background or black.
2337   //
2338   if (d->type == WINDOW)
2339     XClearWindow (dpy, d);
2340   else {
2341     set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2342     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2343   }
2344
2345   CGAffineTransform trans = 
2346     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2347
2348   CGContextSaveGState (cgc);
2349   CGContextConcatCTM (cgc, 
2350                       CGAffineTransformMakeTranslation (dst.origin.x,
2351                                                         dst.origin.y));
2352   CGContextConcatCTM (cgc, trans);
2353   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2354   CGContextDrawImage (cgc, dst2, cgi);
2355   CGContextRestoreGState (cgc);
2356
2357 # ifndef USE_IPHONE
2358   if (nsimg_p) {
2359     CFRelease (cgsrc);
2360     CGImageRelease (cgi);
2361   }
2362 # endif // USE_IPHONE
2363
2364   if (geom_ret) {
2365     geom_ret->x = dst.origin.x;
2366     geom_ret->y = dst.origin.y;
2367     geom_ret->width  = dst.size.width;
2368     geom_ret->height = dst.size.height;
2369   }
2370
2371   invalidate_drawable_cache (d);
2372 }
2373
2374
2375
2376 Pixmap
2377 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2378                              const char *data,
2379                              unsigned int w, unsigned int h,
2380                              unsigned long fg, unsigned int bg,
2381                              unsigned int depth)
2382 {
2383   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2384   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0, 
2385                                 (char *) data, w, h, 0, 0);
2386   XGCValues gcv;
2387   gcv.foreground = fg;
2388   gcv.background = bg;
2389   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2390   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2391   XFreeGC (dpy, gc);
2392   image->data = 0;
2393   XDestroyImage (image);
2394   return p;
2395 }
2396
2397 Pixmap
2398 XCreatePixmap (Display *dpy, Drawable d,
2399                unsigned int width, unsigned int height, unsigned int depth)
2400 {
2401   char *data = (char *) malloc (width * height * 4);
2402   if (! data) return 0;
2403
2404   Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2405   p->type = PIXMAP;
2406   p->frame.size.width  = width;
2407   p->frame.size.height = height;
2408   p->pixmap.depth      = depth;
2409   p->pixmap.cgc_buffer = data;
2410   
2411   /* Quartz doesn't have a 1bpp image type.
2412      Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2413      don't support that!  So we always use 32bpp, regardless of depth. */
2414
2415   p->cgc = CGBitmapContextCreate (data, width, height,
2416                                   8, /* bits per component */
2417                                   width * 4, /* bpl */
2418                                   dpy->colorspace,
2419                                   // Without this, it returns 0...
2420                                   (kCGImageAlphaNoneSkipFirst |
2421                                    kCGBitmapByteOrder32Host)
2422                                   );
2423   Assert (p->cgc, "could not create CGBitmapContext");
2424   return p;
2425 }
2426
2427
2428 int
2429 XFreePixmap (Display *d, Pixmap p)
2430 {
2431   Assert (p && p->type == PIXMAP, "not a pixmap");
2432   invalidate_drawable_cache (p);
2433   CGContextRelease (p->cgc);
2434   if (p->pixmap.cgc_buffer)
2435     free (p->pixmap.cgc_buffer);
2436   free (p);
2437   return 0;
2438 }
2439
2440
2441 static Pixmap
2442 copy_pixmap (Display *dpy, Pixmap p)
2443 {
2444   if (!p) return 0;
2445   Assert (p->type == PIXMAP, "not a pixmap");
2446
2447   int width  = p->frame.size.width;
2448   int height = p->frame.size.height;
2449   char *data = (char *) malloc (width * height * 4);
2450   if (! data) return 0;
2451
2452   memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2453
2454   Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2455   *p2 = *p;
2456   p2->cgi = 0;
2457   p2->pixmap.cgc_buffer = data;
2458   p2->cgc = CGBitmapContextCreate (data, width, height,
2459                                    8, /* bits per component */
2460                                    width * 4, /* bpl */
2461                                    dpy->colorspace,
2462                                    // Without this, it returns 0...
2463                                    (kCGImageAlphaNoneSkipFirst |
2464                                     kCGBitmapByteOrder32Host)
2465                                    );
2466   Assert (p2->cgc, "could not create CGBitmapContext");
2467
2468   return p2;
2469 }
2470
2471
2472 /* Font metric terminology, as used by X11:
2473
2474    "lbearing" is the distance from the logical origin to the leftmost pixel.
2475    If a character's ink extends to the left of the origin, it is negative.
2476
2477    "rbearing" is the distance from the logical origin to the rightmost pixel.
2478
2479    "descent" is the distance from the logical origin to the bottommost pixel.
2480    For characters with descenders, it is negative.
2481
2482    "ascent" is the distance from the logical origin to the topmost pixel.
2483    It is the number of pixels above the baseline.
2484
2485    "width" is the distance from the logical origin to the position where
2486    the logical origin of the next character should be placed.
2487
2488    If "rbearing" is greater than "width", then this character overlaps the
2489    following character.  If smaller, then there is trailing blank space.
2490  */
2491
2492
2493 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2494 //
2495 static void
2496 query_font (Font fid)
2497 {
2498   if (!fid || !fid->nsfont) {
2499     Assert (0, "no NSFont in fid");
2500     return;
2501   }
2502   if (![fid->nsfont fontName]) {
2503     Assert(0, @"broken NSFont in fid");
2504     return;
2505   }
2506
2507   int first = 32;
2508   int last = 255;
2509
2510   XFontStruct *f = &fid->metrics;
2511   XCharStruct *min = &f->min_bounds;
2512   XCharStruct *max = &f->max_bounds;
2513
2514 //#define CEIL(F)  ((F) < 0 ? floor(F) : ceil(F))
2515 //#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
2516 #define CEIL(F)  ceil(F)
2517 #define FLOOR(F) floor(F)
2518
2519   f->fid               = fid;
2520   f->min_char_or_byte2 = first;
2521   f->max_char_or_byte2 = last;
2522   f->default_char      = 'M';
2523   f->ascent            =  CEIL ([fid->nsfont ascender]);
2524   f->descent           = -FLOOR ([fid->nsfont descender]);
2525
2526   min->width    = 255;  // set to smaller values in the loop
2527   min->ascent   = 255;
2528   min->descent  = 255;
2529   min->lbearing = 255;
2530   min->rbearing = 255;
2531
2532   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2533   UniChar i;
2534
2535   CTFontRef ctfont =
2536     CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2537                           [fid->nsfont pointSize],
2538                           NULL);
2539   Assert (ctfont, @"no CTFontRef for UIFont");
2540
2541   for (i = first; i <= last; i++) {
2542     CGSize advancement = CGSizeMake (0, 0);
2543     CGRect bbox = CGRectMake (0, 0, 0, 0);
2544
2545     /* There is no way to get "lbearing", "rbearing" or "descent" out of
2546        NSFont.  'sizeWithFont' gives us "width" and "height" only.
2547        Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2548        width of the character and the ascent of the font.
2549
2550        Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2551        the CoreText library, but there's no non-CoreText way to turn a
2552        unichar into a CGGlyph.
2553      */
2554     CGGlyph cgglyph = 0;
2555
2556     if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
2557       {
2558         bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2559                                                 kCTFontDefaultOrientation,
2560                                                 &cgglyph, NULL, 1);
2561         CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2562                                     &cgglyph, &advancement, 1);
2563       }
2564     else
2565       {
2566         // This is normal, since Latin1 does not encode 0-31 or 127-159.
2567         // NSLog (@"no glyph for character %d\n", i);
2568       }
2569
2570 # if 0
2571     if (i == 224) {  // Latin1 == "agrave", MacRoman == "daggerdouble".
2572       NSString *glyph_name = (NSString *)
2573         CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2574                                      cgglyph);
2575       Assert ([glyph_name isEqualToString:@"agrave"], @"wrong encoding");
2576     }
2577     if (i == 250) {  // Latin1 == "uacute", MacRoman == "dotaccent".
2578       NSString *glyph_name = (NSString *)
2579         CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2580                                      cgglyph);
2581       Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
2582     }
2583 # endif // 9
2584
2585 # ifndef USE_IPHONE
2586     // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2587     bbox.origin.x    -= 2.0/3.0;
2588     bbox.size.width  += 4.0/3.0;
2589     bbox.size.height += 1.0/2.0;
2590 # endif
2591  
2592     /* Now that we know the advancement and bounding box, we can compute
2593        the lbearing and rbearing.
2594      */
2595     XCharStruct *cs = &f->per_char[i-first];
2596
2597 //    cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2598     cs->ascent   = CEIL (bbox.origin.y + bbox.size.height);
2599     cs->descent  = CEIL(-bbox.origin.y);
2600     cs->lbearing = FLOOR (bbox.origin.x);
2601 //  cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2602     cs->rbearing = CEIL (bbox.origin.x + bbox.size.width);
2603     cs->width    = FLOOR (advancement.width + 0.5);
2604
2605 //  Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
2606 //          "bbox w wrong");
2607 //  Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
2608 //          "bbox h wrong");
2609
2610     max->width    = MAX (max->width,    cs->width);
2611     max->ascent   = MAX (max->ascent,   cs->ascent);
2612     max->descent  = MAX (max->descent,  cs->descent);
2613     max->lbearing = MAX (max->lbearing, cs->lbearing);
2614     max->rbearing = MAX (max->rbearing, cs->rbearing);
2615
2616     min->width    = MIN (min->width,    cs->width);
2617     min->ascent   = MIN (min->ascent,   cs->ascent);
2618     min->descent  = MIN (min->descent,  cs->descent);
2619     min->lbearing = MIN (min->lbearing, cs->lbearing);
2620     min->rbearing = MIN (min->rbearing, cs->rbearing);
2621
2622 # undef CEIL
2623 # undef FLOOR
2624
2625 # if 0
2626     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2627                     " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n",
2628             i, i, cs->width, cs->lbearing, cs->rbearing, 
2629             cs->ascent, cs->descent,
2630             bbox.size.width, bbox.size.height,
2631             bbox.origin.x, bbox.origin.y,
2632             advancement.width, advancement.height);
2633 # endif // 0
2634   }
2635
2636   CFRelease (ctfont);
2637 }
2638
2639
2640 // Since 'Font' includes the metrics, this just makes a copy of that.
2641 //
2642 XFontStruct *
2643 XQueryFont (Display *dpy, Font fid)
2644 {
2645   // copy XFontStruct
2646   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2647   *f = fid->metrics;
2648
2649   // copy XCharStruct array
2650   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2651   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2652   memcpy (f->per_char, fid->metrics.per_char,
2653           size * sizeof (XCharStruct));
2654
2655   return f;
2656 }
2657
2658
2659 static Font
2660 copy_font (Font fid)
2661 {
2662   // copy 'Font' struct
2663   Font fid2 = (Font) malloc (sizeof(*fid2));
2664   *fid2 = *fid;
2665
2666   // copy XCharStruct array
2667   int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2668   fid2->metrics.per_char = (XCharStruct *) 
2669     malloc ((size + 2) * sizeof (XCharStruct));
2670   memcpy (fid2->metrics.per_char, fid->metrics.per_char, 
2671           size * sizeof (XCharStruct));
2672
2673   // copy the other pointers
2674   fid2->ps_name = strdup (fid->ps_name);
2675 //  [fid2->nsfont retain];
2676   fid2->metrics.fid = fid2;
2677
2678   return fid2;
2679 }
2680
2681
2682 static NSFont *
2683 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2684           char **name_ret)
2685 {
2686   Assert (size > 0, "zero font size");
2687   const char *name;
2688
2689   if (fixed) {
2690     // 
2691     // "Monaco" only exists in plain.
2692     // "LucidaSansTypewriterStd" gets an AGL bad value error.
2693     // 
2694     if (bold && ital) name = "Courier-BoldOblique";
2695     else if (bold)    name = "Courier-Bold";
2696     else if (ital)    name = "Courier-Oblique";
2697     else              name = "Courier";
2698
2699   } else if (serif) {
2700     // 
2701     // "Georgia" looks better than "Times".
2702     // 
2703     if (bold && ital) name = "Georgia-BoldItalic";
2704     else if (bold)    name = "Georgia-Bold";
2705     else if (ital)    name = "Georgia-Italic";
2706     else              name = "Georgia";
2707
2708   } else {
2709     // 
2710     // "Geneva" only exists in plain.
2711     // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2712     // "Verdana" renders smoother than "Helvetica" for some reason.
2713     // 
2714     if (bold && ital) name = "Verdana-BoldItalic";
2715     else if (bold)    name = "Verdana-Bold";
2716     else if (ital)    name = "Verdana-Italic";
2717     else              name = "Verdana";
2718   }
2719
2720   NSString *nsname = [NSString stringWithCString:name
2721                                         encoding:NSUTF8StringEncoding];
2722   NSFont *f = [NSFont fontWithName:nsname size:size];
2723   if (f)
2724     *name_ret = strdup(name);
2725   return f;
2726 }
2727
2728 static NSFont *
2729 try_native_font (const char *name, float scale,
2730                  char **name_ret, float *size_ret)
2731 {
2732   if (!name) return 0;
2733   const char *spc = strrchr (name, ' ');
2734   if (!spc) return 0;
2735   int dsize = 0;
2736   if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2737   float size = dsize;
2738
2739   if (size <= 4) return 0;
2740
2741   size *= scale;
2742
2743   char *name2 = strdup (name);
2744   name2[strlen(name2) - strlen(spc)] = 0;
2745   NSString *nsname = [NSString stringWithCString:name2
2746                                         encoding:NSUTF8StringEncoding];
2747   NSFont *f = [NSFont fontWithName:nsname size:size];
2748   if (f) {
2749     *name_ret = name2;
2750     *size_ret = size;
2751     return f;
2752   } else {
2753     free (name2);
2754     return 0;
2755   }
2756 }
2757
2758
2759 /* Returns a random font in the given size and face.
2760  */
2761 static NSFont *
2762 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2763 {
2764 # ifndef USE_IPHONE
2765   NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
2766                           (ital ? NSItalicFontMask : NSUnitalicFontMask));
2767   NSArray *fonts = [[NSFontManager sharedFontManager]
2768                      availableFontNamesWithTraits:mask];
2769   if (!fonts) return 0;
2770
2771   int n = [fonts count];
2772   if (n <= 0) return 0;
2773
2774   int j;
2775   for (j = 0; j < n; j++) {
2776     int i = random() % n;
2777     NSString *name = [fonts objectAtIndex:i];
2778     NSFont *f = [NSFont fontWithName:name size:size];
2779     if (!f) continue;
2780
2781     /* Don't use this font if it (probably) doesn't include ASCII characters.
2782      */
2783     NSStringEncoding enc = [f mostCompatibleStringEncoding];
2784     if (! (enc == NSUTF8StringEncoding ||
2785            enc == NSISOLatin1StringEncoding ||
2786            enc == NSNonLossyASCIIStringEncoding ||
2787            enc == NSISOLatin2StringEncoding ||
2788            enc == NSUnicodeStringEncoding ||
2789            enc == NSWindowsCP1250StringEncoding ||
2790            enc == NSWindowsCP1252StringEncoding ||
2791            enc == NSMacOSRomanStringEncoding)) {
2792       // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2793       continue;
2794     }
2795     // NSLog(@"using \"%@\": %d", name, enc);
2796
2797     *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2798     return f;
2799   }
2800
2801   // None of the fonts support ASCII?
2802   return 0;
2803
2804 # else  // USE_IPHONE
2805
2806   NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2807   NSArray *families = [UIFont familyNames];
2808   NSMutableDictionary *famdict = [NSMutableDictionary 
2809                                    dictionaryWithCapacity:100];
2810   NSObject *y = [NSNumber numberWithBool:YES];
2811   for (NSString *name in families) {
2812     // There are many dups in the families array -- uniquify it.
2813     [famdict setValue:y forKey:name];
2814   }
2815
2816   for (NSString *name in famdict) {
2817     for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2818
2819 # define MATCH(X) \
2820          ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2821          != NSNotFound)
2822
2823       BOOL bb = MATCH(@"Bold");
2824       BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2825
2826       if (!bold != !bb) continue;
2827       if (!ital != !ii) continue;
2828
2829       /* Check if it can do ASCII.  No good way to accomplish this!
2830          These are fonts present in iPhone Simulator as of June 2012
2831          that don't include ASCII.
2832        */
2833       if (MATCH(@"AppleGothic") ||      // Korean
2834           MATCH(@"Dingbats") ||         // Dingbats
2835           MATCH(@"Emoji") ||            // Emoticons
2836           MATCH(@"Geeza") ||            // Arabic
2837           MATCH(@"Hebrew") ||           // Hebrew
2838           MATCH(@"HiraKaku") ||         // Japanese
2839           MATCH(@"HiraMin") ||          // Japanese
2840           MATCH(@"Kailasa") ||          // Tibetan
2841           MATCH(@"Ornaments") ||        // Dingbats
2842           MATCH(@"STHeiti")             // Chinese
2843          )
2844         continue;
2845
2846       [fonts addObject:fn];
2847 # undef MATCH
2848     }
2849   }
2850
2851   if (! [fonts count]) return 0;        // Nothing suitable?
2852
2853   int i = random() % [fonts count];
2854   NSString *name = [fonts objectAtIndex:i];
2855   UIFont *ff = [UIFont fontWithName:name size:size];
2856   *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2857
2858   return ff;
2859
2860 # endif // USE_IPHONE
2861 }
2862
2863
2864 static NSFont *
2865 try_xlfd_font (const char *name, float scale,
2866                char **name_ret, float *size_ret)
2867 {
2868   NSFont *nsfont = 0;
2869   BOOL bold  = NO;
2870   BOOL ital  = NO;
2871   BOOL fixed = NO;
2872   BOOL serif = NO;
2873   BOOL rand  = NO;
2874   float size = 0;
2875   char *ps_name = 0;
2876
2877   const char *s = (name ? name : "");
2878   while (*s) {
2879     while (*s && (*s == '*' || *s == '-'))
2880       s++;
2881     const char *s2 = s;
2882     while (*s2 && (*s2 != '*' && *s2 != '-'))
2883       s2++;
2884     
2885     unsigned long L = s2-s;
2886     if (s == s2)
2887       ;
2888 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2889     else if (CMP ("random"))   rand  = YES;
2890     else if (CMP ("bold"))     bold  = YES;
2891     else if (CMP ("i"))        ital  = YES;
2892     else if (CMP ("o"))        ital  = YES;
2893     else if (CMP ("courier"))  fixed = YES;
2894     else if (CMP ("fixed"))    fixed = YES;
2895     else if (CMP ("m"))        fixed = YES;
2896     else if (CMP ("times"))    serif = YES;
2897     else if (CMP ("6x10"))     fixed = YES, size = 8;
2898     else if (CMP ("6x10bold")) fixed = YES, size = 8,  bold = YES;
2899     else if (CMP ("9x15"))     fixed = YES, size = 12;
2900     else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2901     else if (CMP ("vga"))      fixed = YES, size = 12;
2902     else if (CMP ("console"))  fixed = YES, size = 12;
2903     else if (CMP ("gallant"))  fixed = YES, size = 12;
2904 # undef CMP
2905     else if (size == 0) {
2906       int n = 0;
2907       if (1 == sscanf (s, " %d ", &n))
2908         size = n / 10.0;
2909     }
2910
2911     s = s2;
2912   }
2913
2914   if (size < 6 || size > 1000)
2915     size = 12;
2916
2917   size *= scale;
2918
2919   if (rand)
2920     nsfont   = random_font (bold, ital, size, &ps_name);
2921
2922   if (!nsfont)
2923     nsfont   = try_font (fixed, bold, ital, serif, size, &ps_name);
2924
2925   // if that didn't work, turn off attibutes until it does
2926   // (e.g., there is no "Monaco-Bold".)
2927   //
2928   if (!nsfont && serif) {
2929     serif = NO;
2930     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2931   }
2932   if (!nsfont && ital) {
2933     ital = NO;
2934     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2935   }
2936   if (!nsfont && bold) {
2937     bold = NO;
2938     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2939   }
2940   if (!nsfont && fixed) {
2941     fixed = NO;
2942     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2943   }
2944
2945   if (nsfont) {
2946     *name_ret = ps_name;
2947     *size_ret = size;
2948     return nsfont;
2949   } else {
2950     return 0;
2951   }
2952 }
2953
2954
2955 Font
2956 XLoadFont (Display *dpy, const char *name)
2957 {
2958   Font fid = (Font) calloc (1, sizeof(*fid));
2959
2960   float scale = 1;
2961
2962 # ifdef USE_IPHONE
2963   // Scale up fonts on Retina displays.
2964   scale = dpy->main_window->window.view.contentScaleFactor;
2965 # endif
2966
2967   fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2968
2969   if (!fid->nsfont && name &&
2970       strchr (name, ' ') &&
2971       !strchr (name, '*')) {
2972     // If name contains a space but no stars, it is a native font spec --
2973     // return NULL so that we know it really didn't exist.  Else, it is an
2974     //  XLFD font, so keep trying.
2975     XUnloadFont (dpy, fid);
2976     return 0;
2977   }
2978
2979   if (! fid->nsfont)
2980     fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2981
2982   // We should never return NULL for XLFD fonts.
2983   if (!fid->nsfont) {
2984     Assert (0, "no font");
2985     return 0;
2986   }
2987   CFRetain (fid->nsfont);   // needed for garbage collection?
2988
2989   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2990
2991   query_font (fid);
2992
2993   return fid;
2994 }
2995
2996
2997 XFontStruct *
2998 XLoadQueryFont (Display *dpy, const char *name)
2999 {
3000   Font fid = XLoadFont (dpy, name);
3001   if (!fid) return 0;
3002   return XQueryFont (dpy, fid);
3003 }
3004
3005 int
3006 XUnloadFont (Display *dpy, Font fid)
3007 {
3008   if (fid->ps_name)
3009     free (fid->ps_name);
3010   if (fid->metrics.per_char)
3011     free (fid->metrics.per_char);
3012
3013   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
3014   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
3015   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
3016   //      They're probably not very big...
3017   //
3018   //  [fid->nsfont release];
3019   //  CFRelease (fid->nsfont);
3020
3021   free (fid);
3022   return 0;
3023 }
3024
3025 int
3026 XFreeFontInfo (char **names, XFontStruct *info, int n)
3027 {
3028   int i;
3029   if (names) {
3030     for (i = 0; i < n; i++)
3031       if (names[i]) free (names[i]);
3032     free (names);
3033   }
3034   if (info) {
3035     for (i = 0; i < n; i++)
3036       if (info[i].per_char)
3037         free (info[i].per_char);
3038     free (info);
3039   }
3040   return 0;
3041 }
3042
3043 int
3044 XFreeFont (Display *dpy, XFontStruct *f)
3045 {
3046   Font fid = f->fid;
3047   XFreeFontInfo (0, f, 1);
3048   XUnloadFont (dpy, fid);
3049   return 0;
3050 }
3051
3052
3053 int
3054 XSetFont (Display *dpy, GC gc, Font fid)
3055 {
3056   if (gc->gcv.font)
3057     XUnloadFont (dpy, gc->gcv.font);
3058   gc->gcv.font = copy_font (fid);
3059   [gc->gcv.font->nsfont retain];
3060   CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
3061   return 0;
3062 }
3063
3064 int
3065 XTextExtents (XFontStruct *f, const char *s, int length,
3066               int *dir_ret, int *ascent_ret, int *descent_ret,
3067               XCharStruct *cs)
3068 {
3069   memset (cs, 0, sizeof(*cs));
3070   int i;
3071   for (i = 0; i < length; i++) {
3072     unsigned char c = (unsigned char) s[i];
3073     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3074       c = f->default_char;
3075     const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3076     if (i == 0) {
3077       *cs = *cc;
3078     } else {
3079       cs->ascent   = MAX (cs->ascent,   cc->ascent);
3080       cs->descent  = MAX (cs->descent,  cc->descent);
3081       cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3082       cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3083       cs->width   += cc->width;
3084     }
3085   }
3086   *dir_ret = 0;
3087   *ascent_ret  = f->ascent;
3088   *descent_ret = f->descent;
3089   return 0;
3090 }
3091
3092 int
3093 XTextWidth (XFontStruct *f, const char *s, int length)
3094 {
3095   int ascent, descent, dir;
3096   XCharStruct cs;
3097   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3098   return cs.width;
3099 }
3100
3101
3102 static int
3103 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3104              const char  *str, int len, BOOL clear_background_p)
3105 {
3106   if (clear_background_p) {
3107     int ascent, descent, dir;
3108     XCharStruct cs;
3109     XTextExtents (&gc->gcv.font->metrics, str, len,
3110                   &dir, &ascent, &descent, &cs);
3111     draw_rect (dpy, d, gc,
3112                x + MIN (0, cs.lbearing),
3113                y - MAX (0, ascent),
3114                MAX (MAX (0, cs.rbearing) -
3115                     MIN (0, cs.lbearing),
3116                     cs.width),
3117                MAX (0, ascent) + MAX (0, descent),
3118                NO, YES);
3119   }
3120
3121   CGRect wr = d->frame;
3122   CGContextRef cgc = d->cgc;
3123
3124   unsigned long argb = gc->gcv.foreground;
3125   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3126   float a = ((argb >> 24) & 0xFF) / 255.0;
3127   float r = ((argb >> 16) & 0xFF) / 255.0;
3128   float g = ((argb >>  8) & 0xFF) / 255.0;
3129   float b = ((argb      ) & 0xFF) / 255.0;
3130   NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3131
3132   if (!gc->gcv.font) {
3133     Assert (0, "no font");
3134     return 1;
3135   }
3136
3137   NSDictionary *attr =
3138     [NSDictionary dictionaryWithObjectsAndKeys:
3139                     gc->gcv.font->nsfont, NSFontAttributeName,
3140                     fg, NSForegroundColorAttributeName,
3141                   nil];
3142
3143   // Don't understand why we have to do both set_color and
3144   // NSForegroundColorAttributeName, but we do.
3145   //
3146   set_color (cgc, argb, 32, NO, YES);
3147
3148   char *s2 = (char *) malloc (len + 1);
3149   strncpy (s2, str, len);
3150   s2[len] = 0;
3151   NSString *nsstr = [NSString stringWithCString:s2
3152                                        encoding:NSISOLatin1StringEncoding];
3153   NSAttributedString *astr = [[NSAttributedString alloc]
3154                                initWithString:nsstr
3155                                    attributes:attr];
3156   CTLineRef dl = CTLineCreateWithAttributedString (
3157                    (__bridge CFAttributedStringRef) astr);
3158   CGContextSetTextPosition (cgc,
3159                             wr.origin.x + x,
3160                             wr.origin.y + wr.size.height - y);
3161   CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3162   CTLineDraw (dl, cgc);
3163   CFRelease (dl);
3164
3165   invalidate_drawable_cache (d);
3166   return 0;
3167 }
3168
3169
3170 int
3171 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3172              const char  *str, int len)
3173 {
3174   return draw_string (dpy, d, gc, x, y, str, len, NO);
3175 }
3176
3177 int
3178 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3179                   const char *str, int len)
3180 {
3181   return draw_string (dpy, d, gc, x, y, str, len, YES);
3182 }
3183
3184
3185 int
3186 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3187 {
3188   validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3189   gc->gcv.foreground = fg;
3190   return 0;
3191 }
3192
3193
3194 int
3195 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3196 {
3197   validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3198   gc->gcv.background = bg;
3199   return 0;
3200 }
3201
3202 int
3203 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3204 {
3205   gc->gcv.alpha_allowed_p = allowed;
3206   return 0;
3207 }
3208
3209 int
3210 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3211 {
3212   gc->gcv.antialias_p = antialias_p;
3213   return 0;
3214 }
3215
3216
3217 int
3218 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3219                     int line_style, int cap_style, int join_style)
3220 {
3221   gc->gcv.line_width = line_width;
3222   Assert (line_style == LineSolid, "only LineSolid implemented");
3223 //  gc->gcv.line_style = line_style;
3224   gc->gcv.cap_style = cap_style;
3225   gc->gcv.join_style = join_style;
3226   return 0;
3227 }
3228
3229 int
3230 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3231 {
3232   return 0;
3233 }
3234
3235 int
3236 XSetFunction (Display *dpy, GC gc, int which)
3237 {
3238   gc->gcv.function = which;
3239   return 0;
3240 }
3241
3242 int
3243 XSetSubwindowMode (Display *dpy, GC gc, int which)
3244 {
3245   gc->gcv.subwindow_mode = which;
3246   return 0;
3247 }
3248
3249 int
3250 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3251 {
3252   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3253
3254   if (gc->gcv.clip_mask) {
3255     XFreePixmap (dpy, gc->gcv.clip_mask);
3256     CGImageRelease (gc->clip_mask);
3257   }
3258
3259   gc->gcv.clip_mask = copy_pixmap (dpy, m);
3260   if (gc->gcv.clip_mask)
3261     gc->clip_mask =
3262       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3263   else
3264     gc->clip_mask = 0;
3265
3266   return 0;
3267 }
3268
3269 int
3270 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3271 {
3272   gc->gcv.clip_x_origin = x;
3273   gc->gcv.clip_y_origin = y;
3274   return 0;
3275 }
3276
3277
3278 Bool
3279 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3280                int *root_x_ret, int *root_y_ret, 
3281                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3282 {
3283   Assert (w && w->type == WINDOW, "not a window");
3284
3285 # ifdef USE_IPHONE
3286   int x = w->window.last_mouse_x;
3287   int y = w->window.last_mouse_y;
3288   if (root_x_ret) *root_x_ret = x;
3289   if (root_y_ret) *root_y_ret = y;
3290   if (win_x_ret)  *win_x_ret  = x;
3291   if (win_y_ret)  *win_y_ret  = y;
3292
3293 # else  // !USE_IPHONE
3294
3295   NSWindow *nsw = [w->window.view window];
3296   NSPoint wpos;
3297   // get bottom left of window on screen, from bottom left
3298   wpos.x = wpos.y = 0;
3299   wpos = [nsw convertBaseToScreen:wpos];
3300   
3301   NSPoint vpos;
3302   // get bottom left of view on window, from bottom left
3303   vpos.x = vpos.y = 0;
3304   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3305
3306   // get bottom left of view on screen, from bottom left
3307   vpos.x += wpos.x;
3308   vpos.y += wpos.y;
3309   
3310   // get top left of view on screen, from bottom left
3311   vpos.y += w->frame.size.height;
3312   
3313   // get top left of view on screen, from top left
3314   NSArray *screens = [NSScreen screens];
3315   NSScreen *screen = (screens && [screens count] > 0
3316                       ? [screens objectAtIndex:0]
3317                       : [NSScreen mainScreen]);
3318 #ifdef USE_IPHONE
3319   double s = w->window.view.contentScaleFactor;
3320 #else
3321   int s = 1;
3322 #endif
3323   NSRect srect = [screen frame];
3324   vpos.y = (s * srect.size.height) - vpos.y;
3325   
3326   // get the mouse position on window, from bottom left
3327   NSEvent *e = [NSApp currentEvent];
3328   NSPoint p = [e locationInWindow];
3329   
3330   // get mouse position on screen, from bottom left
3331   p.x += wpos.x;
3332   p.y += wpos.y;
3333   
3334   // get mouse position on screen, from top left
3335   p.y = srect.size.height - p.y;
3336
3337   if (root_x_ret) *root_x_ret = (int) p.x;
3338   if (root_y_ret) *root_y_ret = (int) p.y;
3339   if (win_x_ret)  *win_x_ret  = (int) (p.x - vpos.x);
3340   if (win_y_ret)  *win_y_ret  = (int) (p.y - vpos.y);
3341 # endif // !USE_IPHONE
3342   
3343   if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
3344   if (root_ret)   *root_ret   = 0;
3345   if (child_ret)  *child_ret  = 0;
3346   return True;
3347 }
3348
3349 Bool
3350 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3351                        int src_x, int src_y,
3352                        int *dest_x_ret, int *dest_y_ret,
3353                        Window *child_ret)
3354 {
3355   Assert (w && w->type == WINDOW, "not a window");
3356
3357 # ifdef USE_IPHONE
3358
3359   NSPoint p;
3360   p.x = src_x;
3361   p.y = src_y;
3362
3363 # else  // !USE_IPHONE
3364
3365   NSWindow *nsw = [w->window.view window];
3366   NSPoint wpos;
3367   // get bottom left of window on screen, from bottom left
3368   wpos.x = wpos.y = 0;
3369   wpos = [nsw convertBaseToScreen:wpos];
3370   
3371   NSPoint vpos;
3372   // get bottom left of view on window, from bottom left
3373   vpos.x = vpos.y = 0;
3374   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3375
3376   // get bottom left of view on screen, from bottom left
3377   vpos.x += wpos.x;
3378   vpos.y += wpos.y;
3379   
3380   // get top left of view on screen, from bottom left
3381   vpos.y += w->frame.size.height;
3382   
3383   // get top left of view on screen, from top left
3384   NSArray *screens = [NSScreen screens];
3385   NSScreen *screen = (screens && [screens count] > 0
3386                       ? [screens objectAtIndex:0]
3387                       : [NSScreen mainScreen]);
3388 # ifdef USE_IPHONE
3389   double s = w->window.view.contentScaleFactor;
3390 # else
3391   int s = 1;
3392 # endif
3393   NSRect srect = [screen frame];
3394   vpos.y = (s * srect.size.height) - vpos.y;
3395   
3396   // point starts out relative to top left of view
3397   NSPoint p;
3398   p.x = src_x;
3399   p.y = src_y;
3400   
3401   // get point relative to top left of screen
3402   p.x += vpos.x;
3403   p.y += vpos.y;
3404 # endif // !USE_IPHONE
3405
3406   *dest_x_ret = p.x;
3407   *dest_y_ret = p.y;
3408   if (child_ret)
3409     *child_ret = w;
3410   return True;
3411 }
3412
3413
3414 KeySym
3415 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3416 {
3417   return code;
3418 }
3419
3420 int
3421 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3422                XComposeStatus *xc)
3423 {
3424   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3425   char c = 0;
3426   // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3427   if ((unsigned int) ks <= 255)
3428     c = (char) ks;
3429
3430   // Put control characters in the string.  Not meta.
3431   if (e->state & ControlMask) {
3432     if (c >= 'a' && c <= 'z')    // Upcase control.
3433       c -= 'a'-'A';
3434     if (c >= '@' && c <= '_')    // Shift to control page.
3435       c -= '@';
3436     if (c == ' ')                // C-SPC is NULL.
3437       c = 0;
3438   }
3439
3440   if (k_ret) *k_ret = ks;
3441   if (size > 0) buf[0] = c;
3442   if (size > 1) buf[1] = 0;
3443   return (size > 0 ? 1 : 0);
3444 }
3445
3446
3447 int
3448 XFlush (Display *dpy)
3449 {
3450   // Just let the event loop take care of this on its own schedule.
3451   return 0;
3452 }
3453
3454 int
3455 XSync (Display *dpy, Bool flush)
3456 {
3457   return XFlush (dpy);
3458 }
3459
3460
3461 // declared in utils/visual.h
3462 int
3463 has_writable_cells (Screen *s, Visual *v)
3464 {
3465   return 0;
3466 }
3467
3468 int
3469 visual_depth (Screen *s, Visual *v)
3470 {
3471   return 32;
3472 }
3473
3474 int
3475 visual_cells (Screen *s, Visual *v)
3476 {
3477   return 0xFFFFFF;
3478 }
3479
3480 int
3481 visual_class (Screen *s, Visual *v)
3482 {
3483   return TrueColor;
3484 }
3485
3486 int
3487 get_bits_per_pixel (Display *dpy, int depth)
3488 {
3489   Assert (depth == 32 || depth == 1, "unexpected depth");
3490   return depth;
3491 }
3492
3493 // declared in utils/grabclient.h
3494 Bool
3495 use_subwindow_mode_p (Screen *screen, Window window)
3496 {
3497   return False;
3498 }