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