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