From http://www.jwz.org/xscreensaver/xscreensaver-5.33.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
862
863 /* You've got to be fucking kidding me!
864
865    It is *way* faster to draw points by creating and drawing a 1x1 CGImage
866    with repeated calls to CGContextDrawImage than it is to make a single
867    call to CGContextFillRects() with a list of 1x1 rectangles!
868
869    I still wouldn't call it *fast*, however...
870  */
871 #define XDRAWPOINTS_IMAGES
872
873 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
874    the bitmap data directly is faster.  This only works on Pixmaps, though,
875    not Windows.  (Fortunately, on iOS, the Window is really a Pixmap.)
876  */
877 #define XDRAWPOINTS_CGDATA
878
879 int
880 XDrawPoints (Display *dpy, Drawable d, GC gc, 
881              XPoint *points, int count, int mode)
882 {
883   int i;
884   CGRect wr = d->frame;
885
886 # ifdef XDRAWPOINTS_CGDATA
887
888 #  ifdef USE_BACKBUFFER
889   if (1)  // Because of the backbuffer, all iPhone Windows work like Pixmaps.
890 #  else
891   if (d->type == PIXMAP)
892 #  endif
893   {
894     CGContextRef cgc = d->cgc;
895     void *data = CGBitmapContextGetData (cgc);
896     size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
897     size_t w = CGBitmapContextGetWidth (cgc);
898     size_t h = CGBitmapContextGetHeight (cgc);
899
900     Assert (data, "no bitmap data in Drawable");
901
902     unsigned long argb = gc->gcv.foreground;
903     validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
904     if (gc->depth == 1)
905       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
906
907     CGFloat x0 = wr.origin.x;
908     CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
909
910     // It's uglier, but faster, to hoist the conditional out of the loop.
911     if (mode == CoordModePrevious) {
912       CGFloat x = x0, y = y0;
913       for (i = 0; i < count; i++, points++) {
914         x += points->x;
915         y += points->y;
916
917         if (x >= 0 && x < w && y >= 0 && y < h) {
918           unsigned int *p = (unsigned int *)
919             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
920           *p = (unsigned int) argb;
921         }
922       }
923     } else {
924       for (i = 0; i < count; i++, points++) {
925         CGFloat x = x0 + points->x;
926         CGFloat y = y0 + points->y;
927
928         if (x >= 0 && x < w && y >= 0 && y < h) {
929           unsigned int *p = (unsigned int *)
930             ((char *) data + (size_t) y * bpr + (size_t) x * 4);
931           *p = (unsigned int) argb;
932         }
933       }
934     }
935
936   } else        /* d->type == WINDOW */
937
938 # endif /* XDRAWPOINTS_CGDATA */
939   {
940     push_fg_gc (dpy, d, gc, YES);
941
942 # ifdef XDRAWPOINTS_IMAGES
943
944     unsigned int argb = gc->gcv.foreground;
945     validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
946     if (gc->depth == 1)
947       argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
948
949     CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
950                                                            NULL);
951     CGImageRef cgi = CGImageCreate (1, 1,
952                                     8, 32, 4,
953                                     dpy->colorspace, 
954                                     /* Host-ordered, since we're using the
955                                        address of an int as the color data. */
956                                     dpy->screen->bitmap_info,
957                                     prov, 
958                                     NULL,  /* decode[] */
959                                     NO, /* interpolate */
960                                     kCGRenderingIntentDefault);
961     CGDataProviderRelease (prov);
962
963     CGContextRef cgc = d->cgc;
964     CGRect rect;
965     rect.size.width = rect.size.height = 1;
966     for (i = 0; i < count; i++) {
967       if (i > 0 && mode == CoordModePrevious) {
968         rect.origin.x += points->x;
969         rect.origin.x -= points->y;
970       } else {
971         rect.origin.x = wr.origin.x + points->x;
972         rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
973       }
974
975       //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
976       CGContextDrawImage (cgc, rect, cgi);
977       points++;
978     }
979
980     CGImageRelease (cgi);
981
982 # else /* ! XDRAWPOINTS_IMAGES */
983
984     CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
985     CGRect *r = rects;
986   
987     for (i = 0; i < count; i++) {
988       r->size.width = r->size.height = 1;
989       if (i > 0 && mode == CoordModePrevious) {
990         r->origin.x = r[-1].origin.x + points->x;
991         r->origin.y = r[-1].origin.x - points->y;
992       } else {
993         r->origin.x = wr.origin.x + points->x;
994         r->origin.y = wr.origin.y + wr.size.height - points->y;
995       }
996       points++;
997       r++;
998     }
999
1000     CGContextFillRects (d->cgc, rects, count);
1001     free (rects);
1002
1003 # endif /* ! XDRAWPOINTS_IMAGES */
1004
1005     pop_gc (d, gc);
1006   }
1007
1008   invalidate_drawable_cache (d);
1009
1010   return 0;
1011 }
1012
1013
1014 int
1015 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
1016 {
1017   XPoint p;
1018   p.x = x;
1019   p.y = y;
1020   return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
1021 }
1022
1023
1024 static void draw_rect (Display *, Drawable, GC, 
1025                        int x, int y, unsigned int width, unsigned int height, 
1026                        BOOL foreground_p, BOOL fill_p);
1027
1028 static Bool
1029 bitmap_context_p (Drawable d)
1030 {
1031 # ifdef USE_BACKBUFFER
1032   return True;
1033 # else
1034   // Because of the backbuffer, all iPhone Windows work like Pixmaps.
1035   return d->type == PIXMAP;
1036 # endif
1037 }
1038
1039 static void
1040 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
1041                   size_t fill_width, size_t fill_height)
1042 {
1043   Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
1044   while (fill_height) {
1045     // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
1046     wmemset (dst, fill_data, fill_width);
1047     --fill_height;
1048     dst = (char *) dst + dst_pitch;
1049   }
1050 }
1051
1052 static void *
1053 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
1054 {
1055   return (char *)dst + dst_pitch * y + x * 4;
1056 }
1057
1058 static unsigned int
1059 drawable_depth (Drawable d)
1060 {
1061   return (d->type == WINDOW
1062           ? visual_depth (NULL, NULL)
1063           : d->pixmap.depth);
1064 }
1065
1066
1067 int
1068 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
1069            int src_x, int src_y, 
1070            unsigned int width, unsigned int height, 
1071            int dst_x, int dst_y)
1072 {
1073   Assert (gc, "no GC");
1074   Assert ((width  < 65535), "improbably large width");
1075   Assert ((height < 65535), "improbably large height");
1076   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1077   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1078   Assert ((dst_x  < 65535 && dst_x  > -65535), "improbably large dst_x");
1079   Assert ((dst_y  < 65535 && dst_y  > -65535), "improbably large dst_y");
1080
1081   if (width == 0 || height == 0)
1082     return 0;
1083
1084   if (gc->gcv.function == GXset ||
1085       gc->gcv.function == GXclear) {
1086     // "set" and "clear" are dumb drawing modes that ignore the source
1087     // bits and just draw solid rectangles.
1088     set_color (dpy, dst->cgc,
1089                (gc->gcv.function == GXset
1090                 ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
1091                 : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
1092                gc->depth, gc->gcv.alpha_allowed_p, YES);
1093     draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
1094     return 0;
1095   }
1096
1097   CGRect src_frame, dst_frame;   // Sizes and origins of the two drawables
1098   CGRect src_rect,  dst_rect;    // The two rects to draw, clipped to the
1099                                  //  bounds of their drawables.
1100   BOOL clipped = NO;             // Whether we did any clipping of the rects.
1101
1102   src_frame = src->frame;
1103   dst_frame = dst->frame;
1104   
1105   // Initialize src_rect...
1106   //
1107   src_rect.origin.x    = src_frame.origin.x + src_x;
1108   src_rect.origin.y    = src_frame.origin.y + src_frame.size.height
1109                           - height - src_y;
1110   if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
1111   src_rect.size.width  = width;
1112   src_rect.size.height = height;
1113   
1114   // Initialize dst_rect...
1115   //
1116   dst_rect.origin.x    = dst_frame.origin.x + dst_x;
1117   dst_rect.origin.y    = dst_frame.origin.y + dst_frame.size.height
1118                           - height - dst_y;
1119   if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
1120   dst_rect.size.width  = width;
1121   dst_rect.size.height = height;
1122   
1123   // Clip rects to frames...
1124   //
1125
1126 # define CLIP(THIS,THAT,VAL,SIZE) do { \
1127   float off = THIS##_rect.origin.VAL; \
1128   if (off < 0) { \
1129     clipped = YES; \
1130     THIS##_rect.size.SIZE  += off; \
1131     THAT##_rect.size.SIZE  += off; \
1132     THIS##_rect.origin.VAL -= off; \
1133     THAT##_rect.origin.VAL -= off; \
1134   } \
1135   off = (( THIS##_rect.origin.VAL +  THIS##_rect.size.SIZE) - \
1136          (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
1137   if (off > 0) { \
1138     clipped = YES; \
1139     THIS##_rect.size.SIZE  -= off; \
1140     THAT##_rect.size.SIZE  -= off; \
1141   }} while(0)
1142
1143   CLIP (dst, src, x, width);
1144   CLIP (dst, src, y, height);
1145
1146   // Not actually the original dst_rect, just the one before it's clipped to
1147   // the src_frame.
1148   CGRect orig_dst_rect = dst_rect;
1149
1150   CLIP (src, dst, x, width);
1151   CLIP (src, dst, y, height);
1152 # undef CLIP
1153
1154   if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
1155     return 0;
1156
1157   // Sort-of-special case where no pixels can be grabbed from the source,
1158   // and the whole destination is filled with the background color.
1159   if (src_rect.size.width < 0 || src_rect.size.height < 0) {
1160     
1161     Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
1162            (int)src_rect.size.height == (int)dst_rect.size.height,
1163            "size mismatch");
1164     
1165     src_rect.size.width  = 0;
1166     src_rect.size.height = 0;
1167     dst_rect.size.width  = 0;
1168     dst_rect.size.height = 0;
1169   }
1170   
1171   NSObject *releaseme = 0;
1172   CGImageRef cgi;
1173   BOOL mask_p = NO;
1174   BOOL free_cgi_p = NO;
1175
1176
1177   /* If we're copying from a bitmap to a bitmap, and there's nothing funny
1178      going on with clipping masks or depths or anything, optimize it by
1179      just doing a memcpy instead of going through a CGI.
1180    */
1181   if (bitmap_context_p (src)) {
1182
1183     if (bitmap_context_p (dst) &&
1184         gc->gcv.function == GXcopy &&
1185         !gc->gcv.clip_mask &&
1186         drawable_depth (src) == drawable_depth (dst)) {
1187
1188       Assert(!(int)src_frame.origin.x &&
1189              !(int)src_frame.origin.y &&
1190              !(int)dst_frame.origin.x &&
1191              !(int)dst_frame.origin.y,
1192              "unexpected non-zero origin");
1193       
1194       char *src_data = CGBitmapContextGetData(src->cgc);
1195       char *dst_data = CGBitmapContextGetData(dst->cgc);
1196       size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
1197       size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
1198       
1199       // Int to float and back again. It's not very safe, but it seems to work.
1200       int src_x0 = src_rect.origin.x;
1201       int dst_x0 = dst_rect.origin.x;
1202       
1203       // Flip the Y-axis a second time.
1204       int src_y0 = (src_frame.origin.y + src_frame.size.height -
1205                     src_rect.size.height - src_rect.origin.y);
1206       int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
1207                     dst_rect.size.height - dst_rect.origin.y);
1208       
1209       unsigned width0  = (int) src_rect.size.width;
1210       unsigned height0 = (int) src_rect.size.height;
1211       
1212       Assert((int)src_rect.size.width  == (int)dst_rect.size.width ||
1213              (int)src_rect.size.height == (int)dst_rect.size.height,
1214              "size mismatch");
1215       {
1216         char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
1217         char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
1218         size_t src_pitch0 = src_pitch;
1219         size_t dst_pitch0 = dst_pitch;
1220         size_t bytes = width0 * 4;
1221
1222         if (src == dst && dst_y0 > src_y0) {
1223           // Copy upwards if the areas might overlap.
1224           src_data0 += src_pitch0 * (height0 - 1);
1225           dst_data0 += dst_pitch0 * (height0 - 1);
1226           src_pitch0 = -src_pitch0;
1227           dst_pitch0 = -dst_pitch0;
1228         }
1229       
1230         size_t lines0 = height0;
1231         while (lines0) {
1232           // memcpy is an alias for memmove on OS X.
1233           memmove(dst_data0, src_data0, bytes);
1234           src_data0 += src_pitch0;
1235           dst_data0 += dst_pitch0;
1236           --lines0;
1237         }
1238       }
1239
1240       if (clipped) {
1241         int orig_dst_x = orig_dst_rect.origin.x;
1242         int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
1243                           orig_dst_rect.origin.y - orig_dst_rect.size.height);
1244         int orig_width  = orig_dst_rect.size.width;
1245         int orig_height = orig_dst_rect.size.height;
1246
1247         Assert (orig_dst_x >= 0 &&
1248                 orig_dst_x + orig_width  <= (int) dst_frame.size.width &&
1249                 orig_dst_y >= 0 &&
1250                 orig_dst_y + orig_height <= (int) dst_frame.size.height,
1251                 "wrong dimensions");
1252
1253         if (orig_dst_y < dst_y0) {
1254           fill_rect_memset (seek_xy (dst_data, dst_pitch,
1255                                      orig_dst_x, orig_dst_y), dst_pitch,
1256                             (uint32_t) gc->gcv.background, orig_width,
1257                             dst_y0 - orig_dst_y);
1258         }
1259
1260         if (orig_dst_y + orig_height > dst_y0 + height0) {
1261           fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
1262                                      dst_y0 + height0),
1263                             dst_pitch,
1264                             (uint32_t) gc->gcv.background, orig_width,
1265                             orig_dst_y + orig_height - dst_y0 - height0);
1266         }
1267
1268         if (orig_dst_x < dst_x0) {
1269           fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
1270                             dst_pitch, (uint32_t) gc->gcv.background,
1271                             dst_x0 - orig_dst_x, height0);
1272         }
1273
1274         if (dst_x0 + width0 < orig_dst_x + orig_width) {
1275           fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
1276                                      dst_y0),
1277                             dst_pitch, (uint32_t) gc->gcv.background,
1278                             orig_dst_x + orig_width - dst_x0 - width0,
1279                             height0);
1280         }
1281       }
1282
1283       invalidate_drawable_cache (dst);
1284       return 0;
1285     }
1286
1287
1288     // If we are copying from a Pixmap to a Pixmap or Window, we must first
1289     // copy the bits to an intermediary CGImage object, then copy that to the
1290     // destination drawable's CGContext.
1291     //
1292     // (It doesn't seem to be possible to use NSCopyBits() to optimize the
1293     // case of copying from a Pixmap back to itself, but I don't think that
1294     // happens very often anyway.)
1295     //
1296     // First we get a CGImage out of the pixmap CGContext -- it's the whole
1297     // pixmap, but it presumably shares the data pointer instead of copying
1298     // it.  We then cache that CGImage it inside the Pixmap object.  Note:
1299     // invalidate_drawable_cache() must be called to discard this any time a
1300     // modification is made to the pixmap, or we'll end up re-using old bits.
1301     //
1302     if (!src->cgi)
1303       src->cgi = CGBitmapContextCreateImage (src->cgc);
1304     cgi = src->cgi;
1305
1306     // if doing a sub-rect, trim it down.
1307     if (src_rect.origin.x    != src_frame.origin.x   ||
1308         src_rect.origin.y    != src_frame.origin.y   ||
1309         src_rect.size.width  != src_frame.size.width ||
1310         src_rect.size.height != src_frame.size.height) {
1311       // #### I don't understand why this is needed...
1312       src_rect.origin.y = (src_frame.size.height -
1313                            src_rect.size.height - src_rect.origin.y);
1314       // This does not copy image data, so it should be fast.
1315       cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1316       free_cgi_p = YES;
1317     }
1318
1319   if (src->type == PIXMAP && src->pixmap.depth == 1)
1320       mask_p = YES;
1321
1322 # ifndef USE_BACKBUFFER
1323   } else { /* (src->type == WINDOW) */
1324     
1325     NSRect nsfrom;    // NSRect != CGRect on 10.4
1326     nsfrom.origin.x    = src_rect.origin.x;
1327     nsfrom.origin.y    = src_rect.origin.y;
1328     nsfrom.size.width  = src_rect.size.width;
1329     nsfrom.size.height = src_rect.size.height;
1330
1331     if (src == dst) {
1332
1333       // If we are copying from a window to itself, we can use NSCopyBits()
1334       // without first copying the rectangle to an intermediary CGImage.
1335       // This is ~28% faster (but I *expected* it to be twice as fast...)
1336       // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1337       //
1338       cgi = 0;
1339
1340     } else {
1341
1342       // If we are copying from a Window to a Pixmap, we must first copy
1343       // the bits to an intermediary CGImage object, then copy that to the
1344       // Pixmap's CGContext.
1345       //
1346       NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1347                                initWithFocusedViewRect:nsfrom];
1348       unsigned char *data = [bm bitmapData];
1349       int bps = [bm bitsPerSample];
1350       int bpp = [bm bitsPerPixel];
1351       int bpl = [bm bytesPerRow];
1352       releaseme = bm;
1353
1354       // create a CGImage from those bits.
1355       // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1356       // but that method didn't exist in 10.4.)
1357
1358       CGDataProviderRef prov =
1359         CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1360                                       NULL);
1361       cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1362                            bps, bpp, bpl,
1363                            dpy->colorspace, 
1364                            /* Use whatever default bit ordering we got from
1365                               initWithFocusedViewRect.  I would have assumed
1366                               that it was (kCGImageAlphaNoneSkipFirst |
1367                               kCGBitmapByteOrder32Host), but on Intel,
1368                               it's not!
1369                            */
1370                            0,
1371                            prov, 
1372                            NULL,  /* decode[] */
1373                            NO, /* interpolate */
1374                            kCGRenderingIntentDefault);
1375       free_cgi_p = YES;
1376       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1377       CGDataProviderRelease (prov);
1378     }
1379
1380 # endif // !USE_BACKBUFFER
1381   }
1382
1383   CGContextRef cgc = dst->cgc;
1384
1385   if (mask_p) {         // src depth == 1
1386
1387     push_bg_gc (dpy, dst, gc, YES);
1388
1389     // fill the destination rectangle with solid background...
1390     CGContextFillRect (cgc, orig_dst_rect);
1391
1392     Assert (cgc, "no CGC with 1-bit XCopyArea");
1393
1394     // then fill in a solid rectangle of the fg color, using the image as an
1395     // alpha mask.  (the image has only values of BlackPixel or WhitePixel.)
1396     set_color (dpy, cgc, gc->gcv.foreground, gc->depth,
1397                gc->gcv.alpha_allowed_p, YES);
1398     CGContextClipToMask (cgc, dst_rect, cgi);
1399     CGContextFillRect (cgc, dst_rect);
1400
1401     pop_gc (dst, gc);
1402
1403   } else {              // src depth > 1
1404
1405     push_gc (dst, gc);
1406
1407     // If either the src or dst rects did not lie within their drawables,
1408     // then we have adjusted both the src and dst rects to account for 
1409     // the clipping; that means we need to first clear to the background,
1410     // so that clipped bits end up in the bg color instead of simply not
1411     // being copied.
1412     //
1413     if (clipped) {
1414       set_color (dpy, cgc, gc->gcv.background, gc->depth,
1415                  gc->gcv.alpha_allowed_p, YES);
1416       CGContextFillRect (cgc, orig_dst_rect);
1417     }
1418
1419     if (cgi) {
1420       // copy the CGImage onto the destination CGContext
1421       //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1422       CGContextDrawImage (cgc, dst_rect, cgi);
1423     } else {
1424       // No cgi means src == dst, and both are Windows.
1425
1426 # ifdef USE_BACKBUFFER
1427       Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1428       return 0;
1429 # else // !USE_BACKBUFFER
1430       NSRect nsfrom;
1431       nsfrom.origin.x    = src_rect.origin.x;    // NSRect != CGRect on 10.4
1432       nsfrom.origin.y    = src_rect.origin.y;
1433       nsfrom.size.width  = src_rect.size.width;
1434       nsfrom.size.height = src_rect.size.height;
1435       NSPoint nsto;
1436       nsto.x             = dst_rect.origin.x;
1437       nsto.y             = dst_rect.origin.y;
1438       NSCopyBits (0, nsfrom, nsto);
1439 # endif // !USE_BACKBUFFER
1440     }
1441
1442     pop_gc (dst, gc);
1443   }
1444
1445   if (free_cgi_p) CGImageRelease (cgi);
1446
1447   if (releaseme) [releaseme release];
1448   invalidate_drawable_cache (dst);
1449   return 0;
1450 }
1451
1452
1453 int
1454 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1455             int src_x, int src_y,
1456             unsigned width, int height,
1457             int dest_x, int dest_y, unsigned long plane)
1458 {
1459   Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1460   
1461   // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1462   // not to white/black.
1463   return XCopyArea (dpy, src, dest, gc,
1464                     src_x, src_y, width, height, dest_x, dest_y);
1465 }
1466
1467
1468 int
1469 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1470 {
1471   // when drawing a zero-length line, obey line-width and cap-style.
1472   if (x1 == x2 && y1 == y2) {
1473     int w = gc->gcv.line_width;
1474     x1 -= w/2;
1475     y1 -= w/2;
1476     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1477       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1478     else
1479       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1480   }
1481   
1482   CGRect wr = d->frame;
1483   NSPoint p;
1484   p.x = wr.origin.x + x1;
1485   p.y = wr.origin.y + wr.size.height - y1;
1486
1487   push_fg_gc (dpy, d, gc, NO);
1488
1489   CGContextRef cgc = d->cgc;
1490   set_line_mode (cgc, &gc->gcv);
1491   CGContextBeginPath (cgc);
1492   CGContextMoveToPoint (cgc, p.x, p.y);
1493   p.x = wr.origin.x + x2;
1494   p.y = wr.origin.y + wr.size.height - y2;
1495   CGContextAddLineToPoint (cgc, p.x, p.y);
1496   CGContextStrokePath (cgc);
1497   pop_gc (d, gc);
1498   invalidate_drawable_cache (d);
1499   return 0;
1500 }
1501
1502 int
1503 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1504             int mode)
1505 {
1506   int i;
1507   NSPoint p;
1508   CGRect wr = d->frame;
1509   push_fg_gc (dpy, d, gc, NO);
1510
1511   CGContextRef cgc = d->cgc;
1512
1513   set_line_mode (cgc, &gc->gcv);
1514   
1515   // if the first and last points coincide, use closepath to get
1516   // the proper line-joining.
1517   BOOL closed_p = (points[0].x == points[count-1].x &&
1518                    points[0].y == points[count-1].y);
1519   if (closed_p) count--;
1520   
1521   p.x = wr.origin.x + points->x;
1522   p.y = wr.origin.y + wr.size.height - points->y;
1523   points++;
1524   CGContextBeginPath (cgc);
1525   CGContextMoveToPoint (cgc, p.x, p.y);
1526   for (i = 1; i < count; i++) {
1527     if (mode == CoordModePrevious) {
1528       p.x += points->x;
1529       p.y -= points->y;
1530     } else {
1531       p.x = wr.origin.x + points->x;
1532       p.y = wr.origin.y + wr.size.height - points->y;
1533     }
1534     CGContextAddLineToPoint (cgc, p.x, p.y);
1535     points++;
1536   }
1537   if (closed_p) CGContextClosePath (cgc);
1538   CGContextStrokePath (cgc);
1539   pop_gc (d, gc);
1540   invalidate_drawable_cache (d);
1541   return 0;
1542 }
1543
1544
1545 int
1546 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1547 {
1548   int i;
1549   CGRect wr = d->frame;
1550
1551   CGContextRef cgc = d->cgc;
1552
1553   push_fg_gc (dpy, d, gc, NO);
1554   set_line_mode (cgc, &gc->gcv);
1555   CGContextBeginPath (cgc);
1556   for (i = 0; i < count; i++) {
1557     CGContextMoveToPoint    (cgc, 
1558                              wr.origin.x + segments->x1,
1559                              wr.origin.y + wr.size.height - segments->y1);
1560     CGContextAddLineToPoint (cgc,
1561                              wr.origin.x + segments->x2,
1562                              wr.origin.y + wr.size.height - segments->y2);
1563     segments++;
1564   }
1565   CGContextStrokePath (cgc);
1566   pop_gc (d, gc);
1567   invalidate_drawable_cache (d);
1568   return 0;
1569 }
1570
1571
1572 int
1573 XClearWindow (Display *dpy, Window win)
1574 {
1575   Assert (win && win->type == WINDOW, "not a window");
1576   CGRect wr = win->frame;
1577   return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1578 }
1579
1580 int
1581 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1582 {
1583   Assert (w && w->type == WINDOW, "not a window");
1584   validate_pixel (dpy, pixel, 32, NO);
1585   w->window.background = pixel;
1586   return 0;
1587 }
1588
1589 static void
1590 draw_rect (Display *dpy, Drawable d, GC gc, 
1591            int x, int y, unsigned int width, unsigned int height, 
1592            BOOL foreground_p, BOOL fill_p)
1593 {
1594   CGRect wr = d->frame;
1595   CGRect r;
1596   r.origin.x = wr.origin.x + x;
1597   r.origin.y = wr.origin.y + wr.size.height - y - height;
1598   r.size.width = width;
1599   r.size.height = height;
1600
1601   if (gc) {
1602     if (foreground_p)
1603       push_fg_gc (dpy, d, gc, fill_p);
1604     else
1605       push_bg_gc (dpy, d, gc, fill_p);
1606   }
1607
1608   CGContextRef cgc = d->cgc;
1609   if (fill_p)
1610     CGContextFillRect (cgc, r);
1611   else {
1612     if (gc)
1613       set_line_mode (cgc, &gc->gcv);
1614     CGContextStrokeRect (cgc, r);
1615   }
1616
1617   if (gc)
1618     pop_gc (d, gc);
1619   invalidate_drawable_cache (d);
1620 }
1621
1622
1623 int
1624 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
1625                 unsigned int width, unsigned int height)
1626 {
1627   draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1628   return 0;
1629 }
1630
1631 int
1632 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
1633                 unsigned int width, unsigned int height)
1634 {
1635   draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1636   return 0;
1637 }
1638
1639 int
1640 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1641 {
1642   CGRect wr = d->frame;
1643   int i;
1644   CGContextRef cgc = d->cgc;
1645   push_fg_gc (dpy, d, gc, YES);
1646   for (i = 0; i < n; i++) {
1647     CGRect r;
1648     r.origin.x = wr.origin.x + rects->x;
1649     r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1650     r.size.width = rects->width;
1651     r.size.height = rects->height;
1652     CGContextFillRect (cgc, r);
1653     rects++;
1654   }
1655   pop_gc (d, gc);
1656   invalidate_drawable_cache (d);
1657   return 0;
1658 }
1659
1660
1661 int
1662 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1663 {
1664   Assert (win && win->type == WINDOW, "not a window");
1665   CGContextRef cgc = win->cgc;
1666   set_color (dpy, cgc, win->window.background, 32, NO, YES);
1667   draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1668   return 0;
1669 }
1670
1671
1672 int
1673 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1674               XPoint *points, int npoints, int shape, int mode)
1675 {
1676   CGRect wr = d->frame;
1677   int i;
1678   push_fg_gc (dpy, d, gc, YES);
1679   CGContextRef cgc = d->cgc;
1680   CGContextBeginPath (cgc);
1681   float x = 0, y = 0;
1682   for (i = 0; i < npoints; i++) {
1683     if (i > 0 && mode == CoordModePrevious) {
1684       x += points[i].x;
1685       y -= points[i].y;
1686     } else {
1687       x = wr.origin.x + points[i].x;
1688       y = wr.origin.y + wr.size.height - points[i].y;
1689     }
1690         
1691     if (i == 0)
1692       CGContextMoveToPoint (cgc, x, y);
1693     else
1694       CGContextAddLineToPoint (cgc, x, y);
1695   }
1696   CGContextClosePath (cgc);
1697   if (gc->gcv.fill_rule == EvenOddRule)
1698     CGContextEOFillPath (cgc);
1699   else
1700     CGContextFillPath (cgc);
1701   pop_gc (d, gc);
1702   invalidate_drawable_cache (d);
1703   return 0;
1704 }
1705
1706 #define radians(DEG) ((DEG) * M_PI / 180.0)
1707 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1708
1709 static int
1710 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, 
1711           unsigned int width, unsigned int height, int angle1, int angle2,
1712           BOOL fill_p)
1713 {
1714   CGRect wr = d->frame;
1715   CGRect bound;
1716   bound.origin.x = wr.origin.x + x;
1717   bound.origin.y = wr.origin.y + wr.size.height - y - height;
1718   bound.size.width = width;
1719   bound.size.height = height;
1720   
1721   CGPoint ctr;
1722   ctr.x = bound.origin.x + bound.size.width /2;
1723   ctr.y = bound.origin.y + bound.size.height/2;
1724   
1725   float r1 = radians (angle1/64.0);
1726   float r2 = radians (angle2/64.0) + r1;
1727   BOOL clockwise = angle2 < 0;
1728   BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1729   
1730   push_fg_gc (dpy, d, gc, fill_p);
1731
1732   CGContextRef cgc = d->cgc;
1733   CGContextBeginPath (cgc);
1734   
1735   CGContextSaveGState(cgc);
1736   CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1737   CGContextScaleCTM (cgc, width/2.0, height/2.0);
1738   if (fill_p)
1739     CGContextMoveToPoint (cgc, 0, 0);
1740
1741   CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1742   CGContextRestoreGState (cgc);  // restore before stroke, for line width
1743
1744   if (closed_p)
1745     CGContextClosePath (cgc); // for proper line joining
1746   
1747   if (fill_p) {
1748     CGContextFillPath (cgc);
1749   } else {
1750     set_line_mode (cgc, &gc->gcv);
1751     CGContextStrokePath (cgc);
1752   }
1753
1754   pop_gc (d, gc);
1755   invalidate_drawable_cache (d);
1756   return 0;
1757 }
1758
1759 int
1760 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y, 
1761           unsigned int width, unsigned int height, int angle1, int angle2)
1762 {
1763   return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1764 }
1765
1766 int
1767 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y, 
1768           unsigned int width, unsigned int height, int angle1, int angle2)
1769 {
1770   return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1771 }
1772
1773 int
1774 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1775 {
1776   int i;
1777   for (i = 0; i < narcs; i++)
1778     draw_arc (dpy, d, gc, 
1779               arcs[i].x, arcs[i].y, 
1780               arcs[i].width, arcs[i].height, 
1781               arcs[i].angle1, arcs[i].angle2,
1782               NO);
1783   return 0;
1784 }
1785
1786 int
1787 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1788 {
1789   int i;
1790   for (i = 0; i < narcs; i++)
1791     draw_arc (dpy, d, gc, 
1792               arcs[i].x, arcs[i].y, 
1793               arcs[i].width, arcs[i].height, 
1794               arcs[i].angle1, arcs[i].angle2,
1795               YES);
1796   return 0;
1797 }
1798
1799
1800 static void
1801 gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
1802 {
1803   memset (gcv, 0, sizeof(*gcv));
1804   gcv->function   = GXcopy;
1805   gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
1806   gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
1807   gcv->line_width = 1;
1808   gcv->cap_style  = CapNotLast;
1809   gcv->join_style = JoinMiter;
1810   gcv->fill_rule  = EvenOddRule;
1811
1812   gcv->alpha_allowed_p = NO;
1813   gcv->antialias_p     = YES;
1814 }
1815
1816 static void
1817 set_gcv (Display *dpy, GC gc, XGCValues *from, unsigned long mask)
1818 {
1819   if (! mask) return;
1820   Assert (gc && from, "no gc");
1821   if (!gc || !from) return;
1822
1823   if (mask & GCFunction)        gc->gcv.function        = from->function;
1824   if (mask & GCForeground)      gc->gcv.foreground      = from->foreground;
1825   if (mask & GCBackground)      gc->gcv.background      = from->background;
1826   if (mask & GCLineWidth)       gc->gcv.line_width      = from->line_width;
1827   if (mask & GCCapStyle)        gc->gcv.cap_style       = from->cap_style;
1828   if (mask & GCJoinStyle)       gc->gcv.join_style      = from->join_style;
1829   if (mask & GCFillRule)        gc->gcv.fill_rule       = from->fill_rule;
1830   if (mask & GCClipXOrigin)     gc->gcv.clip_x_origin   = from->clip_x_origin;
1831   if (mask & GCClipYOrigin)     gc->gcv.clip_y_origin   = from->clip_y_origin;
1832   if (mask & GCSubwindowMode)   gc->gcv.subwindow_mode  = from->subwindow_mode;
1833   
1834   if (mask & GCClipMask)        XSetClipMask (0, gc, from->clip_mask);
1835   if (mask & GCFont)            XSetFont (0, gc, from->font);
1836
1837   if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth,
1838                                            gc->gcv.alpha_allowed_p);
1839   if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth,
1840                                            gc->gcv.alpha_allowed_p);
1841     
1842   Assert ((! (mask & (GCLineStyle |
1843                       GCPlaneMask |
1844                       GCFillStyle |
1845                       GCTile |
1846                       GCStipple |
1847                       GCTileStipXOrigin |
1848                       GCTileStipYOrigin |
1849                       GCGraphicsExposures |
1850                       GCDashOffset |
1851                       GCDashList |
1852                       GCArcMode))),
1853           "unimplemented gcvalues mask");
1854 }
1855
1856
1857 GC
1858 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1859 {
1860   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1861   if (d->type == WINDOW) {
1862     gc->depth = 32;
1863   } else { /* (d->type == PIXMAP) */
1864     gc->depth = d->pixmap.depth;
1865   }
1866
1867   gcv_defaults (dpy, &gc->gcv, gc->depth);
1868   set_gcv (dpy, gc, xgcv, mask);
1869   return gc;
1870 }
1871
1872 int
1873 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1874 {
1875   set_gcv (dpy, gc, gcv, mask);
1876   return 0;
1877 }
1878
1879
1880 int
1881 XFreeGC (Display *dpy, GC gc)
1882 {
1883   if (gc->gcv.font)
1884     XUnloadFont (dpy, gc->gcv.font);
1885
1886   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1887
1888   if (gc->gcv.clip_mask) {
1889     XFreePixmap (dpy, gc->gcv.clip_mask);
1890     CGImageRelease (gc->clip_mask);
1891   }
1892   free (gc);
1893   return 0;
1894 }
1895
1896
1897 Status
1898 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1899 {
1900   Assert (w && w->type == WINDOW, "not a window");
1901   memset (xgwa, 0, sizeof(*xgwa));
1902   xgwa->x      = w->frame.origin.x;
1903   xgwa->y      = w->frame.origin.y;
1904   xgwa->width  = w->frame.size.width;
1905   xgwa->height = w->frame.size.height;
1906   xgwa->depth  = 32;
1907   xgwa->screen = dpy->screen;
1908   xgwa->visual = dpy->screen->visual;
1909   return 0;
1910 }
1911
1912 Status
1913 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1914               int *x_ret, int *y_ret, 
1915               unsigned int *w_ret, unsigned int *h_ret,
1916               unsigned int *bw_ret, unsigned int *d_ret)
1917 {
1918   *x_ret    = d->frame.origin.x;
1919   *y_ret    = d->frame.origin.y;
1920   *w_ret    = d->frame.size.width;
1921   *h_ret    = d->frame.size.height;
1922   *d_ret    = (d->type == WINDOW ? 32 : d->pixmap.depth);
1923   *root_ret = RootWindow (dpy, 0);
1924   *bw_ret   = 0;
1925   return True;
1926 }
1927
1928
1929 Status
1930 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1931 {
1932   color->pixel = alloc_color (dpy,
1933                               color->red, color->green, color->blue, 0xFFFF);
1934   return 1;
1935 }
1936
1937 Status
1938 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1939                   unsigned long *pmret, unsigned int npl,
1940                   unsigned long *pxret, unsigned int npx)
1941 {
1942   return 0;
1943 }
1944
1945 int
1946 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1947 {
1948   Assert(0, "XStoreColors called");
1949   return 0;
1950 }
1951
1952 int
1953 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1954 {
1955   Assert(0, "XStoreColor called");
1956   return 0;
1957 }
1958
1959 int
1960 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1961              unsigned long planes)
1962 {
1963   return 0;
1964 }
1965
1966 Status
1967 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1968 {
1969   unsigned char r=0, g=0, b=0;
1970   if (*spec == '#' && strlen(spec) == 7) {
1971     static unsigned const char hex[] = {   // yeah yeah, shoot me.
1972       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,
1973       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,
1974       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,
1975       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,
1976       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,
1977       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,
1978       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,
1979       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};
1980     r = (hex[spec[1]] << 4) | hex[spec[2]];
1981     g = (hex[spec[3]] << 4) | hex[spec[4]];
1982     b = (hex[spec[5]] << 4) | hex[spec[6]];
1983   } else if (!strcasecmp(spec,"black")) {
1984 //  r = g = b = 0;
1985   } else if (!strcasecmp(spec,"white")) {
1986     r = g = b = 255;
1987   } else if (!strcasecmp(spec,"red")) {
1988     r = 255;
1989   } else if (!strcasecmp(spec,"green")) {
1990     g = 255;
1991   } else if (!strcasecmp(spec,"blue")) {
1992     b = 255;
1993   } else if (!strcasecmp(spec,"cyan")) {
1994     g = b = 255;
1995   } else if (!strcasecmp(spec,"magenta")) {
1996     r = b = 255;
1997   } else if (!strcasecmp(spec,"yellow")) {
1998     r = g = 255;
1999   } else {
2000     return 0;
2001   }
2002   
2003   ret->red   = (r << 8) | r;
2004   ret->green = (g << 8) | g;
2005   ret->blue  = (b << 8) | b;
2006   ret->flags = DoRed|DoGreen|DoBlue;
2007   return 1;
2008 }
2009
2010 Status
2011 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
2012                   XColor *screen_ret, XColor *exact_ret)
2013 {
2014   if (! XParseColor (dpy, cmap, name, screen_ret))
2015     return False;
2016   *exact_ret = *screen_ret;
2017   return XAllocColor (dpy, cmap, screen_ret);
2018 }
2019
2020 int
2021 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
2022 {
2023   validate_pixel (dpy, color->pixel, 32, NO);
2024   uint8_t rgba[4];
2025   query_color(dpy, color->pixel, rgba);
2026   color->red   = (rgba[0] << 8) | rgba[0];
2027   color->green = (rgba[1] << 8) | rgba[1];
2028   color->blue  = (rgba[2] << 8) | rgba[2];
2029   color->flags = DoRed|DoGreen|DoBlue;
2030   return 0;
2031 }
2032
2033 int
2034 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
2035 {
2036   int i;
2037   for (i = 0; i < n; i++)
2038     XQueryColor (dpy, cmap, &c[i]);
2039   return 0;
2040 }
2041
2042
2043 static unsigned long
2044 ximage_getpixel_1 (XImage *ximage, int x, int y)
2045 {
2046   return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
2047 }
2048
2049 static int
2050 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
2051 {
2052   if (pixel)
2053     ximage->data [y * ximage->bytes_per_line + (x>>3)] |=  (1 << (x & 7));
2054   else
2055     ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
2056
2057   return 0;
2058 }
2059
2060 static unsigned long
2061 ximage_getpixel_32 (XImage *ximage, int x, int y)
2062 {
2063   return ((unsigned long)
2064           *((uint32_t *) ximage->data +
2065             (y * (ximage->bytes_per_line >> 2)) +
2066             x));
2067 }
2068
2069 static int
2070 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
2071 {
2072   *((uint32_t *) ximage->data +
2073     (y * (ximage->bytes_per_line >> 2)) +
2074     x) = (uint32_t) pixel;
2075   return 0;
2076 }
2077
2078
2079 Status
2080 XInitImage (XImage *ximage)
2081 {
2082   if (!ximage->bytes_per_line)
2083     ximage->bytes_per_line = (ximage->depth == 1
2084                               ? (ximage->width + 7) / 8
2085                               : ximage->width * 4);
2086
2087   if (ximage->depth == 1) {
2088     ximage->f.put_pixel = ximage_putpixel_1;
2089     ximage->f.get_pixel = ximage_getpixel_1;
2090   } else if (ximage->depth == 32 || ximage->depth == 24) {
2091     ximage->f.put_pixel = ximage_putpixel_32;
2092     ximage->f.get_pixel = ximage_getpixel_32;
2093   } else {
2094     Assert (0, "unknown depth");
2095   }
2096   return 1;
2097 }
2098
2099
2100 XImage *
2101 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
2102               int format, int offset, char *data,
2103               unsigned int width, unsigned int height,
2104               int bitmap_pad, int bytes_per_line)
2105 {
2106   XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
2107   ximage->width = width;
2108   ximage->height = height;
2109   ximage->format = format;
2110   ximage->data = data;
2111   ximage->bitmap_unit = 8;
2112   ximage->byte_order = LSBFirst;
2113   ximage->bitmap_bit_order = ximage->byte_order;
2114   ximage->bitmap_pad = bitmap_pad;
2115   ximage->depth = depth;
2116   ximage->red_mask   = (depth == 1 ? 0 : dpy->screen->visual->red_mask);
2117   ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask);
2118   ximage->blue_mask  = (depth == 1 ? 0 : dpy->screen->visual->blue_mask);
2119   ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
2120   ximage->bytes_per_line = bytes_per_line;
2121
2122   XInitImage (ximage);
2123   return ximage;
2124 }
2125
2126 XImage *
2127 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
2128 {
2129   XImage *to = (XImage *) malloc (sizeof(*to));
2130   memcpy (to, from, sizeof(*from));
2131   to->width = w;
2132   to->height = h;
2133   to->bytes_per_line = 0;
2134   XInitImage (to);
2135
2136   to->data = (char *) malloc (h * to->bytes_per_line);
2137
2138   if (x >= from->width)
2139     w = 0;
2140   else if (x+w > from->width)
2141     w = from->width - x;
2142
2143   if (y >= from->height)
2144     h = 0;
2145   else if (y+h > from->height)
2146     h = from->height - y;
2147
2148   int tx, ty;
2149   for (ty = 0; ty < h; ty++)
2150     for (tx = 0; tx < w; tx++)
2151       XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
2152   return to;
2153 }
2154
2155
2156 XPixmapFormatValues *
2157 XListPixmapFormats (Display *dpy, int *n_ret)
2158 {
2159   XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
2160   ret[0].depth = 32;
2161   ret[0].bits_per_pixel = 32;
2162   ret[0].scanline_pad = 8;
2163   ret[1].depth = 1;
2164   ret[1].bits_per_pixel = 1;
2165   ret[1].scanline_pad = 8;
2166   *n_ret = 2;
2167   return ret;
2168 }
2169
2170
2171 unsigned long
2172 XGetPixel (XImage *ximage, int x, int y)
2173 {
2174   return ximage->f.get_pixel (ximage, x, y);
2175 }
2176
2177
2178 int
2179 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
2180 {
2181   return ximage->f.put_pixel (ximage, x, y, pixel);
2182 }
2183
2184 int
2185 XDestroyImage (XImage *ximage)
2186 {
2187   if (ximage->data) free (ximage->data);
2188   free (ximage);
2189   return 0;
2190 }
2191
2192
2193 static void
2194 flipbits (unsigned const char *in, unsigned char *out, int length)
2195 {
2196   static const unsigned char table[256] = {
2197     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
2198     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
2199     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
2200     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
2201     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
2202     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
2203     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
2204     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
2205     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
2206     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
2207     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
2208     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
2209     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
2210     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
2211     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
2212     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
2213     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
2214     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
2215     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
2216     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
2217     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
2218     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
2219     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
2220     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
2221     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
2222     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
2223     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 
2224     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
2225     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
2226     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
2227     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
2228     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
2229   };
2230   while (length-- > 0)
2231     *out++ = table[*in++];
2232 }
2233
2234
2235 int
2236 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
2237            int src_x, int src_y, int dest_x, int dest_y,
2238            unsigned int w, unsigned int h)
2239 {
2240   CGRect wr = d->frame;
2241
2242   Assert (gc, "no GC");
2243   Assert ((w < 65535), "improbably large width");
2244   Assert ((h < 65535), "improbably large height");
2245   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
2246   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
2247   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
2248   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
2249
2250   // Clip width and height to the bounds of the Drawable
2251   //
2252   if (dest_x + w > wr.size.width) {
2253     if (dest_x > wr.size.width)
2254       return 0;
2255     w = wr.size.width - dest_x;
2256   }
2257   if (dest_y + h > wr.size.height) {
2258     if (dest_y > wr.size.height)
2259       return 0;
2260     h = wr.size.height - dest_y;
2261   }
2262   if (w <= 0 || h <= 0)
2263     return 0;
2264
2265   // Clip width and height to the bounds of the XImage
2266   //
2267   if (src_x + w > ximage->width) {
2268     if (src_x > ximage->width)
2269       return 0;
2270     w = ximage->width - src_x;
2271   }
2272   if (src_y + h > ximage->height) {
2273     if (src_y > ximage->height)
2274       return 0;
2275     h = ximage->height - src_y;
2276   }
2277   if (w <= 0 || h <= 0)
2278     return 0;
2279
2280   CGContextRef cgc = d->cgc;
2281
2282   if (gc->gcv.function == GXset ||
2283       gc->gcv.function == GXclear) {
2284     // "set" and "clear" are dumb drawing modes that ignore the source
2285     // bits and just draw solid rectangles.
2286     set_color (dpy, cgc, (gc->gcv.function == GXset
2287                         ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
2288                         : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
2289                gc->depth, gc->gcv.alpha_allowed_p, YES);
2290     draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
2291     return 0;
2292   }
2293
2294   int bpl = ximage->bytes_per_line;
2295   int bpp = ximage->bits_per_pixel;
2296   int bsize = bpl * h;
2297   char *data = ximage->data;
2298
2299   CGRect r;
2300   r.origin.x = wr.origin.x + dest_x;
2301   r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
2302   r.size.width = w;
2303   r.size.height = h;
2304
2305   if (bpp == 32) {
2306
2307     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
2308        to create a CGImage from a sub-rectagle of the XImage.
2309      */
2310     data += (src_y * bpl) + (src_x * 4);
2311     CGDataProviderRef prov = 
2312       CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2313
2314     CGImageRef cgi = CGImageCreate (w, h,
2315                                     bpp/4, bpp, bpl,
2316                                     dpy->colorspace, 
2317                                     dpy->screen->bitmap_info,
2318                                     prov, 
2319                                     NULL,  /* decode[] */
2320                                     NO, /* interpolate */
2321                                     kCGRenderingIntentDefault);
2322     CGDataProviderRelease (prov);
2323     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2324     CGContextDrawImage (cgc, r, cgi);
2325     CGImageRelease (cgi);
2326
2327   } else {   // (bpp == 1)
2328
2329     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2330
2331        #### However, the bit order within a byte in a 1bpp XImage is
2332             the wrong way around from what Quartz expects, so first we
2333             have to copy the data to reverse it.  Shit!  Maybe it
2334             would be worthwhile to go through the hacks and #ifdef
2335             each one that diddles 1bpp XImage->data directly...
2336      */
2337     Assert ((src_x % 8) == 0,
2338             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2339
2340     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
2341     unsigned char *flipped = (unsigned char *) malloc (bsize);
2342
2343     flipbits ((unsigned char *) data, flipped, bsize);
2344
2345     CGDataProviderRef prov = 
2346       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2347     CGImageRef mask = CGImageMaskCreate (w, h, 
2348                                          1, bpp, bpl,
2349                                          prov,
2350                                          NULL,  /* decode[] */
2351                                          NO); /* interpolate */
2352     push_fg_gc (dpy, d, gc, YES);
2353
2354     CGContextFillRect (cgc, r);                         // foreground color
2355     CGContextClipToMask (cgc, r, mask);
2356     set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
2357     CGContextFillRect (cgc, r);                         // background color
2358     pop_gc (d, gc);
2359
2360     free (flipped);
2361     CGDataProviderRelease (prov);
2362     CGImageRelease (mask);
2363   }
2364
2365   invalidate_drawable_cache (d);
2366
2367   return 0;
2368 }
2369
2370
2371 XImage *
2372 XGetImage (Display *dpy, Drawable d, int x, int y,
2373            unsigned int width, unsigned int height,
2374            unsigned long plane_mask, int format)
2375 {
2376   const unsigned char *data = 0;
2377   size_t depth, ibpp, ibpl;
2378   convert_mode_t mode;
2379 # ifndef USE_BACKBUFFER
2380   NSBitmapImageRep *bm = 0;
2381 # endif
2382   
2383   Assert ((width  < 65535), "improbably large width");
2384   Assert ((height < 65535), "improbably large height");
2385   Assert ((x < 65535 && x > -65535), "improbably large x");
2386   Assert ((y < 65535 && y > -65535), "improbably large y");
2387
2388   CGContextRef cgc = d->cgc;
2389
2390 #ifndef USE_BACKBUFFER
2391   // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2392   if (d->type == PIXMAP)
2393 # endif
2394   {
2395     depth = (d->type == PIXMAP
2396              ? d->pixmap.depth
2397              : 32);
2398     mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
2399     ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2400     ibpl = CGBitmapContextGetBytesPerRow (cgc);
2401     data = CGBitmapContextGetData (cgc);
2402     Assert (data, "CGBitmapContextGetData failed");
2403
2404 # ifndef USE_BACKBUFFER
2405   } else { /* (d->type == WINDOW) */
2406
2407     // get the bits (desired sub-rectangle) out of the NSView
2408     NSRect nsfrom;
2409     nsfrom.origin.x = x;
2410 //  nsfrom.origin.y = y;
2411     nsfrom.origin.y = d->frame.size.height - height - y;
2412     nsfrom.size.width = width;
2413     nsfrom.size.height = height;
2414     bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2415     depth = 32;
2416     mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0;
2417     ibpp = [bm bitsPerPixel];
2418     ibpl = [bm bytesPerRow];
2419     data = [bm bitmapData];
2420     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2421 # endif // !USE_BACKBUFFER
2422   }
2423   
2424   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
2425   data += (y * ibpl) + (x * (ibpp/8));
2426   
2427   format = (depth == 1 ? XYPixmap : ZPixmap);
2428   XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2429                                 format, 0, 0, width, height, 0, 0);
2430   image->data = (char *) malloc (height * image->bytes_per_line);
2431   
2432   int obpl = image->bytes_per_line;
2433   
2434   /* both PPC and Intel use word-ordered ARGB frame buffers, which
2435      means that on Intel it is BGRA when viewed by bytes (And BGR
2436      when using 24bpp packing).
2437
2438      BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2439      The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2440      indicator of this latest kink.
2441    */
2442   int xx, yy;
2443   if (depth == 1) {
2444     const unsigned char *iline = data;
2445     for (yy = 0; yy < height; yy++) {
2446
2447       const unsigned char *iline2 = iline;
2448       for (xx = 0; xx < width; xx++) {
2449
2450         iline2++;                     // ignore R  or  A  or  A  or  B
2451         iline2++;                     // ignore G  or  B  or  R  or  G
2452         unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
2453         if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
2454
2455         XPutPixel (image, xx, yy, (r ? 1 : 0));
2456       }
2457       iline += ibpl;
2458     }
2459   } else {
2460     const unsigned char *iline = data;
2461     unsigned char *oline = (unsigned char *) image->data;
2462
2463     mode = convert_mode_merge (mode,
2464              convert_mode_invert (
2465                convert_mode_to_rgba (dpy->screen->bitmap_info)));
2466
2467     for (yy = 0; yy < height; yy++) {
2468
2469       convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
2470
2471       oline += obpl;
2472       iline += ibpl;
2473     }
2474   }
2475
2476 # ifndef USE_BACKBUFFER
2477   if (bm) [bm release];
2478 # endif
2479
2480   return image;
2481 }
2482
2483
2484
2485 /* Returns a transformation matrix to do rotation as per the provided
2486    EXIF "Orientation" value.
2487  */
2488 static CGAffineTransform
2489 exif_rotate (int rot, CGSize rect)
2490 {
2491   CGAffineTransform trans = CGAffineTransformIdentity;
2492   switch (rot) {
2493   case 2:               // flip horizontal
2494     trans = CGAffineTransformMakeTranslation (rect.width, 0);
2495     trans = CGAffineTransformScale (trans, -1, 1);
2496     break;
2497
2498   case 3:               // rotate 180
2499     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2500     trans = CGAffineTransformRotate (trans, M_PI);
2501     break;
2502
2503   case 4:               // flip vertical
2504     trans = CGAffineTransformMakeTranslation (0, rect.height);
2505     trans = CGAffineTransformScale (trans, 1, -1);
2506     break;
2507
2508   case 5:               // transpose (UL-to-LR axis)
2509     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2510     trans = CGAffineTransformScale (trans, -1, 1);
2511     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2512     break;
2513
2514   case 6:               // rotate 90
2515     trans = CGAffineTransformMakeTranslation (0, rect.width);
2516     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2517     break;
2518
2519   case 7:               // transverse (UR-to-LL axis)
2520     trans = CGAffineTransformMakeScale (-1, 1);
2521     trans = CGAffineTransformRotate (trans, M_PI / 2);
2522     break;
2523
2524   case 8:               // rotate 270
2525     trans = CGAffineTransformMakeTranslation (rect.height, 0);
2526     trans = CGAffineTransformRotate (trans, M_PI / 2);
2527     break;
2528
2529   default: 
2530     break;
2531   }
2532
2533   return trans;
2534 }
2535
2536
2537 void
2538 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
2539                                 Bool nsimg_p, void *img_arg,
2540                                XRectangle *geom_ret, int exif_rotation)
2541 {
2542   CGImageRef cgi;
2543 # ifndef USE_IPHONE
2544   CGImageSourceRef cgsrc;
2545 # endif // USE_IPHONE
2546   NSSize imgr;
2547
2548   CGContextRef cgc = d->cgc;
2549
2550   if (nsimg_p) {
2551
2552     NSImage *nsimg = (NSImage *) img_arg;
2553     imgr = [nsimg size];
2554
2555 # ifndef USE_IPHONE
2556     // convert the NSImage to a CGImage via the toll-free-bridging 
2557     // of NSData and CFData...
2558     //
2559     NSData *nsdata = [NSBitmapImageRep
2560                        TIFFRepresentationOfImageRepsInArray:
2561                          [nsimg representations]];
2562     CFDataRef cfdata = (CFDataRef) nsdata;
2563     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2564     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2565 # else  // USE_IPHONE
2566     cgi = nsimg.CGImage;
2567 # endif // USE_IPHONE
2568
2569   } else {
2570     cgi = (CGImageRef) img_arg;
2571     imgr.width  = CGImageGetWidth (cgi);
2572     imgr.height = CGImageGetHeight (cgi);
2573   }
2574
2575   Bool rot_p = (exif_rotation >= 5);
2576
2577   if (rot_p)
2578     imgr = NSMakeSize (imgr.height, imgr.width);
2579
2580   CGRect winr = d->frame;
2581   float rw = winr.size.width  / imgr.width;
2582   float rh = winr.size.height / imgr.height;
2583   float r = (rw < rh ? rw : rh);
2584
2585   CGRect dst, dst2;
2586   dst.size.width  = imgr.width  * r;
2587   dst.size.height = imgr.height * r;
2588   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
2589   dst.origin.y = (winr.size.height - dst.size.height) / 2;
2590
2591   dst2.origin.x = dst2.origin.y = 0;
2592   if (rot_p) {
2593     dst2.size.width = dst.size.height; 
2594     dst2.size.height = dst.size.width;
2595   } else {
2596     dst2.size = dst.size;
2597   }
2598
2599   // Clear the part not covered by the image to background or black.
2600   //
2601   if (d->type == WINDOW)
2602     XClearWindow (dpy, d);
2603   else {
2604     set_color (dpy, cgc, BlackPixel(dpy,0), 32, NO, YES);
2605     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2606   }
2607
2608   CGAffineTransform trans = 
2609     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2610
2611   CGContextSaveGState (cgc);
2612   CGContextConcatCTM (cgc, 
2613                       CGAffineTransformMakeTranslation (dst.origin.x,
2614                                                         dst.origin.y));
2615   CGContextConcatCTM (cgc, trans);
2616   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2617   CGContextDrawImage (cgc, dst2, cgi);
2618   CGContextRestoreGState (cgc);
2619
2620 # ifndef USE_IPHONE
2621   if (nsimg_p) {
2622     CFRelease (cgsrc);
2623     CGImageRelease (cgi);
2624   }
2625 # endif // USE_IPHONE
2626
2627   if (geom_ret) {
2628     geom_ret->x = dst.origin.x;
2629     geom_ret->y = dst.origin.y;
2630     geom_ret->width  = dst.size.width;
2631     geom_ret->height = dst.size.height;
2632   }
2633
2634   invalidate_drawable_cache (d);
2635 }
2636
2637
2638
2639 Pixmap
2640 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2641                              const char *data,
2642                              unsigned int w, unsigned int h,
2643                              unsigned long fg, unsigned int bg,
2644                              unsigned int depth)
2645 {
2646   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2647   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0, 
2648                                 (char *) data, w, h, 0, 0);
2649   XGCValues gcv;
2650   gcv.foreground = fg;
2651   gcv.background = bg;
2652   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2653   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2654   XFreeGC (dpy, gc);
2655   image->data = 0;
2656   XDestroyImage (image);
2657   return p;
2658 }
2659
2660 Pixmap
2661 XCreatePixmap (Display *dpy, Drawable d,
2662                unsigned int width, unsigned int height, unsigned int depth)
2663 {
2664   char *data = (char *) malloc (width * height * 4);
2665   if (! data) return 0;
2666
2667   Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2668   p->type = PIXMAP;
2669   p->frame.size.width  = width;
2670   p->frame.size.height = height;
2671   p->pixmap.depth      = depth;
2672   p->pixmap.cgc_buffer = data;
2673   
2674   /* Quartz doesn't have a 1bpp image type.
2675      Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2676      don't support that!  So we always use 32bpp, regardless of depth. */
2677
2678   p->cgc = CGBitmapContextCreate (data, width, height,
2679                                   8, /* bits per component */
2680                                   width * 4, /* bpl */
2681                                   dpy->colorspace,
2682                                   dpy->screen->bitmap_info);
2683   Assert (p->cgc, "could not create CGBitmapContext");
2684   return p;
2685 }
2686
2687
2688 int
2689 XFreePixmap (Display *d, Pixmap p)
2690 {
2691   Assert (p && p->type == PIXMAP, "not a pixmap");
2692   invalidate_drawable_cache (p);
2693   CGContextRelease (p->cgc);
2694   if (p->pixmap.cgc_buffer)
2695     free (p->pixmap.cgc_buffer);
2696   free (p);
2697   return 0;
2698 }
2699
2700
2701 static Pixmap
2702 copy_pixmap (Display *dpy, Pixmap p)
2703 {
2704   if (!p) return 0;
2705   Assert (p->type == PIXMAP, "not a pixmap");
2706
2707   int width  = p->frame.size.width;
2708   int height = p->frame.size.height;
2709   char *data = (char *) malloc (width * height * 4);
2710   if (! data) return 0;
2711
2712   memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2713
2714   Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2715   *p2 = *p;
2716   p2->cgi = 0;
2717   p2->pixmap.cgc_buffer = data;
2718   p2->cgc = CGBitmapContextCreate (data, width, height,
2719                                    8, /* bits per component */
2720                                    width * 4, /* bpl */
2721                                    dpy->colorspace,
2722                                    dpy->screen->bitmap_info);
2723   Assert (p2->cgc, "could not create CGBitmapContext");
2724
2725   return p2;
2726 }
2727
2728
2729 char *
2730 XGetAtomName (Display *dpy, Atom atom)
2731 {
2732   if (atom == XA_FONT)
2733     return strdup ("FONT");
2734
2735   // Note that atoms (that aren't predefined) are just char *.
2736   return strdup ((char *) atom);
2737 }
2738
2739
2740 /* Font metric terminology, as used by X11:
2741
2742    "lbearing" is the distance from the logical origin to the leftmost pixel.
2743    If a character's ink extends to the left of the origin, it is negative.
2744
2745    "rbearing" is the distance from the logical origin to the rightmost pixel.
2746
2747    "descent" is the distance from the logical origin to the bottommost pixel.
2748    For characters with descenders, it is positive.  For superscripts, it
2749    is negative.
2750
2751    "ascent" is the distance from the logical origin to the topmost pixel.
2752    It is the number of pixels above the baseline.
2753
2754    "width" is the distance from the logical origin to the position where
2755    the logical origin of the next character should be placed.
2756
2757    If "rbearing" is greater than "width", then this character overlaps the
2758    following character.  If smaller, then there is trailing blank space.
2759  */
2760 static void
2761 utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
2762 {
2763   // Returns the metrics of the multi-character, single-line UTF8 string.
2764
2765   NSFont *nsfont = fid->nsfont;
2766   Drawable d = XRootWindow (fid->dpy, 0);
2767
2768   CGContextRef cgc = d->cgc;
2769   NSDictionary *attr =
2770     [NSDictionary dictionaryWithObjectsAndKeys:
2771                     nsfont, NSFontAttributeName,
2772                   nil];
2773   NSAttributedString *astr = [[NSAttributedString alloc]
2774                                initWithString:nsstr
2775                                    attributes:attr];
2776   CTLineRef ctline = CTLineCreateWithAttributedString (
2777                        (__bridge CFAttributedStringRef) astr);
2778   CGContextSetTextPosition (cgc, 0, 0);
2779   CGContextSetShouldAntialias (cgc, True);  // #### Guess?
2780
2781   memset (cs, 0, sizeof(*cs));
2782
2783   // "CTRun represents set of consecutive glyphs sharing the same
2784   // attributes and direction".
2785   //
2786   // We also get multiple runs any time font subsitution happens:
2787   // E.g., if the current font is Verdana-Bold, a &larr; character
2788   // in the NSString will actually be rendered in LucidaGrande-Bold.
2789   //
2790   int count = 0;
2791   for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
2792     CTRunRef run = (CTRunRef) runid;
2793     CFRange r = { 0, };
2794     CGRect bbox = CTRunGetImageBounds (run, cgc, r);
2795     CGFloat ascent, descent, leading;
2796     CGFloat advancement =
2797       CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
2798
2799 # ifndef USE_IPHONE
2800     // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2801     bbox.origin.x    -= 2.0/3.0;
2802     bbox.size.width  += 4.0/3.0;
2803     bbox.size.height += 1.0/2.0;
2804 # endif
2805
2806     // Create the metrics for this run:
2807     XCharStruct cc;
2808     cc.ascent   = ceil  (bbox.origin.y + bbox.size.height);
2809     cc.descent  = ceil (-bbox.origin.y);
2810     cc.lbearing = floor (bbox.origin.x);
2811     cc.rbearing = ceil  (bbox.origin.x + bbox.size.width);
2812     cc.width    = floor (advancement + 0.5);
2813
2814     // Add those metrics into the cumulative metrics:
2815     if (count == 0)
2816       *cs = cc;
2817     else
2818       {
2819         cs->ascent   = MAX (cs->ascent,     cc.ascent);
2820         cs->descent  = MAX (cs->descent,    cc.descent);
2821         cs->lbearing = MIN (cs->lbearing,   cs->width + cc.lbearing);
2822         cs->rbearing = MAX (cs->rbearing,   cs->width + cc.rbearing);
2823         cs->width    = MAX (cs->width,      cs->width + cc.width);
2824       }
2825
2826     // Why no y? What about vertical text?
2827     // XCharStruct doesn't encapsulate that but XGlyphInfo does.
2828
2829     count++;
2830   }
2831
2832   CFRelease (ctline);
2833 }
2834
2835
2836
2837 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2838 //
2839 static void
2840 query_font (Font fid)
2841 {
2842   if (!fid || !fid->nsfont) {
2843     Assert (0, "no NSFont in fid");
2844     return;
2845   }
2846   if (![fid->nsfont fontName]) {
2847     Assert(0, @"broken NSFont in fid");
2848     return;
2849   }
2850
2851   int first = 32;
2852   int last = 255;
2853
2854   XFontStruct *f = &fid->metrics;
2855   XCharStruct *min = &f->min_bounds;
2856   XCharStruct *max = &f->max_bounds;
2857
2858   f->fid               = fid;
2859   f->min_char_or_byte2 = first;
2860   f->max_char_or_byte2 = last;
2861   f->default_char      = 'M';
2862   f->ascent            =  ceil ([fid->nsfont ascender]);
2863   f->descent           = -floor ([fid->nsfont descender]);
2864
2865   min->width    = 32767;  // set to smaller values in the loop
2866   min->ascent   = 32767;
2867   min->descent  = 32767;
2868   min->lbearing = 32767;
2869   min->rbearing = 32767;
2870
2871   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2872
2873   for (int i = first; i <= last; i++) {
2874     XCharStruct *cs = &f->per_char[i-first];
2875
2876     char s2[2];
2877     s2[0] = i;
2878     s2[1] = 0;
2879     NSString *nsstr = [NSString stringWithCString:s2
2880                                encoding:NSISOLatin1StringEncoding];
2881     utf8_metrics (fid, nsstr, cs);
2882
2883     max->width    = MAX (max->width,    cs->width);
2884     max->ascent   = MAX (max->ascent,   cs->ascent);
2885     max->descent  = MAX (max->descent,  cs->descent);
2886     max->lbearing = MAX (max->lbearing, cs->lbearing);
2887     max->rbearing = MAX (max->rbearing, cs->rbearing);
2888
2889     min->width    = MIN (min->width,    cs->width);
2890     min->ascent   = MIN (min->ascent,   cs->ascent);
2891     min->descent  = MIN (min->descent,  cs->descent);
2892     min->lbearing = MIN (min->lbearing, cs->lbearing);
2893     min->rbearing = MIN (min->rbearing, cs->rbearing);
2894
2895 # if 0
2896     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2897                     " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n",
2898             i, i, cs->width, cs->lbearing, cs->rbearing, 
2899             cs->ascent, cs->descent,
2900             bbox.size.width, bbox.size.height,
2901             bbox.origin.x, bbox.origin.y,
2902             advancement.width, advancement.height);
2903 # endif // 0
2904   }
2905 }
2906
2907
2908 // Since 'Font' includes the metrics, this just makes a copy of that.
2909 //
2910 XFontStruct *
2911 XQueryFont (Display *dpy, Font fid)
2912 {
2913   // copy XFontStruct
2914   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2915   *f = fid->metrics;
2916
2917   // build XFontProps
2918   f->n_properties = 1;
2919   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
2920   f->properties[0].name = XA_FONT;
2921   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
2922           "atoms probably needs a real implementation");
2923   // If XInternAtom is ever implemented, use it here.
2924   f->properties[0].card32 = (char *)fid->xa_font;
2925
2926   // copy XCharStruct array
2927   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2928   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2929   memcpy (f->per_char, fid->metrics.per_char,
2930           size * sizeof (XCharStruct));
2931
2932   return f;
2933 }
2934
2935
2936 static Font
2937 copy_font (Font fid)
2938 {
2939   // copy 'Font' struct
2940   Font fid2 = (Font) malloc (sizeof(*fid2));
2941   *fid2 = *fid;
2942
2943   // copy XCharStruct array
2944   int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2945   fid2->metrics.per_char = (XCharStruct *) 
2946     malloc ((size + 2) * sizeof (XCharStruct));
2947   memcpy (fid2->metrics.per_char, fid->metrics.per_char, 
2948           size * sizeof (XCharStruct));
2949
2950   // copy the other pointers
2951   fid2->ps_name = strdup (fid->ps_name);
2952   fid2->xa_font = strdup (fid->xa_font);
2953 //  [fid2->nsfont retain];
2954   fid2->metrics.fid = fid2;
2955
2956   return fid2;
2957 }
2958
2959
2960 static NSArray *
2961 font_family_members (NSString *family_name)
2962 {
2963 # ifndef USE_IPHONE
2964   return [[NSFontManager sharedFontManager]
2965           availableMembersOfFontFamily:family_name];
2966 # else
2967   return [UIFont fontNamesForFamilyName:family_name];
2968 # endif
2969 }
2970
2971
2972 static NSString *
2973 default_font_family (NSFontTraitMask require)
2974 {
2975   return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
2976 }
2977
2978
2979 static NSFont *
2980 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2981           NSString *family_name, float size,
2982           char **name_ret)
2983 {
2984   Assert (size > 0, "zero font size");
2985
2986   NSArray *family_members = font_family_members (family_name);
2987   if (!family_members.count)
2988     family_members = font_family_members (default_font_family (traits));
2989
2990 # ifndef USE_IPHONE
2991   for (unsigned k = 0; k != family_members.count; ++k) {
2992
2993     NSArray *member = [family_members objectAtIndex:k];
2994     NSFontTraitMask font_mask =
2995     [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2996
2997     if ((font_mask & mask) == traits) {
2998
2999       NSString *name = [member objectAtIndex:0];
3000       NSFont *f = [NSFont fontWithName:name size:size];
3001       if (!f)
3002         break;
3003
3004       /* Don't use this font if it (probably) doesn't include ASCII characters.
3005        */
3006       NSStringEncoding enc = [f mostCompatibleStringEncoding];
3007       if (! (enc == NSUTF8StringEncoding ||
3008              enc == NSISOLatin1StringEncoding ||
3009              enc == NSNonLossyASCIIStringEncoding ||
3010              enc == NSISOLatin2StringEncoding ||
3011              enc == NSUnicodeStringEncoding ||
3012              enc == NSWindowsCP1250StringEncoding ||
3013              enc == NSWindowsCP1252StringEncoding ||
3014              enc == NSMacOSRomanStringEncoding)) {
3015         // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
3016         break;
3017       }
3018       // NSLog(@"using \"%@\": %d", name, enc);
3019
3020       // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
3021       *name_ret = strdup (name.UTF8String);
3022       return f;
3023     }
3024   }
3025 # else // USE_IPHONE
3026
3027   for (NSString *fn in family_members) {
3028 # define MATCH(X) \
3029          ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
3030          != NSNotFound)
3031
3032     // The magic invocation for getting font names is
3033     // [[UIFontDescriptor
3034     //   fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
3035     //  symbolicTraits]
3036     // ...but this only works on iOS 7 and later.
3037     NSFontTraitMask font_mask = 0;
3038     if (MATCH(@"Bold"))
3039       font_mask |= NSBoldFontMask;
3040     if (MATCH(@"Italic") || MATCH(@"Oblique"))
3041       font_mask |= NSItalicFontMask;
3042
3043     if ((font_mask & mask) == traits) {
3044
3045       /* Check if it can do ASCII.  No good way to accomplish this!
3046          These are fonts present in iPhone Simulator as of June 2012
3047          that don't include ASCII.
3048        */
3049       if (MATCH(@"AppleGothic") ||      // Korean
3050           MATCH(@"Dingbats") ||         // Dingbats
3051           MATCH(@"Emoji") ||            // Emoticons
3052           MATCH(@"Geeza") ||            // Arabic
3053           MATCH(@"Hebrew") ||           // Hebrew
3054           MATCH(@"HiraKaku") ||         // Japanese
3055           MATCH(@"HiraMin") ||          // Japanese
3056           MATCH(@"Kailasa") ||          // Tibetan
3057           MATCH(@"Ornaments") ||        // Dingbats
3058           MATCH(@"STHeiti")             // Chinese
3059        )
3060          break;
3061
3062       *name_ret = strdup (fn.UTF8String);
3063       return [UIFont fontWithName:fn size:size];
3064     }
3065 # undef MATCH
3066   }
3067
3068 # endif
3069
3070   return NULL;
3071 }
3072
3073 static NSFont *
3074 try_native_font (const char *name, float scale,
3075                  char **name_ret, float *size_ret, char **xa_font)
3076 {
3077   if (!name) return 0;
3078   const char *spc = strrchr (name, ' ');
3079   if (!spc) return 0;
3080   int dsize = 0;
3081   if (1 != sscanf (spc, " %d ", &dsize)) return 0;
3082   float size = dsize;
3083
3084   if (size <= 4) return 0;
3085
3086   size *= scale;
3087
3088   char *name2 = strdup (name);
3089   name2[strlen(name2) - strlen(spc)] = 0;
3090   NSString *nsname = [NSString stringWithCString:name2
3091                                         encoding:NSUTF8StringEncoding];
3092   NSFont *f = [NSFont fontWithName:nsname size:size];
3093   if (f) {
3094     *name_ret = name2;
3095     *size_ret = size;
3096     *xa_font = strdup (name); // Maybe this should be an XLFD?
3097     return f;
3098   } else {
3099     free (name2);
3100     return 0;
3101   }
3102 }
3103
3104
3105 /* Returns a random font in the given size and face.
3106  */
3107 static NSFont *
3108 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
3109              float size, NSString **family_ret, char **name_ret)
3110 {
3111
3112 # ifndef USE_IPHONE
3113   // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
3114   // returns an empty list, at least on a system with default fonts only.
3115   NSArray *families = [[NSFontManager sharedFontManager]
3116                        availableFontFamilies];
3117   if (!families) return 0;
3118 # else
3119   NSArray *families = [UIFont familyNames];
3120
3121   // There are many dups in the families array -- uniquify it.
3122   {
3123     NSArray *sorted_families =
3124     [families sortedArrayUsingSelector:@selector(compare:)];
3125     NSMutableArray *new_families =
3126     [NSMutableArray arrayWithCapacity:sorted_families.count];
3127
3128     NSString *prev_family = nil;
3129     for (NSString *family in sorted_families) {
3130       if ([family compare:prev_family])
3131         [new_families addObject:family];
3132     }
3133
3134     families = new_families;
3135   }
3136 # endif // USE_IPHONE
3137
3138   long n = [families count];
3139   if (n <= 0) return 0;
3140
3141   int j;
3142   for (j = 0; j < n; j++) {
3143     int i = random() % n;
3144     NSString *family_name = [families objectAtIndex:i];
3145
3146     NSFont *result = try_font (traits, mask, family_name, size, name_ret);
3147     if (result) {
3148       [*family_ret release];
3149       *family_ret = family_name;
3150       [*family_ret retain];
3151       return result;
3152     }
3153   }
3154
3155   // None of the fonts support ASCII?
3156   return 0;
3157 }
3158
3159
3160 // Fonts need this. XDisplayHeightMM and friends should probably be consistent
3161 // with this as well if they're ever implemented.
3162 static const unsigned dpi = 75;
3163
3164
3165 static const char *
3166 xlfd_field_end (const char *s)
3167 {
3168   const char *s2 = strchr(s, '-');
3169   if (!s2)
3170     s2 = s + strlen(s);
3171   return s2;
3172 }
3173
3174
3175 static size_t
3176 xlfd_next (const char **s, const char **s2)
3177 {
3178   if (!**s2) {
3179     *s = *s2;
3180   } else {
3181     Assert (**s2 == '-', "xlfd parse error");
3182     *s = *s2 + 1;
3183     *s2 = xlfd_field_end (*s);
3184   }
3185
3186   return *s2 - *s;
3187 }
3188
3189
3190 static NSFont *
3191 try_xlfd_font (const char *name, float scale,
3192                char **name_ret, float *size_ret, char **xa_font)
3193 {
3194   NSFont *nsfont = 0;
3195   NSString *family_name = nil;
3196   NSFontTraitMask require = 0, forbid = 0;
3197   BOOL rand  = NO;
3198   float size = 0;
3199   char *ps_name = 0;
3200
3201   const char *s = (name ? name : "");
3202
3203   size_t L = strlen (s);
3204 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
3205 # define UNSPEC   (L == 0 || L == 1 && *s == '*')
3206   if      (CMP ("6x10"))     size = 8,  require |= NSFixedPitchFontMask;
3207   else if (CMP ("6x10bold")) size = 8,  require |= NSFixedPitchFontMask | NSBoldFontMask;
3208   else if (CMP ("fixed"))    size = 12, require |= NSFixedPitchFontMask;
3209   else if (CMP ("9x15"))     size = 12, require |= NSFixedPitchFontMask;
3210   else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
3211   else if (CMP ("vga"))      size = 12, require |= NSFixedPitchFontMask;
3212   else if (CMP ("console"))  size = 12, require |= NSFixedPitchFontMask;
3213   else if (CMP ("gallant"))  size = 12, require |= NSFixedPitchFontMask;
3214   else {
3215
3216     // Incorrect fields are ignored.
3217
3218     if (*s == '-')
3219       ++s;
3220     const char *s2 = xlfd_field_end(s);
3221
3222     // Foundry (ignore)
3223
3224     L = xlfd_next (&s, &s2); // Family name
3225     // This used to substitute Georgia for Times. Now it doesn't.
3226     if (CMP ("random")) {
3227       rand = YES;
3228     } else if (CMP ("fixed")) {
3229       require |= NSFixedPitchFontMask;
3230       family_name = @"Courier";
3231     } else if (!UNSPEC) {
3232       family_name = [[[NSString alloc] initWithBytes:s
3233                                               length:L
3234                                             encoding:NSUTF8StringEncoding]
3235                      autorelease];
3236     }
3237
3238     L = xlfd_next (&s, &s2); // Weight name
3239     if (CMP ("bold") || CMP ("demibold"))
3240       require |= NSBoldFontMask;
3241     else if (CMP ("medium") || CMP ("regular"))
3242       forbid |= NSBoldFontMask;
3243
3244     L = xlfd_next (&s, &s2); // Slant
3245     if (CMP ("i") || CMP ("o"))
3246       require |= NSItalicFontMask;
3247     else if (CMP ("r"))
3248       forbid |= NSItalicFontMask;
3249
3250     xlfd_next (&s, &s2); // Set width name (ignore)
3251     xlfd_next (&s, &s2); // Add style name (ignore)
3252
3253     xlfd_next (&s, &s2); // Pixel size (ignore)
3254
3255     xlfd_next (&s, &s2); // Point size
3256     char *s3;
3257     uintmax_t n = strtoumax(s, &s3, 10);
3258     if (s2 == s3)
3259       size = n / 10.0;
3260
3261     xlfd_next (&s, &s2); // Resolution X (ignore)
3262     xlfd_next (&s, &s2); // Resolution Y (ignore)
3263
3264     xlfd_next (&s, &s2); // Spacing
3265     if (CMP ("p"))
3266       forbid |= NSFixedPitchFontMask;
3267     else if (CMP ("m") || CMP ("c"))
3268       require |= NSFixedPitchFontMask;
3269
3270     // Don't care about average_width or charset registry.
3271   }
3272 # undef CMP
3273 # undef UNSPEC
3274
3275   if (!family_name && !rand)
3276     family_name = default_font_family (require);
3277
3278   if (size < 6 || size > 1000)
3279     size = 12;
3280
3281   size *= scale;
3282
3283   NSFontTraitMask mask = require | forbid;
3284
3285   if (rand) {
3286     nsfont   = random_font (require, mask, size, &family_name, &ps_name);
3287     [family_name autorelease];
3288   }
3289
3290   if (!nsfont)
3291     nsfont   = try_font (require, mask, family_name, size, &ps_name);
3292
3293   // if that didn't work, turn off attibutes until it does
3294   // (e.g., there is no "Monaco-Bold".)
3295   //
3296   if (!nsfont && (mask & NSItalicFontMask)) {
3297     require &= ~NSItalicFontMask;
3298     mask &= ~NSItalicFontMask;
3299     nsfont = try_font (require, mask, family_name, size, &ps_name);
3300   }
3301   if (!nsfont && (mask & NSBoldFontMask)) {
3302     require &= ~NSBoldFontMask;
3303     mask &= ~NSBoldFontMask;
3304     nsfont = try_font (require, mask, family_name, size, &ps_name);
3305   }
3306   if (!nsfont && (mask & NSFixedPitchFontMask)) {
3307     require &= ~NSFixedPitchFontMask;
3308     mask &= ~NSFixedPitchFontMask;
3309     nsfont = try_font (require, mask, family_name, size, &ps_name);
3310   }
3311
3312   if (nsfont) {
3313     *name_ret = ps_name;
3314     *size_ret = size;
3315     float actual_size = size / scale;
3316     asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
3317              family_name.UTF8String,
3318              (require & NSBoldFontMask) ? "bold" : "medium",
3319              (require & NSItalicFontMask) ? 'o' : 'r',
3320              (unsigned)(dpi * actual_size / 72.27 + 0.5),
3321              (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
3322              (require & NSFixedPitchFontMask) ? 'm' : 'p');
3323     return nsfont;
3324   } else {
3325     return 0;
3326   }
3327 }
3328
3329
3330 Font
3331 XLoadFont (Display *dpy, const char *name)
3332 {
3333   Font fid = (Font) calloc (1, sizeof(*fid));
3334
3335   float scale = 1;
3336
3337 # ifdef USE_IPHONE
3338   /* Since iOS screens are physically smaller than desktop screens, scale up
3339      the fonts to make them more readable.
3340
3341      Note that X11 apps on iOS also have the backbuffer sized in points
3342      instead of pixels, resulting in an effective X11 screen size of 768x1024
3343      or so, even if the display has significantly higher resolution.  That is
3344      unrelated to this hack, which is really about DPI.
3345    */
3346   scale = 2;
3347 # endif
3348
3349   fid->dpy = dpy;
3350   fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
3351                                  &fid->xa_font);
3352
3353   if (!fid->nsfont && name &&
3354       strchr (name, ' ') &&
3355       !strchr (name, '*')) {
3356     // If name contains a space but no stars, it is a native font spec --
3357     // return NULL so that we know it really didn't exist.  Else, it is an
3358     //  XLFD font, so keep trying.
3359     XUnloadFont (dpy, fid);
3360     return 0;
3361   }
3362
3363   if (! fid->nsfont)
3364     fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size,
3365                                  &fid->xa_font);
3366
3367   // We should never return NULL for XLFD fonts.
3368   if (!fid->nsfont) {
3369     Assert (0, "no font");
3370     return 0;
3371   }
3372   CFRetain (fid->nsfont);   // needed for garbage collection?
3373
3374   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
3375
3376   query_font (fid);
3377
3378   return fid;
3379 }
3380
3381
3382 XFontStruct *
3383 XLoadQueryFont (Display *dpy, const char *name)
3384 {
3385   Font fid = XLoadFont (dpy, name);
3386   if (!fid) return 0;
3387   return XQueryFont (dpy, fid);
3388 }
3389
3390 int
3391 XUnloadFont (Display *dpy, Font fid)
3392 {
3393   if (fid->ps_name)
3394     free (fid->ps_name);
3395   if (fid->metrics.per_char)
3396     free (fid->metrics.per_char);
3397
3398   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
3399   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
3400   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
3401   //      They're probably not very big...
3402   //
3403   //  [fid->nsfont release];
3404   //  CFRelease (fid->nsfont);
3405
3406   free (fid);
3407   return 0;
3408 }
3409
3410 int
3411 XFreeFontInfo (char **names, XFontStruct *info, int n)
3412 {
3413   int i;
3414   if (names) {
3415     for (i = 0; i < n; i++)
3416       if (names[i]) free (names[i]);
3417     free (names);
3418   }
3419   if (info) {
3420     for (i = 0; i < n; i++)
3421       if (info[i].per_char) {
3422         free (info[i].per_char);
3423         free (info[i].properties);
3424       }
3425     free (info);
3426   }
3427   return 0;
3428 }
3429
3430 int
3431 XFreeFont (Display *dpy, XFontStruct *f)
3432 {
3433   Font fid = f->fid;
3434   XFreeFontInfo (0, f, 1);
3435   XUnloadFont (dpy, fid);
3436   return 0;
3437 }
3438
3439
3440 int
3441 XSetFont (Display *dpy, GC gc, Font fid)
3442 {
3443   if (gc->gcv.font)
3444     XUnloadFont (dpy, gc->gcv.font);
3445   gc->gcv.font = copy_font (fid);
3446   [gc->gcv.font->nsfont retain];
3447   CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
3448   return 0;
3449 }
3450
3451
3452 XFontSet
3453 XCreateFontSet (Display *dpy, char *name, 
3454                 char ***missing_charset_list_return,
3455                 int *missing_charset_count_return,
3456                 char **def_string_return)
3457 {
3458   char *name2 = strdup (name);
3459   char *s = strchr (name, ",");
3460   if (s) *s = 0;
3461   XFontSet set = 0;
3462   XFontStruct *f = XLoadQueryFont (dpy, name2);
3463   if (f)
3464     {
3465       set = (XFontSet) calloc (1, sizeof(*set));
3466       set->font = f;
3467     }
3468   free (name2);
3469   if (missing_charset_list_return)  *missing_charset_list_return = 0;
3470   if (missing_charset_count_return) *missing_charset_count_return = 0;
3471   if (def_string_return) *def_string_return = 0;
3472   return set;
3473 }
3474
3475
3476 void
3477 XFreeFontSet (Display *dpy, XFontSet set)
3478 {
3479   XFreeFont (dpy, set->font);
3480   free (set);
3481 }
3482
3483
3484 const char *
3485 jwxyz_nativeFontName (Font f, float *size)
3486 {
3487   if (size) *size = f->size;
3488   return f->ps_name;
3489 }
3490
3491
3492 void
3493 XFreeStringList (char **list)
3494 {
3495   int i;
3496   if (!list) return;
3497   for (i = 0; list[i]; i++)
3498     XFree (list[i]);
3499   XFree (list);
3500 }
3501
3502
3503 // Returns the verbose Unicode name of this character, like "agrave" or
3504 // "daggerdouble".  Used by fontglide debugMetrics.
3505 //
3506 char *
3507 jwxyz_unicode_character_name (Font fid, unsigned long uc)
3508 {
3509   char *ret = 0;
3510   CTFontRef ctfont =
3511     CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
3512                           [fid->nsfont pointSize],
3513                           NULL);
3514   Assert (ctfont, @"no CTFontRef for UIFont");
3515
3516   CGGlyph cgglyph;
3517   if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
3518     NSString *name = (NSString *)
3519       CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
3520                                    cgglyph);
3521     ret = (name ? strdup ([name UTF8String]) : 0);
3522   }
3523
3524   CFRelease (ctfont);
3525   return ret;
3526 }
3527
3528
3529 // Given a UTF8 string, return an NSString.  Bogus UTF8 characters are ignored.
3530 // We have to do this because stringWithCString returns NULL if there are
3531 // any invalid characters at all.
3532 //
3533 static NSString *
3534 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
3535 {
3536   int out_len = in_len * 4;   // length of string might increase
3537   char *s2 = (char *) malloc (out_len);
3538   char *out = s2;
3539   const char *in_end  = in  + in_len;
3540   const char *out_end = out + out_len;
3541   Bool latin1_p = True;
3542
3543   while (in < in_end)
3544     {
3545       unsigned long uc;
3546       long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
3547       long L2 = utf8_encode (uc, out, out_end - out);
3548       in  += L1;
3549       out += L2;
3550       if (uc > 255) latin1_p = False;
3551     }
3552   *out = 0;
3553   NSString *nsstr =
3554     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3555   free (s2);
3556   if (latin1_pP) *latin1_pP = latin1_p;
3557   return (nsstr ? nsstr : @"");
3558 }
3559
3560
3561 int
3562 XTextExtents (XFontStruct *f, const char *s, int length,
3563               int *dir_ret, int *ascent_ret, int *descent_ret,
3564               XCharStruct *cs)
3565 {
3566   // Unfortunately, adding XCharStructs together to get the extents for a
3567   // string doesn't work: Cocoa uses non-integral character advancements, but
3568   // XCharStruct.width is an integer. Plus that doesn't take into account
3569   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
3570   // Zapfino.
3571
3572   NSString *nsstr = [[[NSString alloc] initWithBytes:s
3573                                               length:length
3574                                             encoding:NSISOLatin1StringEncoding]
3575                      autorelease];
3576   utf8_metrics (f->fid, nsstr, cs);
3577   *dir_ret = 0;
3578   *ascent_ret  = f->ascent;
3579   *descent_ret = f->descent;
3580   return 0;
3581 }
3582
3583 int
3584 XTextWidth (XFontStruct *f, const char *s, int length)
3585 {
3586   int ascent, descent, dir;
3587   XCharStruct cs;
3588   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3589   return cs.width;
3590 }
3591
3592
3593 int
3594 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
3595                 int *dir_ret, int *ascent_ret, int *descent_ret,
3596                 XCharStruct *cs)
3597 {
3598   Bool latin1_p = True;
3599   int i, utf8_len = 0;
3600   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
3601
3602   for (i = 0; i < length; i++)
3603     if (s[i].byte1 > 0) {
3604       latin1_p = False;
3605       break;
3606     }
3607
3608   {
3609     NSString *nsstr = [NSString stringWithCString:utf8
3610                                 encoding:NSUTF8StringEncoding];
3611     utf8_metrics (f->fid, nsstr, cs);
3612   }
3613
3614   *dir_ret = 0;
3615   *ascent_ret  = f->ascent;
3616   *descent_ret = f->descent;
3617   free (utf8);
3618   return 0;
3619 }
3620
3621
3622 /* "Returns the distance in pixels in the primary draw direction from
3623    the drawing origin to the origin of the next character to be drawn."
3624
3625    "overall_ink_return is set to the bbox of the string's character ink."
3626
3627    "The overall_ink_return for a nondescending, horizontally drawn Latin
3628    character is conventionally entirely above the baseline; that is,
3629    overall_ink_return.height <= -overall_ink_return.y."
3630
3631      [So this means that y is the top of the ink, and height grows down:
3632       For above-the-baseline characters, y is negative.]
3633
3634    "The overall_ink_return for a nonkerned character is entirely at, and to
3635    the right of, the origin; that is, overall_ink_return.x >= 0."
3636
3637      [So this means that x is the left of the ink, and width grows right.
3638       For left-of-the-origin characters, x is negative.]
3639
3640    "A character consisting of a single pixel at the origin would set
3641    overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
3642  */
3643 int
3644 Xutf8TextExtents (XFontSet set, const char *str, int len,
3645                   XRectangle *overall_ink_return,
3646                   XRectangle *overall_logical_return)
3647 {
3648   Bool latin1_p;
3649   NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
3650   XCharStruct cs;
3651
3652   utf8_metrics (set->font->fid, nsstr, &cs);
3653
3654   /* "The overall_logical_return is the bounding box that provides minimum
3655      spacing to other graphical features for the string. Other graphical
3656      features, for example, a border surrounding the text, should not
3657      intersect this rectangle."
3658
3659      So I think that means they're the same?  Or maybe "ink" is the bounding
3660      box, and "logical" is the advancement?  But then why is the return value
3661      the advancement?
3662    */
3663   if (overall_ink_return)
3664     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
3665   if (overall_logical_return)
3666     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
3667
3668   return cs.width;
3669 }
3670
3671
3672 static int
3673 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3674              NSString *nsstr)
3675 {
3676   if (! nsstr) return 1;
3677
3678   CGRect wr = d->frame;
3679   CGContextRef cgc = d->cgc;
3680
3681   unsigned long argb = gc->gcv.foreground;
3682   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3683   float rgba[4];
3684   query_color_float (dpy, argb, rgba);
3685   NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
3686                                       green:rgba[1]
3687                                        blue:rgba[2]
3688                                       alpha:rgba[3]];
3689
3690   if (!gc->gcv.font) {
3691     Assert (0, "no font");
3692     return 1;
3693   }
3694
3695   /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
3696       NSFontAttributeName, and NSAttributedString are only present on iOS 6
3697       and later.  We could resurrect the Quartz code from v5.29 and do a
3698       runtime conditional on that, but that would be a pain in the ass.
3699       Probably time to just make iOS 6 a requirement.
3700    */
3701
3702   NSDictionary *attr =
3703     [NSDictionary dictionaryWithObjectsAndKeys:
3704                     gc->gcv.font->nsfont, NSFontAttributeName,
3705                     fg, NSForegroundColorAttributeName,
3706                   nil];
3707
3708   // Don't understand why we have to do both set_color and
3709   // NSForegroundColorAttributeName, but we do.
3710   //
3711   set_color (dpy, cgc, argb, 32, NO, YES);
3712
3713   NSAttributedString *astr = [[NSAttributedString alloc]
3714                                initWithString:nsstr
3715                                    attributes:attr];
3716   CTLineRef dl = CTLineCreateWithAttributedString (
3717                    (__bridge CFAttributedStringRef) astr);
3718
3719   // Not sure why this is necessary, but xoff is positive when the first
3720   // character on the line has a negative lbearing.  Without this, the
3721   // string is rendered with the first ink at 0 instead of at lbearing.
3722   // I have not seen xoff be negative, so I'm not sure if that can happen.
3723   //
3724   // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
3725   // a letter.
3726   //
3727   CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
3728   Assert (xoff >= 0, "unexpected CTLineOffset");
3729   x -= xoff;
3730
3731   CGContextSetTextPosition (cgc,
3732                             wr.origin.x + x,
3733                             wr.origin.y + wr.size.height - y);
3734   CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3735
3736   CTLineDraw (dl, cgc);
3737   CFRelease (dl);
3738
3739   invalidate_drawable_cache (d);
3740   return 0;
3741 }
3742
3743
3744 int
3745 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3746              const char  *str, int len)
3747 {
3748   char *s2 = (char *) malloc (len + 1);
3749   strncpy (s2, str, len);
3750   s2[len] = 0;
3751   NSString *nsstr = [NSString stringWithCString:s2
3752                                        encoding:NSISOLatin1StringEncoding];
3753   int ret = draw_string (dpy, d, gc, x, y, nsstr);
3754   free (s2);
3755   return ret;
3756 }
3757
3758
3759 int
3760 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
3761              const XChar2b *str, int len)
3762 {
3763   char *s2 = XChar2b_to_utf8 (str, 0);   // already sanitized
3764   NSString *nsstr =
3765     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3766   int ret = draw_string (dpy, d, gc, x, y, nsstr);
3767   free (s2);
3768   return ret;
3769 }
3770
3771
3772 void
3773 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3774                  int x, int y, const char *str, int len)
3775 {
3776   char *s2 = (char *) malloc (len + 1);
3777   strncpy (s2, str, len);
3778   s2[len] = 0;
3779   NSString *nsstr = sanitize_utf8 (str, len, 0);
3780   draw_string (dpy, d, gc, x, y, nsstr);
3781   free (s2);
3782 }
3783
3784
3785 int
3786 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3787                   const char *str, int len)
3788 {
3789   int ascent, descent, dir;
3790   XCharStruct cs;
3791   XTextExtents (&gc->gcv.font->metrics, str, len,
3792                 &dir, &ascent, &descent, &cs);
3793   draw_rect (dpy, d, gc,
3794              x + MIN (0, cs.lbearing),
3795              y - MAX (0, ascent),
3796              MAX (MAX (0, cs.rbearing) -
3797                   MIN (0, cs.lbearing),
3798                   cs.width),
3799              MAX (0, ascent) + MAX (0, descent),
3800              NO, YES);
3801   return XDrawString (dpy, d, gc, x, y, str, len);
3802 }
3803
3804
3805 int
3806 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3807 {
3808   validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p);
3809   gc->gcv.foreground = fg;
3810   return 0;
3811 }
3812
3813
3814 int
3815 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3816 {
3817   validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p);
3818   gc->gcv.background = bg;
3819   return 0;
3820 }
3821
3822 int
3823 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3824 {
3825   gc->gcv.alpha_allowed_p = allowed;
3826   return 0;
3827 }
3828
3829 int
3830 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3831 {
3832   gc->gcv.antialias_p = antialias_p;
3833   return 0;
3834 }
3835
3836
3837 int
3838 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3839                     int line_style, int cap_style, int join_style)
3840 {
3841   gc->gcv.line_width = line_width;
3842   Assert (line_style == LineSolid, "only LineSolid implemented");
3843 //  gc->gcv.line_style = line_style;
3844   gc->gcv.cap_style = cap_style;
3845   gc->gcv.join_style = join_style;
3846   return 0;
3847 }
3848
3849 int
3850 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3851 {
3852   return 0;
3853 }
3854
3855 int
3856 XSetFunction (Display *dpy, GC gc, int which)
3857 {
3858   gc->gcv.function = which;
3859   return 0;
3860 }
3861
3862 int
3863 XSetSubwindowMode (Display *dpy, GC gc, int which)
3864 {
3865   gc->gcv.subwindow_mode = which;
3866   return 0;
3867 }
3868
3869 int
3870 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3871 {
3872   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3873
3874   if (gc->gcv.clip_mask) {
3875     XFreePixmap (dpy, gc->gcv.clip_mask);
3876     CGImageRelease (gc->clip_mask);
3877   }
3878
3879   gc->gcv.clip_mask = copy_pixmap (dpy, m);
3880   if (gc->gcv.clip_mask)
3881     gc->clip_mask =
3882       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3883   else
3884     gc->clip_mask = 0;
3885
3886   return 0;
3887 }
3888
3889 int
3890 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3891 {
3892   gc->gcv.clip_x_origin = x;
3893   gc->gcv.clip_y_origin = y;
3894   return 0;
3895 }
3896
3897
3898 Bool
3899 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3900                int *root_x_ret, int *root_y_ret, 
3901                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3902 {
3903   Assert (w && w->type == WINDOW, "not a window");
3904
3905 # ifdef USE_IPHONE
3906   int x = w->window.last_mouse_x;
3907   int y = w->window.last_mouse_y;
3908   if (root_x_ret) *root_x_ret = x;
3909   if (root_y_ret) *root_y_ret = y;
3910   if (win_x_ret)  *win_x_ret  = x;
3911   if (win_y_ret)  *win_y_ret  = y;
3912
3913 # else  // !USE_IPHONE
3914
3915   NSWindow *nsw = [w->window.view window];
3916   NSPoint wpos;
3917   // get bottom left of window on screen, from bottom left
3918   wpos.x = wpos.y = 0;
3919   wpos = [nsw convertBaseToScreen:wpos];
3920   
3921   NSPoint vpos;
3922   // get bottom left of view on window, from bottom left
3923   vpos.x = vpos.y = 0;
3924   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3925
3926   // get bottom left of view on screen, from bottom left
3927   vpos.x += wpos.x;
3928   vpos.y += wpos.y;
3929   
3930   // get top left of view on screen, from bottom left
3931   vpos.y += w->frame.size.height;
3932   
3933   // get top left of view on screen, from top left
3934   NSArray *screens = [NSScreen screens];
3935   NSScreen *screen = (screens && [screens count] > 0
3936                       ? [screens objectAtIndex:0]
3937                       : [NSScreen mainScreen]);
3938   NSRect srect = [screen frame];
3939   vpos.y = srect.size.height - vpos.y;
3940   
3941   // get the mouse position on window, from bottom left
3942   NSEvent *e = [NSApp currentEvent];
3943   NSPoint p = [e locationInWindow];
3944   
3945   // get mouse position on screen, from bottom left
3946   p.x += wpos.x;
3947   p.y += wpos.y;
3948   
3949   // get mouse position on screen, from top left
3950   p.y = srect.size.height - p.y;
3951
3952   if (root_x_ret) *root_x_ret = (int) p.x;
3953   if (root_y_ret) *root_y_ret = (int) p.y;
3954   if (win_x_ret)  *win_x_ret  = (int) (p.x - vpos.x);
3955   if (win_y_ret)  *win_y_ret  = (int) (p.y - vpos.y);
3956 # endif // !USE_IPHONE
3957   
3958   if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
3959   if (root_ret)   *root_ret   = 0;
3960   if (child_ret)  *child_ret  = 0;
3961   return True;
3962 }
3963
3964 Bool
3965 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3966                        int src_x, int src_y,
3967                        int *dest_x_ret, int *dest_y_ret,
3968                        Window *child_ret)
3969 {
3970   Assert (w && w->type == WINDOW, "not a window");
3971
3972 # ifdef USE_IPHONE
3973
3974   NSPoint p;
3975   p.x = src_x;
3976   p.y = src_y;
3977
3978 # else  // !USE_IPHONE
3979
3980   NSWindow *nsw = [w->window.view window];
3981   NSPoint wpos;
3982   // get bottom left of window on screen, from bottom left
3983   wpos.x = wpos.y = 0;
3984   wpos = [nsw convertBaseToScreen:wpos];
3985   
3986   NSPoint vpos;
3987   // get bottom left of view on window, from bottom left
3988   vpos.x = vpos.y = 0;
3989   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3990
3991   // get bottom left of view on screen, from bottom left
3992   vpos.x += wpos.x;
3993   vpos.y += wpos.y;
3994   
3995   // get top left of view on screen, from bottom left
3996   vpos.y += w->frame.size.height;
3997   
3998   // get top left of view on screen, from top left
3999   NSArray *screens = [NSScreen screens];
4000   NSScreen *screen = (screens && [screens count] > 0
4001                       ? [screens objectAtIndex:0]
4002                       : [NSScreen mainScreen]);
4003   NSRect srect = [screen frame];
4004   vpos.y = srect.size.height - vpos.y;
4005   
4006   // point starts out relative to top left of view
4007   NSPoint p;
4008   p.x = src_x;
4009   p.y = src_y;
4010   
4011   // get point relative to top left of screen
4012   p.x += vpos.x;
4013   p.y += vpos.y;
4014 # endif // !USE_IPHONE
4015
4016   *dest_x_ret = p.x;
4017   *dest_y_ret = p.y;
4018   if (child_ret)
4019     *child_ret = w;
4020   return True;
4021 }
4022
4023
4024 KeySym
4025 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
4026 {
4027   return code;
4028 }
4029
4030 int
4031 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
4032                XComposeStatus *xc)
4033 {
4034   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
4035   char c = 0;
4036   // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
4037   if ((unsigned int) ks <= 255)
4038     c = (char) ks;
4039
4040   // Put control characters in the string.  Not meta.
4041   if (e->state & ControlMask) {
4042     if (c >= 'a' && c <= 'z')    // Upcase control.
4043       c -= 'a'-'A';
4044     if (c >= '@' && c <= '_')    // Shift to control page.
4045       c -= '@';
4046     if (c == ' ')                // C-SPC is NULL.
4047       c = 0;
4048   }
4049
4050   if (k_ret) *k_ret = ks;
4051   if (size > 0) buf[0] = c;
4052   if (size > 1) buf[1] = 0;
4053   return (size > 0 ? 1 : 0);
4054 }
4055
4056
4057 int
4058 XFlush (Display *dpy)
4059 {
4060   // Just let the event loop take care of this on its own schedule.
4061   return 0;
4062 }
4063
4064 int
4065 XSync (Display *dpy, Bool flush)
4066 {
4067   return XFlush (dpy);
4068 }
4069
4070
4071 // declared in utils/visual.h
4072 int
4073 has_writable_cells (Screen *s, Visual *v)
4074 {
4075   return 0;
4076 }
4077
4078 int
4079 visual_depth (Screen *s, Visual *v)
4080 {
4081   return 32;
4082 }
4083
4084 int
4085 visual_cells (Screen *s, Visual *v)
4086 {
4087   return (int)(v->red_mask | v->green_mask | v->blue_mask);
4088 }
4089
4090 int
4091 visual_class (Screen *s, Visual *v)
4092 {
4093   return TrueColor;
4094 }
4095
4096 int
4097 get_bits_per_pixel (Display *dpy, int depth)
4098 {
4099   Assert (depth == 32 || depth == 1, "unexpected depth");
4100   return depth;
4101 }
4102
4103 int
4104 screen_number (Screen *screen)
4105 {
4106   Display *dpy = DisplayOfScreen (screen);
4107   int i;
4108   for (i = 0; i < ScreenCount (dpy); i++)
4109     if (ScreenOfDisplay (dpy, i) == screen)
4110       return i;
4111   abort ();
4112   return 0;
4113 }
4114
4115 // declared in utils/grabclient.h
4116 Bool
4117 use_subwindow_mode_p (Screen *screen, Window window)
4118 {
4119   return False;
4120 }