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