1 /* xscreensaver, Copyright (c) 1991-2010 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 But it's a bunch of function definitions that bear some resemblance to
15 Xlib and that do Cocoa-ish things that bear some resemblance to the
16 things that Xlib might have done.
21 #import <Cocoa/Cocoa.h>
23 #import "jwxyz-timers.h"
26 #define Assert(C,S) do { \
34 # define MAX(a,b) ((a)>(b)?(a):(b))
35 # define MIN(a,b) ((a)<(b)?(a):(b))
38 struct jwxyz_Drawable {
39 enum { WINDOW, PIXMAP } type;
46 unsigned long background;
50 void *cgc_buffer; // the bits to which CGContextRef renders
55 struct jwxyz_Display {
58 struct jwxyz_sources_data *timers_data;
60 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
61 This can change if the window is dragged to
62 a different screen. */
64 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
65 our images with this to avoid translation
77 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
85 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
86 // But we need the metrics on both of them, so they go here.
92 jwxyz_make_display (void *nsview_arg)
94 NSView *view = (NSView *) nsview_arg;
97 Display *d = (Display *) calloc (1, sizeof(*d));
98 d->screen = (Screen *) calloc (1, sizeof(Screen));
101 Visual *v = (Visual *) calloc (1, sizeof(Visual));
102 v->class = TrueColor;
103 v->red_mask = 0x00FF0000;
104 v->green_mask = 0x0000FF00;
105 v->blue_mask = 0x000000FF;
107 d->screen->visual = v;
109 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
112 Window w = (Window) calloc (1, sizeof(*w));
114 // kludge! this needs to be set late, so we do it in XClearWindow!
115 // w->cgc = [[[view window] graphicsContext] graphicsPort];
117 w->window.view = view;
118 CFRetain (w->window.view); // needed for garbage collection?
119 w->window.background = BlackPixel(0,0);
124 w->cgc = [[[view window] graphicsContext] graphicsPort];
127 jwxyz_window_resized (d, w);
133 jwxyz_free_display (Display *dpy)
135 jwxyz_XtRemoveInput_all (dpy);
136 // #### jwxyz_XtRemoveTimeOut_all ();
138 free (dpy->screen->visual);
140 free (dpy->main_window);
146 jwxyz_window_view (Window w)
148 Assert (w->type == WINDOW, "not a window");
149 return w->window.view;
153 /* Call this after any modification to the bits on a Pixmap or Window.
154 Most Pixmaps are used frequently as sources and infrequently as
155 destinations, so it pays to cache the data as a CGImage as needed.
158 invalidate_drawable_cache (Drawable d)
161 CGImageRelease (d->cgi);
167 /* Call this when the View changes size or position.
170 jwxyz_window_resized (Display *dpy, Window w)
172 Assert (w->type == WINDOW, "not a window");
173 NSRect r = [w->window.view frame];
174 w->frame.origin.x = r.origin.x; // NSRect -> CGRect
175 w->frame.origin.y = r.origin.y;
176 w->frame.size.width = r.size.width;
177 w->frame.size.height = r.size.height;
179 // Figure out which screen the window is currently on.
182 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
188 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
189 Assert (dpy->cgdpy, "unable to find CGDisplay");
194 // Figure out this screen's colorspace, and use that for every CGImage.
196 CMProfileRef profile = 0;
197 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
198 Assert (profile, "unable to find colorspace profile");
199 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
200 Assert (dpy->colorspace, "unable to find colorspace");
204 // WTF? It's faster if we *do not* use the screen's colorspace!
206 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
209 invalidate_drawable_cache (w);
214 display_sources_data (Display *dpy)
216 return dpy->timers_data;
221 XRootWindow (Display *dpy, int screen)
223 return dpy->main_window;
227 XDefaultScreenOfDisplay (Display *dpy)
233 XDefaultVisualOfScreen (Screen *screen)
235 return screen->visual;
239 XDisplayOfScreen (Screen *s)
245 XDisplayNumberOfScreen (Screen *s)
251 XScreenNumberOfScreen (Screen *s)
257 XDisplayWidth (Display *dpy, int screen)
259 return (int) dpy->main_window->frame.size.width;
263 XDisplayHeight (Display *dpy, int screen)
265 return (int) dpy->main_window->frame.size.height;
269 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
272 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
273 else if (!alpha_allowed_p)
274 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
275 "bogus color pixel");
280 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
281 BOOL alpha_allowed_p, BOOL fill_p)
283 validate_pixel (argb, depth, alpha_allowed_p);
286 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
288 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
290 float a = ((argb >> 24) & 0xFF) / 255.0;
291 float r = ((argb >> 16) & 0xFF) / 255.0;
292 float g = ((argb >> 8) & 0xFF) / 255.0;
293 float b = ((argb ) & 0xFF) / 255.0;
295 CGContextSetRGBFillColor (cgc, r, g, b, a);
297 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
302 set_line_mode (CGContextRef cgc, XGCValues *gcv)
304 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
305 CGContextSetLineJoin (cgc,
306 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
307 gcv->join_style == JoinRound ? kCGLineJoinRound :
309 CGContextSetLineCap (cgc,
310 gcv->cap_style == CapNotLast ? kCGLineCapButt :
311 gcv->cap_style == CapButt ? kCGLineCapButt :
312 gcv->cap_style == CapRound ? kCGLineCapRound :
317 set_clip_mask (Drawable d, GC gc)
319 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
321 Pixmap p = gc->gcv.clip_mask;
323 Assert (p->type == PIXMAP, "not a pixmap");
325 CGRect wr = d->frame;
327 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
328 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
329 - p->frame.size.height;
330 to.size.width = p->frame.size.width;
331 to.size.height = p->frame.size.height;
333 CGContextClipToMask (d->cgc, to, gc->clip_mask);
337 /* Pushes a GC context; sets BlendMode and ClipMask.
340 push_gc (Drawable d, GC gc)
342 CGContextRef cgc = d->cgc;
343 CGContextSaveGState (cgc);
345 switch (gc->gcv.function) {
348 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
349 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
350 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
351 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
352 default: abort(); break;
355 if (gc->gcv.clip_mask)
356 set_clip_mask (d, gc);
359 #define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
362 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
365 push_color_gc (Drawable d, GC gc, unsigned long color,
366 BOOL antialias_p, Bool fill_p)
370 int depth = gc->depth;
371 switch (gc->gcv.function) {
372 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
373 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
376 CGContextRef cgc = d->cgc;
378 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
379 CGContextSetShouldAntialias (cgc, antialias_p);
383 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
386 push_fg_gc (Drawable d, GC gc, Bool fill_p)
388 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
391 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
394 push_bg_gc (Drawable d, GC gc, Bool fill_p)
396 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
401 /* You've got to be fucking kidding me!
403 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
404 with repeated calls to CGContextDrawImage than it is to make a single
405 call to CGContextFillRects() with a list of 1x1 rectangles!
407 I still wouldn't call it *fast*, however...
409 #define XDRAWPOINTS_IMAGES
412 XDrawPoints (Display *dpy, Drawable d, GC gc,
413 XPoint *points, int count, int mode)
416 CGRect wr = d->frame;
418 push_fg_gc (d, gc, YES);
420 # ifdef XDRAWPOINTS_IMAGES
422 unsigned int argb = gc->gcv.foreground;
423 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
425 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
427 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
428 CGImageRef cgi = CGImageCreate (1, 1,
431 /* Host-ordered, since we're using the
432 address of an int as the color data. */
433 (kCGImageAlphaNoneSkipFirst |
434 kCGBitmapByteOrder32Host),
437 NO, /* interpolate */
438 kCGRenderingIntentDefault);
439 CGDataProviderRelease (prov);
441 CGContextRef cgc = d->cgc;
443 rect.size.width = rect.size.height = 1;
444 for (i = 0; i < count; i++) {
445 if (i > 0 && mode == CoordModePrevious) {
446 rect.origin.x += points->x;
447 rect.origin.x -= points->y;
449 rect.origin.x = wr.origin.x + points->x;
450 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
453 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
454 CGContextDrawImage (cgc, rect, cgi);
458 CGImageRelease (cgi);
460 # else /* ! XDRAWPOINTS_IMAGES */
462 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
465 for (i = 0; i < count; i++) {
466 r->size.width = r->size.height = 1;
467 if (i > 0 && mode == CoordModePrevious) {
468 r->origin.x = r[-1].origin.x + points->x;
469 r->origin.y = r[-1].origin.x - points->y;
471 r->origin.x = wr.origin.x + points->x;
472 r->origin.y = wr.origin.y + wr.size.height - points->y;
478 CGContextFillRects (d->cgc, rects, count);
481 # endif /* ! XDRAWPOINTS_IMAGES */
484 invalidate_drawable_cache (d);
491 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
496 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
500 static void draw_rect (Display *, Drawable, GC,
501 int x, int y, unsigned int width, unsigned int height,
502 BOOL foreground_p, BOOL fill_p);
505 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
506 int src_x, int src_y,
507 unsigned int width, unsigned int height,
508 int dst_x, int dst_y)
510 Assert (gc, "no GC");
511 Assert ((width < 65535), "improbably large width");
512 Assert ((height < 65535), "improbably large height");
513 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
514 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
515 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
516 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
518 if (width == 0 || height == 0)
521 if (gc->gcv.function == GXset ||
522 gc->gcv.function == GXclear) {
523 // "set" and "clear" are dumb drawing modes that ignore the source
524 // bits and just draw solid rectangles.
525 set_color (dst->cgc, (gc->gcv.function == GXset
526 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
527 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
528 gc->depth, gc->gcv.alpha_allowed_p, YES);
529 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
533 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
534 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
535 // bounds of their drawables.
536 BOOL clipped = NO; // Whether we did any clipping of the rects.
538 src_frame = src->frame;
539 dst_frame = dst->frame;
541 // Initialize src_rect...
543 src_rect.origin.x = src_frame.origin.x + src_x;
544 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
546 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
547 src_rect.size.width = width;
548 src_rect.size.height = height;
550 // Initialize dst_rect...
552 dst_rect.origin.x = dst_frame.origin.x + dst_x;
553 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
555 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
556 dst_rect.size.width = width;
557 dst_rect.size.height = height;
559 // Clip rects to frames...
561 // CGRect orig_src_rect = src_rect;
562 CGRect orig_dst_rect = dst_rect;
564 # define CLIP(THIS,THAT,VAL,SIZE) do { \
565 float off = THIS##_rect.origin.VAL; \
568 THIS##_rect.size.SIZE += off; \
569 THAT##_rect.size.SIZE += off; \
570 THIS##_rect.origin.VAL -= off; \
571 THAT##_rect.origin.VAL -= off; \
573 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
574 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
577 THIS##_rect.size.SIZE -= off; \
578 THAT##_rect.size.SIZE -= off; \
581 CLIP (dst, src, x, width);
582 CLIP (dst, src, y, height);
583 CLIP (src, dst, x, width);
584 CLIP (src, dst, y, height);
587 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
590 NSObject *releaseme = 0;
593 BOOL free_cgi_p = NO;
595 if (src->type == PIXMAP) {
597 // If we are copying from a Pixmap to a Pixmap or Window, we must first
598 // copy the bits to an intermediary CGImage object, then copy that to the
599 // destination drawable's CGContext.
601 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
602 // case of copying from a Pixmap back to itself, but I don't think that
603 // happens very often anyway.)
605 // First we get a CGImage out of the pixmap CGContext -- it's the whole
606 // pixmap, but it presumably shares the data pointer instead of copying
607 // it. We then cache that CGImage it inside the Pixmap object. Note:
608 // invalidate_drawable_cache() must be called to discard this any time a
609 // modification is made to the pixmap, or we'll end up re-using old bits.
612 src->cgi = CGBitmapContextCreateImage (src->cgc);
615 // if doing a sub-rect, trim it down.
616 if (src_rect.origin.x != src_frame.origin.x ||
617 src_rect.origin.y != src_frame.origin.y ||
618 src_rect.size.width != src_frame.size.width ||
619 src_rect.size.height != src_frame.size.height) {
620 // #### I don't understand why this is needed...
621 src_rect.origin.y = (src_frame.size.height -
622 src_rect.size.height - src_rect.origin.y);
623 // This does not copy image data, so it should be fast.
624 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
628 if (src->pixmap.depth == 1)
631 } else { /* (src->type == WINDOW) */
633 NSRect nsfrom; // NSRect != CGRect on 10.4
634 nsfrom.origin.x = src_rect.origin.x;
635 nsfrom.origin.y = src_rect.origin.y;
636 nsfrom.size.width = src_rect.size.width;
637 nsfrom.size.height = src_rect.size.height;
641 // If we are copying from a window to itself, we can use NSCopyBits()
642 // without first copying the rectangle to an intermediary CGImage.
643 // This is ~28% faster (but I *expected* it to be twice as fast...)
644 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
650 // If we are copying from a Window to a Pixmap, we must first copy
651 // the bits to an intermediary CGImage object, then copy that to the
652 // Pixmap's CGContext.
654 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
655 initWithFocusedViewRect:nsfrom];
656 unsigned char *data = [bm bitmapData];
657 int bps = [bm bitsPerSample];
658 int bpp = [bm bitsPerPixel];
659 int bpl = [bm bytesPerRow];
662 // create a CGImage from those bits.
663 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
664 // but that method didn't exist in 10.4.)
666 CGDataProviderRef prov =
667 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
669 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
672 /* Use whatever default bit ordering we got from
673 initWithFocusedViewRect. I would have assumed
674 that it was (kCGImageAlphaNoneSkipFirst |
675 kCGBitmapByteOrder32Host), but on Intel,
681 NO, /* interpolate */
682 kCGRenderingIntentDefault);
684 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
685 CGDataProviderRelease (prov);
689 if (mask_p) { // src depth == 1
691 push_bg_gc (dst, gc, YES);
693 // fill the destination rectangle with solid background...
694 CGContextFillRect (dst->cgc, orig_dst_rect);
696 // then fill in a solid rectangle of the fg color, using the image as an
697 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
698 set_color (dst->cgc, gc->gcv.foreground, gc->depth,
699 gc->gcv.alpha_allowed_p, YES);
700 CGContextClipToMask (dst->cgc, dst_rect, cgi);
701 CGContextFillRect (dst->cgc, dst_rect);
705 } else { // src depth > 1
709 // If either the src or dst rects did not lie within their drawables,
710 // then we have adjusted both the src and dst rects to account for
711 // the clipping; that means we need to first clear to the background,
712 // so that clipped bits end up in the bg color instead of simply not
716 set_color (dst->cgc, gc->gcv.background, gc->depth,
717 gc->gcv.alpha_allowed_p, YES);
718 CGContextFillRect (dst->cgc, orig_dst_rect);
722 // copy the CGImage onto the destination CGContext
723 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
724 CGContextDrawImage (dst->cgc, dst_rect, cgi);
726 // No cgi means src == dst, and both are Windows.
728 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
729 nsfrom.origin.y = src_rect.origin.y;
730 nsfrom.size.width = src_rect.size.width;
731 nsfrom.size.height = src_rect.size.height;
733 nsto.x = dst_rect.origin.x;
734 nsto.y = dst_rect.origin.y;
735 NSCopyBits (0, nsfrom, nsto);
741 if (free_cgi_p) CGImageRelease (cgi);
742 if (releaseme) [releaseme release];
743 invalidate_drawable_cache (dst);
749 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
750 int src_x, int src_y,
751 unsigned width, int height,
752 int dest_x, int dest_y, unsigned long plane)
754 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
756 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
757 // not to white/black.
758 return XCopyArea (dpy, src, dest, gc,
759 src_x, src_y, width, height, dest_x, dest_y);
764 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
766 // when drawing a zero-length line, obey line-width and cap-style.
767 if (x1 == x2 && y1 == y2) {
768 int w = gc->gcv.line_width;
771 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
772 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
774 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
777 CGRect wr = d->frame;
779 p.x = wr.origin.x + x1;
780 p.y = wr.origin.y + wr.size.height - y1;
782 push_fg_gc (d, gc, NO);
784 set_line_mode (d->cgc, &gc->gcv);
785 CGContextBeginPath (d->cgc);
786 CGContextMoveToPoint (d->cgc, p.x, p.y);
787 p.x = wr.origin.x + x2;
788 p.y = wr.origin.y + wr.size.height - y2;
789 CGContextAddLineToPoint (d->cgc, p.x, p.y);
790 CGContextStrokePath (d->cgc);
792 invalidate_drawable_cache (d);
797 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
802 CGRect wr = d->frame;
803 push_fg_gc (d, gc, NO);
804 set_line_mode (d->cgc, &gc->gcv);
806 // if the first and last points coincide, use closepath to get
807 // the proper line-joining.
808 BOOL closed_p = (points[0].x == points[count-1].x &&
809 points[0].y == points[count-1].y);
810 if (closed_p) count--;
812 p.x = wr.origin.x + points->x;
813 p.y = wr.origin.y + wr.size.height - points->y;
815 CGContextBeginPath (d->cgc);
816 CGContextMoveToPoint (d->cgc, p.x, p.y);
817 for (i = 1; i < count; i++) {
818 if (mode == CoordModePrevious) {
822 p.x = wr.origin.x + points->x;
823 p.y = wr.origin.y + wr.size.height - points->y;
825 CGContextAddLineToPoint (d->cgc, p.x, p.y);
828 if (closed_p) CGContextClosePath (d->cgc);
829 CGContextStrokePath (d->cgc);
831 invalidate_drawable_cache (d);
837 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
840 CGRect wr = d->frame;
842 push_fg_gc (d, gc, NO);
843 set_line_mode (d->cgc, &gc->gcv);
844 CGContextBeginPath (d->cgc);
845 for (i = 0; i < count; i++) {
846 CGContextMoveToPoint (d->cgc,
847 wr.origin.x + segments->x1,
848 wr.origin.y + wr.size.height - segments->y1);
849 CGContextAddLineToPoint (d->cgc,
850 wr.origin.x + segments->x2,
851 wr.origin.y + wr.size.height - segments->y2);
854 CGContextStrokePath (d->cgc);
856 invalidate_drawable_cache (d);
862 XClearWindow (Display *dpy, Window win)
864 Assert (win->type == WINDOW, "not a window");
865 CGRect wr = win->frame;
866 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
870 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
872 Assert (w->type == WINDOW, "not a window");
873 validate_pixel (pixel, 32, NO);
874 w->window.background = pixel;
879 draw_rect (Display *dpy, Drawable d, GC gc,
880 int x, int y, unsigned int width, unsigned int height,
881 BOOL foreground_p, BOOL fill_p)
883 CGRect wr = d->frame;
885 r.origin.x = wr.origin.x + x;
886 r.origin.y = wr.origin.y + wr.size.height - y - height;
887 r.size.width = width;
888 r.size.height = height;
892 push_fg_gc (d, gc, fill_p);
894 push_bg_gc (d, gc, fill_p);
898 CGContextFillRect (d->cgc, r);
901 set_line_mode (d->cgc, &gc->gcv);
902 CGContextStrokeRect (d->cgc, r);
907 invalidate_drawable_cache (d);
912 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
913 unsigned int width, unsigned int height)
915 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
920 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
921 unsigned int width, unsigned int height)
923 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
928 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
930 CGRect wr = d->frame;
932 push_fg_gc (d, gc, YES);
933 for (i = 0; i < n; i++) {
935 r.origin.x = wr.origin.x + rects->x;
936 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
937 r.size.width = rects->width;
938 r.size.height = rects->height;
939 CGContextFillRect (d->cgc, r);
943 invalidate_drawable_cache (d);
949 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
951 Assert (win->type == WINDOW, "not a window");
952 set_color (win->cgc, win->window.background, 32, NO, YES);
953 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
959 XFillPolygon (Display *dpy, Drawable d, GC gc,
960 XPoint *points, int npoints, int shape, int mode)
962 CGRect wr = d->frame;
964 push_fg_gc (d, gc, YES);
965 CGContextBeginPath (d->cgc);
966 for (i = 0; i < npoints; i++) {
968 if (i > 0 && mode == CoordModePrevious) {
972 x = wr.origin.x + points[i].x;
973 y = wr.origin.y + wr.size.height - points[i].y;
977 CGContextMoveToPoint (d->cgc, x, y);
979 CGContextAddLineToPoint (d->cgc, x, y);
981 CGContextClosePath (d->cgc);
982 if (gc->gcv.fill_rule == EvenOddRule)
983 CGContextEOFillPath (d->cgc);
985 CGContextFillPath (d->cgc);
987 invalidate_drawable_cache (d);
991 #define radians(DEG) ((DEG) * M_PI / 180.0)
992 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
995 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
996 unsigned int width, unsigned int height, int angle1, int angle2,
999 CGRect wr = d->frame;
1001 bound.origin.x = wr.origin.x + x;
1002 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1003 bound.size.width = width;
1004 bound.size.height = height;
1007 ctr.x = bound.origin.x + bound.size.width /2;
1008 ctr.y = bound.origin.y + bound.size.height/2;
1010 float r1 = radians (angle1/64.0);
1011 float r2 = radians (angle2/64.0) + r1;
1012 BOOL clockwise = angle2 < 0;
1013 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1015 push_fg_gc (d, gc, fill_p);
1017 CGContextBeginPath (d->cgc);
1019 CGContextSaveGState(d->cgc);
1020 CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
1021 CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
1023 CGContextMoveToPoint (d->cgc, 0, 0);
1025 CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1026 CGContextRestoreGState (d->cgc); // restore before stroke, for line width
1029 CGContextClosePath (d->cgc); // for proper line joining
1032 CGContextFillPath (d->cgc);
1034 set_line_mode (d->cgc, &gc->gcv);
1035 CGContextStrokePath (d->cgc);
1039 invalidate_drawable_cache (d);
1044 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1045 unsigned int width, unsigned int height, int angle1, int angle2)
1047 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1051 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1052 unsigned int width, unsigned int height, int angle1, int angle2)
1054 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1058 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1061 for (i = 0; i < narcs; i++)
1062 draw_arc (dpy, d, gc,
1063 arcs[i].x, arcs[i].y,
1064 arcs[i].width, arcs[i].height,
1065 arcs[i].angle1, arcs[i].angle2,
1071 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1074 for (i = 0; i < narcs; i++)
1075 draw_arc (dpy, d, gc,
1076 arcs[i].x, arcs[i].y,
1077 arcs[i].width, arcs[i].height,
1078 arcs[i].angle1, arcs[i].angle2,
1085 gcv_defaults (XGCValues *gcv, int depth)
1087 memset (gcv, 0, sizeof(*gcv));
1088 gcv->function = GXcopy;
1089 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1090 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1091 gcv->line_width = 1;
1092 gcv->cap_style = CapNotLast;
1093 gcv->join_style = JoinMiter;
1094 gcv->fill_rule = EvenOddRule;
1096 gcv->alpha_allowed_p = NO;
1097 gcv->antialias_p = YES;
1101 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1103 if (mask & GCFunction) gc->gcv.function = from->function;
1104 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1105 if (mask & GCBackground) gc->gcv.background = from->background;
1106 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1107 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1108 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1109 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1110 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1111 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1112 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1114 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1115 if (mask & GCFont) XSetFont (0, gc, from->font);
1117 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1118 gc->gcv.alpha_allowed_p);
1119 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1120 gc->gcv.alpha_allowed_p);
1122 if (mask & GCLineStyle) abort();
1123 if (mask & GCPlaneMask) abort();
1124 if (mask & GCFillStyle) abort();
1125 if (mask & GCTile) abort();
1126 if (mask & GCStipple) abort();
1127 if (mask & GCTileStipXOrigin) abort();
1128 if (mask & GCTileStipYOrigin) abort();
1129 if (mask & GCGraphicsExposures) abort();
1130 if (mask & GCDashOffset) abort();
1131 if (mask & GCDashList) abort();
1132 if (mask & GCArcMode) abort();
1137 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1139 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1140 if (d->type == WINDOW) {
1142 } else { /* (d->type == PIXMAP) */
1143 gc->depth = d->pixmap.depth;
1146 gcv_defaults (&gc->gcv, gc->depth);
1147 set_gcv (gc, xgcv, mask);
1152 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1154 set_gcv (gc, gcv, mask);
1160 XFreeGC (Display *dpy, GC gc)
1163 XUnloadFont (dpy, gc->gcv.font);
1165 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1167 if (gc->gcv.clip_mask) {
1168 XFreePixmap (dpy, gc->gcv.clip_mask);
1169 CGImageRelease (gc->clip_mask);
1177 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1179 Assert (w->type == WINDOW, "not a window");
1180 memset (xgwa, 0, sizeof(*xgwa));
1181 xgwa->x = w->frame.origin.x;
1182 xgwa->y = w->frame.origin.y;
1183 xgwa->width = w->frame.size.width;
1184 xgwa->height = w->frame.size.height;
1186 xgwa->screen = dpy->screen;
1187 xgwa->visual = dpy->screen->visual;
1192 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1193 int *x_ret, int *y_ret,
1194 unsigned int *w_ret, unsigned int *h_ret,
1195 unsigned int *bw_ret, unsigned int *d_ret)
1197 *x_ret = d->frame.origin.x;
1198 *y_ret = d->frame.origin.y;
1199 *w_ret = d->frame.size.width;
1200 *h_ret = d->frame.size.height;
1201 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1202 *root_ret = RootWindow (dpy, 0);
1209 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1211 // store 32 bit ARGB in the pixel field.
1212 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1213 color->pixel = (uint32_t)
1215 (((color->red >> 8) & 0xFF) << 16) |
1216 (((color->green >> 8) & 0xFF) << 8) |
1217 (((color->blue >> 8) & 0xFF) ));
1222 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1223 unsigned long *pmret, unsigned int npl,
1224 unsigned long *pxret, unsigned int npx)
1230 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1232 Assert(0, "XStoreColors called");
1237 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1239 Assert(0, "XStoreColor called");
1244 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1245 unsigned long planes)
1251 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1253 unsigned char r=0, g=0, b=0;
1254 if (*spec == '#' && strlen(spec) == 7) {
1255 static unsigned const char hex[] = { // yeah yeah, shoot me.
1256 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,
1257 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,
1258 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,
1259 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,
1260 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,
1261 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,
1262 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,
1263 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};
1264 r = (hex[spec[1]] << 4) | hex[spec[2]];
1265 g = (hex[spec[3]] << 4) | hex[spec[4]];
1266 b = (hex[spec[5]] << 4) | hex[spec[6]];
1267 } else if (!strcasecmp(spec,"black")) {
1269 } else if (!strcasecmp(spec,"white")) {
1271 } else if (!strcasecmp(spec,"red")) {
1273 } else if (!strcasecmp(spec,"green")) {
1275 } else if (!strcasecmp(spec,"blue")) {
1277 } else if (!strcasecmp(spec,"cyan")) {
1279 } else if (!strcasecmp(spec,"magenta")) {
1281 } else if (!strcasecmp(spec,"yellow")) {
1287 ret->red = (r << 8) | r;
1288 ret->green = (g << 8) | g;
1289 ret->blue = (b << 8) | b;
1290 ret->flags = DoRed|DoGreen|DoBlue;
1295 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1296 XColor *screen_ret, XColor *exact_ret)
1298 if (! XParseColor (dpy, cmap, name, screen_ret))
1300 *exact_ret = *screen_ret;
1301 return XAllocColor (dpy, cmap, screen_ret);
1305 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1307 validate_pixel (color->pixel, 32, NO);
1308 unsigned char r = ((color->pixel >> 16) & 0xFF);
1309 unsigned char g = ((color->pixel >> 8) & 0xFF);
1310 unsigned char b = ((color->pixel ) & 0xFF);
1311 color->red = (r << 8) | r;
1312 color->green = (g << 8) | g;
1313 color->blue = (b << 8) | b;
1314 color->flags = DoRed|DoGreen|DoBlue;
1319 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1322 for (i = 0; i < n; i++)
1323 XQueryColor (dpy, cmap, &c[i]);
1328 static unsigned long
1329 ximage_getpixel_1 (XImage *ximage, int x, int y)
1331 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1335 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1338 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1340 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1345 static unsigned long
1346 ximage_getpixel_32 (XImage *ximage, int x, int y)
1348 return ((unsigned long)
1349 *((uint32_t *) ximage->data +
1350 (y * (ximage->bytes_per_line >> 2)) +
1355 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1357 *((uint32_t *) ximage->data +
1358 (y * (ximage->bytes_per_line >> 2)) +
1359 x) = (uint32_t) pixel;
1365 XInitImage (XImage *ximage)
1367 if (!ximage->bytes_per_line)
1368 ximage->bytes_per_line = (ximage->depth == 1
1369 ? (ximage->width + 7) / 8
1370 : ximage->width * 4);
1372 if (ximage->depth == 1) {
1373 ximage->f.put_pixel = ximage_putpixel_1;
1374 ximage->f.get_pixel = ximage_getpixel_1;
1375 } else if (ximage->depth == 32 || ximage->depth == 24) {
1376 ximage->f.put_pixel = ximage_putpixel_32;
1377 ximage->f.get_pixel = ximage_getpixel_32;
1386 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1387 int format, int offset, char *data,
1388 unsigned int width, unsigned int height,
1389 int bitmap_pad, int bytes_per_line)
1391 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1392 ximage->width = width;
1393 ximage->height = height;
1394 ximage->format = format;
1395 ximage->data = data;
1396 ximage->bitmap_unit = 8;
1397 ximage->byte_order = MSBFirst;
1398 ximage->bitmap_bit_order = ximage->byte_order;
1399 ximage->bitmap_pad = bitmap_pad;
1400 ximage->depth = depth;
1401 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1402 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1403 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1404 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1405 ximage->bytes_per_line = bytes_per_line;
1407 XInitImage (ximage);
1412 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1414 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1415 w, h, from->bitmap_pad, 0);
1416 to->data = (char *) malloc (h * to->bytes_per_line);
1418 if (x >= from->width)
1420 else if (x+w > from->width)
1421 w = from->width - x;
1423 if (y >= from->height)
1425 else if (y+h > from->height)
1426 h = from->height - y;
1429 for (ty = 0; ty < h; ty++)
1430 for (tx = 0; tx < w; tx++)
1431 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1436 XPixmapFormatValues *
1437 XListPixmapFormats (Display *dpy, int *n_ret)
1439 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1441 ret[0].bits_per_pixel = 32;
1442 ret[0].scanline_pad = 8;
1444 ret[1].bits_per_pixel = 1;
1445 ret[1].scanline_pad = 8;
1452 XGetPixel (XImage *ximage, int x, int y)
1454 return ximage->f.get_pixel (ximage, x, y);
1459 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1461 return ximage->f.put_pixel (ximage, x, y, pixel);
1465 XDestroyImage (XImage *ximage)
1467 if (ximage->data) free (ximage->data);
1474 flipbits (unsigned const char *in, unsigned char *out, int length)
1476 static const unsigned char table[256] = {
1477 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1478 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1479 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1480 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1481 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1482 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1483 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1484 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1485 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1486 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1487 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1488 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1489 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1490 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1491 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1492 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1493 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1494 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1495 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1496 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1497 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1498 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1499 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1500 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1501 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1502 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1503 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1504 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1505 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1506 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1507 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1508 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1510 while (--length > 0)
1511 *out++ = table[*in++];
1516 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1517 int src_x, int src_y, int dest_x, int dest_y,
1518 unsigned int w, unsigned int h)
1520 CGRect wr = d->frame;
1522 Assert (gc, "no GC");
1523 Assert ((w < 65535), "improbably large width");
1524 Assert ((h < 65535), "improbably large height");
1525 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1526 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1527 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1528 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1530 // Clip width and height to the bounds of the Drawable
1532 if (dest_x + w > wr.size.width) {
1533 if (dest_x > wr.size.width)
1535 w = wr.size.width - dest_x;
1537 if (dest_y + h > wr.size.height) {
1538 if (dest_y > wr.size.height)
1540 h = wr.size.height - dest_y;
1542 if (w <= 0 || h <= 0)
1545 // Clip width and height to the bounds of the XImage
1547 if (src_x + w > ximage->width) {
1548 if (src_x > ximage->width)
1550 w = ximage->width - src_x;
1552 if (src_y + h > ximage->height) {
1553 if (src_y > ximage->height)
1555 h = ximage->height - src_y;
1557 if (w <= 0 || h <= 0)
1560 if (gc->gcv.function == GXset ||
1561 gc->gcv.function == GXclear) {
1562 // "set" and "clear" are dumb drawing modes that ignore the source
1563 // bits and just draw solid rectangles.
1564 set_color (d->cgc, (gc->gcv.function == GXset
1565 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1566 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1567 gc->depth, gc->gcv.alpha_allowed_p, YES);
1568 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1572 int bpl = ximage->bytes_per_line;
1573 int bpp = ximage->bits_per_pixel;
1574 int bsize = bpl * h;
1575 char *data = ximage->data;
1578 r.origin.x = wr.origin.x + dest_x;
1579 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1585 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1586 to create a CGImage from a sub-rectagle of the XImage.
1588 data += (src_y * bpl) + (src_x * 4);
1589 CGDataProviderRef prov =
1590 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1592 CGImageRef cgi = CGImageCreate (w, h,
1595 /* Need this for XPMs to have the right
1596 colors, e.g. the logo in "maze". */
1597 (kCGImageAlphaNoneSkipFirst |
1598 kCGBitmapByteOrder32Host),
1600 NULL, /* decode[] */
1601 NO, /* interpolate */
1602 kCGRenderingIntentDefault);
1603 CGDataProviderRelease (prov);
1604 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1605 CGContextDrawImage (d->cgc, r, cgi);
1606 CGImageRelease (cgi);
1608 } else { // (bpp == 1)
1610 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1612 #### However, the bit order within a byte in a 1bpp XImage is
1613 the wrong way around from what Quartz expects, so first we
1614 have to copy the data to reverse it. Shit! Maybe it
1615 would be worthwhile to go through the hacks and #ifdef
1616 each one that diddles 1bpp XImage->data directly...
1618 Assert ((src_x % 8) == 0,
1619 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1621 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1622 unsigned char *flipped = (unsigned char *) malloc (bsize);
1624 flipbits ((unsigned char *) data, flipped, bsize);
1626 CGDataProviderRef prov =
1627 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1628 CGImageRef mask = CGImageMaskCreate (w, h,
1631 NULL, /* decode[] */
1632 NO); /* interpolate */
1633 push_fg_gc (d, gc, YES);
1635 CGContextFillRect (d->cgc, r); // foreground color
1636 CGContextClipToMask (d->cgc, r, mask);
1637 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1638 CGContextFillRect (d->cgc, r); // background color
1642 CGDataProviderRelease (prov);
1643 CGImageRelease (mask);
1646 invalidate_drawable_cache (d);
1653 XGetImage (Display *dpy, Drawable d, int x, int y,
1654 unsigned int width, unsigned int height,
1655 unsigned long plane_mask, int format)
1657 const unsigned char *data = 0;
1658 int depth, ibpp, ibpl, alpha_first_p;
1659 NSBitmapImageRep *bm = 0;
1661 Assert ((width < 65535), "improbably large width");
1662 Assert ((height < 65535), "improbably large height");
1663 Assert ((x < 65535 && x > -65535), "improbably large x");
1664 Assert ((y < 65535 && y > -65535), "improbably large y");
1666 if (d->type == PIXMAP) {
1667 depth = d->pixmap.depth;
1668 alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
1669 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1670 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1671 data = CGBitmapContextGetData (d->cgc);
1672 Assert (data, "CGBitmapContextGetData failed");
1674 // get the bits (desired sub-rectangle) out of the NSView
1676 nsfrom.origin.x = x;
1677 nsfrom.origin.y = y;
1678 nsfrom.size.width = width;
1679 nsfrom.size.height = height;
1680 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
1682 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1683 ibpp = [bm bitsPerPixel];
1684 ibpl = [bm bytesPerRow];
1685 data = [bm bitmapData];
1686 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1689 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1690 data += (y * ibpl) + (x * (ibpp/8));
1692 format = (depth == 1 ? XYPixmap : ZPixmap);
1693 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1695 image->data = (char *) malloc (height * image->bytes_per_line);
1697 int obpl = image->bytes_per_line;
1699 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1700 means that on Intel it is BGRA when viewed by bytes (And BGR
1701 when using 24bpp packing).
1703 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1704 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1705 indicator of this latest kink.
1709 const unsigned char *iline = data;
1710 for (yy = 0; yy < height; yy++) {
1712 const unsigned char *iline2 = iline;
1713 for (xx = 0; xx < width; xx++) {
1715 iline2++; // ignore R or A or A or B
1716 iline2++; // ignore G or B or R or G
1717 unsigned char r = *iline2++; // use B or G or G or R
1718 if (ibpp == 32) iline2++; // ignore A or R or B or A
1720 XPutPixel (image, xx, yy, (r ? 1 : 0));
1725 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1726 const unsigned char *iline = data;
1727 unsigned char *oline = (unsigned char *) image->data;
1728 for (yy = 0; yy < height; yy++) {
1730 const unsigned char *iline2 = iline;
1731 unsigned char *oline2 = oline;
1733 if (alpha_first_p) // ARGB
1734 for (xx = 0; xx < width; xx++) {
1735 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1736 unsigned char r = *iline2++;
1737 unsigned char g = *iline2++;
1738 unsigned char b = *iline2++;
1739 uint32_t pixel = ((a << 24) |
1743 *((uint32_t *) oline2) = pixel;
1747 for (xx = 0; xx < width; xx++) {
1748 unsigned char r = *iline2++;
1749 unsigned char g = *iline2++;
1750 unsigned char b = *iline2++;
1751 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1752 uint32_t pixel = ((a << 24) |
1756 *((uint32_t *) oline2) = pixel;
1765 if (bm) [bm release];
1771 /* Returns a transformation matrix to do rotation as per the provided
1772 EXIF "Orientation" value.
1774 static CGAffineTransform
1775 exif_rotate (int rot, CGSize rect)
1777 CGAffineTransform trans = CGAffineTransformIdentity;
1779 case 2: // flip horizontal
1780 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1781 trans = CGAffineTransformScale (trans, -1, 1);
1784 case 3: // rotate 180
1785 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1786 trans = CGAffineTransformRotate (trans, M_PI);
1789 case 4: // flip vertical
1790 trans = CGAffineTransformMakeTranslation (0, rect.height);
1791 trans = CGAffineTransformScale (trans, 1, -1);
1794 case 5: // transpose (UL-to-LR axis)
1795 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1796 trans = CGAffineTransformScale (trans, -1, 1);
1797 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1800 case 6: // rotate 90
1801 trans = CGAffineTransformMakeTranslation (0, rect.width);
1802 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1805 case 7: // transverse (UR-to-LL axis)
1806 trans = CGAffineTransformMakeScale (-1, 1);
1807 trans = CGAffineTransformRotate (trans, M_PI / 2);
1810 case 8: // rotate 270
1811 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1812 trans = CGAffineTransformRotate (trans, M_PI / 2);
1824 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1825 Bool nsimg_p, void *img_arg,
1826 XRectangle *geom_ret, int exif_rotation)
1829 CGImageSourceRef cgsrc;
1834 NSImage *nsimg = (NSImage *) img_arg;
1835 imgr = [nsimg size];
1837 // convert the NSImage to a CGImage via the toll-free-bridging
1838 // of NSData and CFData...
1840 NSData *nsdata = [NSBitmapImageRep
1841 TIFFRepresentationOfImageRepsInArray:
1842 [nsimg representations]];
1843 CFDataRef cfdata = (CFDataRef) nsdata;
1844 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1845 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1848 cgi = (CGImageRef) img_arg;
1849 imgr.width = CGImageGetWidth (cgi);
1850 imgr.height = CGImageGetHeight (cgi);
1853 Bool rot_p = (exif_rotation >= 5);
1856 imgr = NSMakeSize (imgr.height, imgr.width);
1858 CGRect winr = d->frame;
1859 float rw = winr.size.width / imgr.width;
1860 float rh = winr.size.height / imgr.height;
1861 float r = (rw < rh ? rw : rh);
1864 dst.size.width = imgr.width * r;
1865 dst.size.height = imgr.height * r;
1866 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1867 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1869 dst2.origin.x = dst2.origin.y = 0;
1871 dst2.size.width = dst.size.height;
1872 dst2.size.height = dst.size.width;
1874 dst2.size = dst.size;
1877 // Clear the part not covered by the image to background or black.
1879 if (d->type == WINDOW)
1880 XClearWindow (dpy, d);
1882 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1883 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1886 CGAffineTransform trans =
1887 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1889 CGContextSaveGState (d->cgc);
1890 CGContextConcatCTM (d->cgc,
1891 CGAffineTransformMakeTranslation (dst.origin.x,
1893 CGContextConcatCTM (d->cgc, trans);
1894 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1895 CGContextDrawImage (d->cgc, dst2, cgi);
1896 CGContextRestoreGState (d->cgc);
1900 CGImageRelease (cgi);
1904 geom_ret->x = dst.origin.x;
1905 geom_ret->y = dst.origin.y;
1906 geom_ret->width = dst.size.width;
1907 geom_ret->height = dst.size.height;
1910 invalidate_drawable_cache (d);
1915 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1917 unsigned int w, unsigned int h,
1918 unsigned long fg, unsigned int bg,
1921 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1922 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1923 (char *) data, w, h, 0, 0);
1925 gcv.foreground = fg;
1926 gcv.background = bg;
1927 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1928 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1931 XDestroyImage (image);
1936 XCreatePixmap (Display *dpy, Drawable d,
1937 unsigned int width, unsigned int height, unsigned int depth)
1939 char *data = (char *) malloc (width * height * 4);
1940 if (! data) return 0;
1942 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1944 p->frame.size.width = width;
1945 p->frame.size.height = height;
1946 p->pixmap.depth = depth;
1947 p->pixmap.cgc_buffer = data;
1949 /* Quartz doesn't have a 1bpp image type.
1950 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
1951 don't support that! So we always use 32bpp, regardless of depth. */
1953 p->cgc = CGBitmapContextCreate (data, width, height,
1954 8, /* bits per component */
1955 width * 4, /* bpl */
1957 // Without this, it returns 0...
1958 kCGImageAlphaNoneSkipFirst
1960 Assert (p->cgc, "could not create CGBitmapContext");
1966 XFreePixmap (Display *d, Pixmap p)
1968 Assert (p->type == PIXMAP, "not a pixmap");
1969 invalidate_drawable_cache (p);
1970 CGContextRelease (p->cgc);
1971 if (p->pixmap.cgc_buffer)
1972 free (p->pixmap.cgc_buffer);
1979 copy_pixmap (Display *dpy, Pixmap p)
1982 Assert (p->type == PIXMAP, "not a pixmap");
1984 int width = p->frame.size.width;
1985 int height = p->frame.size.height;
1986 char *data = (char *) malloc (width * height * 4);
1987 if (! data) return 0;
1989 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
1991 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1994 p2->pixmap.cgc_buffer = data;
1995 p2->cgc = CGBitmapContextCreate (data, width, height,
1996 8, /* bits per component */
1997 width * 4, /* bpl */
1999 // Without this, it returns 0...
2000 kCGImageAlphaNoneSkipFirst
2002 Assert (p2->cgc, "could not create CGBitmapContext");
2008 /* Font metric terminology, as used by X11:
2010 "lbearing" is the distance from the logical origin to the leftmost pixel.
2011 If a character's ink extends to the left of the origin, it is negative.
2013 "rbearing" is the distance from the logical origin to the rightmost pixel.
2015 "descent" is the distance from the logical origin to the bottommost pixel.
2016 For characters with descenders, it is negative.
2018 "ascent" is the distance from the logical origin to the topmost pixel.
2019 It is the number of pixels above the baseline.
2021 "width" is the distance from the logical origin to the position where
2022 the logical origin of the next character should be placed.
2024 If "rbearing" is greater than "width", then this character overlaps the
2025 following character. If smaller, then there is trailing blank space.
2029 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2032 query_font (Font fid)
2034 if (!fid || !fid->nsfont) {
2035 NSLog(@"no NSFont in fid");
2038 if (![fid->nsfont fontName]) {
2039 NSLog(@"broken NSFont in fid");
2046 XFontStruct *f = &fid->metrics;
2047 XCharStruct *min = &f->min_bounds;
2048 XCharStruct *max = &f->max_bounds;
2050 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2053 f->min_char_or_byte2 = first;
2054 f->max_char_or_byte2 = last;
2055 f->default_char = 'M';
2056 f->ascent = CEIL ([fid->nsfont ascender]);
2057 f->descent = -CEIL ([fid->nsfont descender]);
2059 min->width = 255; // set to smaller values in the loop
2062 min->lbearing = 255;
2063 min->rbearing = 255;
2065 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2068 NSBezierPath *bpath = [NSBezierPath bezierPath];
2070 for (i = first; i <= last; i++) {
2071 unsigned char str[2];
2075 NSString *nsstr = [NSString stringWithCString:(char *) str
2076 encoding:NSISOLatin1StringEncoding];
2078 /* I can't believe we have to go through this bullshit just to
2079 convert a 'char' to an NSGlyph!!
2081 You might think that we could do
2082 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2083 but that doesn't work; my guess is that glyphWithName expects
2084 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2088 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2089 [ts setFont:fid->nsfont];
2090 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2092 /* Without this, the layout manager ends up on a queue somewhere and is
2093 referenced again after we return to the command loop. Since we don't
2094 use this layout manager again, by that time it may have been garbage
2095 collected, and we crash. Setting this seems to cause `lm' to no
2096 longer be referenced once we exit this block. */
2097 [lm setBackgroundLayoutEnabled:NO];
2099 NSTextContainer *tc = [[NSTextContainer alloc] init];
2100 [lm addTextContainer:tc];
2101 [tc release]; // lm retains tc
2102 [ts addLayoutManager:lm];
2103 [lm release]; // ts retains lm
2104 glyph = [lm glyphAtIndex:0];
2108 /* Compute the bounding box and advancement by converting the glyph
2109 to a bezier path. There appears to be *no other way* to find out
2110 the bounding box of a character: [NSFont boundingRectForGlyph] and
2111 [NSString sizeWithAttributes] both return an advancement-sized
2112 rectangle, not a rectangle completely enclosing the glyph's ink.
2114 NSPoint advancement;
2116 advancement.x = advancement.y = 0;
2117 [bpath removeAllPoints];
2118 [bpath moveToPoint:advancement];
2119 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2120 advancement = [bpath currentPoint];
2121 bbox = [bpath bounds];
2123 /* Now that we know the advancement and bounding box, we can compute
2124 the lbearing and rbearing.
2126 XCharStruct *cs = &f->per_char[i-first];
2128 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2129 cs->descent = CEIL(-bbox.origin.y);
2130 cs->lbearing = CEIL (bbox.origin.x);
2131 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2132 cs->width = CEIL (advancement.x);
2134 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2136 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2139 max->width = MAX (max->width, cs->width);
2140 max->ascent = MAX (max->ascent, cs->ascent);
2141 max->descent = MAX (max->descent, cs->descent);
2142 max->lbearing = MAX (max->lbearing, cs->lbearing);
2143 max->rbearing = MAX (max->rbearing, cs->rbearing);
2145 min->width = MIN (min->width, cs->width);
2146 min->ascent = MIN (min->ascent, cs->ascent);
2147 min->descent = MIN (min->descent, cs->descent);
2148 min->lbearing = MIN (min->lbearing, cs->lbearing);
2149 min->rbearing = MIN (min->rbearing, cs->rbearing);
2154 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2155 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2156 i, i, cs->width, cs->lbearing, cs->rbearing,
2157 cs->ascent, cs->descent,
2158 (int) bbox.size.width, (int) bbox.size.height,
2159 (int) bbox.origin.x, (int) bbox.origin.y,
2160 (int) advancement.x, (int) advancement.y);
2167 // Since 'Font' includes the metrics, this just makes a copy of that.
2170 XQueryFont (Display *dpy, Font fid)
2173 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2176 // copy XCharStruct array
2177 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2178 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2179 memcpy (f->per_char, fid->metrics.per_char,
2180 size * sizeof (XCharStruct));
2187 copy_font (Font fid)
2189 // copy 'Font' struct
2190 Font fid2 = (Font) malloc (sizeof(*fid2));
2193 // copy XCharStruct array
2194 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2195 fid2->metrics.per_char = (XCharStruct *)
2196 malloc ((size + 2) * sizeof (XCharStruct));
2197 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2198 size * sizeof (XCharStruct));
2200 // copy the other pointers
2201 fid2->ps_name = strdup (fid->ps_name);
2202 // [fid2->nsfont retain];
2203 fid2->metrics.fid = fid2;
2210 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2213 Assert (size > 0, "zero font size");
2218 // "Monaco" only exists in plain.
2219 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2221 if (bold && ital) name = "Courier-BoldOblique";
2222 else if (bold) name = "Courier-Bold";
2223 else if (ital) name = "Courier-Oblique";
2224 else name = "Courier";
2228 // "Georgia" looks better than "Times".
2230 if (bold && ital) name = "Georgia-BoldItalic";
2231 else if (bold) name = "Georgia-Bold";
2232 else if (ital) name = "Georgia-Italic";
2233 else name = "Georgia";
2237 // "Geneva" only exists in plain.
2238 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2239 // "Verdana" renders smoother than "Helvetica" for some reason.
2241 if (bold && ital) name = "Verdana-BoldItalic";
2242 else if (bold) name = "Verdana-Bold";
2243 else if (ital) name = "Verdana-Italic";
2244 else name = "Verdana";
2247 NSString *nsname = [NSString stringWithCString:name
2248 encoding:NSUTF8StringEncoding];
2249 NSFont *f = [NSFont fontWithName:nsname size:size];
2251 *name_ret = strdup(name);
2256 try_native_font (const char *name, char **name_ret, float *size_ret)
2258 if (!name) return 0;
2259 const char *spc = strrchr (name, ' ');
2262 if (1 != sscanf (spc, " %d ", &size)) return 0;
2263 if (size <= 4) return 0;
2265 char *name2 = strdup (name);
2266 name2[strlen(name2) - strlen(spc)] = 0;
2267 NSString *nsname = [NSString stringWithCString:name2
2268 encoding:NSUTF8StringEncoding];
2269 NSFont *f = [NSFont fontWithName:nsname size:size];
2281 /* Returns a random font in the given size and face.
2284 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2286 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2287 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2288 NSArray *fonts = [[NSFontManager sharedFontManager]
2289 availableFontNamesWithTraits:mask];
2290 if (!fonts) return 0;
2292 int n = [fonts count];
2293 if (n <= 0) return 0;
2296 for (j = 0; j < n; j++) {
2297 int i = random() % n;
2298 NSString *name = [fonts objectAtIndex:i];
2299 NSFont *f = [NSFont fontWithName:name size:size];
2302 /* Don't use this font if it (probably) doesn't include ASCII characters.
2304 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2305 if (! (enc == NSUTF8StringEncoding ||
2306 enc == NSISOLatin1StringEncoding ||
2307 enc == NSNonLossyASCIIStringEncoding ||
2308 enc == NSISOLatin2StringEncoding ||
2309 enc == NSUnicodeStringEncoding ||
2310 enc == NSWindowsCP1250StringEncoding ||
2311 enc == NSWindowsCP1252StringEncoding ||
2312 enc == NSMacOSRomanStringEncoding)) {
2313 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2316 // NSLog(@"using \"%@\": %d", name, enc);
2318 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2322 // None of the fonts support ASCII?
2328 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2339 const char *s = (name ? name : "");
2341 while (*s && (*s == '*' || *s == '-'))
2344 while (*s2 && (*s2 != '*' && *s2 != '-'))
2350 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2351 else if (CMP ("random")) rand = YES;
2352 else if (CMP ("bold")) bold = YES;
2353 else if (CMP ("i")) ital = YES;
2354 else if (CMP ("o")) ital = YES;
2355 else if (CMP ("courier")) fixed = YES;
2356 else if (CMP ("fixed")) fixed = YES;
2357 else if (CMP ("m")) fixed = YES;
2358 else if (CMP ("times")) serif = YES;
2359 else if (CMP ("6x10")) fixed = YES, size = 8;
2360 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2361 else if (CMP ("9x15")) fixed = YES, size = 12;
2362 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2363 else if (CMP ("vga")) fixed = YES, size = 12;
2364 else if (CMP ("console")) fixed = YES, size = 12;
2365 else if (CMP ("gallant")) fixed = YES, size = 12;
2367 else if (size == 0) {
2369 if (1 == sscanf (s, " %d ", &n))
2376 if (size < 6 || size > 1000)
2380 nsfont = random_font (bold, ital, size, &ps_name);
2383 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2385 // if that didn't work, turn off attibutes until it does
2386 // (e.g., there is no "Monaco-Bold".)
2388 if (!nsfont && serif) {
2390 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2392 if (!nsfont && ital) {
2394 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2396 if (!nsfont && bold) {
2398 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2400 if (!nsfont && fixed) {
2402 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2406 *name_ret = ps_name;
2416 XLoadFont (Display *dpy, const char *name)
2418 Font fid = (Font) calloc (1, sizeof(*fid));
2420 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2422 if (!fid->nsfont && name &&
2423 strchr (name, ' ') &&
2424 !strchr (name, '*')) {
2425 // If name contains a space but no stars, it is a native font spec --
2426 // return NULL so that we know it really didn't exist. Else, it is an
2427 // XLFD font, so keep trying.
2428 XUnloadFont (dpy, fid);
2433 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2435 // We should never return NULL for XLFD fonts.
2437 NSLog(@"no NSFont for \"%s\"", name);
2440 CFRetain (fid->nsfont); // needed for garbage collection?
2442 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2451 XLoadQueryFont (Display *dpy, const char *name)
2453 Font fid = XLoadFont (dpy, name);
2455 return XQueryFont (dpy, fid);
2459 XUnloadFont (Display *dpy, Font fid)
2462 free (fid->ps_name);
2463 if (fid->metrics.per_char)
2464 free (fid->metrics.per_char);
2466 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2467 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2468 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2469 // They're probably not very big...
2471 // [fid->nsfont release];
2472 // CFRelease (fid->nsfont);
2479 XFreeFontInfo (char **names, XFontStruct *info, int n)
2483 for (i = 0; i < n; i++)
2484 if (names[i]) free (names[i]);
2488 for (i = 0; i < n; i++)
2489 if (info[i].per_char)
2490 free (info[i].per_char);
2497 XFreeFont (Display *dpy, XFontStruct *f)
2500 XFreeFontInfo (0, f, 1);
2501 XUnloadFont (dpy, fid);
2507 XSetFont (Display *dpy, GC gc, Font fid)
2510 XUnloadFont (dpy, gc->gcv.font);
2511 gc->gcv.font = copy_font (fid);
2512 [gc->gcv.font->nsfont retain];
2513 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2518 XTextExtents (XFontStruct *f, const char *s, int length,
2519 int *dir_ret, int *ascent_ret, int *descent_ret,
2522 memset (cs, 0, sizeof(*cs));
2524 for (i = 0; i < length; i++) {
2525 unsigned char c = (unsigned char) s[i];
2526 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2527 c = f->default_char;
2528 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2532 cs->ascent = MAX (cs->ascent, cc->ascent);
2533 cs->descent = MAX (cs->descent, cc->descent);
2534 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2535 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2536 cs->width += cc->width;
2540 *ascent_ret = f->ascent;
2541 *descent_ret = f->descent;
2546 XTextWidth (XFontStruct *f, const char *s, int length)
2548 int ascent, descent, dir;
2550 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2556 set_font (CGContextRef cgc, GC gc)
2558 Font font = gc->gcv.font;
2560 font = XLoadFont (0, 0);
2561 gc->gcv.font = font;
2562 [gc->gcv.font->nsfont retain];
2563 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2565 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2566 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2571 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2572 const char *str, int len, BOOL clear_background_p)
2574 if (clear_background_p) {
2575 int ascent, descent, dir;
2577 XTextExtents (&gc->gcv.font->metrics, str, len,
2578 &dir, &ascent, &descent, &cs);
2579 draw_rect (dpy, d, gc,
2580 x + MIN (0, cs.lbearing),
2581 y - MAX (0, ascent),
2582 MAX (MAX (0, cs.rbearing) -
2583 MIN (0, cs.lbearing),
2585 MAX (0, ascent) + MAX (0, descent),
2589 CGRect wr = d->frame;
2592 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2593 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2596 push_fg_gc (d, gc, YES);
2597 set_font (d->cgc, gc);
2599 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2600 if (gc->gcv.antialias_p)
2601 CGContextSetShouldAntialias (d->cgc, YES);
2602 CGContextShowTextAtPoint (d->cgc,
2604 wr.origin.y + wr.size.height - y,
2613 unsigned long argb = gc->gcv.foreground;
2614 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2615 float a = ((argb >> 24) & 0xFF) / 255.0;
2616 float r = ((argb >> 16) & 0xFF) / 255.0;
2617 float g = ((argb >> 8) & 0xFF) / 255.0;
2618 float b = ((argb ) & 0xFF) / 255.0;
2619 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2620 NSDictionary *attr =
2621 [NSDictionary dictionaryWithObjectsAndKeys:
2622 gc->gcv.font->nsfont, NSFontAttributeName,
2623 fg, NSForegroundColorAttributeName,
2625 char *s2 = (char *) malloc (len + 1);
2626 strncpy (s2, str, len);
2628 NSString *nsstr = [NSString stringWithCString:s2
2629 encoding:NSISOLatin1StringEncoding];
2632 pos.x = wr.origin.x + x;
2633 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2634 [nsstr drawAtPoint:pos withAttributes:attr];
2638 invalidate_drawable_cache (d);
2644 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2645 const char *str, int len)
2647 return draw_string (dpy, d, gc, x, y, str, len, NO);
2651 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2652 const char *str, int len)
2654 return draw_string (dpy, d, gc, x, y, str, len, YES);
2659 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2661 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2662 gc->gcv.foreground = fg;
2668 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2670 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2671 gc->gcv.background = bg;
2676 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2678 gc->gcv.alpha_allowed_p = allowed;
2683 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2685 gc->gcv.antialias_p = antialias_p;
2691 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2692 int line_style, int cap_style, int join_style)
2694 gc->gcv.line_width = line_width;
2695 Assert (line_style == LineSolid, "only LineSolid implemented");
2696 // gc->gcv.line_style = line_style;
2697 gc->gcv.cap_style = cap_style;
2698 gc->gcv.join_style = join_style;
2703 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2709 XSetFunction (Display *dpy, GC gc, int which)
2711 gc->gcv.function = which;
2716 XSetSubwindowMode (Display *dpy, GC gc, int which)
2718 gc->gcv.subwindow_mode = which;
2723 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2725 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2727 if (gc->gcv.clip_mask) {
2728 XFreePixmap (dpy, gc->gcv.clip_mask);
2729 CGImageRelease (gc->clip_mask);
2732 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2733 if (gc->gcv.clip_mask)
2734 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2742 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2744 gc->gcv.clip_x_origin = x;
2745 gc->gcv.clip_y_origin = y;
2751 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2752 int *root_x_ret, int *root_y_ret,
2753 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2755 Assert (w->type == WINDOW, "not a window");
2756 NSWindow *nsw = [w->window.view window];
2758 // get bottom left of window on screen, from bottom left
2759 wpos.x = wpos.y = 0;
2760 wpos = [nsw convertBaseToScreen:wpos];
2763 // get bottom left of view on window, from bottom left
2764 vpos.x = vpos.y = 0;
2765 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2767 // get bottom left of view on screen, from bottom left
2771 // get top left of view on screen, from bottom left
2772 vpos.y += w->frame.size.height;
2774 // get top left of view on screen, from top left
2775 NSArray *screens = [NSScreen screens];
2776 NSScreen *screen = (screens && [screens count] > 0
2777 ? [screens objectAtIndex:0]
2778 : [NSScreen mainScreen]);
2779 NSRect srect = [screen frame];
2780 vpos.y = srect.size.height - vpos.y;
2782 // get the mouse position on window, from bottom left
2783 NSEvent *e = [NSApp currentEvent];
2784 NSPoint p = [e locationInWindow];
2786 // get mouse position on screen, from bottom left
2790 // get mouse position on screen, from top left
2791 p.y = srect.size.height - p.y;
2793 if (root_x_ret) *root_x_ret = (int) p.x;
2794 if (root_y_ret) *root_y_ret = (int) p.y;
2795 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2796 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2798 if (mask_ret) *mask_ret = 0; // ####
2799 if (root_ret) *root_ret = 0;
2800 if (child_ret) *child_ret = 0;
2805 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2806 int src_x, int src_y,
2807 int *dest_x_ret, int *dest_y_ret,
2810 Assert (w->type == WINDOW, "not a window");
2811 NSWindow *nsw = [w->window.view window];
2813 // get bottom left of window on screen, from bottom left
2814 wpos.x = wpos.y = 0;
2815 wpos = [nsw convertBaseToScreen:wpos];
2818 // get bottom left of view on window, from bottom left
2819 vpos.x = vpos.y = 0;
2820 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2822 // get bottom left of view on screen, from bottom left
2826 // get top left of view on screen, from bottom left
2827 vpos.y += w->frame.size.height;
2829 // get top left of view on screen, from top left
2830 NSArray *screens = [NSScreen screens];
2831 NSScreen *screen = (screens && [screens count] > 0
2832 ? [screens objectAtIndex:0]
2833 : [NSScreen mainScreen]);
2834 NSRect srect = [screen frame];
2835 vpos.y = srect.size.height - vpos.y;
2837 // point starts out relative to top left of view
2842 // get point relative to top left of screen
2855 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2861 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2864 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2865 char c = (char) ks; // could be smarter about modifiers here...
2866 if (k_ret) *k_ret = ks;
2867 if (size > 0) buf[0] = c;
2868 if (size > 1) buf[1] = 0;
2874 XFlush (Display *dpy)
2876 // Just let the event loop take care of this on its own schedule.
2881 XSync (Display *dpy, Bool flush)
2883 return XFlush (dpy);
2887 // declared in utils/visual.h
2889 has_writable_cells (Screen *s, Visual *v)
2895 visual_depth (Screen *s, Visual *v)
2901 visual_cells (Screen *s, Visual *v)
2907 visual_class (Screen *s, Visual *v)
2912 // declared in utils/grabclient.h
2914 use_subwindow_mode_p (Screen *screen, Window window)