1 /* xscreensaver, Copyright (c) 1991-2009 Jamie Zawinski <jwz@jwz.org>
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
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.
21 #import <Cocoa/Cocoa.h>
23 #import "jwxyz-timers.h"
26 #define Assert(C,S) do { \
34 # define MAX(a,b) ((a)>(b)?(a):(b))
35 # define MIN(a,b) ((a)<(b)?(a):(b))
38 struct jwxyz_Drawable {
39 enum { WINDOW, PIXMAP } type;
45 unsigned long background;
53 struct jwxyz_Display {
56 struct jwxyz_sources_data *timers_data;
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. */
62 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
63 our images with this to avoid translation
75 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
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.
90 jwxyz_make_display (void *nsview_arg)
92 NSView *view = (NSView *) nsview_arg;
95 Display *d = (Display *) calloc (1, sizeof(*d));
96 d->screen = (Screen *) calloc (1, sizeof(Screen));
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;
105 d->screen->visual = v;
107 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
110 Window w = (Window) calloc (1, sizeof(*w));
112 // kludge! this needs to be set late, so we do it in XClearWindow!
113 // w->cgc = [[[view window] graphicsContext] graphicsPort];
115 w->window.view = view;
116 CFRetain (w->window.view); // needed for garbage collection?
117 w->window.background = BlackPixel(0,0);
122 w->cgc = [[[view window] graphicsContext] graphicsPort];
125 jwxyz_window_resized (d, w);
131 jwxyz_free_display (Display *dpy)
133 jwxyz_XtRemoveInput_all (dpy);
134 // #### jwxyz_XtRemoveTimeOut_all ();
136 free (dpy->screen->visual);
138 free (dpy->main_window);
144 jwxyz_window_view (Window w)
146 Assert (w->type == WINDOW, "not a window");
147 return w->window.view;
150 /* Call this when the View changes size or position.
153 jwxyz_window_resized (Display *dpy, Window w)
155 Assert (w->type == WINDOW, "not a window");
156 NSRect r = [w->window.view frame];
157 w->frame.origin.x = r.origin.x; // NSRect -> CGRect
158 w->frame.origin.y = r.origin.y;
159 w->frame.size.width = r.size.width;
160 w->frame.size.height = r.size.height;
162 // Figure out which screen the window is currently on.
165 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
171 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
172 Assert (dpy->cgdpy, "unable to find CGDisplay");
177 // Figure out this screen's colorspace, and use that for every CGImage.
179 CMProfileRef profile = 0;
180 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
181 Assert (profile, "unable to find colorspace profile");
182 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
183 Assert (dpy->colorspace, "unable to find colorspace");
187 // WTF? It's faster if we *do not* use the screen's colorspace!
189 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
195 display_sources_data (Display *dpy)
197 return dpy->timers_data;
202 XRootWindow (Display *dpy, int screen)
204 return dpy->main_window;
208 XDefaultScreenOfDisplay (Display *dpy)
214 XDefaultVisualOfScreen (Screen *screen)
216 return screen->visual;
220 XDisplayOfScreen (Screen *s)
226 XDisplayNumberOfScreen (Screen *s)
232 XScreenNumberOfScreen (Screen *s)
238 XDisplayWidth (Display *dpy, int screen)
240 return (int) dpy->main_window->frame.size.width;
244 XDisplayHeight (Display *dpy, int screen)
246 return (int) dpy->main_window->frame.size.height;
251 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
254 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
255 else if (!alpha_allowed_p)
256 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
257 "bogus color pixel");
262 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
263 BOOL alpha_allowed_p, BOOL fill_p)
265 validate_pixel (argb, depth, alpha_allowed_p);
268 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
270 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
272 float a = ((argb >> 24) & 0xFF) / 255.0;
273 float r = ((argb >> 16) & 0xFF) / 255.0;
274 float g = ((argb >> 8) & 0xFF) / 255.0;
275 float b = ((argb ) & 0xFF) / 255.0;
277 CGContextSetRGBFillColor (cgc, r, g, b, a);
279 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
284 set_line_mode (CGContextRef cgc, XGCValues *gcv)
286 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
287 CGContextSetLineJoin (cgc,
288 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
289 gcv->join_style == JoinRound ? kCGLineJoinRound :
291 CGContextSetLineCap (cgc,
292 gcv->cap_style == CapNotLast ? kCGLineCapButt :
293 gcv->cap_style == CapButt ? kCGLineCapButt :
294 gcv->cap_style == CapRound ? kCGLineCapRound :
299 set_clip_mask (Drawable d, GC gc)
301 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
303 Pixmap p = gc->gcv.clip_mask;
305 Assert (p->type == PIXMAP, "not a pixmap");
307 CGRect wr = d->frame;
309 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
310 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
311 - p->frame.size.height;
312 to.size.width = p->frame.size.width;
313 to.size.height = p->frame.size.height;
315 CGContextClipToMask (d->cgc, to, gc->clip_mask);
319 /* Pushes a GC context; sets BlendMode and ClipMask.
322 push_gc (Drawable d, GC gc)
324 CGContextRef cgc = d->cgc;
325 CGContextSaveGState (cgc);
327 switch (gc->gcv.function) {
330 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
331 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
332 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
333 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
334 default: abort(); break;
337 if (gc->gcv.clip_mask)
338 set_clip_mask (d, gc);
341 #define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
344 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
347 push_color_gc (Drawable d, GC gc, unsigned long color,
348 BOOL antialias_p, Bool fill_p)
352 int depth = gc->depth;
353 switch (gc->gcv.function) {
354 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
355 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
358 CGContextRef cgc = d->cgc;
360 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
361 CGContextSetShouldAntialias (cgc, antialias_p);
365 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
368 push_fg_gc (Drawable d, GC gc, Bool fill_p)
370 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
373 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
376 push_bg_gc (Drawable d, GC gc, Bool fill_p)
378 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
383 /* You've got to be fucking kidding me!
385 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
386 with repeated calls to CGContextDrawImage than it is to make a single
387 call to CGContextFillRects()!
389 I still wouldn't call it *fast*, however...
391 #define XDRAWPOINTS_IMAGES
394 XDrawPoints (Display *dpy, Drawable d, GC gc,
395 XPoint *points, int count, int mode)
398 CGRect wr = d->frame;
400 push_fg_gc (d, gc, YES);
402 # ifdef XDRAWPOINTS_IMAGES
404 unsigned int argb = gc->gcv.foreground;
405 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
407 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
409 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
410 CGImageRef cgi = CGImageCreate (1, 1,
413 /* Host-ordered, since we're using the
414 address of an int as the color data. */
415 (kCGImageAlphaNoneSkipFirst |
416 kCGBitmapByteOrder32Host),
419 NO, /* interpolate */
420 kCGRenderingIntentDefault);
421 CGDataProviderRelease (prov);
423 CGContextRef cgc = d->cgc;
425 rect.size.width = rect.size.height = 1;
426 for (i = 0; i < count; i++) {
427 if (i > 0 && mode == CoordModePrevious) {
428 rect.origin.x += points->x;
429 rect.origin.x -= points->y;
431 rect.origin.x = wr.origin.x + points->x;
432 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
435 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
436 CGContextDrawImage (cgc, rect, cgi);
440 CGImageRelease (cgi);
442 # else /* ! XDRAWPOINTS_IMAGES */
444 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
447 for (i = 0; i < count; i++) {
448 r->size.width = r->size.height = 1;
449 if (i > 0 && mode == CoordModePrevious) {
450 r->origin.x = r[-1].origin.x + points->x;
451 r->origin.y = r[-1].origin.x - points->y;
453 r->origin.x = wr.origin.x + points->x;
454 r->origin.y = wr.origin.y + wr.size.height - points->y;
460 CGContextFillRects (d->cgc, rects, count);
463 # endif /* ! XDRAWPOINTS_IMAGES */
472 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
477 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
481 static void draw_rect (Display *, Drawable, GC,
482 int x, int y, unsigned int width, unsigned int height,
483 BOOL foreground_p, BOOL fill_p);
486 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
487 int src_x, int src_y,
488 unsigned int width, unsigned int height,
489 int dst_x, int dst_y)
491 Assert ((width < 65535), "improbably large width");
492 Assert ((height < 65535), "improbably large height");
493 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
494 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
495 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
496 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
498 if (width == 0 || height == 0)
501 if (gc && (gc->gcv.function == GXset ||
502 gc->gcv.function == GXclear)) {
503 // "set" and "clear" are dumb drawing modes that ignore the source
504 // bits and just draw solid rectangles.
505 set_color (dst->cgc, (gc->gcv.function == GXset
506 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
507 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
508 gc->depth, gc->gcv.alpha_allowed_p, YES);
509 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
513 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
514 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
515 // bounds of their drawables.
516 BOOL clipped = NO; // Whether we did any clipping of the rects.
518 src_frame = src->frame;
519 dst_frame = dst->frame;
521 // Initialize src_rect...
523 src_rect.origin.x = src_frame.origin.x + src_x;
524 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
526 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
527 src_rect.size.width = width;
528 src_rect.size.height = height;
530 // Initialize dst_rect...
532 dst_rect.origin.x = dst_frame.origin.x + dst_x;
533 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
535 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
536 dst_rect.size.width = width;
537 dst_rect.size.height = height;
539 // Clip rects to frames...
541 // CGRect orig_src_rect = src_rect;
542 CGRect orig_dst_rect = dst_rect;
544 # define CLIP(THIS,THAT,VAL,SIZE) do { \
545 float off = THIS##_rect.origin.VAL; \
548 THIS##_rect.size.SIZE += off; \
549 THAT##_rect.size.SIZE += off; \
550 THIS##_rect.origin.VAL -= off; \
551 THAT##_rect.origin.VAL -= off; \
553 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
554 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
557 THIS##_rect.size.SIZE -= off; \
558 THAT##_rect.size.SIZE -= off; \
561 CLIP (dst, src, x, width);
562 CLIP (dst, src, y, height);
563 CLIP (src, dst, x, width);
564 CLIP (src, dst, y, height);
568 Assert (src_rect.size.width == dst_rect.size.width, "width out of sync");
569 Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
570 Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
571 Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
572 Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
573 Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
574 Assert (src_rect.origin.x + src_rect.size.width <=
575 src_frame.origin.x + src_frame.size.width, "clip failed src_width");
578 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
581 NSObject *releaseme = 0;
585 if (src->type == PIXMAP) {
587 // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
588 // but it presumably shares the data pointer instead of copying it.
589 cgi = CGBitmapContextCreateImage (src->cgc);
591 // if doing a sub-rect, trim it down.
592 if (src_rect.origin.x != src_frame.origin.x ||
593 src_rect.origin.y != src_frame.origin.y ||
594 src_rect.size.width != src_frame.size.width ||
595 src_rect.size.height != src_frame.size.height) {
596 // #### I don't understand why this is needed...
597 src_rect.origin.y = (src_frame.size.height -
598 src_rect.size.height - src_rect.origin.y);
599 // This does not copy image data, so it should be fast.
600 CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
601 CGImageRelease (cgi);
605 if (src->pixmap.depth == 1)
608 } else { /* (src->type == WINDOW) */
611 nsfrom.origin.x = src_rect.origin.x;
612 nsfrom.origin.y = src_rect.origin.y;
613 nsfrom.size.width = src_rect.size.width;
614 nsfrom.size.height = src_rect.size.height;
617 // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
619 NSBitmapImageRep *bm = [NSBitmapImageRep alloc];
620 [bm initWithFocusedViewRect:nsfrom];
621 unsigned char *data = [bm bitmapData];
622 int bps = [bm bitsPerSample];
623 int bpp = [bm bitsPerPixel];
624 int bpl = [bm bytesPerRow];
629 // QuickDraw way (doesn't work, need NSQuickDrawView)
630 PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
631 char **data = GetPortPixMap (pix);
634 int bpl = GetPixRowBytes (pix) & 0x3FFF;
638 // get the bits (desired sub-rectangle) out of the raw frame buffer.
639 // (This renders wrong, and appears to be even slower anyway.)
641 int window_x, window_y;
642 XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
643 window_x += nsfrom.origin.x;
644 window_y += (dst->frame.size.height
645 - (nsfrom.origin.y + nsfrom.size.height));
647 unsigned char *data = (unsigned char *)
648 CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
649 int bps = CGDisplayBitsPerSample (dpy->cgdpy);
650 int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
651 int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
655 // create a CGImage from those bits
657 CGDataProviderRef prov =
658 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
660 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
663 /* Use whatever default bit ordering we got from
664 initWithFocusedViewRect. I would have assumed
665 that it was (kCGImageAlphaNoneSkipFirst |
666 kCGBitmapByteOrder32Host), but on Intel,
672 NO, /* interpolate */
673 kCGRenderingIntentDefault);
674 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
675 CGDataProviderRelease (prov);
678 if (mask_p) { // src depth == 1
680 push_bg_gc (dst, gc, YES);
682 // fill the destination rectangle with solid background...
683 CGContextFillRect (dst->cgc, orig_dst_rect);
685 // then fill in a solid rectangle of the fg color, using the image as an
686 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
687 set_color (dst->cgc, gc->gcv.foreground, gc->depth,
688 gc->gcv.alpha_allowed_p, YES);
689 CGContextClipToMask (dst->cgc, dst_rect, cgi);
690 CGContextFillRect (dst->cgc, dst_rect);
694 } else { // src depth > 1
698 // If either the src or dst rects did not lie within their drawables,
699 // then we have adjusted both the src and dst rects to account for
700 // the clipping; that means we need to first clear to the background,
701 // so that clipped bits end up in the bg color instead of simply not
705 set_color (dst->cgc, gc->gcv.background, gc->depth,
706 gc->gcv.alpha_allowed_p, YES);
707 CGContextFillRect (dst->cgc, orig_dst_rect);
710 // copy the CGImage onto the destination CGContext
711 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
712 CGContextDrawImage (dst->cgc, dst_rect, cgi);
717 CGImageRelease (cgi);
718 if (releaseme) [releaseme release];
724 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
725 int src_x, int src_y,
726 unsigned width, int height,
727 int dest_x, int dest_y, unsigned long plane)
729 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
731 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
732 // not to white/black.
733 return XCopyArea (dpy, src, dest, gc,
734 src_x, src_y, width, height, dest_x, dest_y);
739 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
741 // when drawing a zero-length line, obey line-width and cap-style.
742 if (x1 == x2 && y1 == y2) {
743 int w = gc->gcv.line_width;
746 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
747 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
749 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
752 CGRect wr = d->frame;
754 p.x = wr.origin.x + x1;
755 p.y = wr.origin.y + wr.size.height - y1;
757 push_fg_gc (d, gc, NO);
759 set_line_mode (d->cgc, &gc->gcv);
760 CGContextBeginPath (d->cgc);
761 CGContextMoveToPoint (d->cgc, p.x, p.y);
762 p.x = wr.origin.x + x2;
763 p.y = wr.origin.y + wr.size.height - y2;
764 CGContextAddLineToPoint (d->cgc, p.x, p.y);
765 CGContextStrokePath (d->cgc);
771 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
776 CGRect wr = d->frame;
777 push_fg_gc (d, gc, NO);
778 set_line_mode (d->cgc, &gc->gcv);
780 // if the first and last points coincide, use closepath to get
781 // the proper line-joining.
782 BOOL closed_p = (points[0].x == points[count-1].x &&
783 points[0].y == points[count-1].y);
784 if (closed_p) count--;
786 p.x = wr.origin.x + points->x;
787 p.y = wr.origin.y + wr.size.height - points->y;
789 CGContextBeginPath (d->cgc);
790 CGContextMoveToPoint (d->cgc, p.x, p.y);
791 for (i = 1; i < count; i++) {
792 if (mode == CoordModePrevious) {
796 p.x = wr.origin.x + points->x;
797 p.y = wr.origin.y + wr.size.height - points->y;
799 CGContextAddLineToPoint (d->cgc, p.x, p.y);
802 if (closed_p) CGContextClosePath (d->cgc);
803 CGContextStrokePath (d->cgc);
810 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
813 CGRect wr = d->frame;
815 push_fg_gc (d, gc, NO);
816 set_line_mode (d->cgc, &gc->gcv);
817 CGContextBeginPath (d->cgc);
818 for (i = 0; i < count; i++) {
819 CGContextMoveToPoint (d->cgc,
820 wr.origin.x + segments->x1,
821 wr.origin.y + wr.size.height - segments->y1);
822 CGContextAddLineToPoint (d->cgc,
823 wr.origin.x + segments->x2,
824 wr.origin.y + wr.size.height - segments->y2);
827 CGContextStrokePath (d->cgc);
834 XClearWindow (Display *dpy, Window win)
836 Assert (win->type == WINDOW, "not a window");
837 CGRect wr = win->frame;
838 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
842 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
844 Assert (w->type == WINDOW, "not a window");
845 validate_pixel (pixel, 32, NO);
846 w->window.background = pixel;
851 draw_rect (Display *dpy, Drawable d, GC gc,
852 int x, int y, unsigned int width, unsigned int height,
853 BOOL foreground_p, BOOL fill_p)
855 CGRect wr = d->frame;
857 r.origin.x = wr.origin.x + x;
858 r.origin.y = wr.origin.y + wr.size.height - y - height;
859 r.size.width = width;
860 r.size.height = height;
864 push_fg_gc (d, gc, fill_p);
866 push_bg_gc (d, gc, fill_p);
870 CGContextFillRect (d->cgc, r);
873 set_line_mode (d->cgc, &gc->gcv);
874 CGContextStrokeRect (d->cgc, r);
883 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
884 unsigned int width, unsigned int height)
886 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
891 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
892 unsigned int width, unsigned int height)
894 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
899 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
901 CGRect wr = d->frame;
903 push_fg_gc (d, gc, YES);
904 for (i = 0; i < n; i++) {
906 r.origin.x = wr.origin.x + rects->x;
907 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
908 r.size.width = rects->width;
909 r.size.height = rects->height;
910 CGContextFillRect (d->cgc, r);
919 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
921 Assert (win->type == WINDOW, "not a window");
922 set_color (win->cgc, win->window.background, 32, NO, YES);
923 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
929 XFillPolygon (Display *dpy, Drawable d, GC gc,
930 XPoint *points, int npoints, int shape, int mode)
932 CGRect wr = d->frame;
934 push_fg_gc (d, gc, YES);
935 CGContextBeginPath (d->cgc);
936 for (i = 0; i < npoints; i++) {
938 if (i > 0 && mode == CoordModePrevious) {
942 x = wr.origin.x + points[i].x;
943 y = wr.origin.y + wr.size.height - points[i].y;
947 CGContextMoveToPoint (d->cgc, x, y);
949 CGContextAddLineToPoint (d->cgc, x, y);
951 CGContextClosePath (d->cgc);
952 if (gc->gcv.fill_rule == EvenOddRule)
953 CGContextEOFillPath (d->cgc);
955 CGContextFillPath (d->cgc);
960 #define radians(DEG) ((DEG) * M_PI / 180.0)
961 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
964 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
965 unsigned int width, unsigned int height, int angle1, int angle2,
968 CGRect wr = d->frame;
970 bound.origin.x = wr.origin.x + x;
971 bound.origin.y = wr.origin.y + wr.size.height - y - height;
972 bound.size.width = width;
973 bound.size.height = height;
976 ctr.x = bound.origin.x + bound.size.width /2;
977 ctr.y = bound.origin.y + bound.size.height/2;
979 float r1 = radians (angle1/64.0);
980 float r2 = radians (angle2/64.0) + r1;
981 BOOL clockwise = angle2 < 0;
982 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
984 push_fg_gc (d, gc, fill_p);
986 CGContextBeginPath (d->cgc);
988 CGContextSaveGState(d->cgc);
989 CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
990 CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
992 CGContextMoveToPoint (d->cgc, 0, 0);
994 CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
995 CGContextRestoreGState (d->cgc); // restore before stroke, for line width
998 CGContextClosePath (d->cgc); // for proper line joining
1001 CGContextFillPath (d->cgc);
1003 set_line_mode (d->cgc, &gc->gcv);
1004 CGContextStrokePath (d->cgc);
1012 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1013 unsigned int width, unsigned int height, int angle1, int angle2)
1015 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1019 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1020 unsigned int width, unsigned int height, int angle1, int angle2)
1022 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1026 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1029 for (i = 0; i < narcs; i++)
1030 draw_arc (dpy, d, gc,
1031 arcs[i].x, arcs[i].y,
1032 arcs[i].width, arcs[i].height,
1033 arcs[i].angle1, arcs[i].angle2,
1039 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1042 for (i = 0; i < narcs; i++)
1043 draw_arc (dpy, d, gc,
1044 arcs[i].x, arcs[i].y,
1045 arcs[i].width, arcs[i].height,
1046 arcs[i].angle1, arcs[i].angle2,
1053 gcv_defaults (XGCValues *gcv, int depth)
1055 memset (gcv, 0, sizeof(*gcv));
1056 gcv->function = GXcopy;
1057 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1058 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1059 gcv->line_width = 1;
1060 gcv->cap_style = CapNotLast;
1061 gcv->join_style = JoinMiter;
1062 gcv->fill_rule = EvenOddRule;
1064 gcv->alpha_allowed_p = NO;
1065 gcv->antialias_p = YES;
1069 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1071 if (mask & GCFunction) gc->gcv.function = from->function;
1072 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1073 if (mask & GCBackground) gc->gcv.background = from->background;
1074 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1075 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1076 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1077 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1078 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1079 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1080 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1082 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1083 if (mask & GCFont) XSetFont (0, gc, from->font);
1085 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1086 gc->gcv.alpha_allowed_p);
1087 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1088 gc->gcv.alpha_allowed_p);
1090 if (mask & GCLineStyle) abort();
1091 if (mask & GCPlaneMask) abort();
1092 if (mask & GCFillStyle) abort();
1093 if (mask & GCTile) abort();
1094 if (mask & GCStipple) abort();
1095 if (mask & GCTileStipXOrigin) abort();
1096 if (mask & GCTileStipYOrigin) abort();
1097 if (mask & GCGraphicsExposures) abort();
1098 if (mask & GCDashOffset) abort();
1099 if (mask & GCDashList) abort();
1100 if (mask & GCArcMode) abort();
1105 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1107 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1108 if (d->type == WINDOW) {
1110 } else { /* (d->type == PIXMAP) */
1111 gc->depth = d->pixmap.depth;
1114 gcv_defaults (&gc->gcv, gc->depth);
1115 set_gcv (gc, xgcv, mask);
1120 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1122 set_gcv (gc, gcv, mask);
1128 XFreeGC (Display *dpy, GC gc)
1131 XUnloadFont (dpy, gc->gcv.font);
1133 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1135 if (gc->gcv.clip_mask) {
1136 XFreePixmap (dpy, gc->gcv.clip_mask);
1137 CGImageRelease (gc->clip_mask);
1145 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1147 Assert (w->type == WINDOW, "not a window");
1148 memset (xgwa, 0, sizeof(*xgwa));
1149 xgwa->x = w->frame.origin.x;
1150 xgwa->y = w->frame.origin.y;
1151 xgwa->width = w->frame.size.width;
1152 xgwa->height = w->frame.size.height;
1154 xgwa->screen = dpy->screen;
1155 xgwa->visual = dpy->screen->visual;
1160 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1161 int *x_ret, int *y_ret,
1162 unsigned int *w_ret, unsigned int *h_ret,
1163 unsigned int *bw_ret, unsigned int *d_ret)
1165 *x_ret = d->frame.origin.x;
1166 *y_ret = d->frame.origin.y;
1167 *w_ret = d->frame.size.width;
1168 *h_ret = d->frame.size.height;
1169 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1170 *root_ret = RootWindow (dpy, 0);
1177 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1179 // store 32 bit ARGB in the pixel field.
1180 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1181 color->pixel = (uint32_t)
1183 (((color->red >> 8) & 0xFF) << 16) |
1184 (((color->green >> 8) & 0xFF) << 8) |
1185 (((color->blue >> 8) & 0xFF) ));
1190 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1191 unsigned long *pmret, unsigned int npl,
1192 unsigned long *pxret, unsigned int npx)
1198 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1200 Assert(0, "XStoreColors called");
1205 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1207 Assert(0, "XStoreColor called");
1212 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1213 unsigned long planes)
1219 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1221 unsigned char r=0, g=0, b=0;
1222 if (*spec == '#' && strlen(spec) == 7) {
1223 static unsigned const char hex[] = { // yeah yeah, shoot me.
1224 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1225 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,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,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,
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 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};
1232 r = (hex[spec[1]] << 4) | hex[spec[2]];
1233 g = (hex[spec[3]] << 4) | hex[spec[4]];
1234 b = (hex[spec[5]] << 4) | hex[spec[6]];
1235 } else if (!strcasecmp(spec,"black")) {
1237 } else if (!strcasecmp(spec,"white")) {
1239 } else if (!strcasecmp(spec,"red")) {
1241 } else if (!strcasecmp(spec,"green")) {
1243 } else if (!strcasecmp(spec,"blue")) {
1245 } else if (!strcasecmp(spec,"cyan")) {
1247 } else if (!strcasecmp(spec,"magenta")) {
1249 } else if (!strcasecmp(spec,"yellow")) {
1255 ret->red = (r << 8) | r;
1256 ret->green = (g << 8) | g;
1257 ret->blue = (b << 8) | b;
1258 ret->flags = DoRed|DoGreen|DoBlue;
1263 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1264 XColor *screen_ret, XColor *exact_ret)
1266 if (! XParseColor (dpy, cmap, name, screen_ret))
1268 *exact_ret = *screen_ret;
1269 return XAllocColor (dpy, cmap, screen_ret);
1273 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1275 validate_pixel (color->pixel, 32, NO);
1276 unsigned char r = ((color->pixel >> 16) & 0xFF);
1277 unsigned char g = ((color->pixel >> 8) & 0xFF);
1278 unsigned char b = ((color->pixel ) & 0xFF);
1279 color->red = (r << 8) | r;
1280 color->green = (g << 8) | g;
1281 color->blue = (b << 8) | b;
1282 color->flags = DoRed|DoGreen|DoBlue;
1287 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1290 for (i = 0; i < n; i++)
1291 XQueryColor (dpy, cmap, &c[i]);
1296 static unsigned long
1297 ximage_getpixel_1 (XImage *ximage, int x, int y)
1299 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1303 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1306 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1308 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1313 static unsigned long
1314 ximage_getpixel_32 (XImage *ximage, int x, int y)
1316 return ((unsigned long)
1317 *((uint32_t *) ximage->data +
1318 (y * (ximage->bytes_per_line >> 2)) +
1323 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1325 *((uint32_t *) ximage->data +
1326 (y * (ximage->bytes_per_line >> 2)) +
1327 x) = (uint32_t) pixel;
1333 XInitImage (XImage *ximage)
1335 if (!ximage->bytes_per_line)
1336 ximage->bytes_per_line = (ximage->depth == 1
1337 ? (ximage->width + 7) / 8
1338 : ximage->width * 4);
1340 if (ximage->depth == 1) {
1341 ximage->f.put_pixel = ximage_putpixel_1;
1342 ximage->f.get_pixel = ximage_getpixel_1;
1343 } else if (ximage->depth == 32 || ximage->depth == 24) {
1344 ximage->f.put_pixel = ximage_putpixel_32;
1345 ximage->f.get_pixel = ximage_getpixel_32;
1354 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1355 int format, int offset, char *data,
1356 unsigned int width, unsigned int height,
1357 int bitmap_pad, int bytes_per_line)
1359 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1360 ximage->width = width;
1361 ximage->height = height;
1362 ximage->format = format;
1363 ximage->data = data;
1364 ximage->bitmap_unit = 8;
1365 ximage->byte_order = MSBFirst;
1366 ximage->bitmap_bit_order = ximage->byte_order;
1367 ximage->bitmap_pad = bitmap_pad;
1368 ximage->depth = depth;
1369 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1370 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1371 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1372 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1373 ximage->bytes_per_line = bytes_per_line;
1375 XInitImage (ximage);
1380 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1382 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1383 w, h, from->bitmap_pad, 0);
1384 to->data = (char *) malloc (h * to->bytes_per_line);
1386 if (x >= from->width)
1388 else if (x+w > from->width)
1389 w = from->width - x;
1391 if (y >= from->height)
1393 else if (y+h > from->height)
1394 h = from->height - y;
1397 for (ty = 0; ty < h; ty++)
1398 for (tx = 0; tx < w; tx++)
1399 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1404 XPixmapFormatValues *
1405 XListPixmapFormats (Display *dpy, int *n_ret)
1407 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1409 ret[0].bits_per_pixel = 32;
1410 ret[0].scanline_pad = 8;
1412 ret[1].bits_per_pixel = 1;
1413 ret[1].scanline_pad = 8;
1420 XGetPixel (XImage *ximage, int x, int y)
1422 return ximage->f.get_pixel (ximage, x, y);
1427 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1429 return ximage->f.put_pixel (ximage, x, y, pixel);
1433 XDestroyImage (XImage *ximage)
1435 if (ximage->data) free (ximage->data);
1442 flipbits (unsigned const char *in, unsigned char *out, int length)
1444 static const unsigned char table[256] = {
1445 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1446 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1447 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1448 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1449 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1450 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1451 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1452 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1453 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1454 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1455 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1456 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1457 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1458 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1459 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1460 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1461 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1462 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1463 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1464 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1465 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1466 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1467 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1468 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1469 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1470 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1471 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1472 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1473 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1474 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1475 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1476 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1478 while (--length > 0)
1479 *out++ = table[*in++];
1484 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1485 int src_x, int src_y, int dest_x, int dest_y,
1486 unsigned int w, unsigned int h)
1488 CGRect wr = d->frame;
1490 Assert ((w < 65535), "improbably large width");
1491 Assert ((h < 65535), "improbably large height");
1492 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1493 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1494 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1495 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1497 // Clip width and height to the bounds of the Drawable
1499 if (dest_x + w > wr.size.width) {
1500 if (dest_x > wr.size.width)
1502 w = wr.size.width - dest_x;
1504 if (dest_y + h > wr.size.height) {
1505 if (dest_y > wr.size.height)
1507 h = wr.size.height - dest_y;
1509 if (w <= 0 || h <= 0)
1512 // Clip width and height to the bounds of the XImage
1514 if (src_x + w > ximage->width) {
1515 if (src_x > ximage->width)
1517 w = ximage->width - src_x;
1519 if (src_y + h > ximage->height) {
1520 if (src_y > ximage->height)
1522 h = ximage->height - src_y;
1524 if (w <= 0 || h <= 0)
1527 if (gc && (gc->gcv.function == GXset ||
1528 gc->gcv.function == GXclear)) {
1529 // "set" and "clear" are dumb drawing modes that ignore the source
1530 // bits and just draw solid rectangles.
1531 set_color (d->cgc, (gc->gcv.function == GXset
1532 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1533 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1534 gc->depth, gc->gcv.alpha_allowed_p, YES);
1535 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1539 int bpl = ximage->bytes_per_line;
1540 int bpp = ximage->bits_per_pixel;
1541 int bsize = bpl * h;
1542 char *data = ximage->data;
1545 r.origin.x = wr.origin.x + dest_x;
1546 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1552 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1553 to create a CGImage from a sub-rectagle of the XImage.
1555 data += (src_y * bpl) + (src_x * 4);
1556 CGDataProviderRef prov =
1557 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1559 CGImageRef cgi = CGImageCreate (w, h,
1562 /* Need this for XPMs to have the right
1563 colors, e.g. the logo in "maze". */
1564 (kCGImageAlphaNoneSkipFirst |
1565 kCGBitmapByteOrder32Host),
1567 NULL, /* decode[] */
1568 NO, /* interpolate */
1569 kCGRenderingIntentDefault);
1570 CGDataProviderRelease (prov);
1571 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1572 CGContextDrawImage (d->cgc, r, cgi);
1573 CGImageRelease (cgi);
1575 } else { // (bpp == 1)
1577 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1579 #### However, the bit order within a byte in a 1bpp XImage is
1580 the wrong way around from what Quartz expects, so first we
1581 have to copy the data to reverse it. Shit! Maybe it
1582 would be worthwhile to go through the hacks and #ifdef
1583 each one that diddles 1bpp XImage->data directly...
1585 Assert ((src_x % 8) == 0,
1586 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1588 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1589 unsigned char *flipped = (unsigned char *) malloc (bsize);
1591 flipbits ((unsigned char *) data, flipped, bsize);
1593 CGDataProviderRef prov =
1594 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1595 CGImageRef mask = CGImageMaskCreate (w, h,
1598 NULL, /* decode[] */
1599 NO); /* interpolate */
1600 push_fg_gc (d, gc, YES);
1602 CGContextFillRect (d->cgc, r); // foreground color
1603 CGContextClipToMask (d->cgc, r, mask);
1604 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1605 CGContextFillRect (d->cgc, r); // background color
1609 CGDataProviderRelease (prov);
1610 CGImageRelease (mask);
1618 XGetImage (Display *dpy, Drawable d, int x, int y,
1619 unsigned int width, unsigned int height,
1620 unsigned long plane_mask, int format)
1622 const unsigned char *data = 0;
1623 int depth, ibpp, ibpl, alpha_first_p;
1624 NSBitmapImageRep *bm = 0;
1626 Assert ((width < 65535), "improbably large width");
1627 Assert ((height < 65535), "improbably large height");
1628 Assert ((x < 65535 && x > -65535), "improbably large x");
1629 Assert ((y < 65535 && y > -65535), "improbably large y");
1631 if (d->type == PIXMAP) {
1632 depth = d->pixmap.depth;
1633 alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
1634 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1635 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1636 data = CGBitmapContextGetData (d->cgc);
1637 Assert (data, "CGBitmapContextGetData failed");
1639 // get the bits (desired sub-rectangle) out of the NSView
1640 bm = [NSBitmapImageRep alloc];
1642 nsfrom.origin.x = x;
1643 nsfrom.origin.y = y;
1644 nsfrom.size.width = width;
1645 nsfrom.size.height = height;
1646 [bm initWithFocusedViewRect:nsfrom];
1648 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1649 ibpp = [bm bitsPerPixel];
1650 ibpl = [bm bytesPerRow];
1651 data = [bm bitmapData];
1652 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1655 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1656 data += (y * ibpl) + (x * (ibpp/8));
1658 format = (depth == 1 ? XYPixmap : ZPixmap);
1659 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1661 image->data = (char *) malloc (height * image->bytes_per_line);
1663 int obpl = image->bytes_per_line;
1665 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1666 means that on Intel it is BGRA when viewed by bytes (And BGR
1667 when using 24bpp packing).
1669 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1670 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1671 indicator of this latest kink.
1675 const unsigned char *iline = data;
1676 for (yy = 0; yy < height; yy++) {
1678 const unsigned char *iline2 = iline;
1679 for (xx = 0; xx < width; xx++) {
1681 iline2++; // ignore R or A or A or B
1682 iline2++; // ignore G or B or R or G
1683 unsigned char r = *iline2++; // use B or G or G or R
1684 if (ibpp == 32) iline2++; // ignore A or R or B or A
1686 XPutPixel (image, xx, yy, (r ? 1 : 0));
1691 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1692 const unsigned char *iline = data;
1693 unsigned char *oline = (unsigned char *) image->data;
1694 for (yy = 0; yy < height; yy++) {
1696 const unsigned char *iline2 = iline;
1697 unsigned char *oline2 = oline;
1699 if (alpha_first_p) // ARGB
1700 for (xx = 0; xx < width; xx++) {
1701 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1702 unsigned char r = *iline2++;
1703 unsigned char g = *iline2++;
1704 unsigned char b = *iline2++;
1705 uint32_t pixel = ((a << 24) |
1709 *((uint32_t *) oline2) = pixel;
1713 for (xx = 0; xx < width; xx++) {
1714 unsigned char r = *iline2++;
1715 unsigned char g = *iline2++;
1716 unsigned char b = *iline2++;
1717 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1718 uint32_t pixel = ((a << 24) |
1722 *((uint32_t *) oline2) = pixel;
1731 if (bm) [bm release];
1737 /* Returns a transformation matrix to do rotation as per the provided
1738 EXIF "Orientation" value.
1740 static CGAffineTransform
1741 exif_rotate (int rot, CGSize rect)
1743 CGAffineTransform trans = CGAffineTransformIdentity;
1745 case 2: // flip horizontal
1746 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1747 trans = CGAffineTransformScale (trans, -1, 1);
1750 case 3: // rotate 180
1751 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1752 trans = CGAffineTransformRotate (trans, M_PI);
1755 case 4: // flip vertical
1756 trans = CGAffineTransformMakeTranslation (0, rect.height);
1757 trans = CGAffineTransformScale (trans, 1, -1);
1760 case 5: // transpose (UL-to-LR axis)
1761 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1762 trans = CGAffineTransformScale (trans, -1, 1);
1763 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1766 case 6: // rotate 90
1767 trans = CGAffineTransformMakeTranslation (0, rect.width);
1768 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1771 case 7: // transverse (UR-to-LL axis)
1772 trans = CGAffineTransformMakeScale (-1, 1);
1773 trans = CGAffineTransformRotate (trans, M_PI / 2);
1776 case 8: // rotate 270
1777 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1778 trans = CGAffineTransformRotate (trans, M_PI / 2);
1790 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1791 Bool nsimg_p, void *img_arg,
1792 XRectangle *geom_ret, int exif_rotation)
1795 CGImageSourceRef cgsrc;
1800 NSImage *nsimg = (NSImage *) img_arg;
1801 imgr = [nsimg size];
1803 // convert the NSImage to a CGImage via the toll-free-bridging
1804 // of NSData and CFData...
1806 NSData *nsdata = [NSBitmapImageRep
1807 TIFFRepresentationOfImageRepsInArray:
1808 [nsimg representations]];
1809 CFDataRef cfdata = (CFDataRef) nsdata;
1810 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1811 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1814 cgi = (CGImageRef) img_arg;
1815 imgr.width = CGImageGetWidth (cgi);
1816 imgr.height = CGImageGetHeight (cgi);
1819 Bool rot_p = (exif_rotation >= 5);
1822 imgr = NSMakeSize (imgr.height, imgr.width);
1824 CGRect winr = d->frame;
1825 float rw = winr.size.width / imgr.width;
1826 float rh = winr.size.height / imgr.height;
1827 float r = (rw < rh ? rw : rh);
1830 dst.size.width = imgr.width * r;
1831 dst.size.height = imgr.height * r;
1832 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1833 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1835 dst2.origin.x = dst2.origin.y = 0;
1837 dst2.size.width = dst.size.height;
1838 dst2.size.height = dst.size.width;
1840 dst2.size = dst.size;
1843 // Clear the part not covered by the image to background or black.
1845 if (d->type == WINDOW)
1846 XClearWindow (dpy, d);
1848 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1849 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1852 CGAffineTransform trans =
1853 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1855 CGContextSaveGState (d->cgc);
1856 CGContextConcatCTM (d->cgc,
1857 CGAffineTransformMakeTranslation (dst.origin.x,
1859 CGContextConcatCTM (d->cgc, trans);
1860 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1861 CGContextDrawImage (d->cgc, dst2, cgi);
1862 CGContextRestoreGState (d->cgc);
1866 CGImageRelease (cgi);
1870 geom_ret->x = dst.origin.x;
1871 geom_ret->y = dst.origin.y;
1872 geom_ret->width = dst.size.width;
1873 geom_ret->height = dst.size.height;
1879 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1881 unsigned int w, unsigned int h,
1882 unsigned long fg, unsigned int bg,
1885 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1886 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1887 (char *) data, w, h, 0, 0);
1889 gcv.foreground = fg;
1890 gcv.background = bg;
1891 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1892 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1895 XDestroyImage (image);
1900 XCreatePixmap (Display *dpy, Drawable d,
1901 unsigned int width, unsigned int height, unsigned int depth)
1903 char *data = (char *) malloc (width * height * 4);
1904 if (! data) return 0;
1906 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1908 p->frame.size.width = width;
1909 p->frame.size.height = height;
1910 p->pixmap.depth = depth;
1912 /* Quartz doesn't have a 1bpp image type.
1913 We used to use 8bpp gray images instead of 1bpp, but some Mac video
1914 don't support that! So we always use 32bpp, regardless of depth. */
1916 p->cgc = CGBitmapContextCreate (data, width, height,
1917 8, /* bits per component */
1918 width * 4, /* bpl */
1920 // Without this, it returns 0...
1921 kCGImageAlphaNoneSkipFirst
1923 Assert (p->cgc, "could not create CGBitmapContext");
1929 XFreePixmap (Display *d, Pixmap p)
1931 Assert (p->type == PIXMAP, "not a pixmap");
1932 CGContextRelease (p->cgc);
1939 copy_pixmap (Pixmap p)
1942 Assert (p->type == PIXMAP, "not a pixmap");
1943 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1945 CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
1950 /* Font metric terminology, as used by X11:
1952 "lbearing" is the distance from the logical origin to the leftmost pixel.
1953 If a character's ink extends to the left of the origin, it is negative.
1955 "rbearing" is the distance from the logical origin to the rightmost pixel.
1957 "descent" is the distance from the logical origin to the bottommost pixel.
1958 For characters with descenders, it is negative.
1960 "ascent" is the distance from the logical origin to the topmost pixel.
1961 It is the number of pixels above the baseline.
1963 "width" is the distance from the logical origin to the position where
1964 the logical origin of the next character should be placed.
1966 If "rbearing" is greater than "width", then this character overlaps the
1967 following character. If smaller, then there is trailing blank space.
1971 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1974 query_font (Font fid)
1976 if (!fid || !fid->nsfont) {
1977 NSLog(@"no NSFont in fid");
1980 if (![fid->nsfont fontName]) {
1981 NSLog(@"broken NSFont in fid");
1988 XFontStruct *f = &fid->metrics;
1989 XCharStruct *min = &f->min_bounds;
1990 XCharStruct *max = &f->max_bounds;
1992 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1995 f->min_char_or_byte2 = first;
1996 f->max_char_or_byte2 = last;
1997 f->default_char = 'M';
1998 f->ascent = CEIL ([fid->nsfont ascender]);
1999 f->descent = -CEIL ([fid->nsfont descender]);
2001 min->width = 255; // set to smaller values in the loop
2004 min->lbearing = 255;
2005 min->rbearing = 255;
2007 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2010 NSBezierPath *bpath = [NSBezierPath bezierPath];
2012 for (i = first; i <= last; i++) {
2013 unsigned char str[2];
2017 NSString *nsstr = [NSString stringWithCString:(char *) str
2018 encoding:NSISOLatin1StringEncoding];
2020 /* I can't believe we have to go through this bullshit just to
2021 convert a 'char' to an NSGlyph!!
2023 You might think that we could do
2024 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2025 but that doesn't work; my guess is that glyphWithName expects
2026 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2030 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2031 [ts setFont:fid->nsfont];
2032 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2034 /* Without this, the layout manager ends up on a queue somewhere and is
2035 referenced again after we return to the command loop. Since we don't
2036 use this layout manager again, by that time it may have been garbage
2037 collected, and we crash. Setting this seems to cause `lm' to no
2038 longer be referenced once we exit this block. */
2039 [lm setBackgroundLayoutEnabled:NO];
2041 NSTextContainer *tc = [[NSTextContainer alloc] init];
2042 [lm addTextContainer:tc];
2043 [tc release]; // lm retains tc
2044 [ts addLayoutManager:lm];
2045 [lm release]; // ts retains lm
2046 glyph = [lm glyphAtIndex:0];
2050 /* Compute the bounding box and advancement by converting the glyph
2051 to a bezier path. There appears to be *no other way* to find out
2052 the bounding box of a character: [NSFont boundingRectForGlyph] and
2053 [NSString sizeWithAttributes] both return an advancement-sized
2054 rectangle, not a rectangle completely enclosing the glyph's ink.
2056 NSPoint advancement;
2058 advancement.x = advancement.y = 0;
2059 [bpath removeAllPoints];
2060 [bpath moveToPoint:advancement];
2061 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2062 advancement = [bpath currentPoint];
2063 bbox = [bpath bounds];
2065 /* Now that we know the advancement and bounding box, we can compute
2066 the lbearing and rbearing.
2068 XCharStruct *cs = &f->per_char[i-first];
2070 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2071 cs->descent = CEIL(-bbox.origin.y);
2072 cs->lbearing = CEIL (bbox.origin.x);
2073 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2074 cs->width = CEIL (advancement.x);
2076 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2078 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2081 max->width = MAX (max->width, cs->width);
2082 max->ascent = MAX (max->ascent, cs->ascent);
2083 max->descent = MAX (max->descent, cs->descent);
2084 max->lbearing = MAX (max->lbearing, cs->lbearing);
2085 max->rbearing = MAX (max->rbearing, cs->rbearing);
2087 min->width = MIN (min->width, cs->width);
2088 min->ascent = MIN (min->ascent, cs->ascent);
2089 min->descent = MIN (min->descent, cs->descent);
2090 min->lbearing = MIN (min->lbearing, cs->lbearing);
2091 min->rbearing = MIN (min->rbearing, cs->rbearing);
2096 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2097 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2098 i, i, cs->width, cs->lbearing, cs->rbearing,
2099 cs->ascent, cs->descent,
2100 (int) bbox.size.width, (int) bbox.size.height,
2101 (int) bbox.origin.x, (int) bbox.origin.y,
2102 (int) advancement.x, (int) advancement.y);
2109 // Since 'Font' includes the metrics, this just makes a copy of that.
2112 XQueryFont (Display *dpy, Font fid)
2115 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2118 // copy XCharStruct array
2119 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2120 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2121 memcpy (f->per_char, fid->metrics.per_char,
2122 size * sizeof (XCharStruct));
2129 copy_font (Font fid)
2131 // copy 'Font' struct
2132 Font fid2 = (Font) malloc (sizeof(*fid2));
2135 // copy XCharStruct array
2136 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2137 fid2->metrics.per_char = (XCharStruct *)
2138 malloc ((size + 2) * sizeof (XCharStruct));
2139 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2140 size * sizeof (XCharStruct));
2142 // copy the other pointers
2143 fid2->ps_name = strdup (fid->ps_name);
2144 // [fid2->nsfont retain];
2145 fid2->metrics.fid = fid2;
2152 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2155 Assert (size > 0, "zero font size");
2160 // "Monaco" only exists in plain.
2161 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2163 if (bold && ital) name = "Courier-BoldOblique";
2164 else if (bold) name = "Courier-Bold";
2165 else if (ital) name = "Courier-Oblique";
2166 else name = "Courier";
2170 // "Georgia" looks better than "Times".
2172 if (bold && ital) name = "Georgia-BoldItalic";
2173 else if (bold) name = "Georgia-Bold";
2174 else if (ital) name = "Georgia-Italic";
2175 else name = "Georgia";
2179 // "Geneva" only exists in plain.
2180 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2181 // "Verdana" renders smoother than "Helvetica" for some reason.
2183 if (bold && ital) name = "Verdana-BoldItalic";
2184 else if (bold) name = "Verdana-Bold";
2185 else if (ital) name = "Verdana-Italic";
2186 else name = "Verdana";
2189 NSString *nsname = [NSString stringWithCString:name
2190 encoding:NSUTF8StringEncoding];
2191 NSFont *f = [NSFont fontWithName:nsname size:size];
2193 *name_ret = strdup(name);
2198 try_native_font (const char *name, char **name_ret, float *size_ret)
2200 if (!name) return 0;
2201 const char *spc = strrchr (name, ' ');
2204 if (1 != sscanf (spc, " %d ", &size)) return 0;
2205 if (size <= 4) return 0;
2207 char *name2 = strdup (name);
2208 name2[strlen(name2) - strlen(spc)] = 0;
2209 NSString *nsname = [NSString stringWithCString:name2
2210 encoding:NSUTF8StringEncoding];
2211 NSFont *f = [NSFont fontWithName:nsname size:size];
2223 /* Returns a random font in the given size and face.
2226 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2228 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2229 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2230 NSArray *fonts = [[NSFontManager sharedFontManager]
2231 availableFontNamesWithTraits:mask];
2232 if (!fonts) return 0;
2234 int n = [fonts count];
2235 if (n <= 0) return 0;
2238 for (j = 0; j < n; j++) {
2239 int i = random() % n;
2240 NSString *name = [fonts objectAtIndex:i];
2241 NSFont *f = [NSFont fontWithName:name size:size];
2244 /* Don't use this font if it (probably) doesn't include ASCII characters.
2246 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2247 if (! (enc == NSUTF8StringEncoding ||
2248 enc == NSISOLatin1StringEncoding ||
2249 enc == NSNonLossyASCIIStringEncoding ||
2250 enc == NSISOLatin2StringEncoding ||
2251 enc == NSUnicodeStringEncoding ||
2252 enc == NSWindowsCP1250StringEncoding ||
2253 enc == NSWindowsCP1252StringEncoding ||
2254 enc == NSMacOSRomanStringEncoding)) {
2255 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2258 // NSLog(@"using \"%@\": %d", name, enc);
2260 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2264 // None of the fonts support ASCII?
2270 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2281 const char *s = (name ? name : "");
2283 while (*s && (*s == '*' || *s == '-'))
2286 while (*s2 && (*s2 != '*' && *s2 != '-'))
2292 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2293 else if (CMP ("random")) rand = YES;
2294 else if (CMP ("bold")) bold = YES;
2295 else if (CMP ("i")) ital = YES;
2296 else if (CMP ("o")) ital = YES;
2297 else if (CMP ("courier")) fixed = YES;
2298 else if (CMP ("fixed")) fixed = YES;
2299 else if (CMP ("m")) fixed = YES;
2300 else if (CMP ("times")) serif = YES;
2301 else if (CMP ("6x10")) fixed = YES, size = 8;
2302 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2303 else if (CMP ("9x15")) fixed = YES, size = 12;
2304 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2305 else if (CMP ("vga")) fixed = YES, size = 12;
2306 else if (CMP ("console")) fixed = YES, size = 12;
2307 else if (CMP ("gallant")) fixed = YES, size = 12;
2309 else if (size == 0) {
2311 if (1 == sscanf (s, " %d ", &n))
2318 if (size < 6 || size > 1000)
2322 nsfont = random_font (bold, ital, size, &ps_name);
2325 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2327 // if that didn't work, turn off attibutes until it does
2328 // (e.g., there is no "Monaco-Bold".)
2330 if (!nsfont && serif) {
2332 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2334 if (!nsfont && ital) {
2336 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2338 if (!nsfont && bold) {
2340 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2342 if (!nsfont && fixed) {
2344 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2348 *name_ret = ps_name;
2358 XLoadFont (Display *dpy, const char *name)
2360 Font fid = (Font) calloc (1, sizeof(*fid));
2362 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2364 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2366 NSLog(@"no NSFont for \"%s\"", name);
2369 CFRetain (fid->nsfont); // needed for garbage collection?
2371 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2380 XLoadQueryFont (Display *dpy, const char *name)
2382 Font fid = XLoadFont (dpy, name);
2383 return XQueryFont (dpy, fid);
2387 XUnloadFont (Display *dpy, Font fid)
2389 free (fid->ps_name);
2390 free (fid->metrics.per_char);
2392 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2393 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2394 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2395 // They're probably not very big...
2397 // [fid->nsfont release];
2398 // CFRelease (fid->nsfont);
2405 XFreeFontInfo (char **names, XFontStruct *info, int n)
2409 for (i = 0; i < n; i++)
2410 if (names[i]) free (names[i]);
2414 for (i = 0; i < n; i++)
2415 if (info[i].per_char)
2416 free (info[i].per_char);
2423 XFreeFont (Display *dpy, XFontStruct *f)
2426 XFreeFontInfo (0, f, 1);
2427 XUnloadFont (dpy, fid);
2433 XSetFont (Display *dpy, GC gc, Font fid)
2436 XUnloadFont (dpy, gc->gcv.font);
2437 gc->gcv.font = copy_font (fid);
2438 [gc->gcv.font->nsfont retain];
2439 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2444 XTextExtents (XFontStruct *f, const char *s, int length,
2445 int *dir_ret, int *ascent_ret, int *descent_ret,
2448 memset (cs, 0, sizeof(*cs));
2450 for (i = 0; i < length; i++) {
2451 unsigned char c = (unsigned char) s[i];
2452 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2453 c = f->default_char;
2454 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2458 cs->ascent = MAX (cs->ascent, cc->ascent);
2459 cs->descent = MAX (cs->descent, cc->descent);
2460 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2461 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2462 cs->width += cc->width;
2466 *ascent_ret = f->ascent;
2467 *descent_ret = f->descent;
2472 XTextWidth (XFontStruct *f, const char *s, int length)
2474 int ascent, descent, dir;
2476 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2482 set_font (CGContextRef cgc, GC gc)
2484 Font font = gc->gcv.font;
2486 font = XLoadFont (0, 0);
2487 gc->gcv.font = font;
2488 [gc->gcv.font->nsfont retain];
2489 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2491 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2492 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2497 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2498 const char *str, int len, BOOL clear_background_p)
2500 if (clear_background_p) {
2501 int ascent, descent, dir;
2503 XTextExtents (&gc->gcv.font->metrics, str, len,
2504 &dir, &ascent, &descent, &cs);
2505 draw_rect (dpy, d, gc,
2506 x + MIN (0, cs.lbearing),
2507 y - MAX (0, ascent),
2508 MAX (MAX (0, cs.rbearing) -
2509 MIN (0, cs.lbearing),
2511 MAX (0, ascent) + MAX (0, descent),
2515 CGRect wr = d->frame;
2518 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2519 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2522 push_fg_gc (d, gc, YES);
2523 set_font (d->cgc, gc);
2525 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2526 if (gc->gcv.antialias_p)
2527 CGContextSetShouldAntialias (d->cgc, YES);
2528 CGContextShowTextAtPoint (d->cgc,
2530 wr.origin.y + wr.size.height - y,
2539 unsigned long argb = gc->gcv.foreground;
2540 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2541 float a = ((argb >> 24) & 0xFF) / 255.0;
2542 float r = ((argb >> 16) & 0xFF) / 255.0;
2543 float g = ((argb >> 8) & 0xFF) / 255.0;
2544 float b = ((argb ) & 0xFF) / 255.0;
2545 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2546 NSDictionary *attr =
2547 [NSDictionary dictionaryWithObjectsAndKeys:
2548 gc->gcv.font->nsfont, NSFontAttributeName,
2549 fg, NSForegroundColorAttributeName,
2551 char *s2 = (char *) malloc (len + 1);
2552 strncpy (s2, str, len);
2554 NSString *nsstr = [NSString stringWithCString:s2
2555 encoding:NSISOLatin1StringEncoding];
2558 pos.x = wr.origin.x + x;
2559 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2560 [nsstr drawAtPoint:pos withAttributes:attr];
2569 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2570 const char *str, int len)
2572 return draw_string (dpy, d, gc, x, y, str, len, NO);
2576 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2577 const char *str, int len)
2579 return draw_string (dpy, d, gc, x, y, str, len, YES);
2584 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2586 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2587 gc->gcv.foreground = fg;
2593 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2595 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2596 gc->gcv.background = bg;
2601 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2603 gc->gcv.alpha_allowed_p = allowed;
2608 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2610 gc->gcv.antialias_p = antialias_p;
2616 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2617 int line_style, int cap_style, int join_style)
2619 gc->gcv.line_width = line_width;
2620 Assert (line_style == LineSolid, "only LineSolid implemented");
2621 // gc->gcv.line_style = line_style;
2622 gc->gcv.cap_style = cap_style;
2623 gc->gcv.join_style = join_style;
2628 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2634 XSetFunction (Display *dpy, GC gc, int which)
2636 gc->gcv.function = which;
2641 XSetSubwindowMode (Display *dpy, GC gc, int which)
2643 gc->gcv.subwindow_mode = which;
2648 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2650 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2652 if (gc->gcv.clip_mask) {
2653 XFreePixmap (dpy, gc->gcv.clip_mask);
2654 CGImageRelease (gc->clip_mask);
2657 gc->gcv.clip_mask = copy_pixmap (m);
2658 if (gc->gcv.clip_mask)
2659 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2667 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2669 gc->gcv.clip_x_origin = x;
2670 gc->gcv.clip_y_origin = y;
2676 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2677 int *root_x_ret, int *root_y_ret,
2678 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2680 Assert (w->type == WINDOW, "not a window");
2681 NSWindow *nsw = [w->window.view window];
2683 // get bottom left of window on screen, from bottom left
2684 wpos.x = wpos.y = 0;
2685 wpos = [nsw convertBaseToScreen:wpos];
2688 // get bottom left of view on window, from bottom left
2689 vpos.x = vpos.y = 0;
2690 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2692 // get bottom left of view on screen, from bottom left
2696 // get top left of view on screen, from bottom left
2697 vpos.y += w->frame.size.height;
2699 // get top left of view on screen, from top left
2700 NSArray *screens = [NSScreen screens];
2701 NSScreen *screen = (screens && [screens count] > 0
2702 ? [screens objectAtIndex:0]
2703 : [NSScreen mainScreen]);
2704 NSRect srect = [screen frame];
2705 vpos.y = srect.size.height - vpos.y;
2707 // get the mouse position on window, from bottom left
2708 NSEvent *e = [NSApp currentEvent];
2709 NSPoint p = [e locationInWindow];
2711 // get mouse position on screen, from bottom left
2715 // get mouse position on screen, from top left
2716 p.y = srect.size.height - p.y;
2718 if (root_x_ret) *root_x_ret = (int) p.x;
2719 if (root_y_ret) *root_y_ret = (int) p.y;
2720 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2721 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2723 if (mask_ret) *mask_ret = 0; // ####
2724 if (root_ret) *root_ret = 0;
2725 if (child_ret) *child_ret = 0;
2730 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2731 int src_x, int src_y,
2732 int *dest_x_ret, int *dest_y_ret,
2735 Assert (w->type == WINDOW, "not a window");
2736 NSWindow *nsw = [w->window.view window];
2738 // get bottom left of window on screen, from bottom left
2739 wpos.x = wpos.y = 0;
2740 wpos = [nsw convertBaseToScreen:wpos];
2743 // get bottom left of view on window, from bottom left
2744 vpos.x = vpos.y = 0;
2745 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2747 // get bottom left of view on screen, from bottom left
2751 // get top left of view on screen, from bottom left
2752 vpos.y += w->frame.size.height;
2754 // get top left of view on screen, from top left
2755 NSArray *screens = [NSScreen screens];
2756 NSScreen *screen = (screens && [screens count] > 0
2757 ? [screens objectAtIndex:0]
2758 : [NSScreen mainScreen]);
2759 NSRect srect = [screen frame];
2760 vpos.y = srect.size.height - vpos.y;
2762 // point starts out relative to top left of view
2767 // get point relative to top left of screen
2780 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2786 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2789 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2790 char c = (char) ks; // could be smarter about modifiers here...
2791 if (k_ret) *k_ret = ks;
2792 if (size > 0) buf[0] = c;
2793 if (size > 1) buf[1] = 0;
2799 XFlush (Display *dpy)
2801 // Just let the event loop take care of this on its own schedule.
2806 XSync (Display *dpy, Bool flush)
2808 return XFlush (dpy);
2812 // declared in utils/visual.h
2814 has_writable_cells (Screen *s, Visual *v)
2820 visual_depth (Screen *s, Visual *v)
2826 visual_cells (Screen *s, Visual *v)
2832 visual_class (Screen *s, Visual *v)
2837 // declared in utils/grabclient.h
2839 use_subwindow_mode_p (Screen *screen, Window window)