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