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 Assert ((w < 65535), "improbably large width");
1483 Assert ((h < 65535), "improbably large height");
1484 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1485 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1486 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1487 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1489 // Clip width and height to the bounds of the Drawable
1491 if (dest_x + w > wr.size.width) {
1492 if (dest_x > wr.size.width)
1494 w = wr.size.width - dest_x;
1496 if (dest_y + h > wr.size.height) {
1497 if (dest_y > wr.size.height)
1499 h = wr.size.height - dest_y;
1501 if (w <= 0 || h <= 0)
1504 // Clip width and height to the bounds of the XImage
1506 if (src_x + w > ximage->width) {
1507 if (src_x > ximage->width)
1509 w = ximage->width - src_x;
1511 if (src_y + h > ximage->height) {
1512 if (src_y > ximage->height)
1514 h = ximage->height - src_y;
1516 if (w <= 0 || h <= 0)
1519 if (gc && (gc->gcv.function == GXset ||
1520 gc->gcv.function == GXclear)) {
1521 // "set" and "clear" are dumb drawing modes that ignore the source
1522 // bits and just draw solid rectangles.
1523 set_color (d->cgc, (gc->gcv.function == GXset
1524 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1525 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1526 gc->depth, gc->gcv.alpha_allowed_p, YES);
1527 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1531 int bpl = ximage->bytes_per_line;
1532 int bpp = ximage->bits_per_pixel;
1533 int bsize = bpl * h;
1534 char *data = ximage->data;
1537 r.origin.x = wr.origin.x + dest_x;
1538 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1544 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1545 to create a CGImage from a sub-rectagle of the XImage.
1547 data += (src_y * bpl) + (src_x * 4);
1548 CGDataProviderRef prov =
1549 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1551 CGImageRef cgi = CGImageCreate (w, h,
1554 /* Need this for XPMs to have the right
1555 colors, e.g. the logo in "maze". */
1556 (kCGImageAlphaNoneSkipFirst |
1557 kCGBitmapByteOrder32Host),
1559 NULL, /* decode[] */
1560 NO, /* interpolate */
1561 kCGRenderingIntentDefault);
1562 CGDataProviderRelease (prov);
1563 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1564 CGContextDrawImage (d->cgc, r, cgi);
1565 CGImageRelease (cgi);
1567 } else { // (bpp == 1)
1569 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1571 #### However, the bit order within a byte in a 1bpp XImage is
1572 the wrong way around from what Quartz expects, so first we
1573 have to copy the data to reverse it. Shit! Maybe it
1574 would be worthwhile to go through the hacks and #ifdef
1575 each one that diddles 1bpp XImage->data directly...
1577 Assert ((src_x % 8) == 0,
1578 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1580 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1581 unsigned char *flipped = (unsigned char *) malloc (bsize);
1583 flipbits ((unsigned char *) data, flipped, bsize);
1585 CGDataProviderRef prov =
1586 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1587 CGImageRef mask = CGImageMaskCreate (w, h,
1590 NULL, /* decode[] */
1591 NO); /* interpolate */
1592 push_fg_gc (d, gc, YES);
1594 CGContextFillRect (d->cgc, r); // foreground color
1595 CGContextClipToMask (d->cgc, r, mask);
1596 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1597 CGContextFillRect (d->cgc, r); // background color
1601 CGDataProviderRelease (prov);
1602 CGImageRelease (mask);
1610 XGetImage (Display *dpy, Drawable d, int x, int y,
1611 unsigned int width, unsigned int height,
1612 unsigned long plane_mask, int format)
1614 const unsigned char *data = 0;
1615 int depth, ibpp, ibpl;
1616 NSBitmapImageRep *bm = 0;
1618 Assert ((width < 65535), "improbably large width");
1619 Assert ((height < 65535), "improbably large height");
1620 Assert ((x < 65535 && x > -65535), "improbably large x");
1621 Assert ((y < 65535 && y > -65535), "improbably large y");
1623 if (d->type == PIXMAP) {
1624 depth = d->pixmap.depth;
1625 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1626 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1627 data = CGBitmapContextGetData (d->cgc);
1628 Assert (data, "CGBitmapContextGetData failed");
1630 // get the bits (desired sub-rectangle) out of the NSView
1631 bm = [NSBitmapImageRep alloc];
1633 nsfrom.origin.x = x;
1634 nsfrom.origin.y = y;
1635 nsfrom.size.width = width;
1636 nsfrom.size.height = height;
1637 [bm initWithFocusedViewRect:nsfrom];
1639 ibpp = [bm bitsPerPixel];
1640 ibpl = [bm bytesPerRow];
1641 data = [bm bitmapData];
1642 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1645 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1646 data += (y * ibpl) + (x * (ibpp/8));
1648 format = (depth == 1 ? XYPixmap : ZPixmap);
1649 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1651 image->data = (char *) malloc (height * image->bytes_per_line);
1653 int obpl = image->bytes_per_line;
1655 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1656 means that on Intel it is BGRA when viewed by bytes (And BGR
1657 when using 24bpp packing).
1661 const unsigned char *iline = data;
1662 for (yy = 0; yy < height; yy++) {
1664 const unsigned char *iline2 = iline;
1665 for (xx = 0; xx < width; xx++) {
1667 iline2++; // ignore b or a
1668 iline2++; // ignore g or r
1669 unsigned char r = *iline2++; // r or g
1670 if (ibpp == 32) iline2++; // ignore a or b
1672 XPutPixel (image, xx, yy, (r ? 1 : 0));
1677 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1678 const unsigned char *iline = data;
1679 unsigned char *oline = (unsigned char *) image->data;
1680 for (yy = 0; yy < height; yy++) {
1682 const unsigned char *iline2 = iline;
1683 unsigned char *oline2 = oline;
1684 for (xx = 0; xx < width; xx++) {
1686 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1687 unsigned char r = *iline2++;
1688 unsigned char g = *iline2++;
1689 unsigned char b = *iline2++;
1690 unsigned long pixel = ((a << 24) |
1694 *((unsigned int *) oline2) = pixel;
1702 if (bm) [bm release];
1708 jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
1709 XRectangle *geom_ret)
1711 NSImage *nsimg = (NSImage *) nsimg_arg;
1713 // convert the NSImage to a CGImage via the toll-free-bridging
1714 // of NSData and CFData...
1716 NSData *nsdata = [NSBitmapImageRep
1717 TIFFRepresentationOfImageRepsInArray:
1718 [nsimg representations]];
1719 CFDataRef cfdata = (CFDataRef) nsdata;
1720 CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1721 CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1723 NSSize imgr = [nsimg size];
1724 CGRect winr = d->frame;
1725 float rw = winr.size.width / imgr.width;
1726 float rh = winr.size.height / imgr.height;
1727 float r = (rw < rh ? rw : rh);
1730 dst.size.width = imgr.width * r;
1731 dst.size.height = imgr.height * r;
1732 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1733 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1735 // Clear the part not covered by the image to background or black.
1737 if (d->type == WINDOW)
1738 XClearWindow (dpy, d);
1740 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1741 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1744 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1745 CGContextDrawImage (d->cgc, dst, cgi);
1748 CGImageRelease (cgi);
1751 geom_ret->x = dst.origin.x;
1752 geom_ret->y = dst.origin.y;
1753 geom_ret->width = dst.size.width;
1754 geom_ret->height = dst.size.height;
1760 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1762 unsigned int w, unsigned int h,
1763 unsigned long fg, unsigned int bg,
1766 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1767 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1768 (char *) data, w, h, 0, 0);
1770 gcv.foreground = fg;
1771 gcv.background = bg;
1772 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1773 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1776 XDestroyImage (image);
1781 XCreatePixmap (Display *dpy, Drawable d,
1782 unsigned int width, unsigned int height, unsigned int depth)
1784 char *data = (char *) malloc (width * height * 4);
1785 if (! data) return 0;
1787 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1789 p->frame.size.width = width;
1790 p->frame.size.height = height;
1791 p->pixmap.depth = depth;
1793 /* Quartz doesn't have a 1bpp image type.
1794 We used to use 8bpp gray images instead of 1bpp, but some Mac video
1795 don't support that! So we always use 32bpp, regardless of depth. */
1797 p->cgc = CGBitmapContextCreate (data, width, height,
1798 8, /* bits per component */
1799 width * 4, /* bpl */
1801 // Without this, it returns 0...
1802 kCGImageAlphaNoneSkipFirst
1804 Assert (p->cgc, "could not create CGBitmapContext");
1810 XFreePixmap (Display *d, Pixmap p)
1812 Assert (p->type == PIXMAP, "not a pixmap");
1813 CGContextRelease (p->cgc);
1820 copy_pixmap (Pixmap p)
1823 Assert (p->type == PIXMAP, "not a pixmap");
1824 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1826 CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
1831 /* Font metric terminology, as used by X11:
1833 "lbearing" is the distance from the logical origin to the leftmost pixel.
1834 If a character's ink extends to the left of the origin, it is negative.
1836 "rbearing" is the distance from the logical origin to the rightmost pixel.
1838 "descent" is the distance from the logical origin to the bottommost pixel.
1839 For characters with descenders, it is negative.
1841 "ascent" is the distance from the logical origin to the topmost pixel.
1842 It is the number of pixels above the baseline.
1844 "width" is the distance from the logical origin to the position where
1845 the logical origin of the next character should be placed.
1847 If "rbearing" is greater than "width", then this character overlaps the
1848 following character. If smaller, then there is trailing blank space.
1852 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1855 query_font (Font fid)
1857 if (!fid || !fid->nsfont) {
1858 NSLog(@"no NSFont in fid");
1861 if (![fid->nsfont fontName]) {
1862 NSLog(@"broken NSFont in fid");
1869 XFontStruct *f = &fid->metrics;
1870 XCharStruct *min = &f->min_bounds;
1871 XCharStruct *max = &f->max_bounds;
1873 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1876 f->min_char_or_byte2 = first;
1877 f->max_char_or_byte2 = last;
1878 f->default_char = 'M';
1879 f->ascent = CEIL ([fid->nsfont ascender]);
1880 f->descent = -CEIL ([fid->nsfont descender]);
1882 min->width = 255; // set to smaller values in the loop
1885 min->lbearing = 255;
1886 min->rbearing = 255;
1888 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1891 NSBezierPath *bpath = [NSBezierPath bezierPath];
1893 for (i = first; i <= last; i++) {
1894 unsigned char str[2];
1898 NSString *nsstr = [NSString stringWithCString:(char *) str
1899 encoding:NSISOLatin1StringEncoding];
1901 /* I can't believe we have to go through this bullshit just to
1902 convert a 'char' to an NSGlyph!!
1904 You might think that we could do
1905 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
1906 but that doesn't work; my guess is that glyphWithName expects
1907 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
1911 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
1912 [ts setFont:fid->nsfont];
1913 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
1914 NSTextContainer *tc = [[NSTextContainer alloc] init];
1915 [lm addTextContainer:tc];
1916 [tc release]; // lm retains tc
1917 [ts addLayoutManager:lm];
1918 [lm release]; // ts retains lm
1919 glyph = [lm glyphAtIndex:0];
1923 /* Compute the bounding box and advancement by converting the glyph
1924 to a bezier path. There appears to be *no other way* to find out
1925 the bounding box of a character: [NSFont boundingRectForGlyph] and
1926 [NSString sizeWithAttributes] both return an advancement-sized
1927 rectangle, not a rectangle completely enclosing the glyph's ink.
1929 NSPoint advancement;
1931 advancement.x = advancement.y = 0;
1932 [bpath removeAllPoints];
1933 [bpath moveToPoint:advancement];
1934 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
1935 advancement = [bpath currentPoint];
1936 bbox = [bpath bounds];
1938 /* Now that we know the advancement and bounding box, we can compute
1939 the lbearing and rbearing.
1941 XCharStruct *cs = &f->per_char[i-first];
1943 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
1944 cs->descent = CEIL(-bbox.origin.y);
1945 cs->lbearing = CEIL (bbox.origin.x);
1946 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
1947 cs->width = CEIL (advancement.x);
1949 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
1951 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
1954 max->width = MAX (max->width, cs->width);
1955 max->ascent = MAX (max->ascent, cs->ascent);
1956 max->descent = MAX (max->descent, cs->descent);
1957 max->lbearing = MAX (max->lbearing, cs->lbearing);
1958 max->rbearing = MAX (max->rbearing, cs->rbearing);
1960 min->width = MIN (min->width, cs->width);
1961 min->ascent = MIN (min->ascent, cs->ascent);
1962 min->descent = MIN (min->descent, cs->descent);
1963 min->lbearing = MIN (min->lbearing, cs->lbearing);
1964 min->rbearing = MIN (min->rbearing, cs->rbearing);
1969 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
1970 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
1971 i, i, cs->width, cs->lbearing, cs->rbearing,
1972 cs->ascent, cs->descent,
1973 (int) bbox.size.width, (int) bbox.size.height,
1974 (int) bbox.origin.x, (int) bbox.origin.y,
1975 (int) advancement.x, (int) advancement.y);
1982 // Since 'Font' includes the metrics, this just makes a copy of that.
1985 XQueryFont (Display *dpy, Font fid)
1988 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
1991 // copy XCharStruct array
1992 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
1993 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1994 memcpy (f->per_char, fid->metrics.per_char,
1995 size * sizeof (XCharStruct));
2002 copy_font (Font fid)
2004 // copy 'Font' struct
2005 Font fid2 = (Font) malloc (sizeof(*fid2));
2008 // copy XCharStruct array
2009 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2010 fid2->metrics.per_char = (XCharStruct *)
2011 malloc ((size + 2) * sizeof (XCharStruct));
2012 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2013 size * sizeof (XCharStruct));
2015 // copy the other pointers
2016 fid2->ps_name = strdup (fid->ps_name);
2017 // [fid2->nsfont retain];
2018 fid2->metrics.fid = fid2;
2025 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2028 Assert (size > 0, "zero font size");
2033 // "Monaco" only exists in plain.
2034 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2036 if (bold && ital) name = "Courier-BoldOblique";
2037 else if (bold) name = "Courier-Bold";
2038 else if (ital) name = "Courier-Oblique";
2039 else name = "Courier";
2043 // "Georgia" looks better than "Times".
2045 if (bold && ital) name = "Georgia-BoldItalic";
2046 else if (bold) name = "Georgia-Bold";
2047 else if (ital) name = "Georgia-Italic";
2048 else name = "Georgia";
2052 // "Geneva" only exists in plain.
2053 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2054 // "Verdana" renders smoother than "Helvetica" for some reason.
2056 if (bold && ital) name = "Verdana-BoldItalic";
2057 else if (bold) name = "Verdana-Bold";
2058 else if (ital) name = "Verdana-Italic";
2059 else name = "Verdana";
2062 NSString *nsname = [NSString stringWithCString:name
2063 encoding:NSUTF8StringEncoding];
2064 NSFont *f = [NSFont fontWithName:nsname size:size];
2066 *name_ret = strdup(name);
2071 try_native_font (const char *name, char **name_ret, float *size_ret)
2073 if (!name) return 0;
2074 const char *spc = strrchr (name, ' ');
2077 if (1 != sscanf (spc, " %d ", &size)) return 0;
2078 if (size <= 4) return 0;
2080 char *name2 = strdup (name);
2081 name2[strlen(name2) - strlen(spc)] = 0;
2082 NSString *nsname = [NSString stringWithCString:name2
2083 encoding:NSUTF8StringEncoding];
2084 NSFont *f = [NSFont fontWithName:nsname size:size];
2096 /* Returns a random font in the given size and face.
2099 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2101 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2102 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2103 NSArray *fonts = [[NSFontManager sharedFontManager]
2104 availableFontNamesWithTraits:mask];
2105 if (!fonts) return 0;
2107 int n = [fonts count];
2108 if (n <= 0) return 0;
2111 for (j = 0; j < n; j++) {
2112 int i = random() % n;
2113 NSString *name = [fonts objectAtIndex:i];
2114 NSFont *f = [NSFont fontWithName:name size:size];
2117 /* Don't use this font if it (probably) doesn't include ASCII characters.
2119 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2120 if (! (enc == NSUTF8StringEncoding ||
2121 enc == NSISOLatin1StringEncoding ||
2122 enc == NSNonLossyASCIIStringEncoding ||
2123 enc == NSISOLatin2StringEncoding ||
2124 enc == NSUnicodeStringEncoding ||
2125 enc == NSWindowsCP1250StringEncoding ||
2126 enc == NSWindowsCP1252StringEncoding ||
2127 enc == NSMacOSRomanStringEncoding)) {
2128 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2131 // NSLog(@"using \"%@\": %d", name, enc);
2133 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2137 // None of the fonts support ASCII?
2143 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2154 const char *s = (name ? name : "");
2156 while (*s && (*s == '*' || *s == '-'))
2159 while (*s2 && (*s2 != '*' && *s2 != '-'))
2165 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2166 else if (CMP ("random")) rand = YES;
2167 else if (CMP ("bold")) bold = YES;
2168 else if (CMP ("i")) ital = YES;
2169 else if (CMP ("o")) ital = YES;
2170 else if (CMP ("courier")) fixed = YES;
2171 else if (CMP ("fixed")) fixed = YES;
2172 else if (CMP ("m")) fixed = YES;
2173 else if (CMP ("times")) serif = YES;
2174 else if (CMP ("6x10")) fixed = YES, size = 8;
2175 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2176 else if (CMP ("9x15")) fixed = YES, size = 12;
2177 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2178 else if (CMP ("vga")) fixed = YES, size = 12;
2179 else if (CMP ("console")) fixed = YES, size = 12;
2180 else if (CMP ("gallant")) fixed = YES, size = 12;
2182 else if (size == 0) {
2184 if (1 == sscanf (s, " %d ", &n))
2191 if (size < 6 || size > 1000)
2195 nsfont = random_font (bold, ital, size, &ps_name);
2198 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2200 // if that didn't work, turn off attibutes until it does
2201 // (e.g., there is no "Monaco-Bold".)
2203 if (!nsfont && serif) {
2205 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2207 if (!nsfont && ital) {
2209 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2211 if (!nsfont && bold) {
2213 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2215 if (!nsfont && fixed) {
2217 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2221 *name_ret = ps_name;
2231 XLoadFont (Display *dpy, const char *name)
2233 Font fid = (Font) calloc (1, sizeof(*fid));
2235 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2237 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2239 NSLog(@"no NSFont for \"%s\"", name);
2243 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2251 /* This translates the NSFont into the numbers that aglUseFont() wants.
2254 jwxyz_font_info (Font f, int *size_ret, int *face_ret)
2256 char *name = strdup (f->ps_name);
2257 char *dash = strchr (name, '-');
2261 // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
2262 if (strcasestr (dash, "bold")) flags |= 1;
2263 if (strcasestr (dash, "italic")) flags |= 2;
2264 if (strcasestr (dash, "oblique")) flags |= 2;
2267 NSString *nname = [NSString stringWithCString:name
2268 encoding:NSUTF8StringEncoding];
2269 ATSFontFamilyRef id =
2270 ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
2273 // WTF? aglUseFont gets a BadValue if size is small!!
2274 if (size < 9) size = 9;
2276 //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
2277 Assert (id >= 0, "no ATS font family");
2286 XLoadQueryFont (Display *dpy, const char *name)
2288 Font fid = XLoadFont (dpy, name);
2289 return XQueryFont (dpy, fid);
2293 XUnloadFont (Display *dpy, Font fid)
2295 free (fid->ps_name);
2296 free (fid->metrics.per_char);
2298 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2299 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2300 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2301 // They're probably not very big...
2303 // [fid->nsfont release];
2310 XFreeFontInfo (char **names, XFontStruct *info, int n)
2314 for (i = 0; i < n; i++)
2315 if (names[i]) free (names[i]);
2319 for (i = 0; i < n; i++)
2320 if (info[i].per_char)
2321 free (info[i].per_char);
2328 XFreeFont (Display *dpy, XFontStruct *f)
2331 XFreeFontInfo (0, f, 1);
2332 XUnloadFont (dpy, fid);
2338 XSetFont (Display *dpy, GC gc, Font fid)
2341 XUnloadFont (dpy, gc->gcv.font);
2342 gc->gcv.font = copy_font (fid);
2343 [gc->gcv.font->nsfont retain];
2348 XTextExtents (XFontStruct *f, const char *s, int length,
2349 int *dir_ret, int *ascent_ret, int *descent_ret,
2352 memset (cs, 0, sizeof(*cs));
2354 for (i = 0; i < length; i++) {
2355 unsigned char c = (unsigned char) s[i];
2356 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2357 c = f->default_char;
2358 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2362 cs->ascent = MAX (cs->ascent, cc->ascent);
2363 cs->descent = MAX (cs->descent, cc->descent);
2364 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2365 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2366 cs->width += cc->width;
2370 *ascent_ret = f->ascent;
2371 *descent_ret = f->descent;
2376 XTextWidth (XFontStruct *f, const char *s, int length)
2378 int ascent, descent, dir;
2380 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2386 set_font (CGContextRef cgc, GC gc)
2388 Font font = gc->gcv.font;
2390 font = XLoadFont (0, 0);
2391 gc->gcv.font = font;
2392 [gc->gcv.font->nsfont retain];
2394 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2395 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2400 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2401 const char *str, int len, BOOL clear_background_p)
2403 if (clear_background_p) {
2404 int ascent, descent, dir;
2406 XTextExtents (&gc->gcv.font->metrics, str, len,
2407 &dir, &ascent, &descent, &cs);
2408 draw_rect (dpy, d, gc,
2409 x + MIN (0, cs.lbearing),
2410 y - MAX (0, ascent),
2411 MAX (MAX (0, cs.rbearing) -
2412 MIN (0, cs.lbearing),
2414 MAX (0, ascent) + MAX (0, descent),
2418 CGRect wr = d->frame;
2421 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2422 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2425 push_fg_gc (d, gc, YES);
2426 set_font (d->cgc, gc);
2428 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2429 if (! gc->gcv.antialias_p)
2430 CGContextSetShouldAntialias (d->cgc, YES); // always antialias text
2431 CGContextShowTextAtPoint (d->cgc,
2433 wr.origin.y + wr.size.height - y,
2442 unsigned long argb = gc->gcv.foreground;
2443 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2444 float a = ((argb >> 24) & 0xFF) / 255.0;
2445 float r = ((argb >> 16) & 0xFF) / 255.0;
2446 float g = ((argb >> 8) & 0xFF) / 255.0;
2447 float b = ((argb ) & 0xFF) / 255.0;
2448 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2449 NSDictionary *attr =
2450 [NSDictionary dictionaryWithObjectsAndKeys:
2451 gc->gcv.font->nsfont, NSFontAttributeName,
2452 fg, NSForegroundColorAttributeName,
2454 char *s2 = (char *) malloc (len + 1);
2455 strncpy (s2, str, len);
2457 NSString *nsstr = [NSString stringWithCString:s2
2458 encoding:NSISOLatin1StringEncoding];
2461 pos.x = wr.origin.x + x;
2462 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2463 [nsstr drawAtPoint:pos withAttributes:attr];
2472 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2473 const char *str, int len)
2475 return draw_string (dpy, d, gc, x, y, str, len, NO);
2479 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2480 const char *str, int len)
2482 return draw_string (dpy, d, gc, x, y, str, len, YES);
2487 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2489 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2490 gc->gcv.foreground = fg;
2496 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2498 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2499 gc->gcv.background = bg;
2504 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2506 gc->gcv.alpha_allowed_p = allowed;
2511 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2513 gc->gcv.antialias_p = antialias_p;
2519 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2520 int line_style, int cap_style, int join_style)
2522 gc->gcv.line_width = line_width;
2523 Assert (line_style == LineSolid, "only LineSolid implemented");
2524 // gc->gcv.line_style = line_style;
2525 gc->gcv.cap_style = cap_style;
2526 gc->gcv.join_style = join_style;
2531 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2537 XSetFunction (Display *dpy, GC gc, int which)
2539 gc->gcv.function = which;
2544 XSetSubwindowMode (Display *dpy, GC gc, int which)
2546 gc->gcv.subwindow_mode = which;
2551 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2553 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2555 if (gc->gcv.clip_mask) {
2556 XFreePixmap (dpy, gc->gcv.clip_mask);
2557 CGImageRelease (gc->clip_mask);
2560 gc->gcv.clip_mask = copy_pixmap (m);
2561 if (gc->gcv.clip_mask)
2562 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2570 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2572 gc->gcv.clip_x_origin = x;
2573 gc->gcv.clip_y_origin = y;
2579 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2580 int *root_x_ret, int *root_y_ret,
2581 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2583 Assert (w->type == WINDOW, "not a window");
2584 NSWindow *nsw = [w->window.view window];
2586 // get bottom left of window on screen, from bottom left
2587 wpos.x = wpos.y = 0;
2588 wpos = [nsw convertBaseToScreen:wpos];
2591 // get bottom left of view on window, from bottom left
2592 vpos.x = vpos.y = 0;
2593 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2595 // get bottom left of view on screen, from bottom left
2599 // get top left of view on screen, from bottom left
2600 vpos.y += w->frame.size.height;
2602 // get top left of view on screen, from top left
2603 NSArray *screens = [NSScreen screens];
2604 NSScreen *screen = (screens && [screens count] > 0
2605 ? [screens objectAtIndex:0]
2606 : [NSScreen mainScreen]);
2607 NSRect srect = [screen frame];
2608 vpos.y = srect.size.height - vpos.y;
2610 // get the mouse position on window, from bottom left
2611 NSEvent *e = [NSApp currentEvent];
2612 NSPoint p = [e locationInWindow];
2614 // get mouse position on screen, from bottom left
2618 // get mouse position on screen, from top left
2619 p.y = srect.size.height - p.y;
2621 if (root_x_ret) *root_x_ret = (int) p.x;
2622 if (root_y_ret) *root_y_ret = (int) p.y;
2623 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2624 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2626 if (mask_ret) *mask_ret = 0; // ####
2627 if (root_ret) *root_ret = 0;
2628 if (child_ret) *child_ret = 0;
2633 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2634 int src_x, int src_y,
2635 int *dest_x_ret, int *dest_y_ret,
2638 Assert (w->type == WINDOW, "not a window");
2639 NSWindow *nsw = [w->window.view window];
2641 // get bottom left of window on screen, from bottom left
2642 wpos.x = wpos.y = 0;
2643 wpos = [nsw convertBaseToScreen:wpos];
2646 // get bottom left of view on window, from bottom left
2647 vpos.x = vpos.y = 0;
2648 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2650 // get bottom left of view on screen, from bottom left
2654 // get top left of view on screen, from bottom left
2655 vpos.y += w->frame.size.height;
2657 // get top left of view on screen, from top left
2658 NSArray *screens = [NSScreen screens];
2659 NSScreen *screen = (screens && [screens count] > 0
2660 ? [screens objectAtIndex:0]
2661 : [NSScreen mainScreen]);
2662 NSRect srect = [screen frame];
2663 vpos.y = srect.size.height - vpos.y;
2665 // point starts out relative to top left of view
2670 // get point relative to top left of screen
2683 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2689 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2692 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2693 char c = (char) ks; // could be smarter about modifiers here...
2694 if (k_ret) *k_ret = ks;
2695 if (size > 0) buf[0] = c;
2696 if (size > 1) buf[1] = 0;
2702 XFlush (Display *dpy)
2704 // Just let the event loop take care of this on its own schedule.
2709 XSync (Display *dpy, Bool flush)
2711 return XFlush (dpy);
2715 // declared in utils/visual.h
2717 has_writable_cells (Screen *s, Visual *v)
2723 visual_depth (Screen *s, Visual *v)
2729 visual_cells (Screen *s, Visual *v)
2735 visual_class (Screen *s, Visual *v)
2740 // declared in utils/grabclient.h
2742 use_subwindow_mode_p (Screen *screen, Window window)