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