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