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