http://www.jwz.org/xscreensaver/xscreensaver-5.07.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   Assert ((w < 65535), "improbably large width");
1483   Assert ((h < 65535), "improbably large height");
1484   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1485   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1486   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1487   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1488
1489   // Clip width and height to the bounds of the Drawable
1490   //
1491   if (dest_x + w > wr.size.width) {
1492     if (dest_x > wr.size.width)
1493       return 0;
1494     w = wr.size.width - dest_x;
1495   }
1496   if (dest_y + h > wr.size.height) {
1497     if (dest_y > wr.size.height)
1498       return 0;
1499     h = wr.size.height - dest_y;
1500   }
1501   if (w <= 0 || h <= 0)
1502     return 0;
1503
1504   // Clip width and height to the bounds of the XImage
1505   //
1506   if (src_x + w > ximage->width) {
1507     if (src_x > ximage->width)
1508       return 0;
1509     w = ximage->width - src_x;
1510   }
1511   if (src_y + h > ximage->height) {
1512     if (src_y > ximage->height)
1513       return 0;
1514     h = ximage->height - src_y;
1515   }
1516   if (w <= 0 || h <= 0)
1517     return 0;
1518
1519   if (gc && (gc->gcv.function == GXset ||
1520              gc->gcv.function == GXclear)) {
1521     // "set" and "clear" are dumb drawing modes that ignore the source
1522     // bits and just draw solid rectangles.
1523     set_color (d->cgc, (gc->gcv.function == GXset
1524                         ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1525                         : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1526                gc->depth, gc->gcv.alpha_allowed_p, YES);
1527     draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1528     return 0;
1529   }
1530
1531   int bpl = ximage->bytes_per_line;
1532   int bpp = ximage->bits_per_pixel;
1533   int bsize = bpl * h;
1534   char *data = ximage->data;
1535
1536   CGRect r;
1537   r.origin.x = wr.origin.x + dest_x;
1538   r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1539   r.size.width = w;
1540   r.size.height = h;
1541
1542   if (bpp == 32) {
1543
1544     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1545        to create a CGImage from a sub-rectagle of the XImage.
1546      */
1547     data += (src_y * bpl) + (src_x * 4);
1548     CGDataProviderRef prov = 
1549       CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1550
1551     CGImageRef cgi = CGImageCreate (w, h,
1552                                     bpp/4, bpp, bpl,
1553                                     dpy->colorspace, 
1554                                     /* Need this for XPMs to have the right
1555                                        colors, e.g. the logo in "maze". */
1556                                     (kCGImageAlphaNoneSkipFirst |
1557                                      kCGBitmapByteOrder32Host),
1558                                     prov, 
1559                                     NULL,  /* decode[] */
1560                                     NO, /* interpolate */
1561                                     kCGRenderingIntentDefault);
1562     CGDataProviderRelease (prov);
1563     //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1564     CGContextDrawImage (d->cgc, r, cgi);
1565     CGImageRelease (cgi);
1566
1567   } else {   // (bpp == 1)
1568
1569     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1570
1571        #### However, the bit order within a byte in a 1bpp XImage is
1572             the wrong way around from what Quartz expects, so first we
1573             have to copy the data to reverse it.  Shit!  Maybe it
1574             would be worthwhile to go through the hacks and #ifdef
1575             each one that diddles 1bpp XImage->data directly...
1576      */
1577     Assert ((src_x % 8) == 0,
1578             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1579
1580     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1581     unsigned char *flipped = (unsigned char *) malloc (bsize);
1582
1583     flipbits ((unsigned char *) data, flipped, bsize);
1584
1585     CGDataProviderRef prov = 
1586       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1587     CGImageRef mask = CGImageMaskCreate (w, h, 
1588                                          1, bpp, bpl,
1589                                          prov,
1590                                          NULL,  /* decode[] */
1591                                          NO); /* interpolate */
1592     push_fg_gc (d, gc, YES);
1593
1594     CGContextFillRect (d->cgc, r);                      // foreground color
1595     CGContextClipToMask (d->cgc, r, mask);
1596     set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1597     CGContextFillRect (d->cgc, r);                      // background color
1598     pop_gc (d, gc);
1599
1600     free (flipped);
1601     CGDataProviderRelease (prov);
1602     CGImageRelease (mask);
1603   }
1604
1605   return 0;
1606 }
1607
1608
1609 XImage *
1610 XGetImage (Display *dpy, Drawable d, int x, int y,
1611            unsigned int width, unsigned int height,
1612            unsigned long plane_mask, int format)
1613 {
1614   const unsigned char *data = 0;
1615   int depth, ibpp, ibpl;
1616   NSBitmapImageRep *bm = 0;
1617   
1618   Assert ((width  < 65535), "improbably large width");
1619   Assert ((height < 65535), "improbably large height");
1620   Assert ((x < 65535 && x > -65535), "improbably large x");
1621   Assert ((y < 65535 && y > -65535), "improbably large y");
1622
1623   if (d->type == PIXMAP) {
1624     depth = d->pixmap.depth;
1625     ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1626     ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1627     data = CGBitmapContextGetData (d->cgc);
1628     Assert (data, "CGBitmapContextGetData failed");
1629   } else {
1630     // get the bits (desired sub-rectangle) out of the NSView
1631     bm = [NSBitmapImageRep alloc];
1632     NSRect nsfrom;
1633     nsfrom.origin.x = x;
1634     nsfrom.origin.y = y;
1635     nsfrom.size.width = width;
1636     nsfrom.size.height = height;
1637     [bm initWithFocusedViewRect:nsfrom];
1638     depth = 32;
1639     ibpp = [bm bitsPerPixel];
1640     ibpl = [bm bytesPerRow];
1641     data = [bm bitmapData];
1642     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1643   }
1644   
1645   // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
1646   data += (y * ibpl) + (x * (ibpp/8));
1647   
1648   format = (depth == 1 ? XYPixmap : ZPixmap);
1649   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1650                                 0, 0);
1651   image->data = (char *) malloc (height * image->bytes_per_line);
1652   
1653   int obpl = image->bytes_per_line;
1654   
1655   /* both PPC and Intel use word-ordered ARGB frame buffers, which
1656      means that on Intel it is BGRA when viewed by bytes (And BGR
1657      when using 24bpp packing).
1658    */
1659   int xx, yy;
1660   if (depth == 1) {
1661     const unsigned char *iline = data;
1662     for (yy = 0; yy < height; yy++) {
1663
1664       const unsigned char *iline2 = iline;
1665       for (xx = 0; xx < width; xx++) {
1666
1667         iline2++;                     // ignore b or a
1668         iline2++;                     // ignore g or r
1669         unsigned char r = *iline2++;  //        r or g
1670         if (ibpp == 32) iline2++;     // ignore a or b
1671
1672         XPutPixel (image, xx, yy, (r ? 1 : 0));
1673       }
1674       iline += ibpl;
1675     }
1676   } else {
1677     Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1678     const unsigned char *iline = data;
1679     unsigned char *oline = (unsigned char *) image->data;
1680     for (yy = 0; yy < height; yy++) {
1681
1682       const unsigned char *iline2 = iline;
1683       unsigned char *oline2 = oline;
1684       for (xx = 0; xx < width; xx++) {
1685
1686         unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1687         unsigned char r = *iline2++;
1688         unsigned char g = *iline2++;
1689         unsigned char b = *iline2++;
1690         unsigned long pixel = ((a << 24) |
1691                                (r << 16) |
1692                                (g <<  8) |
1693                                (b <<  0));
1694         *((unsigned int *) oline2) = pixel;
1695         oline2 += 4;
1696       }
1697       oline += obpl;
1698       iline += ibpl;
1699     }
1700   }
1701
1702   if (bm) [bm release];
1703
1704   return image;
1705 }
1706
1707 void
1708 jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
1709                     XRectangle *geom_ret)
1710 {
1711   NSImage *nsimg = (NSImage *) nsimg_arg;
1712
1713   // convert the NSImage to a CGImage via the toll-free-bridging 
1714   // of NSData and CFData...
1715   //
1716   NSData *nsdata = [NSBitmapImageRep
1717                         TIFFRepresentationOfImageRepsInArray:
1718                           [nsimg representations]];
1719   CFDataRef cfdata = (CFDataRef) nsdata;
1720   CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1721   CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1722
1723   NSSize imgr = [nsimg size];
1724   CGRect winr = d->frame;
1725   float rw = winr.size.width  / imgr.width;
1726   float rh = winr.size.height / imgr.height;
1727   float r = (rw < rh ? rw : rh);
1728
1729   CGRect dst;
1730   dst.size.width  = imgr.width  * r;
1731   dst.size.height = imgr.height * r;
1732   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
1733   dst.origin.y = (winr.size.height - dst.size.height) / 2;
1734
1735   // Clear the part not covered by the image to background or black.
1736   //
1737   if (d->type == WINDOW)
1738     XClearWindow (dpy, d);
1739   else {
1740     set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1741     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1742   }
1743
1744   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1745   CGContextDrawImage (d->cgc, dst, cgi);
1746
1747   CFRelease (cgsrc);
1748   CGImageRelease (cgi);
1749
1750   if (geom_ret) {
1751     geom_ret->x = dst.origin.x;
1752     geom_ret->y = dst.origin.y;
1753     geom_ret->width  = dst.size.width;
1754     geom_ret->height = dst.size.height;
1755   }
1756 }
1757
1758
1759 Pixmap
1760 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1761                              const char *data,
1762                              unsigned int w, unsigned int h,
1763                              unsigned long fg, unsigned int bg,
1764                              unsigned int depth)
1765 {
1766   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1767   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0, 
1768                                 (char *) data, w, h, 0, 0);
1769   XGCValues gcv;
1770   gcv.foreground = fg;
1771   gcv.background = bg;
1772   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1773   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1774   XFreeGC (dpy, gc);
1775   image->data = 0;
1776   XDestroyImage (image);
1777   return p;
1778 }
1779
1780 Pixmap
1781 XCreatePixmap (Display *dpy, Drawable d,
1782                unsigned int width, unsigned int height, unsigned int depth)
1783 {
1784   char *data = (char *) malloc (width * height * 4);
1785   if (! data) return 0;
1786
1787   Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1788   p->type = PIXMAP;
1789   p->frame.size.width  = width;
1790   p->frame.size.height = height;
1791   p->pixmap.depth      = depth;
1792   
1793   /* Quartz doesn't have a 1bpp image type.
1794      We used to use 8bpp gray images instead of 1bpp, but some Mac video
1795      don't support that!  So we always use 32bpp, regardless of depth. */
1796
1797   p->cgc = CGBitmapContextCreate (data, width, height,
1798                                   8, /* bits per component */
1799                                   width * 4, /* bpl */
1800                                   dpy->colorspace,
1801                                   // Without this, it returns 0...
1802                                   kCGImageAlphaNoneSkipFirst
1803                                   );
1804   Assert (p->cgc, "could not create CGBitmapContext");
1805   return p;
1806 }
1807
1808
1809 int
1810 XFreePixmap (Display *d, Pixmap p)
1811 {
1812   Assert (p->type == PIXMAP, "not a pixmap");
1813   CGContextRelease (p->cgc);
1814   free (p);
1815   return 0;
1816 }
1817
1818
1819 static Pixmap
1820 copy_pixmap (Pixmap p)
1821 {
1822   if (!p) return 0;
1823   Assert (p->type == PIXMAP, "not a pixmap");
1824   Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1825   *p2 = *p;
1826   CGContextRetain (p2->cgc);   // #### is this ok? need to copy it instead?
1827   return p2;
1828 }
1829
1830
1831 /* Font metric terminology, as used by X11:
1832
1833    "lbearing" is the distance from the logical origin to the leftmost pixel.
1834    If a character's ink extends to the left of the origin, it is negative.
1835
1836    "rbearing" is the distance from the logical origin to the rightmost pixel.
1837
1838    "descent" is the distance from the logical origin to the bottommost pixel.
1839    For characters with descenders, it is negative.
1840
1841    "ascent" is the distance from the logical origin to the topmost pixel.
1842    It is the number of pixels above the baseline.
1843
1844    "width" is the distance from the logical origin to the position where
1845    the logical origin of the next character should be placed.
1846
1847    If "rbearing" is greater than "width", then this character overlaps the
1848    following character.  If smaller, then there is trailing blank space.
1849  */
1850
1851
1852 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1853 //
1854 static void
1855 query_font (Font fid)
1856 {
1857   if (!fid || !fid->nsfont) {
1858     NSLog(@"no NSFont in fid");
1859     abort();
1860   }
1861   if (![fid->nsfont fontName]) {
1862     NSLog(@"broken NSFont in fid");
1863     abort();
1864   }
1865
1866   int first = 32;
1867   int last = 255;
1868
1869   XFontStruct *f = &fid->metrics;
1870   XCharStruct *min = &f->min_bounds;
1871   XCharStruct *max = &f->max_bounds;
1872
1873 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1874
1875   f->fid               = fid;
1876   f->min_char_or_byte2 = first;
1877   f->max_char_or_byte2 = last;
1878   f->default_char      = 'M';
1879   f->ascent            =  CEIL ([fid->nsfont ascender]);
1880   f->descent           = -CEIL ([fid->nsfont descender]);
1881
1882   min->width    = 255;  // set to smaller values in the loop
1883   min->ascent   = 255;
1884   min->descent  = 255;
1885   min->lbearing = 255;
1886   min->rbearing = 255;
1887
1888   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1889   int i;
1890
1891   NSBezierPath *bpath = [NSBezierPath bezierPath];
1892
1893   for (i = first; i <= last; i++) {
1894     unsigned char str[2];
1895     str[0] = i;
1896     str[1] = 0;
1897
1898     NSString *nsstr = [NSString stringWithCString:(char *) str
1899                                          encoding:NSISOLatin1StringEncoding];
1900
1901     /* I can't believe we have to go through this bullshit just to
1902        convert a 'char' to an NSGlyph!!
1903
1904        You might think that we could do
1905           NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
1906        but that doesn't work; my guess is that glyphWithName expects
1907        full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
1908      */
1909     NSGlyph glyph;
1910     {
1911       NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
1912       [ts setFont:fid->nsfont];
1913       NSLayoutManager *lm = [[NSLayoutManager alloc] init];
1914       NSTextContainer *tc = [[NSTextContainer alloc] init];
1915       [lm addTextContainer:tc];
1916       [tc release];     // lm retains tc
1917       [ts addLayoutManager:lm];
1918       [lm release];     // ts retains lm
1919       glyph = [lm glyphAtIndex:0];
1920       [ts release];
1921     }
1922
1923     /* Compute the bounding box and advancement by converting the glyph
1924        to a bezier path.  There appears to be *no other way* to find out
1925        the bounding box of a character: [NSFont boundingRectForGlyph] and
1926        [NSString sizeWithAttributes] both return an advancement-sized
1927        rectangle, not a rectangle completely enclosing the glyph's ink.
1928      */
1929     NSPoint advancement;
1930     NSRect bbox;
1931     advancement.x = advancement.y = 0;
1932     [bpath removeAllPoints];
1933     [bpath moveToPoint:advancement];
1934     [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
1935     advancement = [bpath currentPoint];
1936     bbox = [bpath bounds];
1937
1938     /* Now that we know the advancement and bounding box, we can compute
1939        the lbearing and rbearing.
1940      */
1941     XCharStruct *cs = &f->per_char[i-first];
1942
1943     cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
1944     cs->descent  = CEIL(-bbox.origin.y);
1945     cs->lbearing = CEIL (bbox.origin.x);
1946     cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
1947     cs->width    = CEIL (advancement.x);
1948
1949     Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
1950             "bbox w wrong");
1951     Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
1952             "bbox h wrong");
1953
1954     max->width    = MAX (max->width,    cs->width);
1955     max->ascent   = MAX (max->ascent,   cs->ascent);
1956     max->descent  = MAX (max->descent,  cs->descent);
1957     max->lbearing = MAX (max->lbearing, cs->lbearing);
1958     max->rbearing = MAX (max->rbearing, cs->rbearing);
1959
1960     min->width    = MIN (min->width,    cs->width);
1961     min->ascent   = MIN (min->ascent,   cs->ascent);
1962     min->descent  = MIN (min->descent,  cs->descent);
1963     min->lbearing = MIN (min->lbearing, cs->lbearing);
1964     min->rbearing = MIN (min->rbearing, cs->rbearing);
1965
1966 # undef CEIL
1967
1968 #if 0
1969     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
1970                     " bb=%3d x %3d @ %3d %3d  adv=%3d %3d\n",
1971             i, i, cs->width, cs->lbearing, cs->rbearing, 
1972             cs->ascent, cs->descent,
1973             (int) bbox.size.width, (int) bbox.size.height,
1974             (int) bbox.origin.x, (int) bbox.origin.y,
1975             (int) advancement.x, (int) advancement.y);
1976 #endif
1977   }
1978
1979 }
1980
1981
1982 // Since 'Font' includes the metrics, this just makes a copy of that.
1983 //
1984 XFontStruct *
1985 XQueryFont (Display *dpy, Font fid)
1986 {
1987   // copy XFontStruct
1988   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
1989   *f = fid->metrics;
1990
1991   // copy XCharStruct array
1992   int size = f->max_char_or_byte2 - f->min_char_or_byte2;
1993   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1994   memcpy (f->per_char, fid->metrics.per_char,
1995           size * sizeof (XCharStruct));
1996
1997   return f;
1998 }
1999
2000
2001 static Font
2002 copy_font (Font fid)
2003 {
2004   // copy 'Font' struct
2005   Font fid2 = (Font) malloc (sizeof(*fid2));
2006   *fid2 = *fid;
2007
2008   // copy XCharStruct array
2009   int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2010   fid2->metrics.per_char = (XCharStruct *) 
2011     malloc ((size + 2) * sizeof (XCharStruct));
2012   memcpy (fid2->metrics.per_char, fid->metrics.per_char, 
2013           size * sizeof (XCharStruct));
2014
2015   // copy the other pointers
2016   fid2->ps_name = strdup (fid->ps_name);
2017 //  [fid2->nsfont retain];
2018   fid2->metrics.fid = fid2;
2019
2020   return fid2;
2021 }
2022
2023
2024 static NSFont *
2025 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2026           char **name_ret)
2027 {
2028   Assert (size > 0, "zero font size");
2029   const char *name;
2030
2031   if (fixed) {
2032     // 
2033     // "Monaco" only exists in plain.
2034     // "LucidaSansTypewriterStd" gets an AGL bad value error.
2035     // 
2036     if (bold && ital) name = "Courier-BoldOblique";
2037     else if (bold)    name = "Courier-Bold";
2038     else if (ital)    name = "Courier-Oblique";
2039     else              name = "Courier";
2040
2041   } else if (serif) {
2042     // 
2043     // "Georgia" looks better than "Times".
2044     // 
2045     if (bold && ital) name = "Georgia-BoldItalic";
2046     else if (bold)    name = "Georgia-Bold";
2047     else if (ital)    name = "Georgia-Italic";
2048     else              name = "Georgia";
2049
2050   } else {
2051     // 
2052     // "Geneva" only exists in plain.
2053     // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2054     // "Verdana" renders smoother than "Helvetica" for some reason.
2055     // 
2056     if (bold && ital) name = "Verdana-BoldItalic";
2057     else if (bold)    name = "Verdana-Bold";
2058     else if (ital)    name = "Verdana-Italic";
2059     else              name = "Verdana";
2060   }
2061
2062   NSString *nsname = [NSString stringWithCString:name
2063                                         encoding:NSUTF8StringEncoding];
2064   NSFont *f = [NSFont fontWithName:nsname size:size];
2065   if (f)
2066     *name_ret = strdup(name);
2067   return f;
2068 }
2069
2070 static NSFont *
2071 try_native_font (const char *name, char **name_ret, float *size_ret)
2072 {
2073   if (!name) return 0;
2074   const char *spc = strrchr (name, ' ');
2075   if (!spc) return 0;
2076   int size = 0;
2077   if (1 != sscanf (spc, " %d ", &size)) return 0;
2078   if (size <= 4) return 0;
2079
2080   char *name2 = strdup (name);
2081   name2[strlen(name2) - strlen(spc)] = 0;
2082   NSString *nsname = [NSString stringWithCString:name2
2083                                         encoding:NSUTF8StringEncoding];
2084   NSFont *f = [NSFont fontWithName:nsname size:size];
2085   if (f) {
2086     *name_ret = name2;
2087     *size_ret = size;
2088     return f;
2089   } else {
2090     free (name2);
2091     return 0;
2092   }
2093 }
2094
2095
2096 /* Returns a random font in the given size and face.
2097  */
2098 static NSFont *
2099 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2100 {
2101   NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
2102                           (ital ? NSItalicFontMask : NSUnitalicFontMask));
2103   NSArray *fonts = [[NSFontManager sharedFontManager]
2104                      availableFontNamesWithTraits:mask];
2105   if (!fonts) return 0;
2106
2107   int n = [fonts count];
2108   if (n <= 0) return 0;
2109
2110   int j;
2111   for (j = 0; j < n; j++) {
2112     int i = random() % n;
2113     NSString *name = [fonts objectAtIndex:i];
2114     NSFont *f = [NSFont fontWithName:name size:size];
2115     if (!f) continue;
2116
2117     /* Don't use this font if it (probably) doesn't include ASCII characters.
2118      */
2119     NSStringEncoding enc = [f mostCompatibleStringEncoding];
2120     if (! (enc == NSUTF8StringEncoding ||
2121            enc == NSISOLatin1StringEncoding ||
2122            enc == NSNonLossyASCIIStringEncoding ||
2123            enc == NSISOLatin2StringEncoding ||
2124            enc == NSUnicodeStringEncoding ||
2125            enc == NSWindowsCP1250StringEncoding ||
2126            enc == NSWindowsCP1252StringEncoding ||
2127            enc == NSMacOSRomanStringEncoding)) {
2128       // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2129       continue;
2130     }
2131     // NSLog(@"using \"%@\": %d", name, enc);
2132
2133     *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2134     return f;
2135   }
2136
2137   // None of the fonts support ASCII?
2138   return 0;
2139 }
2140
2141
2142 static NSFont *
2143 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2144 {
2145   NSFont *nsfont = 0;
2146   BOOL bold  = NO;
2147   BOOL ital  = NO;
2148   BOOL fixed = NO;
2149   BOOL serif = NO;
2150   BOOL rand  = NO;
2151   float size = 0;
2152   char *ps_name = 0;
2153
2154   const char *s = (name ? name : "");
2155   while (*s) {
2156     while (*s && (*s == '*' || *s == '-'))
2157       s++;
2158     const char *s2 = s;
2159     while (*s2 && (*s2 != '*' && *s2 != '-'))
2160       s2++;
2161     
2162     int L = s2-s;
2163     if (s == s2)
2164       ;
2165 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2166     else if (CMP ("random"))   rand  = YES;
2167     else if (CMP ("bold"))     bold  = YES;
2168     else if (CMP ("i"))        ital  = YES;
2169     else if (CMP ("o"))        ital  = YES;
2170     else if (CMP ("courier"))  fixed = YES;
2171     else if (CMP ("fixed"))    fixed = YES;
2172     else if (CMP ("m"))        fixed = YES;
2173     else if (CMP ("times"))    serif = YES;
2174     else if (CMP ("6x10"))     fixed = YES, size = 8;
2175     else if (CMP ("6x10bold")) fixed = YES, size = 8,  bold = YES;
2176     else if (CMP ("9x15"))     fixed = YES, size = 12;
2177     else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2178     else if (CMP ("vga"))      fixed = YES, size = 12;
2179     else if (CMP ("console"))  fixed = YES, size = 12;
2180     else if (CMP ("gallant"))  fixed = YES, size = 12;
2181 # undef CMP
2182     else if (size == 0) {
2183       int n = 0;
2184       if (1 == sscanf (s, " %d ", &n))
2185         size = n / 10.0;
2186     }
2187
2188     s = s2;
2189   }
2190
2191   if (size < 6 || size > 1000)
2192     size = 12;
2193
2194   if (rand)
2195     nsfont   = random_font (bold, ital, size, &ps_name);
2196
2197   if (!nsfont)
2198     nsfont   = try_font (fixed, bold, ital, serif, size, &ps_name);
2199
2200   // if that didn't work, turn off attibutes until it does
2201   // (e.g., there is no "Monaco-Bold".)
2202   //
2203   if (!nsfont && serif) {
2204     serif = NO;
2205     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2206   }
2207   if (!nsfont && ital) {
2208     ital = NO;
2209     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2210   }
2211   if (!nsfont && bold) {
2212     bold = NO;
2213     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2214   }
2215   if (!nsfont && fixed) {
2216     fixed = NO;
2217     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2218   }
2219
2220   if (nsfont) {
2221     *name_ret = ps_name;
2222     *size_ret = size;
2223     return nsfont;
2224   } else {
2225     return 0;
2226   }
2227 }
2228
2229
2230 Font
2231 XLoadFont (Display *dpy, const char *name)
2232 {
2233   Font fid = (Font) calloc (1, sizeof(*fid));
2234
2235   fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2236   if (! fid->nsfont)
2237     fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2238   if (!fid->nsfont) {
2239     NSLog(@"no NSFont for \"%s\"", name);
2240     abort();
2241   }
2242
2243   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2244
2245   query_font (fid);
2246
2247   return fid;
2248 }
2249
2250
2251 /* This translates the NSFont into the numbers that aglUseFont() wants.
2252  */
2253 int
2254 jwxyz_font_info (Font f, int *size_ret, int *face_ret)
2255 {
2256   char *name = strdup (f->ps_name);
2257   char *dash = strchr (name, '-');
2258   int flags = 0;
2259   int size = f->size;
2260   if (dash) {
2261     // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
2262     if (strcasestr (dash, "bold"))    flags |= 1;
2263     if (strcasestr (dash, "italic"))  flags |= 2;
2264     if (strcasestr (dash, "oblique")) flags |= 2;
2265     *dash = 0;
2266   }
2267   NSString *nname = [NSString stringWithCString:name
2268                                        encoding:NSUTF8StringEncoding];
2269   ATSFontFamilyRef id =
2270     ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
2271
2272
2273   // WTF?  aglUseFont gets a BadValue if size is small!!
2274   if (size < 9) size = 9;
2275
2276   //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
2277   Assert (id >= 0, "no ATS font family");
2278
2279   *size_ret = size;
2280   *face_ret = flags;
2281   return id;
2282 }
2283
2284
2285 XFontStruct *
2286 XLoadQueryFont (Display *dpy, const char *name)
2287 {
2288   Font fid = XLoadFont (dpy, name);
2289   return XQueryFont (dpy, fid);
2290 }
2291
2292 int
2293 XUnloadFont (Display *dpy, Font fid)
2294 {
2295   free (fid->ps_name);
2296   free (fid->metrics.per_char);
2297
2298   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
2299   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
2300   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
2301   //      They're probably not very big...
2302   //
2303   //  [fid->nsfont release];
2304
2305   free (fid);
2306   return 0;
2307 }
2308
2309 int
2310 XFreeFontInfo (char **names, XFontStruct *info, int n)
2311 {
2312   int i;
2313   if (names) {
2314     for (i = 0; i < n; i++)
2315       if (names[i]) free (names[i]);
2316     free (names);
2317   }
2318   if (info) {
2319     for (i = 0; i < n; i++)
2320       if (info[i].per_char)
2321         free (info[i].per_char);
2322     free (info);
2323   }
2324   return 0;
2325 }
2326
2327 int
2328 XFreeFont (Display *dpy, XFontStruct *f)
2329 {
2330   Font fid = f->fid;
2331   XFreeFontInfo (0, f, 1);
2332   XUnloadFont (dpy, fid);
2333   return 0;
2334 }
2335
2336
2337 int
2338 XSetFont (Display *dpy, GC gc, Font fid)
2339 {
2340   if (gc->gcv.font)
2341     XUnloadFont (dpy, gc->gcv.font);
2342   gc->gcv.font = copy_font (fid);
2343   [gc->gcv.font->nsfont retain];
2344   return 0;
2345 }
2346
2347 int
2348 XTextExtents (XFontStruct *f, const char *s, int length,
2349               int *dir_ret, int *ascent_ret, int *descent_ret,
2350               XCharStruct *cs)
2351 {
2352   memset (cs, 0, sizeof(*cs));
2353   int i;
2354   for (i = 0; i < length; i++) {
2355     unsigned char c = (unsigned char) s[i];
2356     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2357       c = f->default_char;
2358     const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2359     if (i == 0) {
2360       *cs = *cc;
2361     } else {
2362       cs->ascent   = MAX (cs->ascent,   cc->ascent);
2363       cs->descent  = MAX (cs->descent,  cc->descent);
2364       cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2365       cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2366       cs->width   += cc->width;
2367     }
2368   }
2369   *dir_ret = 0;
2370   *ascent_ret  = f->ascent;
2371   *descent_ret = f->descent;
2372   return 0;
2373 }
2374
2375 int
2376 XTextWidth (XFontStruct *f, const char *s, int length)
2377 {
2378   int ascent, descent, dir;
2379   XCharStruct cs;
2380   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2381   return cs.width;
2382 }
2383
2384
2385 static void
2386 set_font (CGContextRef cgc, GC gc)
2387 {
2388   Font font = gc->gcv.font;
2389   if (! font) {
2390     font = XLoadFont (0, 0);
2391     gc->gcv.font = font;
2392     [gc->gcv.font->nsfont retain];
2393   }
2394   CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2395   CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2396 }
2397
2398
2399 static int
2400 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2401              const char  *str, int len, BOOL clear_background_p)
2402 {
2403   if (clear_background_p) {
2404     int ascent, descent, dir;
2405     XCharStruct cs;
2406     XTextExtents (&gc->gcv.font->metrics, str, len,
2407                   &dir, &ascent, &descent, &cs);
2408     draw_rect (dpy, d, gc,
2409                x + MIN (0, cs.lbearing),
2410                y - MAX (0, ascent),
2411                MAX (MAX (0, cs.rbearing) -
2412                     MIN (0, cs.lbearing),
2413                     cs.width),
2414                MAX (0, ascent) + MAX (0, descent),
2415                NO, YES);
2416   }
2417
2418   CGRect wr = d->frame;
2419
2420 # if 1
2421   /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2422      But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2423    */
2424
2425   push_fg_gc (d, gc, YES);
2426   set_font (d->cgc, gc);
2427
2428   CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2429   if (! gc->gcv.antialias_p)
2430     CGContextSetShouldAntialias (d->cgc, YES);  // always antialias text
2431   CGContextShowTextAtPoint (d->cgc,
2432                             wr.origin.x + x,
2433                             wr.origin.y + wr.size.height - y,
2434                             str, len);
2435   pop_gc (d, gc);
2436
2437 #else /* !0 */
2438
2439   /* The Cocoa way...
2440    */
2441
2442   unsigned long argb = gc->gcv.foreground;
2443   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2444   float a = ((argb >> 24) & 0xFF) / 255.0;
2445   float r = ((argb >> 16) & 0xFF) / 255.0;
2446   float g = ((argb >>  8) & 0xFF) / 255.0;
2447   float b = ((argb      ) & 0xFF) / 255.0;
2448   NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2449   NSDictionary *attr =
2450     [NSDictionary dictionaryWithObjectsAndKeys:
2451                     gc->gcv.font->nsfont, NSFontAttributeName,
2452                     fg, NSForegroundColorAttributeName,
2453                   nil];
2454   char *s2 = (char *) malloc (len + 1);
2455   strncpy (s2, str, len);
2456   s2[len] = 0;
2457   NSString *nsstr = [NSString stringWithCString:s2
2458                                          encoding:NSISOLatin1StringEncoding];
2459   free (s2);
2460   NSPoint pos;
2461   pos.x = wr.origin.x + x;
2462   pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2463   [nsstr drawAtPoint:pos withAttributes:attr];
2464
2465 #endif  /* 0 */
2466
2467   return 0;
2468 }
2469
2470
2471 int
2472 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2473              const char  *str, int len)
2474 {
2475   return draw_string (dpy, d, gc, x, y, str, len, NO);
2476 }
2477
2478 int
2479 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2480                   const char *str, int len)
2481 {
2482   return draw_string (dpy, d, gc, x, y, str, len, YES);
2483 }
2484
2485
2486 int
2487 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2488 {
2489   validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2490   gc->gcv.foreground = fg;
2491   return 0;
2492 }
2493
2494
2495 int
2496 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2497 {
2498   validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2499   gc->gcv.background = bg;
2500   return 0;
2501 }
2502
2503 int
2504 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2505 {
2506   gc->gcv.alpha_allowed_p = allowed;
2507   return 0;
2508 }
2509
2510 int
2511 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2512 {
2513   gc->gcv.antialias_p = antialias_p;
2514   return 0;
2515 }
2516
2517
2518 int
2519 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2520                     int line_style, int cap_style, int join_style)
2521 {
2522   gc->gcv.line_width = line_width;
2523   Assert (line_style == LineSolid, "only LineSolid implemented");
2524 //  gc->gcv.line_style = line_style;
2525   gc->gcv.cap_style = cap_style;
2526   gc->gcv.join_style = join_style;
2527   return 0;
2528 }
2529
2530 int
2531 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2532 {
2533   return 0;
2534 }
2535
2536 int
2537 XSetFunction (Display *dpy, GC gc, int which)
2538 {
2539   gc->gcv.function = which;
2540   return 0;
2541 }
2542
2543 int
2544 XSetSubwindowMode (Display *dpy, GC gc, int which)
2545 {
2546   gc->gcv.subwindow_mode = which;
2547   return 0;
2548 }
2549
2550 int
2551 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2552 {
2553   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2554
2555   if (gc->gcv.clip_mask) {
2556     XFreePixmap (dpy, gc->gcv.clip_mask);
2557     CGImageRelease (gc->clip_mask);
2558   }
2559
2560   gc->gcv.clip_mask = copy_pixmap (m);
2561   if (gc->gcv.clip_mask)
2562     gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2563   else
2564     gc->clip_mask = 0;
2565
2566   return 0;
2567 }
2568
2569 int
2570 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2571 {
2572   gc->gcv.clip_x_origin = x;
2573   gc->gcv.clip_y_origin = y;
2574   return 0;
2575 }
2576
2577
2578 Bool
2579 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2580                int *root_x_ret, int *root_y_ret, 
2581                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2582 {
2583   Assert (w->type == WINDOW, "not a window");
2584   NSWindow *nsw = [w->window.view window];
2585   NSPoint wpos;
2586   // get bottom left of window on screen, from bottom left
2587   wpos.x = wpos.y = 0;
2588   wpos = [nsw convertBaseToScreen:wpos];
2589   
2590   NSPoint vpos;
2591   // get bottom left of view on window, from bottom left
2592   vpos.x = vpos.y = 0;
2593   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2594
2595   // get bottom left of view on screen, from bottom left
2596   vpos.x += wpos.x;
2597   vpos.y += wpos.y;
2598   
2599   // get top left of view on screen, from bottom left
2600   vpos.y += w->frame.size.height;
2601   
2602   // get top left of view on screen, from top left
2603   NSArray *screens = [NSScreen screens];
2604   NSScreen *screen = (screens && [screens count] > 0
2605                       ? [screens objectAtIndex:0]
2606                       : [NSScreen mainScreen]);
2607   NSRect srect = [screen frame];
2608   vpos.y = srect.size.height - vpos.y;
2609   
2610   // get the mouse position on window, from bottom left
2611   NSEvent *e = [NSApp currentEvent];
2612   NSPoint p = [e locationInWindow];
2613   
2614   // get mouse position on screen, from bottom left
2615   p.x += wpos.x;
2616   p.y += wpos.y;
2617   
2618   // get mouse position on screen, from top left
2619   p.y = srect.size.height - p.y;
2620
2621   if (root_x_ret) *root_x_ret = (int) p.x;
2622   if (root_y_ret) *root_y_ret = (int) p.y;
2623   if (win_x_ret)  *win_x_ret  = (int) (p.x - vpos.x);
2624   if (win_y_ret)  *win_y_ret  = (int) (p.y - vpos.y);
2625   
2626   if (mask_ret)   *mask_ret   = 0;  // ####
2627   if (root_ret)   *root_ret   = 0;
2628   if (child_ret)  *child_ret  = 0;
2629   return True;
2630 }
2631
2632 Bool
2633 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2634                        int src_x, int src_y,
2635                        int *dest_x_ret, int *dest_y_ret,
2636                        Window *child_ret)
2637 {
2638   Assert (w->type == WINDOW, "not a window");
2639   NSWindow *nsw = [w->window.view window];
2640   NSPoint wpos;
2641   // get bottom left of window on screen, from bottom left
2642   wpos.x = wpos.y = 0;
2643   wpos = [nsw convertBaseToScreen:wpos];
2644   
2645   NSPoint vpos;
2646   // get bottom left of view on window, from bottom left
2647   vpos.x = vpos.y = 0;
2648   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2649
2650   // get bottom left of view on screen, from bottom left
2651   vpos.x += wpos.x;
2652   vpos.y += wpos.y;
2653   
2654   // get top left of view on screen, from bottom left
2655   vpos.y += w->frame.size.height;
2656   
2657   // get top left of view on screen, from top left
2658   NSArray *screens = [NSScreen screens];
2659   NSScreen *screen = (screens && [screens count] > 0
2660                       ? [screens objectAtIndex:0]
2661                       : [NSScreen mainScreen]);
2662   NSRect srect = [screen frame];
2663   vpos.y = srect.size.height - vpos.y;
2664   
2665   // point starts out relative to top left of view
2666   NSPoint p;
2667   p.x = src_x;
2668   p.y = src_y;
2669   
2670   // get point relative to top left of screen
2671   p.x += vpos.x;
2672   p.y += vpos.y;
2673
2674   *dest_x_ret = p.x;
2675   *dest_y_ret = p.y;
2676   if (child_ret)
2677     *child_ret = w;
2678   return True;
2679 }
2680
2681
2682 KeySym
2683 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2684 {
2685   return code;
2686 }
2687
2688 int
2689 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2690                XComposeStatus *xc)
2691 {
2692   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2693   char c = (char) ks;     // could be smarter about modifiers here...
2694   if (k_ret) *k_ret = ks;
2695   if (size > 0) buf[0] = c;
2696   if (size > 1) buf[1] = 0;
2697   return 0;
2698 }
2699
2700
2701 int
2702 XFlush (Display *dpy)
2703 {
2704   // Just let the event loop take care of this on its own schedule.
2705   return 0;
2706 }
2707
2708 int
2709 XSync (Display *dpy, Bool flush)
2710 {
2711   return XFlush (dpy);
2712 }
2713
2714
2715 // declared in utils/visual.h
2716 int
2717 has_writable_cells (Screen *s, Visual *v)
2718 {
2719   return 0;
2720 }
2721
2722 int
2723 visual_depth (Screen *s, Visual *v)
2724 {
2725   return 32;
2726 }
2727
2728 int
2729 visual_cells (Screen *s, Visual *v)
2730 {
2731   return 0xFFFFFF;
2732 }
2733
2734 int
2735 visual_class (Screen *s, Visual *v)
2736 {
2737   return TrueColor;
2738 }
2739
2740 // declared in utils/grabclient.h
2741 Bool
2742 use_subwindow_mode_p (Screen *screen, Window window)
2743 {
2744   return False;
2745 }