http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / OSX / jwxyz.m
1 /* xscreensaver, Copyright (c) 1991-2006 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   int first = 32;
1846   int last = 255;
1847
1848   XFontStruct *f = &fid->metrics;
1849   XCharStruct *min = &f->min_bounds;
1850   XCharStruct *max = &f->max_bounds;
1851
1852 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1853
1854   f->fid               = fid;
1855   f->min_char_or_byte2 = first;
1856   f->max_char_or_byte2 = last;
1857   f->default_char      = 'M';
1858   f->ascent            =  CEIL ([fid->nsfont ascender]);
1859   f->descent           = -CEIL ([fid->nsfont descender]);
1860
1861   min->width    = 255;  // set to smaller values in the loop
1862   min->ascent   = 255;
1863   min->descent  = 255;
1864   min->lbearing = 255;
1865   min->rbearing = 255;
1866
1867   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1868   int i;
1869
1870   NSBezierPath *bpath = [NSBezierPath bezierPath];
1871
1872   for (i = first; i <= last; i++) {
1873     unsigned char str[2];
1874     str[0] = i;
1875     str[1] = 0;
1876
1877     NSString *nsstr = [NSString stringWithCString:(char *) str
1878                                          encoding:NSISOLatin1StringEncoding];
1879
1880     /* I can't believe we have to go through this bullshit just to
1881        convert a 'char' to an NSGlyph!!
1882
1883        You might think that we could do
1884           NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
1885        but that doesn't work; my guess is that glyphWithName expects
1886        full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
1887      */
1888     NSGlyph glyph;
1889     {
1890       NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
1891       [ts setFont:fid->nsfont];
1892       NSLayoutManager *lm = [[NSLayoutManager alloc] init];
1893       NSTextContainer *tc = [[NSTextContainer alloc] init];
1894       [lm addTextContainer:tc];
1895       [tc release];     // lm retains tc
1896       [ts addLayoutManager:lm];
1897       [lm release];     // ts retains lm
1898       glyph = [lm glyphAtIndex:0];
1899       [ts release];
1900     }
1901
1902     /* Compute the bounding box and advancement by converting the glyph
1903        to a bezier path.  There appears to be *no other way* to find out
1904        the bounding box of a character: [NSFont boundingRectForGlyph] and
1905        [NSString sizeWithAttributes] both return an advancement-sized
1906        rectangle, not a rectangle completely enclosing the glyph's ink.
1907      */
1908     NSPoint advancement;
1909     NSRect bbox;
1910     advancement.x = advancement.y = 0;
1911     [bpath removeAllPoints];
1912     [bpath moveToPoint:advancement];
1913     [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
1914     advancement = [bpath currentPoint];
1915     bbox = [bpath bounds];
1916
1917     /* Now that we know the advancement and bounding box, we can compute
1918        the lbearing and rbearing.
1919      */
1920     XCharStruct *cs = &f->per_char[i-first];
1921
1922     cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
1923     cs->descent  = CEIL(-bbox.origin.y);
1924     cs->lbearing = CEIL (bbox.origin.x);
1925     cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
1926     cs->width    = CEIL (advancement.x);
1927
1928     Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
1929             "bbox w wrong");
1930     Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
1931             "bbox h wrong");
1932
1933     max->width    = MAX (max->width,    cs->width);
1934     max->ascent   = MAX (max->ascent,   cs->ascent);
1935     max->descent  = MAX (max->descent,  cs->descent);
1936     max->lbearing = MAX (max->lbearing, cs->lbearing);
1937     max->rbearing = MAX (max->rbearing, cs->rbearing);
1938
1939     min->width    = MIN (min->width,    cs->width);
1940     min->ascent   = MIN (min->ascent,   cs->ascent);
1941     min->descent  = MIN (min->descent,  cs->descent);
1942     min->lbearing = MIN (min->lbearing, cs->lbearing);
1943     min->rbearing = MIN (min->rbearing, cs->rbearing);
1944
1945 # undef CEIL
1946
1947 #if 0
1948     fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
1949                     " bb=%3d x %3d @ %3d %3d  adv=%3d %3d\n",
1950             i, i, cs->width, cs->lbearing, cs->rbearing, 
1951             cs->ascent, cs->descent,
1952             (int) bbox.size.width, (int) bbox.size.height,
1953             (int) bbox.origin.x, (int) bbox.origin.y,
1954             (int) advancement.x, (int) advancement.y);
1955 #endif
1956   }
1957
1958 }
1959
1960
1961 // Since 'Font' includes the metrics, this just makes a copy of that.
1962 //
1963 XFontStruct *
1964 XQueryFont (Display *dpy, Font fid)
1965 {
1966   // copy XFontStruct
1967   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
1968   *f = fid->metrics;
1969
1970   // copy XCharStruct array
1971   int size = f->max_char_or_byte2 - f->min_char_or_byte2;
1972   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1973   memcpy (f->per_char, fid->metrics.per_char,
1974           size * sizeof (XCharStruct));
1975
1976   return f;
1977 }
1978
1979
1980 static Font
1981 copy_font (Font fid)
1982 {
1983   // copy 'Font' struct
1984   Font fid2 = (Font) malloc (sizeof(*fid2));
1985   *fid2 = *fid;
1986
1987   // copy XCharStruct array
1988   int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
1989   fid2->metrics.per_char = (XCharStruct *) 
1990     malloc ((size + 2) * sizeof (XCharStruct));
1991   memcpy (fid2->metrics.per_char, fid->metrics.per_char, 
1992           size * sizeof (XCharStruct));
1993
1994   // copy the other pointers
1995   fid2->ps_name = strdup (fid->ps_name);
1996   [fid2->nsfont retain];
1997   fid2->metrics.fid = fid2;
1998
1999   return fid2;
2000 }
2001
2002
2003 static NSFont *
2004 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2005           char **name_ret)
2006 {
2007   const char *prefix = (fixed ? "Monaco" : (serif ? "Times" : "Helvetica"));
2008   const char *suffix = (bold && ital
2009                         ? (serif ? "-BoldItalic" : "-BoldOblique")
2010                         : (bold ? "-Bold" :
2011                            ital ? (serif ? "-Italic" : "-Oblique") : ""));
2012   char *name = (char *) malloc (strlen(prefix) + strlen(suffix) + 1);
2013   strcpy (name, prefix);
2014   strcat (name, suffix);
2015
2016   NSString *nsname = [NSString stringWithCString:name
2017                                         encoding:NSUTF8StringEncoding];
2018   NSFont *f = [NSFont fontWithName:nsname size:size];
2019   if (f) {
2020     *name_ret = name;
2021   } else {
2022     free (name);
2023   }
2024   return f;
2025 }
2026
2027 static NSFont *
2028 try_native_font (const char *name, char **name_ret, float *size_ret)
2029 {
2030   if (!name) return 0;
2031   const char *spc = strrchr (name, ' ');
2032   if (!spc) return 0;
2033   int size = 0;
2034   if (1 != sscanf (spc, " %d ", &size)) return 0;
2035   if (size <= 4) return 0;
2036
2037   char *name2 = strdup (name);
2038   name2[strlen(name2) - strlen(spc)] = 0;
2039   NSString *nsname = [NSString stringWithCString:name2
2040                                         encoding:NSUTF8StringEncoding];
2041   NSFont *f = [NSFont fontWithName:nsname size:size];
2042   if (f) {
2043     *name_ret = name2;
2044     *size_ret = size;
2045     return f;
2046   } else {
2047     free (name2);
2048     return 0;
2049   }
2050 }
2051
2052
2053 /* Returns a random font in the given size and face.
2054  */
2055 static NSFont *
2056 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2057 {
2058   NSFontTraitMask mask = ((bold ? NSUnboldFontMask   : NSBoldFontMask) |
2059                           (ital ? NSUnitalicFontMask : NSItalicFontMask));
2060   NSArray *fonts = [[NSFontManager sharedFontManager]
2061                      availableFontNamesWithTraits:mask];
2062   if (!fonts) return 0;
2063
2064   int n = [fonts count];
2065   if (n <= 0) return 0;
2066
2067   int j;
2068   for (j = 0; j < n; j++) {
2069     int i = random() % n;
2070     NSString *name = [fonts objectAtIndex:i];
2071     NSFont *f = [NSFont fontWithName:name size:size];
2072     if (!f) continue;
2073
2074     /* Don't use this font if it (probably) doesn't include ASCII characters.
2075      */
2076     NSStringEncoding enc = [f mostCompatibleStringEncoding];
2077     if (! (enc == NSUTF8StringEncoding ||
2078            enc == NSISOLatin1StringEncoding ||
2079            enc == NSNonLossyASCIIStringEncoding ||
2080            enc == NSISOLatin2StringEncoding ||
2081            enc == NSUnicodeStringEncoding ||
2082            enc == NSWindowsCP1250StringEncoding ||
2083            enc == NSWindowsCP1252StringEncoding ||
2084            enc == NSMacOSRomanStringEncoding)) {
2085       // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2086       continue;
2087     }
2088     // NSLog(@"using \"%@\": %d", name, enc);
2089
2090     *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2091     return f;
2092   }
2093
2094   // None of the fonts support ASCII?
2095   return 0;
2096 }
2097
2098
2099 static NSFont *
2100 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2101 {
2102   NSFont *nsfont = 0;
2103   BOOL bold  = NO;
2104   BOOL ital  = NO;
2105   BOOL fixed = NO;
2106   BOOL serif = NO;
2107   BOOL rand  = NO;
2108   float size = 0;
2109   char *ps_name = 0;
2110
2111   const char *s = (name ? name : "");
2112   while (*s) {
2113     while (*s && (*s == '*' || *s == '-'))
2114       s++;
2115     const char *s2 = s;
2116     while (*s2 && (*s2 != '*' && *s2 != '-'))
2117       s2++;
2118     
2119     int L = s2-s;
2120     if (s == s2)
2121       ;
2122 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2123     else if (CMP ("random"))   rand  = YES;
2124     else if (CMP ("bold"))     bold  = YES;
2125     else if (CMP ("i"))        ital  = YES;
2126     else if (CMP ("o"))        ital  = YES;
2127     else if (CMP ("courier"))  fixed = YES;
2128     else if (CMP ("fixed"))    fixed = YES;
2129     else if (CMP ("m"))        fixed = YES;
2130     else if (CMP ("times"))    serif = YES;
2131     else if (CMP ("6x10"))     fixed = YES, size = 8;
2132     else if (CMP ("6x10bold")) fixed = YES, size = 8,  bold = YES;
2133     else if (CMP ("9x15"))     fixed = YES, size = 12;
2134     else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2135     else if (CMP ("vga"))      fixed = YES, size = 12;
2136     else if (CMP ("console"))  fixed = YES, size = 12;
2137     else if (CMP ("gallant"))  fixed = YES, size = 12;
2138 # undef CMP
2139     else if (size == 0) {
2140       int n = 0;
2141       if (1 == sscanf (s, " %d ", &n))
2142         size = n / 10.0;
2143     }
2144
2145     s = s2;
2146   }
2147
2148   if (size < 6 || size > 1000)
2149     size = 12;
2150
2151   if (rand)
2152     nsfont   = random_font (bold, ital, size, &ps_name);
2153
2154   if (!nsfont)
2155     nsfont   = try_font (fixed, bold, ital, serif, size, &ps_name);
2156
2157   // if that didn't work, turn off attibutes until it does
2158   // (e.g., there is no "Monaco-Bold".)
2159   //
2160   if (!nsfont && serif) {
2161     serif = NO;
2162     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2163   }
2164   if (!nsfont && ital) {
2165     ital = NO;
2166     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2167   }
2168   if (!nsfont && bold) {
2169     bold = NO;
2170     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2171   }
2172   if (!nsfont && fixed) {
2173     fixed = NO;
2174     nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2175   }
2176
2177   if (nsfont) {
2178     *name_ret = ps_name;
2179     *size_ret = size;
2180     return nsfont;
2181   } else {
2182     return 0;
2183   }
2184 }
2185
2186
2187 Font
2188 XLoadFont (Display *dpy, const char *name)
2189 {
2190   Font fid = (Font) calloc (1, sizeof(*fid));
2191
2192   fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2193   if (! fid->nsfont)
2194     fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2195   if (!fid->nsfont) {
2196     NSLog(@"no NSFont for \"%s\"", name);
2197     abort();
2198   }
2199
2200   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2201
2202   query_font (fid);
2203
2204   return fid;
2205 }
2206
2207
2208 /* This translates the NSFont into the numbers that aglUseFont() wants.
2209  */
2210 int
2211 jwxyz_font_info (Font f, int *size_ret, int *face_ret)
2212 {
2213   char *name = strdup (f->ps_name);
2214   char *dash = strchr (name, '-');
2215   int flags = 0;
2216   int size = f->size;
2217   if (dash) {
2218     // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
2219     if (strcasestr (dash, "bold"))    flags |= 1;
2220     if (strcasestr (dash, "italic"))  flags |= 2;
2221     if (strcasestr (dash, "oblique")) flags |= 2;
2222     *dash = 0;
2223   }
2224   NSString *nname = [NSString stringWithCString:name
2225                                        encoding:NSUTF8StringEncoding];
2226   ATSFontFamilyRef id =
2227     ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
2228
2229
2230   // WTF?  aglUseFont gets a BadValue if size is small!!
2231   if (size < 9) size = 9;
2232
2233   //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
2234   Assert (id >= 0, "no ATS font family");
2235
2236   *size_ret = size;
2237   *face_ret = flags;
2238   return id;
2239 }
2240
2241
2242 XFontStruct *
2243 XLoadQueryFont (Display *dpy, const char *name)
2244 {
2245   Font fid = XLoadFont (dpy, name);
2246   return XQueryFont (dpy, fid);
2247 }
2248
2249 int
2250 XUnloadFont (Display *dpy, Font fid)
2251 {
2252   free (fid->ps_name);
2253   free (fid->metrics.per_char);
2254   [fid->nsfont release];
2255   free (fid);
2256   return 0;
2257 }
2258
2259 int
2260 XFreeFontInfo (char **names, XFontStruct *info, int n)
2261 {
2262   int i;
2263   if (names) {
2264     for (i = 0; i < n; i++)
2265       if (names[i]) free (names[i]);
2266     free (names);
2267   }
2268   if (info) {
2269     for (i = 0; i < n; i++)
2270       if (info[i].per_char)
2271         free (info[i].per_char);
2272     free (info);
2273   }
2274   return 0;
2275 }
2276
2277 int
2278 XFreeFont (Display *dpy, XFontStruct *f)
2279 {
2280   Font fid = f->fid;
2281   XFreeFontInfo (0, f, 1);
2282   XUnloadFont (dpy, fid);
2283   return 0;
2284 }
2285
2286
2287 int
2288 XSetFont (Display *dpy, GC gc, Font fid)
2289 {
2290   if (gc->gcv.font)
2291     XUnloadFont (dpy, gc->gcv.font);
2292   gc->gcv.font = copy_font (fid);
2293   return 0;
2294 }
2295
2296 Font  // really GContext
2297 XGContextFromGC (GC gc)    // WTF is this actually supposed to do?
2298 {
2299   return gc->gcv.font;
2300 }
2301
2302 int
2303 XTextExtents (XFontStruct *f, const char *s, int length,
2304               int *dir_ret, int *ascent_ret, int *descent_ret,
2305               XCharStruct *cs)
2306 {
2307   memset (cs, 0, sizeof(*cs));
2308   int i;
2309   for (i = 0; i < length; i++) {
2310     unsigned char c = (unsigned char) s[i];
2311     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2312       c = f->default_char;
2313     const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2314     if (i == 0) {
2315       *cs = *cc;
2316     } else {
2317       cs->ascent   = MAX (cs->ascent,   cc->ascent);
2318       cs->descent  = MAX (cs->descent,  cc->descent);
2319       cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2320       cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2321       cs->width   += cc->width;
2322     }
2323   }
2324   *dir_ret = 0;
2325   *ascent_ret  = f->ascent;
2326   *descent_ret = f->descent;
2327   return 0;
2328 }
2329
2330 int
2331 XTextWidth (XFontStruct *f, const char *s, int length)
2332 {
2333   int ascent, descent, dir;
2334   XCharStruct cs;
2335   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2336   return cs.width;
2337 }
2338
2339
2340 static void
2341 set_font (CGContextRef cgc, GC gc)
2342 {
2343   Font font = gc->gcv.font;
2344   if (! font)
2345     font = gc->gcv.font = XLoadFont (0, 0);
2346   CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2347   CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2348 }
2349
2350
2351 static int
2352 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2353              const char  *str, int len, BOOL clear_background_p)
2354 {
2355   if (clear_background_p) {
2356     int ascent, descent, dir;
2357     XCharStruct cs;
2358     XTextExtents (&gc->gcv.font->metrics, str, len,
2359                   &dir, &ascent, &descent, &cs);
2360     draw_rect (dpy, d, gc,
2361                x + MIN (0, cs.lbearing),
2362                y - MAX (0, ascent),
2363                MAX (MAX (0, cs.rbearing) -
2364                     MIN (0, cs.lbearing),
2365                     cs.width),
2366                MAX (0, ascent) + MAX (0, descent),
2367                NO, YES);
2368   }
2369
2370   CGRect wr = d->frame;
2371
2372 # if 1
2373   /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2374      But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2375    */
2376
2377   push_fg_gc (d, gc, YES);
2378   set_font (d->cgc, gc);
2379
2380   CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2381   if (! gc->gcv.antialias_p)
2382     CGContextSetShouldAntialias (d->cgc, YES);  // always antialias text
2383   CGContextShowTextAtPoint (d->cgc,
2384                             wr.origin.x + x,
2385                             wr.origin.y + wr.size.height - y,
2386                             str, len);
2387   pop_gc (d, gc);
2388
2389 #else /* !0 */
2390
2391   /* The Cocoa way...
2392    */
2393
2394   unsigned long argb = gc->gcv.foreground;
2395   if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2396   float a = ((argb >> 24) & 0xFF) / 255.0;
2397   float r = ((argb >> 16) & 0xFF) / 255.0;
2398   float g = ((argb >>  8) & 0xFF) / 255.0;
2399   float b = ((argb      ) & 0xFF) / 255.0;
2400   NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2401   NSDictionary *attr =
2402     [NSDictionary dictionaryWithObjectsAndKeys:
2403                     gc->gcv.font->nsfont, NSFontAttributeName,
2404                     fg, NSForegroundColorAttributeName,
2405                   nil];
2406   char *s2 = (char *) malloc (len + 1);
2407   strncpy (s2, str, len);
2408   s2[len] = 0;
2409   NSString *nsstr = [NSString stringWithCString:s2
2410                                          encoding:NSISOLatin1StringEncoding];
2411   free (s2);
2412   NSPoint pos;
2413   pos.x = wr.origin.x + x;
2414   pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2415   [nsstr drawAtPoint:pos withAttributes:attr];
2416
2417 #endif  /* 0 */
2418
2419   return 0;
2420 }
2421
2422
2423 int
2424 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2425              const char  *str, int len)
2426 {
2427   return draw_string (dpy, d, gc, x, y, str, len, NO);
2428 }
2429
2430 int
2431 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2432                   const char *str, int len)
2433 {
2434   return draw_string (dpy, d, gc, x, y, str, len, YES);
2435 }
2436
2437
2438 int
2439 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2440 {
2441   validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2442   gc->gcv.foreground = fg;
2443   return 0;
2444 }
2445
2446
2447 int
2448 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2449 {
2450   validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2451   gc->gcv.background = bg;
2452   return 0;
2453 }
2454
2455 int
2456 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2457 {
2458   gc->gcv.alpha_allowed_p = allowed;
2459   return 0;
2460 }
2461
2462 int
2463 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2464 {
2465   gc->gcv.antialias_p = antialias_p;
2466   return 0;
2467 }
2468
2469
2470 int
2471 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2472                     int line_style, int cap_style, int join_style)
2473 {
2474   gc->gcv.line_width = line_width;
2475   Assert (line_style == LineSolid, "only LineSolid implemented");
2476 //  gc->gcv.line_style = line_style;
2477   gc->gcv.cap_style = cap_style;
2478   gc->gcv.join_style = join_style;
2479   return 0;
2480 }
2481
2482 int
2483 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2484 {
2485   return 0;
2486 }
2487
2488 int
2489 XSetFunction (Display *dpy, GC gc, int which)
2490 {
2491   gc->gcv.function = which;
2492   return 0;
2493 }
2494
2495 int
2496 XSetSubwindowMode (Display *dpy, GC gc, int which)
2497 {
2498   gc->gcv.subwindow_mode = which;
2499   return 0;
2500 }
2501
2502 int
2503 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2504 {
2505   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2506
2507   if (gc->gcv.clip_mask) {
2508     XFreePixmap (dpy, gc->gcv.clip_mask);
2509     CGImageRelease (gc->clip_mask);
2510   }
2511
2512   gc->gcv.clip_mask = copy_pixmap (m);
2513   if (gc->gcv.clip_mask)
2514     gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2515   else
2516     gc->clip_mask = 0;
2517
2518   return 0;
2519 }
2520
2521 int
2522 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2523 {
2524   gc->gcv.clip_x_origin = x;
2525   gc->gcv.clip_y_origin = y;
2526   return 0;
2527 }
2528
2529
2530 Bool
2531 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2532                int *root_x_ret, int *root_y_ret, 
2533                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2534 {
2535   Assert (w->type == WINDOW, "not a window");
2536   NSWindow *nsw = [w->window.view window];
2537   NSPoint wpos;
2538   // get bottom left of window on screen, from bottom left
2539   wpos.x = wpos.y = 0;
2540   wpos = [nsw convertBaseToScreen:wpos];
2541   
2542   NSPoint vpos;
2543   // get bottom left of view on window, from bottom left
2544   vpos.x = vpos.y = 0;
2545   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2546
2547   // get bottom left of view on screen, from bottom left
2548   vpos.x += wpos.x;
2549   vpos.y += wpos.y;
2550   
2551   // get top left of view on screen, from bottom left
2552   vpos.y += w->frame.size.height;
2553   
2554   // get top left of view on screen, from top left
2555   NSArray *screens = [NSScreen screens];
2556   NSScreen *screen = (screens && [screens count] > 0
2557                       ? [screens objectAtIndex:0]
2558                       : [NSScreen mainScreen]);
2559   NSRect srect = [screen frame];
2560   vpos.y = srect.size.height - vpos.y;
2561   
2562   // get the mouse position on window, from bottom left
2563   NSEvent *e = [NSApp currentEvent];
2564   NSPoint p = [e locationInWindow];
2565   
2566   // get mouse position on screen, from bottom left
2567   p.x += wpos.x;
2568   p.y += wpos.y;
2569   
2570   // get mouse position on screen, from top left
2571   p.y = srect.size.height - p.y;
2572
2573   if (root_x_ret) *root_x_ret = (int) p.x;
2574   if (root_y_ret) *root_y_ret = (int) p.y;
2575   if (win_x_ret)  *win_x_ret  = (int) (p.x - vpos.x);
2576   if (win_y_ret)  *win_y_ret  = (int) (p.y - vpos.y);
2577   
2578   if (mask_ret)   *mask_ret   = 0;  // ####
2579   if (root_ret)   *root_ret   = 0;
2580   if (child_ret)  *child_ret  = 0;
2581   return True;
2582 }
2583
2584 Bool
2585 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2586                        int src_x, int src_y,
2587                        int *dest_x_ret, int *dest_y_ret,
2588                        Window *child_ret)
2589 {
2590   Assert (w->type == WINDOW, "not a window");
2591   NSWindow *nsw = [w->window.view window];
2592   NSPoint wpos;
2593   // get bottom left of window on screen, from bottom left
2594   wpos.x = wpos.y = 0;
2595   wpos = [nsw convertBaseToScreen:wpos];
2596   
2597   NSPoint vpos;
2598   // get bottom left of view on window, from bottom left
2599   vpos.x = vpos.y = 0;
2600   vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2601
2602   // get bottom left of view on screen, from bottom left
2603   vpos.x += wpos.x;
2604   vpos.y += wpos.y;
2605   
2606   // get top left of view on screen, from bottom left
2607   vpos.y += w->frame.size.height;
2608   
2609   // get top left of view on screen, from top left
2610   NSArray *screens = [NSScreen screens];
2611   NSScreen *screen = (screens && [screens count] > 0
2612                       ? [screens objectAtIndex:0]
2613                       : [NSScreen mainScreen]);
2614   NSRect srect = [screen frame];
2615   vpos.y = srect.size.height - vpos.y;
2616   
2617   // point starts out relative to top left of view
2618   NSPoint p;
2619   p.x = src_x;
2620   p.y = src_y;
2621   
2622   // get point relative to top left of screen
2623   p.x += vpos.x;
2624   p.y += vpos.y;
2625
2626   *dest_x_ret = p.x;
2627   *dest_y_ret = p.y;
2628   if (child_ret)
2629     *child_ret = w;
2630   return True;
2631 }
2632
2633
2634 KeySym
2635 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2636 {
2637   return code;
2638 }
2639
2640 int
2641 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2642                XComposeStatus *xc)
2643 {
2644   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2645   char c = (char) ks;     // could be smarter about modifiers here...
2646   if (k_ret) *k_ret = ks;
2647   if (size > 0) buf[0] = c;
2648   if (size > 1) buf[1] = 0;
2649   return 0;
2650 }
2651
2652
2653 int
2654 XFlush (Display *dpy)
2655 {
2656   // Just let the event loop take care of this on its own schedule.
2657   return 0;
2658 }
2659
2660 int
2661 XSync (Display *dpy, Bool flush)
2662 {
2663   return XFlush (dpy);
2664 }
2665
2666
2667 // declared in utils/visual.h
2668 int
2669 has_writable_cells (Screen *s, Visual *v)
2670 {
2671   return 0;
2672 }
2673
2674 int
2675 visual_depth (Screen *s, Visual *v)
2676 {
2677   return 32;
2678 }
2679
2680 int
2681 visual_cells (Screen *s, Visual *v)
2682 {
2683   return 0xFFFFFF;
2684 }
2685
2686 int
2687 visual_class (Screen *s, Visual *v)
2688 {
2689   return TrueColor;
2690 }
2691
2692 // declared in utils/grabclient.h
2693 Bool
2694 use_subwindow_mode_p (Screen *screen, Window window)
2695 {
2696   return False;
2697 }