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