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