1 /* xscreensaver, Copyright (c) 1991-2008 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.
20 #import <Cocoa/Cocoa.h>
22 #import "jwxyz-timers.h"
25 #define Assert(C,S) do { \
33 # define MAX(a,b) ((a)>(b)?(a):(b))
34 # define MIN(a,b) ((a)<(b)?(a):(b))
37 struct jwxyz_Drawable {
38 enum { WINDOW, PIXMAP } type;
44 unsigned long background;
52 struct jwxyz_Display {
55 struct jwxyz_sources_data *timers_data;
57 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
58 This can change if the window is dragged to
59 a different screen. */
61 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
62 our images with this to avoid translation
74 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
82 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
83 // But we need the metrics on both of them, so they go here.
89 jwxyz_make_display (void *nsview_arg)
91 NSView *view = (NSView *) nsview_arg;
94 Display *d = (Display *) calloc (1, sizeof(*d));
95 d->screen = (Screen *) calloc (1, sizeof(Screen));
98 Visual *v = (Visual *) calloc (1, sizeof(Visual));
100 v->red_mask = 0x00FF0000;
101 v->green_mask = 0x0000FF00;
102 v->blue_mask = 0x000000FF;
104 d->screen->visual = v;
106 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
109 Window w = (Window) calloc (1, sizeof(*w));
111 // kludge! this needs to be set late, so we do it in XClearWindow!
112 // w->cgc = [[[view window] graphicsContext] graphicsPort];
114 w->window.view = view;
115 w->window.background = BlackPixel(0,0);
120 w->cgc = [[[view window] graphicsContext] graphicsPort];
123 jwxyz_window_resized (d, w);
129 jwxyz_free_display (Display *dpy)
131 jwxyz_XtRemoveInput_all (dpy);
132 // #### jwxyz_XtRemoveTimeOut_all ();
134 free (dpy->screen->visual);
136 free (dpy->main_window);
142 jwxyz_window_view (Window w)
144 Assert (w->type == WINDOW, "not a window");
145 return w->window.view;
148 /* Call this when the View changes size or position.
151 jwxyz_window_resized (Display *dpy, Window w)
153 Assert (w->type == WINDOW, "not a window");
154 NSRect r = [w->window.view frame];
155 w->frame.origin.x = r.origin.x; // NSRect -> CGRect
156 w->frame.origin.y = r.origin.y;
157 w->frame.size.width = r.size.width;
158 w->frame.size.height = r.size.height;
160 // Figure out which screen the window is currently on.
163 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
169 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
170 Assert (dpy->cgdpy, "unable to find CGDisplay");
175 // Figure out this screen's colorspace, and use that for every CGImage.
177 CMProfileRef profile = 0;
178 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
179 Assert (profile, "unable to find colorspace profile");
180 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
181 Assert (dpy->colorspace, "unable to find colorspace");
185 // WTF? It's faster if we *do not* use the screen's colorspace!
187 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
193 display_sources_data (Display *dpy)
195 return dpy->timers_data;
200 XRootWindow (Display *dpy, int screen)
202 return dpy->main_window;
206 XDefaultScreenOfDisplay (Display *dpy)
212 XDefaultVisualOfScreen (Screen *screen)
214 return screen->visual;
218 XDisplayOfScreen (Screen *s)
224 XDisplayNumberOfScreen (Screen *s)
230 XScreenNumberOfScreen (Screen *s)
236 XDisplayWidth (Display *dpy, int screen)
238 return (int) dpy->main_window->frame.size.width;
242 XDisplayHeight (Display *dpy, int screen)
244 return (int) dpy->main_window->frame.size.height;
249 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
252 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
253 else if (!alpha_allowed_p)
254 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
255 "bogus color pixel");
260 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
261 BOOL alpha_allowed_p, BOOL fill_p)
263 validate_pixel (argb, depth, alpha_allowed_p);
266 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
268 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
270 float a = ((argb >> 24) & 0xFF) / 255.0;
271 float r = ((argb >> 16) & 0xFF) / 255.0;
272 float g = ((argb >> 8) & 0xFF) / 255.0;
273 float b = ((argb ) & 0xFF) / 255.0;
275 CGContextSetRGBFillColor (cgc, r, g, b, a);
277 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
282 set_line_mode (CGContextRef cgc, XGCValues *gcv)
284 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
285 CGContextSetLineJoin (cgc,
286 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
287 gcv->join_style == JoinRound ? kCGLineJoinRound :
289 CGContextSetLineCap (cgc,
290 gcv->cap_style == CapNotLast ? kCGLineCapButt :
291 gcv->cap_style == CapButt ? kCGLineCapButt :
292 gcv->cap_style == CapRound ? kCGLineCapRound :
297 set_clip_mask (Drawable d, GC gc)
299 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
301 Pixmap p = gc->gcv.clip_mask;
303 Assert (p->type == PIXMAP, "not a pixmap");
305 CGRect wr = d->frame;
307 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
308 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
309 - p->frame.size.height;
310 to.size.width = p->frame.size.width;
311 to.size.height = p->frame.size.height;
313 CGContextClipToMask (d->cgc, to, gc->clip_mask);
317 /* Pushes a GC context; sets BlendMode and ClipMask.
320 push_gc (Drawable d, GC gc)
322 CGContextRef cgc = d->cgc;
323 CGContextSaveGState (cgc);
325 switch (gc->gcv.function) {
328 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
329 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
330 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
331 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
332 default: abort(); break;
335 if (gc->gcv.clip_mask)
336 set_clip_mask (d, gc);
339 #define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
342 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
345 push_color_gc (Drawable d, GC gc, unsigned long color,
346 BOOL antialias_p, Bool fill_p)
350 int depth = gc->depth;
351 switch (gc->gcv.function) {
352 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
353 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
356 CGContextRef cgc = d->cgc;
358 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
359 CGContextSetShouldAntialias (cgc, antialias_p);
363 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
366 push_fg_gc (Drawable d, GC gc, Bool fill_p)
368 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
371 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
374 push_bg_gc (Drawable d, GC gc, Bool fill_p)
376 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
381 /* You've got to be fucking kidding me!
383 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
384 with repeated calls to CGContextDrawImage than it is to make a single
385 call to CGContextFillRects()!
387 I still wouldn't call it *fast*, however...
389 #define XDRAWPOINTS_IMAGES
392 XDrawPoints (Display *dpy, Drawable d, GC gc,
393 XPoint *points, int count, int mode)
396 CGRect wr = d->frame;
398 # ifdef XDRAWPOINTS_IMAGES
400 unsigned int argb = gc->gcv.foreground;
401 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
403 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
405 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
406 CGImageRef cgi = CGImageCreate (1, 1,
409 /* Host-ordered, since we're using the
410 address of an int as the color data. */
411 (kCGImageAlphaNoneSkipFirst |
412 kCGBitmapByteOrder32Host),
415 NO, /* interpolate */
416 kCGRenderingIntentDefault);
417 CGDataProviderRelease (prov);
419 CGContextRef cgc = d->cgc;
421 rect.size.width = rect.size.height = 1;
422 for (i = 0; i < count; i++) {
423 if (i > 0 && mode == CoordModePrevious) {
424 rect.origin.x += points->x;
425 rect.origin.x -= points->y;
427 rect.origin.x = wr.origin.x + points->x;
428 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
431 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
432 CGContextDrawImage (cgc, rect, cgi);
436 CGImageRelease (cgi);
438 # else /* ! XDRAWPOINTS_IMAGES */
440 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
443 push_fg_gc (d, gc, YES);
444 for (i = 0; i < count; i++) {
445 r->size.width = r->size.height = 1;
446 if (i > 0 && mode == CoordModePrevious) {
447 r->origin.x = r[-1].origin.x + points->x;
448 r->origin.y = r[-1].origin.x - points->y;
450 r->origin.x = wr.origin.x + points->x;
451 r->origin.y = wr.origin.y + wr.size.height - points->y;
457 CGContextFillRects (d->cgc, rects, count);
460 # endif /* ! XDRAWPOINTS_IMAGES */
467 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
472 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
476 static void draw_rect (Display *, Drawable, GC,
477 int x, int y, unsigned int width, unsigned int height,
478 BOOL foreground_p, BOOL fill_p);
481 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
482 int src_x, int src_y,
483 unsigned int width, unsigned int height,
484 int dst_x, int dst_y)
486 Assert ((width < 65535), "improbably large width");
487 Assert ((height < 65535), "improbably large height");
488 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
489 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
490 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
491 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
493 if (width == 0 || height == 0)
496 if (gc && (gc->gcv.function == GXset ||
497 gc->gcv.function == GXclear)) {
498 // "set" and "clear" are dumb drawing modes that ignore the source
499 // bits and just draw solid rectangles.
500 set_color (dst->cgc, (gc->gcv.function == GXset
501 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
502 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
503 gc->depth, gc->gcv.alpha_allowed_p, YES);
504 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
508 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
509 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
510 // bounds of their drawables.
511 BOOL clipped = NO; // Whether we did any clipping of the rects.
513 src_frame = src->frame;
514 dst_frame = dst->frame;
516 // Initialize src_rect...
518 src_rect.origin.x = src_frame.origin.x + src_x;
519 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
521 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
522 src_rect.size.width = width;
523 src_rect.size.height = height;
525 // Initialize dst_rect...
527 dst_rect.origin.x = dst_frame.origin.x + dst_x;
528 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
530 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
531 dst_rect.size.width = width;
532 dst_rect.size.height = height;
534 // Clip rects to frames...
536 // CGRect orig_src_rect = src_rect;
537 CGRect orig_dst_rect = dst_rect;
539 # define CLIP(THIS,THAT,VAL,SIZE) do { \
540 float off = THIS##_rect.origin.VAL; \
543 THIS##_rect.size.SIZE += off; \
544 THAT##_rect.size.SIZE += off; \
545 THIS##_rect.origin.VAL -= off; \
546 THAT##_rect.origin.VAL -= off; \
548 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
549 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
552 THIS##_rect.size.SIZE -= off; \
553 THAT##_rect.size.SIZE -= off; \
556 CLIP (dst, src, x, width);
557 CLIP (dst, src, y, height);
558 CLIP (src, dst, x, width);
559 CLIP (src, dst, y, height);
563 Assert (src_rect.size.width == dst_rect.size.width, "width out of sync");
564 Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
565 Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
566 Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
567 Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
568 Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
569 Assert (src_rect.origin.x + src_rect.size.width <=
570 src_frame.origin.x + src_frame.size.width, "clip failed src_width");
573 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
576 NSObject *releaseme = 0;
580 if (src->type == PIXMAP) {
582 // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
583 // but it presumably shares the data pointer instead of copying it.
584 cgi = CGBitmapContextCreateImage (src->cgc);
586 // if doing a sub-rect, trim it down.
587 if (src_rect.origin.x != src_frame.origin.x ||
588 src_rect.origin.y != src_frame.origin.y ||
589 src_rect.size.width != src_frame.size.width ||
590 src_rect.size.height != src_frame.size.height) {
591 // #### I don't understand why this is needed...
592 src_rect.origin.y = (src_frame.size.height -
593 src_rect.size.height - src_rect.origin.y);
594 // This does not copy image data, so it should be fast.
595 CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
596 CGImageRelease (cgi);
600 if (src->pixmap.depth == 1)
603 } else { /* (src->type == WINDOW) */
606 nsfrom.origin.x = src_rect.origin.x;
607 nsfrom.origin.y = src_rect.origin.y;
608 nsfrom.size.width = src_rect.size.width;
609 nsfrom.size.height = src_rect.size.height;
612 // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
614 NSBitmapImageRep *bm = [NSBitmapImageRep alloc];
615 [bm initWithFocusedViewRect:nsfrom];
616 unsigned char *data = [bm bitmapData];
617 int bps = [bm bitsPerSample];
618 int bpp = [bm bitsPerPixel];
619 int bpl = [bm bytesPerRow];
624 // QuickDraw way (doesn't work, need NSQuickDrawView)
625 PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
626 char **data = GetPortPixMap (pix);
629 int bpl = GetPixRowBytes (pix) & 0x3FFF;
633 // get the bits (desired sub-rectangle) out of the raw frame buffer.
634 // (This renders wrong, and appears to be even slower anyway.)
636 int window_x, window_y;
637 XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
638 window_x += nsfrom.origin.x;
639 window_y += (dst->frame.size.height
640 - (nsfrom.origin.y + nsfrom.size.height));
642 unsigned char *data = (unsigned char *)
643 CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
644 int bps = CGDisplayBitsPerSample (dpy->cgdpy);
645 int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
646 int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
650 // create a CGImage from those bits
652 CGDataProviderRef prov =
653 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
655 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
658 /* Use whatever default bit ordering we got from
659 initWithFocusedViewRect. I would have assumed
660 that it was (kCGImageAlphaNoneSkipFirst |
661 kCGBitmapByteOrder32Host), but on Intel,
667 NO, /* interpolate */
668 kCGRenderingIntentDefault);
669 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
670 CGDataProviderRelease (prov);
673 if (mask_p) { // src depth == 1
675 push_bg_gc (dst, gc, YES);
677 // fill the destination rectangle with solid background...
678 CGContextFillRect (dst->cgc, orig_dst_rect);
680 // then fill in a solid rectangle of the fg color, using the image as an
681 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
682 set_color (dst->cgc, gc->gcv.foreground, gc->depth,
683 gc->gcv.alpha_allowed_p, YES);
684 CGContextClipToMask (dst->cgc, dst_rect, cgi);
685 CGContextFillRect (dst->cgc, dst_rect);
689 } else { // src depth > 1
693 // If either the src or dst rects did not lie within their drawables,
694 // then we have adjusted both the src and dst rects to account for
695 // the clipping; that means we need to first clear to the background,
696 // so that clipped bits end up in the bg color instead of simply not
700 set_color (dst->cgc, gc->gcv.background, gc->depth,
701 gc->gcv.alpha_allowed_p, YES);
702 CGContextFillRect (dst->cgc, orig_dst_rect);
705 // copy the CGImage onto the destination CGContext
706 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
707 CGContextDrawImage (dst->cgc, dst_rect, cgi);
712 CGImageRelease (cgi);
713 if (releaseme) [releaseme release];
719 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
720 int src_x, int src_y,
721 unsigned width, int height,
722 int dest_x, int dest_y, unsigned long plane)
724 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
726 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
727 // not to white/black.
728 return XCopyArea (dpy, src, dest, gc,
729 src_x, src_y, width, height, dest_x, dest_y);
734 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
736 // when drawing a zero-length line, obey line-width and cap-style.
737 if (x1 == x2 && y1 == y2) {
738 int w = gc->gcv.line_width;
741 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
742 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
744 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
747 CGRect wr = d->frame;
749 p.x = wr.origin.x + x1;
750 p.y = wr.origin.y + wr.size.height - y1;
752 push_fg_gc (d, gc, NO);
754 set_line_mode (d->cgc, &gc->gcv);
755 CGContextBeginPath (d->cgc);
756 CGContextMoveToPoint (d->cgc, p.x, p.y);
757 p.x = wr.origin.x + x2;
758 p.y = wr.origin.y + wr.size.height - y2;
759 CGContextAddLineToPoint (d->cgc, p.x, p.y);
760 CGContextStrokePath (d->cgc);
766 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
771 CGRect wr = d->frame;
772 push_fg_gc (d, gc, NO);
773 set_line_mode (d->cgc, &gc->gcv);
775 // if the first and last points coincide, use closepath to get
776 // the proper line-joining.
777 BOOL closed_p = (points[0].x == points[count-1].x &&
778 points[0].y == points[count-1].y);
779 if (closed_p) count--;
781 p.x = wr.origin.x + points->x;
782 p.y = wr.origin.y + wr.size.height - points->y;
784 CGContextBeginPath (d->cgc);
785 CGContextMoveToPoint (d->cgc, p.x, p.y);
786 for (i = 1; i < count; i++) {
787 if (mode == CoordModePrevious) {
791 p.x = wr.origin.x + points->x;
792 p.y = wr.origin.y + wr.size.height - points->y;
794 CGContextAddLineToPoint (d->cgc, p.x, p.y);
797 if (closed_p) CGContextClosePath (d->cgc);
798 CGContextStrokePath (d->cgc);
805 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
808 CGRect wr = d->frame;
810 push_fg_gc (d, gc, NO);
811 set_line_mode (d->cgc, &gc->gcv);
812 CGContextBeginPath (d->cgc);
813 for (i = 0; i < count; i++) {
814 CGContextMoveToPoint (d->cgc,
815 wr.origin.x + segments->x1,
816 wr.origin.y + wr.size.height - segments->y1);
817 CGContextAddLineToPoint (d->cgc,
818 wr.origin.x + segments->x2,
819 wr.origin.y + wr.size.height - segments->y2);
822 CGContextStrokePath (d->cgc);
829 XClearWindow (Display *dpy, Window win)
831 Assert (win->type == WINDOW, "not a window");
832 CGRect wr = win->frame;
833 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
837 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
839 Assert (w->type == WINDOW, "not a window");
840 validate_pixel (pixel, 32, NO);
841 w->window.background = pixel;
846 draw_rect (Display *dpy, Drawable d, GC gc,
847 int x, int y, unsigned int width, unsigned int height,
848 BOOL foreground_p, BOOL fill_p)
850 CGRect wr = d->frame;
852 r.origin.x = wr.origin.x + x;
853 r.origin.y = wr.origin.y + wr.size.height - y - height;
854 r.size.width = width;
855 r.size.height = height;
859 push_fg_gc (d, gc, fill_p);
861 push_bg_gc (d, gc, fill_p);
865 CGContextFillRect (d->cgc, r);
868 set_line_mode (d->cgc, &gc->gcv);
869 CGContextStrokeRect (d->cgc, r);
878 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
879 unsigned int width, unsigned int height)
881 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
886 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
887 unsigned int width, unsigned int height)
889 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
894 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
896 CGRect wr = d->frame;
898 push_fg_gc (d, gc, YES);
899 for (i = 0; i < n; i++) {
901 r.origin.x = wr.origin.x + rects->x;
902 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
903 r.size.width = rects->width;
904 r.size.height = rects->height;
905 CGContextFillRect (d->cgc, r);
914 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
916 Assert (win->type == WINDOW, "not a window");
917 set_color (win->cgc, win->window.background, 32, NO, YES);
918 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
924 XFillPolygon (Display *dpy, Drawable d, GC gc,
925 XPoint *points, int npoints, int shape, int mode)
927 CGRect wr = d->frame;
929 push_fg_gc (d, gc, YES);
930 CGContextBeginPath (d->cgc);
931 for (i = 0; i < npoints; i++) {
933 if (i > 0 && mode == CoordModePrevious) {
937 x = wr.origin.x + points[i].x;
938 y = wr.origin.y + wr.size.height - points[i].y;
942 CGContextMoveToPoint (d->cgc, x, y);
944 CGContextAddLineToPoint (d->cgc, x, y);
946 CGContextClosePath (d->cgc);
947 if (gc->gcv.fill_rule == EvenOddRule)
948 CGContextEOFillPath (d->cgc);
950 CGContextFillPath (d->cgc);
955 #define radians(DEG) ((DEG) * M_PI / 180.0)
956 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
959 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
960 unsigned int width, unsigned int height, int angle1, int angle2,
963 CGRect wr = d->frame;
965 bound.origin.x = wr.origin.x + x;
966 bound.origin.y = wr.origin.y + wr.size.height - y - height;
967 bound.size.width = width;
968 bound.size.height = height;
971 ctr.x = bound.origin.x + bound.size.width /2;
972 ctr.y = bound.origin.y + bound.size.height/2;
974 float r1 = radians (angle1/64.0);
975 float r2 = radians (angle2/64.0) + r1;
976 BOOL clockwise = angle2 < 0;
977 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
979 push_fg_gc (d, gc, fill_p);
981 CGContextBeginPath (d->cgc);
983 CGContextSaveGState(d->cgc);
984 CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
985 CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
987 CGContextMoveToPoint (d->cgc, 0, 0);
989 CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
990 CGContextRestoreGState (d->cgc); // restore before stroke, for line width
993 CGContextClosePath (d->cgc); // for proper line joining
996 CGContextFillPath (d->cgc);
998 set_line_mode (d->cgc, &gc->gcv);
999 CGContextStrokePath (d->cgc);
1007 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1008 unsigned int width, unsigned int height, int angle1, int angle2)
1010 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1014 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1015 unsigned int width, unsigned int height, int angle1, int angle2)
1017 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1021 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1024 for (i = 0; i < narcs; i++)
1025 draw_arc (dpy, d, gc,
1026 arcs[i].x, arcs[i].y,
1027 arcs[i].width, arcs[i].height,
1028 arcs[i].angle1, arcs[i].angle2,
1034 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1037 for (i = 0; i < narcs; i++)
1038 draw_arc (dpy, d, gc,
1039 arcs[i].x, arcs[i].y,
1040 arcs[i].width, arcs[i].height,
1041 arcs[i].angle1, arcs[i].angle2,
1048 gcv_defaults (XGCValues *gcv, int depth)
1050 memset (gcv, 0, sizeof(*gcv));
1051 gcv->function = GXcopy;
1052 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1053 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1054 gcv->line_width = 1;
1055 gcv->cap_style = CapNotLast;
1056 gcv->join_style = JoinMiter;
1057 gcv->fill_rule = EvenOddRule;
1059 gcv->alpha_allowed_p = NO;
1060 gcv->antialias_p = YES;
1064 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1066 if (mask & GCFunction) gc->gcv.function = from->function;
1067 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1068 if (mask & GCBackground) gc->gcv.background = from->background;
1069 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1070 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1071 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1072 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1073 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1074 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1075 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1077 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1078 if (mask & GCFont) XSetFont (0, gc, from->font);
1080 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1081 gc->gcv.alpha_allowed_p);
1082 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1083 gc->gcv.alpha_allowed_p);
1085 if (mask & GCLineStyle) abort();
1086 if (mask & GCPlaneMask) abort();
1087 if (mask & GCFillStyle) abort();
1088 if (mask & GCTile) abort();
1089 if (mask & GCStipple) abort();
1090 if (mask & GCTileStipXOrigin) abort();
1091 if (mask & GCTileStipYOrigin) abort();
1092 if (mask & GCGraphicsExposures) abort();
1093 if (mask & GCDashOffset) abort();
1094 if (mask & GCDashList) abort();
1095 if (mask & GCArcMode) abort();
1100 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1102 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1103 if (d->type == WINDOW) {
1105 } else { /* (d->type == PIXMAP) */
1106 gc->depth = d->pixmap.depth;
1109 gcv_defaults (&gc->gcv, gc->depth);
1110 set_gcv (gc, xgcv, mask);
1115 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1117 set_gcv (gc, gcv, mask);
1123 XFreeGC (Display *dpy, GC gc)
1126 XUnloadFont (dpy, gc->gcv.font);
1128 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1130 if (gc->gcv.clip_mask) {
1131 XFreePixmap (dpy, gc->gcv.clip_mask);
1132 CGImageRelease (gc->clip_mask);
1140 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1142 Assert (w->type == WINDOW, "not a window");
1143 memset (xgwa, 0, sizeof(*xgwa));
1144 xgwa->x = w->frame.origin.x;
1145 xgwa->y = w->frame.origin.y;
1146 xgwa->width = w->frame.size.width;
1147 xgwa->height = w->frame.size.height;
1149 xgwa->screen = dpy->screen;
1150 xgwa->visual = dpy->screen->visual;
1155 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1156 int *x_ret, int *y_ret,
1157 unsigned int *w_ret, unsigned int *h_ret,
1158 unsigned int *bw_ret, unsigned int *d_ret)
1160 *x_ret = d->frame.origin.x;
1161 *y_ret = d->frame.origin.y;
1162 *w_ret = d->frame.size.width;
1163 *h_ret = d->frame.size.height;
1164 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1165 *root_ret = RootWindow (dpy, 0);
1172 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1174 // store 32 bit ARGB in the pixel field.
1175 color->pixel = (( 0xFF << 24) |
1176 (((color->red >> 8) & 0xFF) << 16) |
1177 (((color->green >> 8) & 0xFF) << 8) |
1178 (((color->blue >> 8) & 0xFF) ));
1183 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1184 unsigned long *pmret, unsigned int npl,
1185 unsigned long *pxret, unsigned int npx)
1191 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1193 Assert(0, "XStoreColors called");
1198 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1200 Assert(0, "XStoreColor called");
1205 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1206 unsigned long planes)
1212 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1214 unsigned char r=0, g=0, b=0;
1215 if (*spec == '#' && strlen(spec) == 7) {
1216 static unsigned const char hex[] = { // yeah yeah, shoot me.
1217 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1218 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1219 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1220 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1221 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1222 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1223 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1224 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1225 r = (hex[spec[1]] << 4) | hex[spec[2]];
1226 g = (hex[spec[3]] << 4) | hex[spec[4]];
1227 b = (hex[spec[5]] << 4) | hex[spec[6]];
1228 } else if (!strcasecmp(spec,"black")) {
1230 } else if (!strcasecmp(spec,"white")) {
1232 } else if (!strcasecmp(spec,"red")) {
1234 } else if (!strcasecmp(spec,"green")) {
1236 } else if (!strcasecmp(spec,"blue")) {
1238 } else if (!strcasecmp(spec,"cyan")) {
1240 } else if (!strcasecmp(spec,"magenta")) {
1242 } else if (!strcasecmp(spec,"yellow")) {
1248 ret->red = (r << 8) | r;
1249 ret->green = (g << 8) | g;
1250 ret->blue = (b << 8) | b;
1251 ret->flags = DoRed|DoGreen|DoBlue;
1256 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1257 XColor *screen_ret, XColor *exact_ret)
1259 if (! XParseColor (dpy, cmap, name, screen_ret))
1261 *exact_ret = *screen_ret;
1262 return XAllocColor (dpy, cmap, screen_ret);
1266 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1268 validate_pixel (color->pixel, 32, NO);
1269 unsigned char r = ((color->pixel >> 16) & 0xFF);
1270 unsigned char g = ((color->pixel >> 8) & 0xFF);
1271 unsigned char b = ((color->pixel ) & 0xFF);
1272 color->red = (r << 8) | r;
1273 color->green = (g << 8) | g;
1274 color->blue = (b << 8) | b;
1275 color->flags = DoRed|DoGreen|DoBlue;
1280 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1283 for (i = 0; i < n; i++)
1284 XQueryColor (dpy, cmap, &c[i]);
1289 static unsigned long
1290 ximage_getpixel_1 (XImage *ximage, int x, int y)
1292 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1296 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1299 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1301 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1306 static unsigned long
1307 ximage_getpixel_32 (XImage *ximage, int x, int y)
1309 return *((unsigned long *) ximage->data +
1310 (y * (ximage->bytes_per_line >> 2)) +
1315 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1317 *((unsigned long *) ximage->data +
1318 (y * (ximage->bytes_per_line >> 2)) +
1325 XInitImage (XImage *ximage)
1327 if (!ximage->bytes_per_line)
1328 ximage->bytes_per_line = (ximage->depth == 1
1329 ? (ximage->width + 7) / 8
1330 : ximage->width * 4);
1332 if (ximage->depth == 1) {
1333 ximage->f.put_pixel = ximage_putpixel_1;
1334 ximage->f.get_pixel = ximage_getpixel_1;
1335 } else if (ximage->depth == 32 || ximage->depth == 24) {
1336 ximage->f.put_pixel = ximage_putpixel_32;
1337 ximage->f.get_pixel = ximage_getpixel_32;
1346 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1347 int format, int offset, char *data,
1348 unsigned int width, unsigned int height,
1349 int bitmap_pad, int bytes_per_line)
1351 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1352 ximage->width = width;
1353 ximage->height = height;
1354 ximage->format = format;
1355 ximage->data = data;
1356 ximage->bitmap_unit = 8;
1357 ximage->byte_order = MSBFirst;
1358 ximage->bitmap_bit_order = ximage->byte_order;
1359 ximage->bitmap_pad = bitmap_pad;
1360 ximage->depth = depth;
1361 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1362 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1363 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1364 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1365 ximage->bytes_per_line = bytes_per_line;
1367 XInitImage (ximage);
1372 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1374 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1375 w, h, from->bitmap_pad, 0);
1376 to->data = (char *) malloc (h * to->bytes_per_line);
1378 if (x >= from->width)
1380 else if (x+w > from->width)
1381 w = from->width - x;
1383 if (y >= from->height)
1385 else if (y+h > from->height)
1386 h = from->height - y;
1389 for (ty = 0; ty < h; ty++)
1390 for (tx = 0; tx < w; tx++)
1391 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1396 XPixmapFormatValues *
1397 XListPixmapFormats (Display *dpy, int *n_ret)
1399 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1401 ret[0].bits_per_pixel = 32;
1402 ret[0].scanline_pad = 8;
1404 ret[1].bits_per_pixel = 1;
1405 ret[1].scanline_pad = 8;
1412 XGetPixel (XImage *ximage, int x, int y)
1414 return ximage->f.get_pixel (ximage, x, y);
1419 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1421 return ximage->f.put_pixel (ximage, x, y, pixel);
1425 XDestroyImage (XImage *ximage)
1427 if (ximage->data) free (ximage->data);
1434 flipbits (unsigned const char *in, unsigned char *out, int length)
1436 static const unsigned char table[256] = {
1437 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1438 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1439 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1440 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1441 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1442 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1443 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1444 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1445 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1446 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1447 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1448 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1449 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1450 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1451 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1452 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1453 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1454 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1455 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1456 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1457 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1458 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1459 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1460 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1461 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1462 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1463 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1464 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1465 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1466 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1467 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1468 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1470 while (--length > 0)
1471 *out++ = table[*in++];
1476 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1477 int src_x, int src_y, int dest_x, int dest_y,
1478 unsigned int w, unsigned int h)
1480 CGRect wr = d->frame;
1482 // Clip width and height to the bounds of the Drawable
1484 if (dest_x + w > wr.size.width) {
1485 if (dest_x > wr.size.width)
1487 w = wr.size.width - dest_x;
1489 if (dest_y + h > wr.size.height) {
1490 if (dest_y > wr.size.height)
1492 h = wr.size.height - dest_y;
1494 if (w <= 0 || h <= 0)
1497 // Clip width and height to the bounds of the XImage
1499 if (src_x + w > ximage->width) {
1500 if (src_x > ximage->width)
1502 w = ximage->width - src_x;
1504 if (src_y + h > ximage->height) {
1505 if (src_y > ximage->height)
1507 h = ximage->height - src_y;
1509 if (w <= 0 || h <= 0)
1512 if (gc && (gc->gcv.function == GXset ||
1513 gc->gcv.function == GXclear)) {
1514 // "set" and "clear" are dumb drawing modes that ignore the source
1515 // bits and just draw solid rectangles.
1516 set_color (d->cgc, (gc->gcv.function == GXset
1517 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1518 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1519 gc->depth, gc->gcv.alpha_allowed_p, YES);
1520 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1524 int bpl = ximage->bytes_per_line;
1525 int bpp = ximage->bits_per_pixel;
1526 int bsize = bpl * h;
1527 char *data = ximage->data;
1530 r.origin.x = wr.origin.x + dest_x;
1531 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1537 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1538 to create a CGImage from a sub-rectagle of the XImage.
1540 data += (src_y * bpl) + (src_x * 4);
1541 CGDataProviderRef prov =
1542 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1544 CGImageRef cgi = CGImageCreate (w, h,
1547 /* Need this for XPMs to have the right
1548 colors, e.g. the logo in "maze". */
1549 (kCGImageAlphaNoneSkipFirst |
1550 kCGBitmapByteOrder32Host),
1552 NULL, /* decode[] */
1553 NO, /* interpolate */
1554 kCGRenderingIntentDefault);
1555 CGDataProviderRelease (prov);
1556 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1557 CGContextDrawImage (d->cgc, r, cgi);
1558 CGImageRelease (cgi);
1560 } else { // (bpp == 1)
1562 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1564 #### However, the bit order within a byte in a 1bpp XImage is
1565 the wrong way around from what Quartz expects, so first we
1566 have to copy the data to reverse it. Shit! Maybe it
1567 would be worthwhile to go through the hacks and #ifdef
1568 each one that diddles 1bpp XImage->data directly...
1570 Assert ((src_x % 8) == 0,
1571 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1573 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1574 unsigned char *flipped = (unsigned char *) malloc (bsize);
1576 flipbits ((unsigned char *) data, flipped, bsize);
1578 CGDataProviderRef prov =
1579 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1580 CGImageRef mask = CGImageMaskCreate (w, h,
1583 NULL, /* decode[] */
1584 NO); /* interpolate */
1585 push_fg_gc (d, gc, YES);
1587 CGContextFillRect (d->cgc, r); // foreground color
1588 CGContextClipToMask (d->cgc, r, mask);
1589 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1590 CGContextFillRect (d->cgc, r); // background color
1594 CGDataProviderRelease (prov);
1595 CGImageRelease (mask);
1603 XGetImage (Display *dpy, Drawable d, int x, int y,
1604 unsigned int width, unsigned int height,
1605 unsigned long plane_mask, int format)
1607 const unsigned char *data = 0;
1608 int depth, ibpp, ibpl;
1609 NSBitmapImageRep *bm = 0;
1611 if (d->type == PIXMAP) {
1612 depth = d->pixmap.depth;
1613 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1614 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1615 data = CGBitmapContextGetData (d->cgc);
1616 Assert (data, "CGBitmapContextGetData failed");
1618 // get the bits (desired sub-rectangle) out of the NSView
1619 bm = [NSBitmapImageRep alloc];
1621 nsfrom.origin.x = x;
1622 nsfrom.origin.y = y;
1623 nsfrom.size.width = width;
1624 nsfrom.size.height = height;
1625 [bm initWithFocusedViewRect:nsfrom];
1627 ibpp = [bm bitsPerPixel];
1628 ibpl = [bm bytesPerRow];
1629 data = [bm bitmapData];
1630 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1632 data += (y * ibpl) + (x * (ibpp/8));
1635 format = (depth == 1 ? XYPixmap : ZPixmap);
1636 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1638 image->data = (char *) malloc (height * image->bytes_per_line);
1640 int obpl = image->bytes_per_line;
1642 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1643 means that on Intel it is BGRA when viewed by bytes (And BGR
1644 when using 24bpp packing).
1648 const unsigned char *iline = data;
1649 for (yy = y; yy < y+height; yy++) {
1651 const unsigned char *iline2 = iline;
1652 for (xx = x; xx < x+width; xx++) {
1654 iline2++; // ignore b or a
1655 iline2++; // ignore g or r
1656 unsigned char r = *iline2++; // r or g
1657 if (ibpp == 32) iline2++; // ignore a or b
1659 XPutPixel (image, xx, yy, (r ? 1 : 0));
1664 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1665 const unsigned char *iline = data;
1666 unsigned char *oline = (unsigned char *) image->data;
1667 oline += (y * obpl);
1668 for (yy = y; yy < y+height; yy++) {
1670 const unsigned char *iline2 = iline;
1671 unsigned char *oline2 = oline;
1672 for (xx = x; xx < x+width; xx++) {
1674 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1675 unsigned char r = *iline2++;
1676 unsigned char g = *iline2++;
1677 unsigned char b = *iline2++;
1678 unsigned long pixel = ((a << 24) |
1682 *((unsigned int *) oline2) = pixel;
1690 if (bm) [bm release];
1696 jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
1697 XRectangle *geom_ret)
1699 NSImage *nsimg = (NSImage *) nsimg_arg;
1701 // convert the NSImage to a CGImage via the toll-free-bridging
1702 // of NSData and CFData...
1704 NSData *nsdata = [NSBitmapImageRep
1705 TIFFRepresentationOfImageRepsInArray:
1706 [nsimg representations]];
1707 CFDataRef cfdata = (CFDataRef) nsdata;
1708 CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1709 CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1711 NSSize imgr = [nsimg size];
1712 CGRect winr = d->frame;
1713 float rw = winr.size.width / imgr.width;
1714 float rh = winr.size.height / imgr.height;
1715 float r = (rw < rh ? rw : rh);
1718 dst.size.width = imgr.width * r;
1719 dst.size.height = imgr.height * r;
1720 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1721 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1723 // Clear the part not covered by the image to background or black.
1725 if (d->type == WINDOW)
1726 XClearWindow (dpy, d);
1728 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1729 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1732 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1733 CGContextDrawImage (d->cgc, dst, cgi);
1736 CGImageRelease (cgi);
1739 geom_ret->x = dst.origin.x;
1740 geom_ret->y = dst.origin.y;
1741 geom_ret->width = dst.size.width;
1742 geom_ret->height = dst.size.height;
1748 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1750 unsigned int w, unsigned int h,
1751 unsigned long fg, unsigned int bg,
1754 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1755 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1756 (char *) data, w, h, 0, 0);
1758 gcv.foreground = fg;
1759 gcv.background = bg;
1760 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1761 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1764 XDestroyImage (image);
1769 XCreatePixmap (Display *dpy, Drawable d,
1770 unsigned int width, unsigned int height, unsigned int depth)
1772 char *data = (char *) malloc (width * height * 4);
1773 if (! data) return 0;
1775 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1777 p->frame.size.width = width;
1778 p->frame.size.height = height;
1779 p->pixmap.depth = depth;
1781 /* Quartz doesn't have a 1bpp image type.
1782 We used to use 8bpp gray images instead of 1bpp, but some Mac video
1783 don't support that! So we always use 32bpp, regardless of depth. */
1785 p->cgc = CGBitmapContextCreate (data, width, height,
1786 8, /* bits per component */
1787 width * 4, /* bpl */
1789 // Without this, it returns 0...
1790 kCGImageAlphaNoneSkipFirst
1792 Assert (p->cgc, "could not create CGBitmapContext");
1798 XFreePixmap (Display *d, Pixmap p)
1800 Assert (p->type == PIXMAP, "not a pixmap");
1801 CGContextRelease (p->cgc);
1808 copy_pixmap (Pixmap p)
1811 Assert (p->type == PIXMAP, "not a pixmap");
1812 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1814 CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
1819 /* Font metric terminology, as used by X11:
1821 "lbearing" is the distance from the logical origin to the leftmost pixel.
1822 If a character's ink extends to the left of the origin, it is negative.
1824 "rbearing" is the distance from the logical origin to the rightmost pixel.
1826 "descent" is the distance from the logical origin to the bottommost pixel.
1827 For characters with descenders, it is negative.
1829 "ascent" is the distance from the logical origin to the topmost pixel.
1830 It is the number of pixels above the baseline.
1832 "width" is the distance from the logical origin to the position where
1833 the logical origin of the next character should be placed.
1835 If "rbearing" is greater than "width", then this character overlaps the
1836 following character. If smaller, then there is trailing blank space.
1840 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1843 query_font (Font fid)
1845 if (!fid || !fid->nsfont) {
1846 NSLog(@"no NSFont in fid");
1849 if (![fid->nsfont fontName]) {
1850 NSLog(@"broken NSFont in fid");
1857 XFontStruct *f = &fid->metrics;
1858 XCharStruct *min = &f->min_bounds;
1859 XCharStruct *max = &f->max_bounds;
1861 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1864 f->min_char_or_byte2 = first;
1865 f->max_char_or_byte2 = last;
1866 f->default_char = 'M';
1867 f->ascent = CEIL ([fid->nsfont ascender]);
1868 f->descent = -CEIL ([fid->nsfont descender]);
1870 min->width = 255; // set to smaller values in the loop
1873 min->lbearing = 255;
1874 min->rbearing = 255;
1876 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1879 NSBezierPath *bpath = [NSBezierPath bezierPath];
1881 for (i = first; i <= last; i++) {
1882 unsigned char str[2];
1886 NSString *nsstr = [NSString stringWithCString:(char *) str
1887 encoding:NSISOLatin1StringEncoding];
1889 /* I can't believe we have to go through this bullshit just to
1890 convert a 'char' to an NSGlyph!!
1892 You might think that we could do
1893 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
1894 but that doesn't work; my guess is that glyphWithName expects
1895 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
1899 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
1900 [ts setFont:fid->nsfont];
1901 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
1902 NSTextContainer *tc = [[NSTextContainer alloc] init];
1903 [lm addTextContainer:tc];
1904 [tc release]; // lm retains tc
1905 [ts addLayoutManager:lm];
1906 [lm release]; // ts retains lm
1907 glyph = [lm glyphAtIndex:0];
1911 /* Compute the bounding box and advancement by converting the glyph
1912 to a bezier path. There appears to be *no other way* to find out
1913 the bounding box of a character: [NSFont boundingRectForGlyph] and
1914 [NSString sizeWithAttributes] both return an advancement-sized
1915 rectangle, not a rectangle completely enclosing the glyph's ink.
1917 NSPoint advancement;
1919 advancement.x = advancement.y = 0;
1920 [bpath removeAllPoints];
1921 [bpath moveToPoint:advancement];
1922 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
1923 advancement = [bpath currentPoint];
1924 bbox = [bpath bounds];
1926 /* Now that we know the advancement and bounding box, we can compute
1927 the lbearing and rbearing.
1929 XCharStruct *cs = &f->per_char[i-first];
1931 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
1932 cs->descent = CEIL(-bbox.origin.y);
1933 cs->lbearing = CEIL (bbox.origin.x);
1934 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
1935 cs->width = CEIL (advancement.x);
1937 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
1939 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
1942 max->width = MAX (max->width, cs->width);
1943 max->ascent = MAX (max->ascent, cs->ascent);
1944 max->descent = MAX (max->descent, cs->descent);
1945 max->lbearing = MAX (max->lbearing, cs->lbearing);
1946 max->rbearing = MAX (max->rbearing, cs->rbearing);
1948 min->width = MIN (min->width, cs->width);
1949 min->ascent = MIN (min->ascent, cs->ascent);
1950 min->descent = MIN (min->descent, cs->descent);
1951 min->lbearing = MIN (min->lbearing, cs->lbearing);
1952 min->rbearing = MIN (min->rbearing, cs->rbearing);
1957 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
1958 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
1959 i, i, cs->width, cs->lbearing, cs->rbearing,
1960 cs->ascent, cs->descent,
1961 (int) bbox.size.width, (int) bbox.size.height,
1962 (int) bbox.origin.x, (int) bbox.origin.y,
1963 (int) advancement.x, (int) advancement.y);
1970 // Since 'Font' includes the metrics, this just makes a copy of that.
1973 XQueryFont (Display *dpy, Font fid)
1976 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
1979 // copy XCharStruct array
1980 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
1981 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1982 memcpy (f->per_char, fid->metrics.per_char,
1983 size * sizeof (XCharStruct));
1990 copy_font (Font fid)
1992 // copy 'Font' struct
1993 Font fid2 = (Font) malloc (sizeof(*fid2));
1996 // copy XCharStruct array
1997 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
1998 fid2->metrics.per_char = (XCharStruct *)
1999 malloc ((size + 2) * sizeof (XCharStruct));
2000 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2001 size * sizeof (XCharStruct));
2003 // copy the other pointers
2004 fid2->ps_name = strdup (fid->ps_name);
2005 // [fid2->nsfont retain];
2006 fid2->metrics.fid = fid2;
2013 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2016 Assert (size > 0, "zero font size");
2017 const char *prefix = (fixed ? "Monaco" : (serif ? "Times" : "Helvetica"));
2018 const char *suffix = (bold && ital
2019 ? (serif ? "-BoldItalic" : "-BoldOblique")
2021 ital ? (serif ? "-Italic" : "-Oblique") : ""));
2022 char *name = (char *) malloc (strlen(prefix) + strlen(suffix) + 1);
2023 strcpy (name, prefix);
2024 strcat (name, suffix);
2026 NSString *nsname = [NSString stringWithCString:name
2027 encoding:NSUTF8StringEncoding];
2028 NSFont *f = [NSFont fontWithName:nsname size:size];
2038 try_native_font (const char *name, char **name_ret, float *size_ret)
2040 if (!name) return 0;
2041 const char *spc = strrchr (name, ' ');
2044 if (1 != sscanf (spc, " %d ", &size)) return 0;
2045 if (size <= 4) return 0;
2047 char *name2 = strdup (name);
2048 name2[strlen(name2) - strlen(spc)] = 0;
2049 NSString *nsname = [NSString stringWithCString:name2
2050 encoding:NSUTF8StringEncoding];
2051 NSFont *f = [NSFont fontWithName:nsname size:size];
2063 /* Returns a random font in the given size and face.
2066 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2068 NSFontTraitMask mask = ((bold ? NSUnboldFontMask : NSBoldFontMask) |
2069 (ital ? NSUnitalicFontMask : NSItalicFontMask));
2070 NSArray *fonts = [[NSFontManager sharedFontManager]
2071 availableFontNamesWithTraits:mask];
2072 if (!fonts) return 0;
2074 int n = [fonts count];
2075 if (n <= 0) return 0;
2078 for (j = 0; j < n; j++) {
2079 int i = random() % n;
2080 NSString *name = [fonts objectAtIndex:i];
2081 NSFont *f = [NSFont fontWithName:name size:size];
2084 /* Don't use this font if it (probably) doesn't include ASCII characters.
2086 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2087 if (! (enc == NSUTF8StringEncoding ||
2088 enc == NSISOLatin1StringEncoding ||
2089 enc == NSNonLossyASCIIStringEncoding ||
2090 enc == NSISOLatin2StringEncoding ||
2091 enc == NSUnicodeStringEncoding ||
2092 enc == NSWindowsCP1250StringEncoding ||
2093 enc == NSWindowsCP1252StringEncoding ||
2094 enc == NSMacOSRomanStringEncoding)) {
2095 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2098 // NSLog(@"using \"%@\": %d", name, enc);
2100 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2104 // None of the fonts support ASCII?
2110 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2121 const char *s = (name ? name : "");
2123 while (*s && (*s == '*' || *s == '-'))
2126 while (*s2 && (*s2 != '*' && *s2 != '-'))
2132 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2133 else if (CMP ("random")) rand = YES;
2134 else if (CMP ("bold")) bold = YES;
2135 else if (CMP ("i")) ital = YES;
2136 else if (CMP ("o")) ital = YES;
2137 else if (CMP ("courier")) fixed = YES;
2138 else if (CMP ("fixed")) fixed = YES;
2139 else if (CMP ("m")) fixed = YES;
2140 else if (CMP ("times")) serif = YES;
2141 else if (CMP ("6x10")) fixed = YES, size = 8;
2142 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2143 else if (CMP ("9x15")) fixed = YES, size = 12;
2144 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2145 else if (CMP ("vga")) fixed = YES, size = 12;
2146 else if (CMP ("console")) fixed = YES, size = 12;
2147 else if (CMP ("gallant")) fixed = YES, size = 12;
2149 else if (size == 0) {
2151 if (1 == sscanf (s, " %d ", &n))
2158 if (size < 6 || size > 1000)
2162 nsfont = random_font (bold, ital, size, &ps_name);
2165 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2167 // if that didn't work, turn off attibutes until it does
2168 // (e.g., there is no "Monaco-Bold".)
2170 if (!nsfont && serif) {
2172 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2174 if (!nsfont && ital) {
2176 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2178 if (!nsfont && bold) {
2180 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2182 if (!nsfont && fixed) {
2184 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2188 *name_ret = ps_name;
2198 XLoadFont (Display *dpy, const char *name)
2200 Font fid = (Font) calloc (1, sizeof(*fid));
2202 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2204 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2206 NSLog(@"no NSFont for \"%s\"", name);
2210 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2218 /* This translates the NSFont into the numbers that aglUseFont() wants.
2221 jwxyz_font_info (Font f, int *size_ret, int *face_ret)
2223 char *name = strdup (f->ps_name);
2224 char *dash = strchr (name, '-');
2228 // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
2229 if (strcasestr (dash, "bold")) flags |= 1;
2230 if (strcasestr (dash, "italic")) flags |= 2;
2231 if (strcasestr (dash, "oblique")) flags |= 2;
2234 NSString *nname = [NSString stringWithCString:name
2235 encoding:NSUTF8StringEncoding];
2236 ATSFontFamilyRef id =
2237 ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
2240 // WTF? aglUseFont gets a BadValue if size is small!!
2241 if (size < 9) size = 9;
2243 //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
2244 Assert (id >= 0, "no ATS font family");
2253 XLoadQueryFont (Display *dpy, const char *name)
2255 Font fid = XLoadFont (dpy, name);
2256 return XQueryFont (dpy, fid);
2260 XUnloadFont (Display *dpy, Font fid)
2262 free (fid->ps_name);
2263 free (fid->metrics.per_char);
2265 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2266 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2267 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2268 // They're probably not very big...
2270 // [fid->nsfont release];
2277 XFreeFontInfo (char **names, XFontStruct *info, int n)
2281 for (i = 0; i < n; i++)
2282 if (names[i]) free (names[i]);
2286 for (i = 0; i < n; i++)
2287 if (info[i].per_char)
2288 free (info[i].per_char);
2295 XFreeFont (Display *dpy, XFontStruct *f)
2298 XFreeFontInfo (0, f, 1);
2299 XUnloadFont (dpy, fid);
2305 XSetFont (Display *dpy, GC gc, Font fid)
2308 XUnloadFont (dpy, gc->gcv.font);
2309 gc->gcv.font = copy_font (fid);
2310 [gc->gcv.font->nsfont retain];
2315 XTextExtents (XFontStruct *f, const char *s, int length,
2316 int *dir_ret, int *ascent_ret, int *descent_ret,
2319 memset (cs, 0, sizeof(*cs));
2321 for (i = 0; i < length; i++) {
2322 unsigned char c = (unsigned char) s[i];
2323 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2324 c = f->default_char;
2325 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2329 cs->ascent = MAX (cs->ascent, cc->ascent);
2330 cs->descent = MAX (cs->descent, cc->descent);
2331 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2332 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2333 cs->width += cc->width;
2337 *ascent_ret = f->ascent;
2338 *descent_ret = f->descent;
2343 XTextWidth (XFontStruct *f, const char *s, int length)
2345 int ascent, descent, dir;
2347 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2353 set_font (CGContextRef cgc, GC gc)
2355 Font font = gc->gcv.font;
2357 font = XLoadFont (0, 0);
2358 gc->gcv.font = font;
2359 [gc->gcv.font->nsfont retain];
2361 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2362 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2367 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2368 const char *str, int len, BOOL clear_background_p)
2370 if (clear_background_p) {
2371 int ascent, descent, dir;
2373 XTextExtents (&gc->gcv.font->metrics, str, len,
2374 &dir, &ascent, &descent, &cs);
2375 draw_rect (dpy, d, gc,
2376 x + MIN (0, cs.lbearing),
2377 y - MAX (0, ascent),
2378 MAX (MAX (0, cs.rbearing) -
2379 MIN (0, cs.lbearing),
2381 MAX (0, ascent) + MAX (0, descent),
2385 CGRect wr = d->frame;
2388 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2389 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2392 push_fg_gc (d, gc, YES);
2393 set_font (d->cgc, gc);
2395 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2396 if (! gc->gcv.antialias_p)
2397 CGContextSetShouldAntialias (d->cgc, YES); // always antialias text
2398 CGContextShowTextAtPoint (d->cgc,
2400 wr.origin.y + wr.size.height - y,
2409 unsigned long argb = gc->gcv.foreground;
2410 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2411 float a = ((argb >> 24) & 0xFF) / 255.0;
2412 float r = ((argb >> 16) & 0xFF) / 255.0;
2413 float g = ((argb >> 8) & 0xFF) / 255.0;
2414 float b = ((argb ) & 0xFF) / 255.0;
2415 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2416 NSDictionary *attr =
2417 [NSDictionary dictionaryWithObjectsAndKeys:
2418 gc->gcv.font->nsfont, NSFontAttributeName,
2419 fg, NSForegroundColorAttributeName,
2421 char *s2 = (char *) malloc (len + 1);
2422 strncpy (s2, str, len);
2424 NSString *nsstr = [NSString stringWithCString:s2
2425 encoding:NSISOLatin1StringEncoding];
2428 pos.x = wr.origin.x + x;
2429 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2430 [nsstr drawAtPoint:pos withAttributes:attr];
2439 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2440 const char *str, int len)
2442 return draw_string (dpy, d, gc, x, y, str, len, NO);
2446 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2447 const char *str, int len)
2449 return draw_string (dpy, d, gc, x, y, str, len, YES);
2454 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2456 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2457 gc->gcv.foreground = fg;
2463 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2465 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2466 gc->gcv.background = bg;
2471 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2473 gc->gcv.alpha_allowed_p = allowed;
2478 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2480 gc->gcv.antialias_p = antialias_p;
2486 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2487 int line_style, int cap_style, int join_style)
2489 gc->gcv.line_width = line_width;
2490 Assert (line_style == LineSolid, "only LineSolid implemented");
2491 // gc->gcv.line_style = line_style;
2492 gc->gcv.cap_style = cap_style;
2493 gc->gcv.join_style = join_style;
2498 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2504 XSetFunction (Display *dpy, GC gc, int which)
2506 gc->gcv.function = which;
2511 XSetSubwindowMode (Display *dpy, GC gc, int which)
2513 gc->gcv.subwindow_mode = which;
2518 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2520 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2522 if (gc->gcv.clip_mask) {
2523 XFreePixmap (dpy, gc->gcv.clip_mask);
2524 CGImageRelease (gc->clip_mask);
2527 gc->gcv.clip_mask = copy_pixmap (m);
2528 if (gc->gcv.clip_mask)
2529 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2537 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2539 gc->gcv.clip_x_origin = x;
2540 gc->gcv.clip_y_origin = y;
2546 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2547 int *root_x_ret, int *root_y_ret,
2548 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2550 Assert (w->type == WINDOW, "not a window");
2551 NSWindow *nsw = [w->window.view window];
2553 // get bottom left of window on screen, from bottom left
2554 wpos.x = wpos.y = 0;
2555 wpos = [nsw convertBaseToScreen:wpos];
2558 // get bottom left of view on window, from bottom left
2559 vpos.x = vpos.y = 0;
2560 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2562 // get bottom left of view on screen, from bottom left
2566 // get top left of view on screen, from bottom left
2567 vpos.y += w->frame.size.height;
2569 // get top left of view on screen, from top left
2570 NSArray *screens = [NSScreen screens];
2571 NSScreen *screen = (screens && [screens count] > 0
2572 ? [screens objectAtIndex:0]
2573 : [NSScreen mainScreen]);
2574 NSRect srect = [screen frame];
2575 vpos.y = srect.size.height - vpos.y;
2577 // get the mouse position on window, from bottom left
2578 NSEvent *e = [NSApp currentEvent];
2579 NSPoint p = [e locationInWindow];
2581 // get mouse position on screen, from bottom left
2585 // get mouse position on screen, from top left
2586 p.y = srect.size.height - p.y;
2588 if (root_x_ret) *root_x_ret = (int) p.x;
2589 if (root_y_ret) *root_y_ret = (int) p.y;
2590 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2591 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2593 if (mask_ret) *mask_ret = 0; // ####
2594 if (root_ret) *root_ret = 0;
2595 if (child_ret) *child_ret = 0;
2600 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2601 int src_x, int src_y,
2602 int *dest_x_ret, int *dest_y_ret,
2605 Assert (w->type == WINDOW, "not a window");
2606 NSWindow *nsw = [w->window.view window];
2608 // get bottom left of window on screen, from bottom left
2609 wpos.x = wpos.y = 0;
2610 wpos = [nsw convertBaseToScreen:wpos];
2613 // get bottom left of view on window, from bottom left
2614 vpos.x = vpos.y = 0;
2615 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2617 // get bottom left of view on screen, from bottom left
2621 // get top left of view on screen, from bottom left
2622 vpos.y += w->frame.size.height;
2624 // get top left of view on screen, from top left
2625 NSArray *screens = [NSScreen screens];
2626 NSScreen *screen = (screens && [screens count] > 0
2627 ? [screens objectAtIndex:0]
2628 : [NSScreen mainScreen]);
2629 NSRect srect = [screen frame];
2630 vpos.y = srect.size.height - vpos.y;
2632 // point starts out relative to top left of view
2637 // get point relative to top left of screen
2650 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2656 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2659 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2660 char c = (char) ks; // could be smarter about modifiers here...
2661 if (k_ret) *k_ret = ks;
2662 if (size > 0) buf[0] = c;
2663 if (size > 1) buf[1] = 0;
2669 XFlush (Display *dpy)
2671 // Just let the event loop take care of this on its own schedule.
2676 XSync (Display *dpy, Bool flush)
2678 return XFlush (dpy);
2682 // declared in utils/visual.h
2684 has_writable_cells (Screen *s, Visual *v)
2690 visual_depth (Screen *s, Visual *v)
2696 visual_cells (Screen *s, Visual *v)
2702 visual_class (Screen *s, Visual *v)
2707 // declared in utils/grabclient.h
2709 use_subwindow_mode_p (Screen *screen, Window window)