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