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