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