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