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 push_fg_gc (d, gc, YES);
400 # ifdef XDRAWPOINTS_IMAGES
402 unsigned int argb = gc->gcv.foreground;
403 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
405 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
407 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
408 CGImageRef cgi = CGImageCreate (1, 1,
411 /* Host-ordered, since we're using the
412 address of an int as the color data. */
413 (kCGImageAlphaNoneSkipFirst |
414 kCGBitmapByteOrder32Host),
417 NO, /* interpolate */
418 kCGRenderingIntentDefault);
419 CGDataProviderRelease (prov);
421 CGContextRef cgc = d->cgc;
423 rect.size.width = rect.size.height = 1;
424 for (i = 0; i < count; i++) {
425 if (i > 0 && mode == CoordModePrevious) {
426 rect.origin.x += points->x;
427 rect.origin.x -= points->y;
429 rect.origin.x = wr.origin.x + points->x;
430 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
433 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
434 CGContextDrawImage (cgc, rect, cgi);
438 CGImageRelease (cgi);
440 # else /* ! XDRAWPOINTS_IMAGES */
442 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
445 for (i = 0; i < count; i++) {
446 r->size.width = r->size.height = 1;
447 if (i > 0 && mode == CoordModePrevious) {
448 r->origin.x = r[-1].origin.x + points->x;
449 r->origin.y = r[-1].origin.x - points->y;
451 r->origin.x = wr.origin.x + points->x;
452 r->origin.y = wr.origin.y + wr.size.height - points->y;
458 CGContextFillRects (d->cgc, rects, count);
461 # endif /* ! XDRAWPOINTS_IMAGES */
470 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
475 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
479 static void draw_rect (Display *, Drawable, GC,
480 int x, int y, unsigned int width, unsigned int height,
481 BOOL foreground_p, BOOL fill_p);
484 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
485 int src_x, int src_y,
486 unsigned int width, unsigned int height,
487 int dst_x, int dst_y)
489 Assert ((width < 65535), "improbably large width");
490 Assert ((height < 65535), "improbably large height");
491 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
492 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
493 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
494 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
496 if (width == 0 || height == 0)
499 if (gc && (gc->gcv.function == GXset ||
500 gc->gcv.function == GXclear)) {
501 // "set" and "clear" are dumb drawing modes that ignore the source
502 // bits and just draw solid rectangles.
503 set_color (dst->cgc, (gc->gcv.function == GXset
504 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
505 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
506 gc->depth, gc->gcv.alpha_allowed_p, YES);
507 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
511 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
512 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
513 // bounds of their drawables.
514 BOOL clipped = NO; // Whether we did any clipping of the rects.
516 src_frame = src->frame;
517 dst_frame = dst->frame;
519 // Initialize src_rect...
521 src_rect.origin.x = src_frame.origin.x + src_x;
522 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
524 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
525 src_rect.size.width = width;
526 src_rect.size.height = height;
528 // Initialize dst_rect...
530 dst_rect.origin.x = dst_frame.origin.x + dst_x;
531 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
533 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
534 dst_rect.size.width = width;
535 dst_rect.size.height = height;
537 // Clip rects to frames...
539 // CGRect orig_src_rect = src_rect;
540 CGRect orig_dst_rect = dst_rect;
542 # define CLIP(THIS,THAT,VAL,SIZE) do { \
543 float off = THIS##_rect.origin.VAL; \
546 THIS##_rect.size.SIZE += off; \
547 THAT##_rect.size.SIZE += off; \
548 THIS##_rect.origin.VAL -= off; \
549 THAT##_rect.origin.VAL -= off; \
551 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
552 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
555 THIS##_rect.size.SIZE -= off; \
556 THAT##_rect.size.SIZE -= off; \
559 CLIP (dst, src, x, width);
560 CLIP (dst, src, y, height);
561 CLIP (src, dst, x, width);
562 CLIP (src, dst, y, height);
566 Assert (src_rect.size.width == dst_rect.size.width, "width out of sync");
567 Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
568 Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
569 Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
570 Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
571 Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
572 Assert (src_rect.origin.x + src_rect.size.width <=
573 src_frame.origin.x + src_frame.size.width, "clip failed src_width");
576 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
579 NSObject *releaseme = 0;
583 if (src->type == PIXMAP) {
585 // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
586 // but it presumably shares the data pointer instead of copying it.
587 cgi = CGBitmapContextCreateImage (src->cgc);
589 // if doing a sub-rect, trim it down.
590 if (src_rect.origin.x != src_frame.origin.x ||
591 src_rect.origin.y != src_frame.origin.y ||
592 src_rect.size.width != src_frame.size.width ||
593 src_rect.size.height != src_frame.size.height) {
594 // #### I don't understand why this is needed...
595 src_rect.origin.y = (src_frame.size.height -
596 src_rect.size.height - src_rect.origin.y);
597 // This does not copy image data, so it should be fast.
598 CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
599 CGImageRelease (cgi);
603 if (src->pixmap.depth == 1)
606 } else { /* (src->type == WINDOW) */
609 nsfrom.origin.x = src_rect.origin.x;
610 nsfrom.origin.y = src_rect.origin.y;
611 nsfrom.size.width = src_rect.size.width;
612 nsfrom.size.height = src_rect.size.height;
615 // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
617 NSBitmapImageRep *bm = [NSBitmapImageRep alloc];
618 [bm initWithFocusedViewRect:nsfrom];
619 unsigned char *data = [bm bitmapData];
620 int bps = [bm bitsPerSample];
621 int bpp = [bm bitsPerPixel];
622 int bpl = [bm bytesPerRow];
627 // QuickDraw way (doesn't work, need NSQuickDrawView)
628 PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
629 char **data = GetPortPixMap (pix);
632 int bpl = GetPixRowBytes (pix) & 0x3FFF;
636 // get the bits (desired sub-rectangle) out of the raw frame buffer.
637 // (This renders wrong, and appears to be even slower anyway.)
639 int window_x, window_y;
640 XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
641 window_x += nsfrom.origin.x;
642 window_y += (dst->frame.size.height
643 - (nsfrom.origin.y + nsfrom.size.height));
645 unsigned char *data = (unsigned char *)
646 CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
647 int bps = CGDisplayBitsPerSample (dpy->cgdpy);
648 int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
649 int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
653 // create a CGImage from those bits
655 CGDataProviderRef prov =
656 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
658 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
661 /* Use whatever default bit ordering we got from
662 initWithFocusedViewRect. I would have assumed
663 that it was (kCGImageAlphaNoneSkipFirst |
664 kCGBitmapByteOrder32Host), but on Intel,
670 NO, /* interpolate */
671 kCGRenderingIntentDefault);
672 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
673 CGDataProviderRelease (prov);
676 if (mask_p) { // src depth == 1
678 push_bg_gc (dst, gc, YES);
680 // fill the destination rectangle with solid background...
681 CGContextFillRect (dst->cgc, orig_dst_rect);
683 // then fill in a solid rectangle of the fg color, using the image as an
684 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
685 set_color (dst->cgc, gc->gcv.foreground, gc->depth,
686 gc->gcv.alpha_allowed_p, YES);
687 CGContextClipToMask (dst->cgc, dst_rect, cgi);
688 CGContextFillRect (dst->cgc, dst_rect);
692 } else { // src depth > 1
696 // If either the src or dst rects did not lie within their drawables,
697 // then we have adjusted both the src and dst rects to account for
698 // the clipping; that means we need to first clear to the background,
699 // so that clipped bits end up in the bg color instead of simply not
703 set_color (dst->cgc, gc->gcv.background, gc->depth,
704 gc->gcv.alpha_allowed_p, YES);
705 CGContextFillRect (dst->cgc, orig_dst_rect);
708 // copy the CGImage onto the destination CGContext
709 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
710 CGContextDrawImage (dst->cgc, dst_rect, cgi);
715 CGImageRelease (cgi);
716 if (releaseme) [releaseme release];
722 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
723 int src_x, int src_y,
724 unsigned width, int height,
725 int dest_x, int dest_y, unsigned long plane)
727 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
729 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
730 // not to white/black.
731 return XCopyArea (dpy, src, dest, gc,
732 src_x, src_y, width, height, dest_x, dest_y);
737 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
739 // when drawing a zero-length line, obey line-width and cap-style.
740 if (x1 == x2 && y1 == y2) {
741 int w = gc->gcv.line_width;
744 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
745 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
747 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
750 CGRect wr = d->frame;
752 p.x = wr.origin.x + x1;
753 p.y = wr.origin.y + wr.size.height - y1;
755 push_fg_gc (d, gc, NO);
757 set_line_mode (d->cgc, &gc->gcv);
758 CGContextBeginPath (d->cgc);
759 CGContextMoveToPoint (d->cgc, p.x, p.y);
760 p.x = wr.origin.x + x2;
761 p.y = wr.origin.y + wr.size.height - y2;
762 CGContextAddLineToPoint (d->cgc, p.x, p.y);
763 CGContextStrokePath (d->cgc);
769 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
774 CGRect wr = d->frame;
775 push_fg_gc (d, gc, NO);
776 set_line_mode (d->cgc, &gc->gcv);
778 // if the first and last points coincide, use closepath to get
779 // the proper line-joining.
780 BOOL closed_p = (points[0].x == points[count-1].x &&
781 points[0].y == points[count-1].y);
782 if (closed_p) count--;
784 p.x = wr.origin.x + points->x;
785 p.y = wr.origin.y + wr.size.height - points->y;
787 CGContextBeginPath (d->cgc);
788 CGContextMoveToPoint (d->cgc, p.x, p.y);
789 for (i = 1; i < count; i++) {
790 if (mode == CoordModePrevious) {
794 p.x = wr.origin.x + points->x;
795 p.y = wr.origin.y + wr.size.height - points->y;
797 CGContextAddLineToPoint (d->cgc, p.x, p.y);
800 if (closed_p) CGContextClosePath (d->cgc);
801 CGContextStrokePath (d->cgc);
808 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
811 CGRect wr = d->frame;
813 push_fg_gc (d, gc, NO);
814 set_line_mode (d->cgc, &gc->gcv);
815 CGContextBeginPath (d->cgc);
816 for (i = 0; i < count; i++) {
817 CGContextMoveToPoint (d->cgc,
818 wr.origin.x + segments->x1,
819 wr.origin.y + wr.size.height - segments->y1);
820 CGContextAddLineToPoint (d->cgc,
821 wr.origin.x + segments->x2,
822 wr.origin.y + wr.size.height - segments->y2);
825 CGContextStrokePath (d->cgc);
832 XClearWindow (Display *dpy, Window win)
834 Assert (win->type == WINDOW, "not a window");
835 CGRect wr = win->frame;
836 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
840 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
842 Assert (w->type == WINDOW, "not a window");
843 validate_pixel (pixel, 32, NO);
844 w->window.background = pixel;
849 draw_rect (Display *dpy, Drawable d, GC gc,
850 int x, int y, unsigned int width, unsigned int height,
851 BOOL foreground_p, BOOL fill_p)
853 CGRect wr = d->frame;
855 r.origin.x = wr.origin.x + x;
856 r.origin.y = wr.origin.y + wr.size.height - y - height;
857 r.size.width = width;
858 r.size.height = height;
862 push_fg_gc (d, gc, fill_p);
864 push_bg_gc (d, gc, fill_p);
868 CGContextFillRect (d->cgc, r);
871 set_line_mode (d->cgc, &gc->gcv);
872 CGContextStrokeRect (d->cgc, r);
881 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
882 unsigned int width, unsigned int height)
884 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
889 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
890 unsigned int width, unsigned int height)
892 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
897 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
899 CGRect wr = d->frame;
901 push_fg_gc (d, gc, YES);
902 for (i = 0; i < n; i++) {
904 r.origin.x = wr.origin.x + rects->x;
905 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
906 r.size.width = rects->width;
907 r.size.height = rects->height;
908 CGContextFillRect (d->cgc, r);
917 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
919 Assert (win->type == WINDOW, "not a window");
920 set_color (win->cgc, win->window.background, 32, NO, YES);
921 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
927 XFillPolygon (Display *dpy, Drawable d, GC gc,
928 XPoint *points, int npoints, int shape, int mode)
930 CGRect wr = d->frame;
932 push_fg_gc (d, gc, YES);
933 CGContextBeginPath (d->cgc);
934 for (i = 0; i < npoints; i++) {
936 if (i > 0 && mode == CoordModePrevious) {
940 x = wr.origin.x + points[i].x;
941 y = wr.origin.y + wr.size.height - points[i].y;
945 CGContextMoveToPoint (d->cgc, x, y);
947 CGContextAddLineToPoint (d->cgc, x, y);
949 CGContextClosePath (d->cgc);
950 if (gc->gcv.fill_rule == EvenOddRule)
951 CGContextEOFillPath (d->cgc);
953 CGContextFillPath (d->cgc);
958 #define radians(DEG) ((DEG) * M_PI / 180.0)
959 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
962 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
963 unsigned int width, unsigned int height, int angle1, int angle2,
966 CGRect wr = d->frame;
968 bound.origin.x = wr.origin.x + x;
969 bound.origin.y = wr.origin.y + wr.size.height - y - height;
970 bound.size.width = width;
971 bound.size.height = height;
974 ctr.x = bound.origin.x + bound.size.width /2;
975 ctr.y = bound.origin.y + bound.size.height/2;
977 float r1 = radians (angle1/64.0);
978 float r2 = radians (angle2/64.0) + r1;
979 BOOL clockwise = angle2 < 0;
980 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
982 push_fg_gc (d, gc, fill_p);
984 CGContextBeginPath (d->cgc);
986 CGContextSaveGState(d->cgc);
987 CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
988 CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
990 CGContextMoveToPoint (d->cgc, 0, 0);
992 CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
993 CGContextRestoreGState (d->cgc); // restore before stroke, for line width
996 CGContextClosePath (d->cgc); // for proper line joining
999 CGContextFillPath (d->cgc);
1001 set_line_mode (d->cgc, &gc->gcv);
1002 CGContextStrokePath (d->cgc);
1010 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1011 unsigned int width, unsigned int height, int angle1, int angle2)
1013 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1017 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1018 unsigned int width, unsigned int height, int angle1, int angle2)
1020 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1024 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1027 for (i = 0; i < narcs; i++)
1028 draw_arc (dpy, d, gc,
1029 arcs[i].x, arcs[i].y,
1030 arcs[i].width, arcs[i].height,
1031 arcs[i].angle1, arcs[i].angle2,
1037 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1040 for (i = 0; i < narcs; i++)
1041 draw_arc (dpy, d, gc,
1042 arcs[i].x, arcs[i].y,
1043 arcs[i].width, arcs[i].height,
1044 arcs[i].angle1, arcs[i].angle2,
1051 gcv_defaults (XGCValues *gcv, int depth)
1053 memset (gcv, 0, sizeof(*gcv));
1054 gcv->function = GXcopy;
1055 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1056 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1057 gcv->line_width = 1;
1058 gcv->cap_style = CapNotLast;
1059 gcv->join_style = JoinMiter;
1060 gcv->fill_rule = EvenOddRule;
1062 gcv->alpha_allowed_p = NO;
1063 gcv->antialias_p = YES;
1067 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1069 if (mask & GCFunction) gc->gcv.function = from->function;
1070 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1071 if (mask & GCBackground) gc->gcv.background = from->background;
1072 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1073 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1074 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1075 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1076 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1077 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1078 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1080 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1081 if (mask & GCFont) XSetFont (0, gc, from->font);
1083 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1084 gc->gcv.alpha_allowed_p);
1085 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1086 gc->gcv.alpha_allowed_p);
1088 if (mask & GCLineStyle) abort();
1089 if (mask & GCPlaneMask) abort();
1090 if (mask & GCFillStyle) abort();
1091 if (mask & GCTile) abort();
1092 if (mask & GCStipple) abort();
1093 if (mask & GCTileStipXOrigin) abort();
1094 if (mask & GCTileStipYOrigin) abort();
1095 if (mask & GCGraphicsExposures) abort();
1096 if (mask & GCDashOffset) abort();
1097 if (mask & GCDashList) abort();
1098 if (mask & GCArcMode) abort();
1103 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1105 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1106 if (d->type == WINDOW) {
1108 } else { /* (d->type == PIXMAP) */
1109 gc->depth = d->pixmap.depth;
1112 gcv_defaults (&gc->gcv, gc->depth);
1113 set_gcv (gc, xgcv, mask);
1118 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1120 set_gcv (gc, gcv, mask);
1126 XFreeGC (Display *dpy, GC gc)
1129 XUnloadFont (dpy, gc->gcv.font);
1131 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1133 if (gc->gcv.clip_mask) {
1134 XFreePixmap (dpy, gc->gcv.clip_mask);
1135 CGImageRelease (gc->clip_mask);
1143 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1145 Assert (w->type == WINDOW, "not a window");
1146 memset (xgwa, 0, sizeof(*xgwa));
1147 xgwa->x = w->frame.origin.x;
1148 xgwa->y = w->frame.origin.y;
1149 xgwa->width = w->frame.size.width;
1150 xgwa->height = w->frame.size.height;
1152 xgwa->screen = dpy->screen;
1153 xgwa->visual = dpy->screen->visual;
1158 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1159 int *x_ret, int *y_ret,
1160 unsigned int *w_ret, unsigned int *h_ret,
1161 unsigned int *bw_ret, unsigned int *d_ret)
1163 *x_ret = d->frame.origin.x;
1164 *y_ret = d->frame.origin.y;
1165 *w_ret = d->frame.size.width;
1166 *h_ret = d->frame.size.height;
1167 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1168 *root_ret = RootWindow (dpy, 0);
1175 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1177 // store 32 bit ARGB in the pixel field.
1178 color->pixel = (( 0xFF << 24) |
1179 (((color->red >> 8) & 0xFF) << 16) |
1180 (((color->green >> 8) & 0xFF) << 8) |
1181 (((color->blue >> 8) & 0xFF) ));
1186 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1187 unsigned long *pmret, unsigned int npl,
1188 unsigned long *pxret, unsigned int npx)
1194 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1196 Assert(0, "XStoreColors called");
1201 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1203 Assert(0, "XStoreColor called");
1208 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1209 unsigned long planes)
1215 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1217 unsigned char r=0, g=0, b=0;
1218 if (*spec == '#' && strlen(spec) == 7) {
1219 static unsigned const char hex[] = { // yeah yeah, shoot me.
1220 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,
1221 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,
1222 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,
1223 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,
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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1226 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,
1227 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1228 r = (hex[spec[1]] << 4) | hex[spec[2]];
1229 g = (hex[spec[3]] << 4) | hex[spec[4]];
1230 b = (hex[spec[5]] << 4) | hex[spec[6]];
1231 } else if (!strcasecmp(spec,"black")) {
1233 } else if (!strcasecmp(spec,"white")) {
1235 } else if (!strcasecmp(spec,"red")) {
1237 } else if (!strcasecmp(spec,"green")) {
1239 } else if (!strcasecmp(spec,"blue")) {
1241 } else if (!strcasecmp(spec,"cyan")) {
1243 } else if (!strcasecmp(spec,"magenta")) {
1245 } else if (!strcasecmp(spec,"yellow")) {
1251 ret->red = (r << 8) | r;
1252 ret->green = (g << 8) | g;
1253 ret->blue = (b << 8) | b;
1254 ret->flags = DoRed|DoGreen|DoBlue;
1259 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1260 XColor *screen_ret, XColor *exact_ret)
1262 if (! XParseColor (dpy, cmap, name, screen_ret))
1264 *exact_ret = *screen_ret;
1265 return XAllocColor (dpy, cmap, screen_ret);
1269 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1271 validate_pixel (color->pixel, 32, NO);
1272 unsigned char r = ((color->pixel >> 16) & 0xFF);
1273 unsigned char g = ((color->pixel >> 8) & 0xFF);
1274 unsigned char b = ((color->pixel ) & 0xFF);
1275 color->red = (r << 8) | r;
1276 color->green = (g << 8) | g;
1277 color->blue = (b << 8) | b;
1278 color->flags = DoRed|DoGreen|DoBlue;
1283 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1286 for (i = 0; i < n; i++)
1287 XQueryColor (dpy, cmap, &c[i]);
1292 static unsigned long
1293 ximage_getpixel_1 (XImage *ximage, int x, int y)
1295 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1299 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1302 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1304 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1309 static unsigned long
1310 ximage_getpixel_32 (XImage *ximage, int x, int y)
1312 return *((unsigned long *) ximage->data +
1313 (y * (ximage->bytes_per_line >> 2)) +
1318 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1320 *((unsigned long *) ximage->data +
1321 (y * (ximage->bytes_per_line >> 2)) +
1328 XInitImage (XImage *ximage)
1330 if (!ximage->bytes_per_line)
1331 ximage->bytes_per_line = (ximage->depth == 1
1332 ? (ximage->width + 7) / 8
1333 : ximage->width * 4);
1335 if (ximage->depth == 1) {
1336 ximage->f.put_pixel = ximage_putpixel_1;
1337 ximage->f.get_pixel = ximage_getpixel_1;
1338 } else if (ximage->depth == 32 || ximage->depth == 24) {
1339 ximage->f.put_pixel = ximage_putpixel_32;
1340 ximage->f.get_pixel = ximage_getpixel_32;
1349 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1350 int format, int offset, char *data,
1351 unsigned int width, unsigned int height,
1352 int bitmap_pad, int bytes_per_line)
1354 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1355 ximage->width = width;
1356 ximage->height = height;
1357 ximage->format = format;
1358 ximage->data = data;
1359 ximage->bitmap_unit = 8;
1360 ximage->byte_order = MSBFirst;
1361 ximage->bitmap_bit_order = ximage->byte_order;
1362 ximage->bitmap_pad = bitmap_pad;
1363 ximage->depth = depth;
1364 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1365 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1366 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1367 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1368 ximage->bytes_per_line = bytes_per_line;
1370 XInitImage (ximage);
1375 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1377 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1378 w, h, from->bitmap_pad, 0);
1379 to->data = (char *) malloc (h * to->bytes_per_line);
1381 if (x >= from->width)
1383 else if (x+w > from->width)
1384 w = from->width - x;
1386 if (y >= from->height)
1388 else if (y+h > from->height)
1389 h = from->height - y;
1392 for (ty = 0; ty < h; ty++)
1393 for (tx = 0; tx < w; tx++)
1394 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1399 XPixmapFormatValues *
1400 XListPixmapFormats (Display *dpy, int *n_ret)
1402 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1404 ret[0].bits_per_pixel = 32;
1405 ret[0].scanline_pad = 8;
1407 ret[1].bits_per_pixel = 1;
1408 ret[1].scanline_pad = 8;
1415 XGetPixel (XImage *ximage, int x, int y)
1417 return ximage->f.get_pixel (ximage, x, y);
1422 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1424 return ximage->f.put_pixel (ximage, x, y, pixel);
1428 XDestroyImage (XImage *ximage)
1430 if (ximage->data) free (ximage->data);
1437 flipbits (unsigned const char *in, unsigned char *out, int length)
1439 static const unsigned char table[256] = {
1440 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1441 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1442 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1443 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1444 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1445 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1446 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1447 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1448 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1449 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1450 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1451 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1452 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1453 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1454 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1455 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1456 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1457 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1458 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1459 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1460 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1461 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1462 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1463 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1464 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1465 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1466 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1467 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1468 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1469 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1470 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1471 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1473 while (--length > 0)
1474 *out++ = table[*in++];
1479 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1480 int src_x, int src_y, int dest_x, int dest_y,
1481 unsigned int w, unsigned int h)
1483 CGRect wr = d->frame;
1485 Assert ((w < 65535), "improbably large width");
1486 Assert ((h < 65535), "improbably large height");
1487 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1488 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1489 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1490 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1492 // Clip width and height to the bounds of the Drawable
1494 if (dest_x + w > wr.size.width) {
1495 if (dest_x > wr.size.width)
1497 w = wr.size.width - dest_x;
1499 if (dest_y + h > wr.size.height) {
1500 if (dest_y > wr.size.height)
1502 h = wr.size.height - dest_y;
1504 if (w <= 0 || h <= 0)
1507 // Clip width and height to the bounds of the XImage
1509 if (src_x + w > ximage->width) {
1510 if (src_x > ximage->width)
1512 w = ximage->width - src_x;
1514 if (src_y + h > ximage->height) {
1515 if (src_y > ximage->height)
1517 h = ximage->height - src_y;
1519 if (w <= 0 || h <= 0)
1522 if (gc && (gc->gcv.function == GXset ||
1523 gc->gcv.function == GXclear)) {
1524 // "set" and "clear" are dumb drawing modes that ignore the source
1525 // bits and just draw solid rectangles.
1526 set_color (d->cgc, (gc->gcv.function == GXset
1527 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1528 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1529 gc->depth, gc->gcv.alpha_allowed_p, YES);
1530 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1534 int bpl = ximage->bytes_per_line;
1535 int bpp = ximage->bits_per_pixel;
1536 int bsize = bpl * h;
1537 char *data = ximage->data;
1540 r.origin.x = wr.origin.x + dest_x;
1541 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1547 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1548 to create a CGImage from a sub-rectagle of the XImage.
1550 data += (src_y * bpl) + (src_x * 4);
1551 CGDataProviderRef prov =
1552 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1554 CGImageRef cgi = CGImageCreate (w, h,
1557 /* Need this for XPMs to have the right
1558 colors, e.g. the logo in "maze". */
1559 (kCGImageAlphaNoneSkipFirst |
1560 kCGBitmapByteOrder32Host),
1562 NULL, /* decode[] */
1563 NO, /* interpolate */
1564 kCGRenderingIntentDefault);
1565 CGDataProviderRelease (prov);
1566 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1567 CGContextDrawImage (d->cgc, r, cgi);
1568 CGImageRelease (cgi);
1570 } else { // (bpp == 1)
1572 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1574 #### However, the bit order within a byte in a 1bpp XImage is
1575 the wrong way around from what Quartz expects, so first we
1576 have to copy the data to reverse it. Shit! Maybe it
1577 would be worthwhile to go through the hacks and #ifdef
1578 each one that diddles 1bpp XImage->data directly...
1580 Assert ((src_x % 8) == 0,
1581 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1583 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1584 unsigned char *flipped = (unsigned char *) malloc (bsize);
1586 flipbits ((unsigned char *) data, flipped, bsize);
1588 CGDataProviderRef prov =
1589 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1590 CGImageRef mask = CGImageMaskCreate (w, h,
1593 NULL, /* decode[] */
1594 NO); /* interpolate */
1595 push_fg_gc (d, gc, YES);
1597 CGContextFillRect (d->cgc, r); // foreground color
1598 CGContextClipToMask (d->cgc, r, mask);
1599 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1600 CGContextFillRect (d->cgc, r); // background color
1604 CGDataProviderRelease (prov);
1605 CGImageRelease (mask);
1613 XGetImage (Display *dpy, Drawable d, int x, int y,
1614 unsigned int width, unsigned int height,
1615 unsigned long plane_mask, int format)
1617 const unsigned char *data = 0;
1618 int depth, ibpp, ibpl;
1619 NSBitmapImageRep *bm = 0;
1621 Assert ((width < 65535), "improbably large width");
1622 Assert ((height < 65535), "improbably large height");
1623 Assert ((x < 65535 && x > -65535), "improbably large x");
1624 Assert ((y < 65535 && y > -65535), "improbably large y");
1626 if (d->type == PIXMAP) {
1627 depth = d->pixmap.depth;
1628 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1629 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1630 data = CGBitmapContextGetData (d->cgc);
1631 Assert (data, "CGBitmapContextGetData failed");
1633 // get the bits (desired sub-rectangle) out of the NSView
1634 bm = [NSBitmapImageRep alloc];
1636 nsfrom.origin.x = x;
1637 nsfrom.origin.y = y;
1638 nsfrom.size.width = width;
1639 nsfrom.size.height = height;
1640 [bm initWithFocusedViewRect:nsfrom];
1642 ibpp = [bm bitsPerPixel];
1643 ibpl = [bm bytesPerRow];
1644 data = [bm bitmapData];
1645 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1648 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1649 data += (y * ibpl) + (x * (ibpp/8));
1651 format = (depth == 1 ? XYPixmap : ZPixmap);
1652 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1654 image->data = (char *) malloc (height * image->bytes_per_line);
1656 int obpl = image->bytes_per_line;
1658 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1659 means that on Intel it is BGRA when viewed by bytes (And BGR
1660 when using 24bpp packing).
1664 const unsigned char *iline = data;
1665 for (yy = 0; yy < height; yy++) {
1667 const unsigned char *iline2 = iline;
1668 for (xx = 0; xx < width; xx++) {
1670 iline2++; // ignore b or a
1671 iline2++; // ignore g or r
1672 unsigned char r = *iline2++; // r or g
1673 if (ibpp == 32) iline2++; // ignore a or b
1675 XPutPixel (image, xx, yy, (r ? 1 : 0));
1680 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1681 const unsigned char *iline = data;
1682 unsigned char *oline = (unsigned char *) image->data;
1683 for (yy = 0; yy < height; yy++) {
1685 const unsigned char *iline2 = iline;
1686 unsigned char *oline2 = oline;
1687 for (xx = 0; xx < width; xx++) {
1689 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1690 unsigned char r = *iline2++;
1691 unsigned char g = *iline2++;
1692 unsigned char b = *iline2++;
1693 unsigned long pixel = ((a << 24) |
1697 *((unsigned int *) oline2) = pixel;
1705 if (bm) [bm release];
1711 /* Returns a transformation matrix to do rotation as per the provided
1712 EXIF "Orientation" value.
1714 static CGAffineTransform
1715 exif_rotate (int rot, CGSize rect)
1717 CGAffineTransform trans = CGAffineTransformIdentity;
1719 case 2: // flip horizontal
1720 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1721 trans = CGAffineTransformScale (trans, -1, 1);
1724 case 3: // rotate 180
1725 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1726 trans = CGAffineTransformRotate (trans, M_PI);
1729 case 4: // flip vertical
1730 trans = CGAffineTransformMakeTranslation (0, rect.height);
1731 trans = CGAffineTransformScale (trans, 1, -1);
1734 case 5: // transpose (UL-to-LR axis)
1735 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1736 trans = CGAffineTransformScale (trans, -1, 1);
1737 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1740 case 6: // rotate 90
1741 trans = CGAffineTransformMakeTranslation (0, rect.width);
1742 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1745 case 7: // transverse (UR-to-LL axis)
1746 trans = CGAffineTransformMakeScale (-1, 1);
1747 trans = CGAffineTransformRotate (trans, M_PI / 2);
1750 case 8: // rotate 270
1751 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1752 trans = CGAffineTransformRotate (trans, M_PI / 2);
1764 jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
1765 XRectangle *geom_ret, int exif_rotation)
1767 NSImage *nsimg = (NSImage *) nsimg_arg;
1769 // convert the NSImage to a CGImage via the toll-free-bridging
1770 // of NSData and CFData...
1772 NSData *nsdata = [NSBitmapImageRep
1773 TIFFRepresentationOfImageRepsInArray:
1774 [nsimg representations]];
1775 CFDataRef cfdata = (CFDataRef) nsdata;
1776 CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1777 CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1779 NSSize imgr = [nsimg size];
1780 Bool rot_p = (exif_rotation >= 5);
1783 imgr = NSMakeSize (imgr.height, imgr.width);
1785 CGRect winr = d->frame;
1786 float rw = winr.size.width / imgr.width;
1787 float rh = winr.size.height / imgr.height;
1788 float r = (rw < rh ? rw : rh);
1791 dst.size.width = imgr.width * r;
1792 dst.size.height = imgr.height * r;
1793 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1794 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1796 dst2.origin.x = dst2.origin.y = 0;
1798 dst2.size.width = dst.size.height;
1799 dst2.size.height = dst.size.width;
1801 dst2.size = dst.size;
1804 // Clear the part not covered by the image to background or black.
1806 if (d->type == WINDOW)
1807 XClearWindow (dpy, d);
1809 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1810 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1813 CGAffineTransform trans =
1814 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1816 CGContextSaveGState (d->cgc);
1817 CGContextConcatCTM (d->cgc,
1818 CGAffineTransformMakeTranslation (dst.origin.x,
1820 CGContextConcatCTM (d->cgc, trans);
1821 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1822 CGContextDrawImage (d->cgc, dst2, cgi);
1823 CGContextRestoreGState (d->cgc);
1826 CGImageRelease (cgi);
1829 geom_ret->x = dst.origin.x;
1830 geom_ret->y = dst.origin.y;
1831 geom_ret->width = dst.size.width;
1832 geom_ret->height = dst.size.height;
1838 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1840 unsigned int w, unsigned int h,
1841 unsigned long fg, unsigned int bg,
1844 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1845 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1846 (char *) data, w, h, 0, 0);
1848 gcv.foreground = fg;
1849 gcv.background = bg;
1850 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1851 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1854 XDestroyImage (image);
1859 XCreatePixmap (Display *dpy, Drawable d,
1860 unsigned int width, unsigned int height, unsigned int depth)
1862 char *data = (char *) malloc (width * height * 4);
1863 if (! data) return 0;
1865 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1867 p->frame.size.width = width;
1868 p->frame.size.height = height;
1869 p->pixmap.depth = depth;
1871 /* Quartz doesn't have a 1bpp image type.
1872 We used to use 8bpp gray images instead of 1bpp, but some Mac video
1873 don't support that! So we always use 32bpp, regardless of depth. */
1875 p->cgc = CGBitmapContextCreate (data, width, height,
1876 8, /* bits per component */
1877 width * 4, /* bpl */
1879 // Without this, it returns 0...
1880 kCGImageAlphaNoneSkipFirst
1882 Assert (p->cgc, "could not create CGBitmapContext");
1888 XFreePixmap (Display *d, Pixmap p)
1890 Assert (p->type == PIXMAP, "not a pixmap");
1891 CGContextRelease (p->cgc);
1898 copy_pixmap (Pixmap p)
1901 Assert (p->type == PIXMAP, "not a pixmap");
1902 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1904 CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
1909 /* Font metric terminology, as used by X11:
1911 "lbearing" is the distance from the logical origin to the leftmost pixel.
1912 If a character's ink extends to the left of the origin, it is negative.
1914 "rbearing" is the distance from the logical origin to the rightmost pixel.
1916 "descent" is the distance from the logical origin to the bottommost pixel.
1917 For characters with descenders, it is negative.
1919 "ascent" is the distance from the logical origin to the topmost pixel.
1920 It is the number of pixels above the baseline.
1922 "width" is the distance from the logical origin to the position where
1923 the logical origin of the next character should be placed.
1925 If "rbearing" is greater than "width", then this character overlaps the
1926 following character. If smaller, then there is trailing blank space.
1930 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1933 query_font (Font fid)
1935 if (!fid || !fid->nsfont) {
1936 NSLog(@"no NSFont in fid");
1939 if (![fid->nsfont fontName]) {
1940 NSLog(@"broken NSFont in fid");
1947 XFontStruct *f = &fid->metrics;
1948 XCharStruct *min = &f->min_bounds;
1949 XCharStruct *max = &f->max_bounds;
1951 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1954 f->min_char_or_byte2 = first;
1955 f->max_char_or_byte2 = last;
1956 f->default_char = 'M';
1957 f->ascent = CEIL ([fid->nsfont ascender]);
1958 f->descent = -CEIL ([fid->nsfont descender]);
1960 min->width = 255; // set to smaller values in the loop
1963 min->lbearing = 255;
1964 min->rbearing = 255;
1966 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1969 NSBezierPath *bpath = [NSBezierPath bezierPath];
1971 for (i = first; i <= last; i++) {
1972 unsigned char str[2];
1976 NSString *nsstr = [NSString stringWithCString:(char *) str
1977 encoding:NSISOLatin1StringEncoding];
1979 /* I can't believe we have to go through this bullshit just to
1980 convert a 'char' to an NSGlyph!!
1982 You might think that we could do
1983 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
1984 but that doesn't work; my guess is that glyphWithName expects
1985 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
1989 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
1990 [ts setFont:fid->nsfont];
1991 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
1992 NSTextContainer *tc = [[NSTextContainer alloc] init];
1993 [lm addTextContainer:tc];
1994 [tc release]; // lm retains tc
1995 [ts addLayoutManager:lm];
1996 [lm release]; // ts retains lm
1997 glyph = [lm glyphAtIndex:0];
2001 /* Compute the bounding box and advancement by converting the glyph
2002 to a bezier path. There appears to be *no other way* to find out
2003 the bounding box of a character: [NSFont boundingRectForGlyph] and
2004 [NSString sizeWithAttributes] both return an advancement-sized
2005 rectangle, not a rectangle completely enclosing the glyph's ink.
2007 NSPoint advancement;
2009 advancement.x = advancement.y = 0;
2010 [bpath removeAllPoints];
2011 [bpath moveToPoint:advancement];
2012 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2013 advancement = [bpath currentPoint];
2014 bbox = [bpath bounds];
2016 /* Now that we know the advancement and bounding box, we can compute
2017 the lbearing and rbearing.
2019 XCharStruct *cs = &f->per_char[i-first];
2021 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2022 cs->descent = CEIL(-bbox.origin.y);
2023 cs->lbearing = CEIL (bbox.origin.x);
2024 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2025 cs->width = CEIL (advancement.x);
2027 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2029 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2032 max->width = MAX (max->width, cs->width);
2033 max->ascent = MAX (max->ascent, cs->ascent);
2034 max->descent = MAX (max->descent, cs->descent);
2035 max->lbearing = MAX (max->lbearing, cs->lbearing);
2036 max->rbearing = MAX (max->rbearing, cs->rbearing);
2038 min->width = MIN (min->width, cs->width);
2039 min->ascent = MIN (min->ascent, cs->ascent);
2040 min->descent = MIN (min->descent, cs->descent);
2041 min->lbearing = MIN (min->lbearing, cs->lbearing);
2042 min->rbearing = MIN (min->rbearing, cs->rbearing);
2047 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2048 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2049 i, i, cs->width, cs->lbearing, cs->rbearing,
2050 cs->ascent, cs->descent,
2051 (int) bbox.size.width, (int) bbox.size.height,
2052 (int) bbox.origin.x, (int) bbox.origin.y,
2053 (int) advancement.x, (int) advancement.y);
2060 // Since 'Font' includes the metrics, this just makes a copy of that.
2063 XQueryFont (Display *dpy, Font fid)
2066 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2069 // copy XCharStruct array
2070 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2071 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2072 memcpy (f->per_char, fid->metrics.per_char,
2073 size * sizeof (XCharStruct));
2080 copy_font (Font fid)
2082 // copy 'Font' struct
2083 Font fid2 = (Font) malloc (sizeof(*fid2));
2086 // copy XCharStruct array
2087 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2088 fid2->metrics.per_char = (XCharStruct *)
2089 malloc ((size + 2) * sizeof (XCharStruct));
2090 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2091 size * sizeof (XCharStruct));
2093 // copy the other pointers
2094 fid2->ps_name = strdup (fid->ps_name);
2095 // [fid2->nsfont retain];
2096 fid2->metrics.fid = fid2;
2103 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2106 Assert (size > 0, "zero font size");
2111 // "Monaco" only exists in plain.
2112 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2114 if (bold && ital) name = "Courier-BoldOblique";
2115 else if (bold) name = "Courier-Bold";
2116 else if (ital) name = "Courier-Oblique";
2117 else name = "Courier";
2121 // "Georgia" looks better than "Times".
2123 if (bold && ital) name = "Georgia-BoldItalic";
2124 else if (bold) name = "Georgia-Bold";
2125 else if (ital) name = "Georgia-Italic";
2126 else name = "Georgia";
2130 // "Geneva" only exists in plain.
2131 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2132 // "Verdana" renders smoother than "Helvetica" for some reason.
2134 if (bold && ital) name = "Verdana-BoldItalic";
2135 else if (bold) name = "Verdana-Bold";
2136 else if (ital) name = "Verdana-Italic";
2137 else name = "Verdana";
2140 NSString *nsname = [NSString stringWithCString:name
2141 encoding:NSUTF8StringEncoding];
2142 NSFont *f = [NSFont fontWithName:nsname size:size];
2144 *name_ret = strdup(name);
2149 try_native_font (const char *name, char **name_ret, float *size_ret)
2151 if (!name) return 0;
2152 const char *spc = strrchr (name, ' ');
2155 if (1 != sscanf (spc, " %d ", &size)) return 0;
2156 if (size <= 4) return 0;
2158 char *name2 = strdup (name);
2159 name2[strlen(name2) - strlen(spc)] = 0;
2160 NSString *nsname = [NSString stringWithCString:name2
2161 encoding:NSUTF8StringEncoding];
2162 NSFont *f = [NSFont fontWithName:nsname size:size];
2174 /* Returns a random font in the given size and face.
2177 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2179 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2180 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2181 NSArray *fonts = [[NSFontManager sharedFontManager]
2182 availableFontNamesWithTraits:mask];
2183 if (!fonts) return 0;
2185 int n = [fonts count];
2186 if (n <= 0) return 0;
2189 for (j = 0; j < n; j++) {
2190 int i = random() % n;
2191 NSString *name = [fonts objectAtIndex:i];
2192 NSFont *f = [NSFont fontWithName:name size:size];
2195 /* Don't use this font if it (probably) doesn't include ASCII characters.
2197 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2198 if (! (enc == NSUTF8StringEncoding ||
2199 enc == NSISOLatin1StringEncoding ||
2200 enc == NSNonLossyASCIIStringEncoding ||
2201 enc == NSISOLatin2StringEncoding ||
2202 enc == NSUnicodeStringEncoding ||
2203 enc == NSWindowsCP1250StringEncoding ||
2204 enc == NSWindowsCP1252StringEncoding ||
2205 enc == NSMacOSRomanStringEncoding)) {
2206 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2209 // NSLog(@"using \"%@\": %d", name, enc);
2211 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2215 // None of the fonts support ASCII?
2221 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2232 const char *s = (name ? name : "");
2234 while (*s && (*s == '*' || *s == '-'))
2237 while (*s2 && (*s2 != '*' && *s2 != '-'))
2243 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2244 else if (CMP ("random")) rand = YES;
2245 else if (CMP ("bold")) bold = YES;
2246 else if (CMP ("i")) ital = YES;
2247 else if (CMP ("o")) ital = YES;
2248 else if (CMP ("courier")) fixed = YES;
2249 else if (CMP ("fixed")) fixed = YES;
2250 else if (CMP ("m")) fixed = YES;
2251 else if (CMP ("times")) serif = YES;
2252 else if (CMP ("6x10")) fixed = YES, size = 8;
2253 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2254 else if (CMP ("9x15")) fixed = YES, size = 12;
2255 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2256 else if (CMP ("vga")) fixed = YES, size = 12;
2257 else if (CMP ("console")) fixed = YES, size = 12;
2258 else if (CMP ("gallant")) fixed = YES, size = 12;
2260 else if (size == 0) {
2262 if (1 == sscanf (s, " %d ", &n))
2269 if (size < 6 || size > 1000)
2273 nsfont = random_font (bold, ital, size, &ps_name);
2276 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2278 // if that didn't work, turn off attibutes until it does
2279 // (e.g., there is no "Monaco-Bold".)
2281 if (!nsfont && serif) {
2283 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2285 if (!nsfont && ital) {
2287 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2289 if (!nsfont && bold) {
2291 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2293 if (!nsfont && fixed) {
2295 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2299 *name_ret = ps_name;
2309 XLoadFont (Display *dpy, const char *name)
2311 Font fid = (Font) calloc (1, sizeof(*fid));
2313 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2315 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2317 NSLog(@"no NSFont for \"%s\"", name);
2321 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2329 /* This translates the NSFont into the numbers that aglUseFont() wants.
2332 jwxyz_font_info (Font f, int *size_ret, int *face_ret)
2334 char *name = strdup (f->ps_name);
2335 char *dash = strchr (name, '-');
2339 // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
2340 if (strcasestr (dash, "bold")) flags |= 1;
2341 if (strcasestr (dash, "italic")) flags |= 2;
2342 if (strcasestr (dash, "oblique")) flags |= 2;
2345 NSString *nname = [NSString stringWithCString:name
2346 encoding:NSUTF8StringEncoding];
2347 ATSFontFamilyRef id =
2348 ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
2351 // WTF? aglUseFont gets a BadValue if size is small!!
2352 if (size < 9) size = 9;
2354 //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
2355 Assert (id >= 0, "no ATS font family");
2364 XLoadQueryFont (Display *dpy, const char *name)
2366 Font fid = XLoadFont (dpy, name);
2367 return XQueryFont (dpy, fid);
2371 XUnloadFont (Display *dpy, Font fid)
2373 free (fid->ps_name);
2374 free (fid->metrics.per_char);
2376 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2377 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2378 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2379 // They're probably not very big...
2381 // [fid->nsfont release];
2388 XFreeFontInfo (char **names, XFontStruct *info, int n)
2392 for (i = 0; i < n; i++)
2393 if (names[i]) free (names[i]);
2397 for (i = 0; i < n; i++)
2398 if (info[i].per_char)
2399 free (info[i].per_char);
2406 XFreeFont (Display *dpy, XFontStruct *f)
2409 XFreeFontInfo (0, f, 1);
2410 XUnloadFont (dpy, fid);
2416 XSetFont (Display *dpy, GC gc, Font fid)
2419 XUnloadFont (dpy, gc->gcv.font);
2420 gc->gcv.font = copy_font (fid);
2421 [gc->gcv.font->nsfont retain];
2426 XTextExtents (XFontStruct *f, const char *s, int length,
2427 int *dir_ret, int *ascent_ret, int *descent_ret,
2430 memset (cs, 0, sizeof(*cs));
2432 for (i = 0; i < length; i++) {
2433 unsigned char c = (unsigned char) s[i];
2434 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2435 c = f->default_char;
2436 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2440 cs->ascent = MAX (cs->ascent, cc->ascent);
2441 cs->descent = MAX (cs->descent, cc->descent);
2442 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2443 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2444 cs->width += cc->width;
2448 *ascent_ret = f->ascent;
2449 *descent_ret = f->descent;
2454 XTextWidth (XFontStruct *f, const char *s, int length)
2456 int ascent, descent, dir;
2458 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2464 set_font (CGContextRef cgc, GC gc)
2466 Font font = gc->gcv.font;
2468 font = XLoadFont (0, 0);
2469 gc->gcv.font = font;
2470 [gc->gcv.font->nsfont retain];
2472 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2473 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2478 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2479 const char *str, int len, BOOL clear_background_p)
2481 if (clear_background_p) {
2482 int ascent, descent, dir;
2484 XTextExtents (&gc->gcv.font->metrics, str, len,
2485 &dir, &ascent, &descent, &cs);
2486 draw_rect (dpy, d, gc,
2487 x + MIN (0, cs.lbearing),
2488 y - MAX (0, ascent),
2489 MAX (MAX (0, cs.rbearing) -
2490 MIN (0, cs.lbearing),
2492 MAX (0, ascent) + MAX (0, descent),
2496 CGRect wr = d->frame;
2499 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2500 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2503 push_fg_gc (d, gc, YES);
2504 set_font (d->cgc, gc);
2506 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2507 if (! gc->gcv.antialias_p)
2508 CGContextSetShouldAntialias (d->cgc, YES); // always antialias text
2509 CGContextShowTextAtPoint (d->cgc,
2511 wr.origin.y + wr.size.height - y,
2520 unsigned long argb = gc->gcv.foreground;
2521 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2522 float a = ((argb >> 24) & 0xFF) / 255.0;
2523 float r = ((argb >> 16) & 0xFF) / 255.0;
2524 float g = ((argb >> 8) & 0xFF) / 255.0;
2525 float b = ((argb ) & 0xFF) / 255.0;
2526 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2527 NSDictionary *attr =
2528 [NSDictionary dictionaryWithObjectsAndKeys:
2529 gc->gcv.font->nsfont, NSFontAttributeName,
2530 fg, NSForegroundColorAttributeName,
2532 char *s2 = (char *) malloc (len + 1);
2533 strncpy (s2, str, len);
2535 NSString *nsstr = [NSString stringWithCString:s2
2536 encoding:NSISOLatin1StringEncoding];
2539 pos.x = wr.origin.x + x;
2540 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2541 [nsstr drawAtPoint:pos withAttributes:attr];
2550 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2551 const char *str, int len)
2553 return draw_string (dpy, d, gc, x, y, str, len, NO);
2557 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2558 const char *str, int len)
2560 return draw_string (dpy, d, gc, x, y, str, len, YES);
2565 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2567 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2568 gc->gcv.foreground = fg;
2574 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2576 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2577 gc->gcv.background = bg;
2582 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2584 gc->gcv.alpha_allowed_p = allowed;
2589 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2591 gc->gcv.antialias_p = antialias_p;
2597 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2598 int line_style, int cap_style, int join_style)
2600 gc->gcv.line_width = line_width;
2601 Assert (line_style == LineSolid, "only LineSolid implemented");
2602 // gc->gcv.line_style = line_style;
2603 gc->gcv.cap_style = cap_style;
2604 gc->gcv.join_style = join_style;
2609 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2615 XSetFunction (Display *dpy, GC gc, int which)
2617 gc->gcv.function = which;
2622 XSetSubwindowMode (Display *dpy, GC gc, int which)
2624 gc->gcv.subwindow_mode = which;
2629 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2631 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2633 if (gc->gcv.clip_mask) {
2634 XFreePixmap (dpy, gc->gcv.clip_mask);
2635 CGImageRelease (gc->clip_mask);
2638 gc->gcv.clip_mask = copy_pixmap (m);
2639 if (gc->gcv.clip_mask)
2640 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2648 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2650 gc->gcv.clip_x_origin = x;
2651 gc->gcv.clip_y_origin = y;
2657 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2658 int *root_x_ret, int *root_y_ret,
2659 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2661 Assert (w->type == WINDOW, "not a window");
2662 NSWindow *nsw = [w->window.view window];
2664 // get bottom left of window on screen, from bottom left
2665 wpos.x = wpos.y = 0;
2666 wpos = [nsw convertBaseToScreen:wpos];
2669 // get bottom left of view on window, from bottom left
2670 vpos.x = vpos.y = 0;
2671 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2673 // get bottom left of view on screen, from bottom left
2677 // get top left of view on screen, from bottom left
2678 vpos.y += w->frame.size.height;
2680 // get top left of view on screen, from top left
2681 NSArray *screens = [NSScreen screens];
2682 NSScreen *screen = (screens && [screens count] > 0
2683 ? [screens objectAtIndex:0]
2684 : [NSScreen mainScreen]);
2685 NSRect srect = [screen frame];
2686 vpos.y = srect.size.height - vpos.y;
2688 // get the mouse position on window, from bottom left
2689 NSEvent *e = [NSApp currentEvent];
2690 NSPoint p = [e locationInWindow];
2692 // get mouse position on screen, from bottom left
2696 // get mouse position on screen, from top left
2697 p.y = srect.size.height - p.y;
2699 if (root_x_ret) *root_x_ret = (int) p.x;
2700 if (root_y_ret) *root_y_ret = (int) p.y;
2701 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2702 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2704 if (mask_ret) *mask_ret = 0; // ####
2705 if (root_ret) *root_ret = 0;
2706 if (child_ret) *child_ret = 0;
2711 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2712 int src_x, int src_y,
2713 int *dest_x_ret, int *dest_y_ret,
2716 Assert (w->type == WINDOW, "not a window");
2717 NSWindow *nsw = [w->window.view window];
2719 // get bottom left of window on screen, from bottom left
2720 wpos.x = wpos.y = 0;
2721 wpos = [nsw convertBaseToScreen:wpos];
2724 // get bottom left of view on window, from bottom left
2725 vpos.x = vpos.y = 0;
2726 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2728 // get bottom left of view on screen, from bottom left
2732 // get top left of view on screen, from bottom left
2733 vpos.y += w->frame.size.height;
2735 // get top left of view on screen, from top left
2736 NSArray *screens = [NSScreen screens];
2737 NSScreen *screen = (screens && [screens count] > 0
2738 ? [screens objectAtIndex:0]
2739 : [NSScreen mainScreen]);
2740 NSRect srect = [screen frame];
2741 vpos.y = srect.size.height - vpos.y;
2743 // point starts out relative to top left of view
2748 // get point relative to top left of screen
2761 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2767 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2770 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2771 char c = (char) ks; // could be smarter about modifiers here...
2772 if (k_ret) *k_ret = ks;
2773 if (size > 0) buf[0] = c;
2774 if (size > 1) buf[1] = 0;
2780 XFlush (Display *dpy)
2782 // Just let the event loop take care of this on its own schedule.
2787 XSync (Display *dpy, Bool flush)
2789 return XFlush (dpy);
2793 // declared in utils/visual.h
2795 has_writable_cells (Screen *s, Visual *v)
2801 visual_depth (Screen *s, Visual *v)
2807 visual_cells (Screen *s, Visual *v)
2813 visual_class (Screen *s, Visual *v)
2818 // declared in utils/grabclient.h
2820 use_subwindow_mode_p (Screen *screen, Window window)