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