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