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