1 /* xscreensaver, Copyright (c) 1991-2012 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.
23 # import <UIKit/UIKit.h>
24 # import <UIKit/UIScreen.h>
25 # import <QuartzCore/QuartzCore.h>
26 # define NSView UIView
27 # define NSRect CGRect
28 # define NSPoint CGPoint
29 # define NSSize CGSize
30 # define NSColor UIColor
31 # define NSImage UIImage
32 # define NSEvent UIEvent
33 # define NSFont UIFont
34 # define NSGlyph CGGlyph
35 # define NSWindow UIWindow
36 # define NSMakeSize CGSizeMake
37 # define NSBezierPath UIBezierPath
39 # import <Cocoa/Cocoa.h>
43 #import "jwxyz-timers.h"
46 #define Assert(C,S) do { \
54 # define MAX(a,b) ((a)>(b)?(a):(b))
55 # define MIN(a,b) ((a)<(b)?(a):(b))
58 struct jwxyz_Drawable {
59 enum { WINDOW, PIXMAP } type;
66 unsigned long background;
67 int last_mouse_x, last_mouse_y;
71 void *cgc_buffer; // the bits to which CGContextRef renders
76 struct jwxyz_Display {
79 struct jwxyz_sources_data *timers_data;
82 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
83 This can change if the window is dragged to
84 a different screen. */
87 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
88 our images with this to avoid translation
100 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
106 float size; // points
108 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
109 // But we need the metrics on both of them, so they go here.
115 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
117 CGContextRef cgc = (CGContextRef) cgc_arg;
118 NSView *view = (NSView *) nsview_arg;
121 Display *d = (Display *) calloc (1, sizeof(*d));
122 d->screen = (Screen *) calloc (1, sizeof(Screen));
125 Visual *v = (Visual *) calloc (1, sizeof(Visual));
126 v->class = TrueColor;
127 v->red_mask = 0x00FF0000;
128 v->green_mask = 0x0000FF00;
129 v->blue_mask = 0x000000FF;
131 d->screen->visual = v;
133 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
136 Window w = (Window) calloc (1, sizeof(*w));
138 w->window.view = view;
139 CFRetain (w->window.view); // needed for garbage collection?
140 w->window.background = BlackPixel(0,0);
147 cgc = [[[view window] graphicsContext] graphicsPort];
153 Assert (cgc, "no CGContext");
158 jwxyz_free_display (Display *dpy)
160 jwxyz_XtRemoveInput_all (dpy);
161 // #### jwxyz_XtRemoveTimeOut_all ();
163 free (dpy->screen->visual);
165 free (dpy->main_window);
171 jwxyz_window_view (Window w)
173 Assert (w->type == WINDOW, "not a window");
174 return w->window.view;
178 /* Call this after any modification to the bits on a Pixmap or Window.
179 Most Pixmaps are used frequently as sources and infrequently as
180 destinations, so it pays to cache the data as a CGImage as needed.
183 invalidate_drawable_cache (Drawable d)
186 CGImageRelease (d->cgi);
192 /* Call this when the View changes size or position.
195 jwxyz_window_resized (Display *dpy, Window w,
196 int new_x, int new_y, int new_width, int new_height,
199 CGContextRef cgc = (CGContextRef) cgc_arg;
200 Assert (w->type == WINDOW, "not a window");
201 w->frame.origin.x = new_x;
202 w->frame.origin.y = new_y;
203 w->frame.size.width = new_width;
204 w->frame.size.height = new_height;
206 if (cgc) w->cgc = cgc;
207 Assert (w->cgc, "no CGContext");
210 // Figure out which screen the window is currently on.
213 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
219 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
220 Assert (dpy->cgdpy, "unable to find CGDisplay");
222 # endif // USE_IPHONE
226 // Figure out this screen's colorspace, and use that for every CGImage.
228 CMProfileRef profile = 0;
229 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
230 Assert (profile, "unable to find colorspace profile");
231 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
232 Assert (dpy->colorspace, "unable to find colorspace");
236 // WTF? It's faster if we *do not* use the screen's colorspace!
238 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
241 invalidate_drawable_cache (w);
247 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
249 Assert (w->type == WINDOW, "not a window");
250 w->window.last_mouse_x = x;
251 w->window.last_mouse_y = y;
258 display_sources_data (Display *dpy)
260 return dpy->timers_data;
265 XRootWindow (Display *dpy, int screen)
267 return dpy->main_window;
271 XDefaultScreenOfDisplay (Display *dpy)
277 XDefaultVisualOfScreen (Screen *screen)
279 return screen->visual;
283 XDisplayOfScreen (Screen *s)
289 XDisplayNumberOfScreen (Screen *s)
295 XScreenNumberOfScreen (Screen *s)
301 XDisplayWidth (Display *dpy, int screen)
303 return (int) dpy->main_window->frame.size.width;
307 XDisplayHeight (Display *dpy, int screen)
309 return (int) dpy->main_window->frame.size.height;
313 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
316 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
317 else if (!alpha_allowed_p)
318 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
319 "bogus color pixel");
324 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
325 BOOL alpha_allowed_p, BOOL fill_p)
327 validate_pixel (argb, depth, alpha_allowed_p);
330 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
332 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
334 float a = ((argb >> 24) & 0xFF) / 255.0;
335 float r = ((argb >> 16) & 0xFF) / 255.0;
336 float g = ((argb >> 8) & 0xFF) / 255.0;
337 float b = ((argb ) & 0xFF) / 255.0;
339 CGContextSetRGBFillColor (cgc, r, g, b, a);
341 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
346 set_line_mode (CGContextRef cgc, XGCValues *gcv)
348 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
349 CGContextSetLineJoin (cgc,
350 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
351 gcv->join_style == JoinRound ? kCGLineJoinRound :
353 CGContextSetLineCap (cgc,
354 gcv->cap_style == CapNotLast ? kCGLineCapButt :
355 gcv->cap_style == CapButt ? kCGLineCapButt :
356 gcv->cap_style == CapRound ? kCGLineCapRound :
361 set_clip_mask (Drawable d, GC gc)
363 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
365 Pixmap p = gc->gcv.clip_mask;
367 Assert (p->type == PIXMAP, "not a pixmap");
369 CGRect wr = d->frame;
371 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
372 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
373 - p->frame.size.height;
374 to.size.width = p->frame.size.width;
375 to.size.height = p->frame.size.height;
377 CGContextClipToMask (d->cgc, to, gc->clip_mask);
381 /* Pushes a GC context; sets BlendMode and ClipMask.
384 push_gc (Drawable d, GC gc)
386 CGContextRef cgc = d->cgc;
387 CGContextSaveGState (cgc);
389 switch (gc->gcv.function) {
392 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
393 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
394 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
395 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
396 default: abort(); break;
399 if (gc->gcv.clip_mask)
400 set_clip_mask (d, gc);
403 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
406 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
409 push_color_gc (Drawable d, GC gc, unsigned long color,
410 BOOL antialias_p, Bool fill_p)
414 int depth = gc->depth;
415 switch (gc->gcv.function) {
416 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
417 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
420 CGContextRef cgc = d->cgc;
421 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
422 CGContextSetShouldAntialias (cgc, antialias_p);
426 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
429 push_fg_gc (Drawable d, GC gc, Bool fill_p)
431 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
434 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
437 push_bg_gc (Drawable d, GC gc, Bool fill_p)
439 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
444 /* You've got to be fucking kidding me!
446 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
447 with repeated calls to CGContextDrawImage than it is to make a single
448 call to CGContextFillRects() with a list of 1x1 rectangles!
450 I still wouldn't call it *fast*, however...
452 #define XDRAWPOINTS_IMAGES
455 XDrawPoints (Display *dpy, Drawable d, GC gc,
456 XPoint *points, int count, int mode)
459 CGRect wr = d->frame;
461 push_fg_gc (d, gc, YES);
463 # ifdef XDRAWPOINTS_IMAGES
465 unsigned int argb = gc->gcv.foreground;
466 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
468 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
470 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
471 CGImageRef cgi = CGImageCreate (1, 1,
474 /* Host-ordered, since we're using the
475 address of an int as the color data. */
476 (kCGImageAlphaNoneSkipFirst |
477 kCGBitmapByteOrder32Host),
480 NO, /* interpolate */
481 kCGRenderingIntentDefault);
482 CGDataProviderRelease (prov);
484 CGContextRef cgc = d->cgc;
486 rect.size.width = rect.size.height = 1;
487 for (i = 0; i < count; i++) {
488 if (i > 0 && mode == CoordModePrevious) {
489 rect.origin.x += points->x;
490 rect.origin.x -= points->y;
492 rect.origin.x = wr.origin.x + points->x;
493 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
496 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
497 CGContextDrawImage (cgc, rect, cgi);
501 CGImageRelease (cgi);
503 # else /* ! XDRAWPOINTS_IMAGES */
505 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
508 for (i = 0; i < count; i++) {
509 r->size.width = r->size.height = 1;
510 if (i > 0 && mode == CoordModePrevious) {
511 r->origin.x = r[-1].origin.x + points->x;
512 r->origin.y = r[-1].origin.x - points->y;
514 r->origin.x = wr.origin.x + points->x;
515 r->origin.y = wr.origin.y + wr.size.height - points->y;
521 CGContextFillRects (d->cgc, rects, count);
524 # endif /* ! XDRAWPOINTS_IMAGES */
527 invalidate_drawable_cache (d);
534 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
539 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
543 static void draw_rect (Display *, Drawable, GC,
544 int x, int y, unsigned int width, unsigned int height,
545 BOOL foreground_p, BOOL fill_p);
548 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
549 int src_x, int src_y,
550 unsigned int width, unsigned int height,
551 int dst_x, int dst_y)
553 Assert (gc, "no GC");
554 Assert ((width < 65535), "improbably large width");
555 Assert ((height < 65535), "improbably large height");
556 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
557 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
558 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
559 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
561 if (width == 0 || height == 0)
564 if (gc->gcv.function == GXset ||
565 gc->gcv.function == GXclear) {
566 // "set" and "clear" are dumb drawing modes that ignore the source
567 // bits and just draw solid rectangles.
569 (gc->gcv.function == GXset
570 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
571 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
572 gc->depth, gc->gcv.alpha_allowed_p, YES);
573 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
577 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
578 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
579 // bounds of their drawables.
580 BOOL clipped = NO; // Whether we did any clipping of the rects.
582 src_frame = src->frame;
583 dst_frame = dst->frame;
585 // Initialize src_rect...
587 src_rect.origin.x = src_frame.origin.x + src_x;
588 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
590 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
591 src_rect.size.width = width;
592 src_rect.size.height = height;
594 // Initialize dst_rect...
596 dst_rect.origin.x = dst_frame.origin.x + dst_x;
597 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
599 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
600 dst_rect.size.width = width;
601 dst_rect.size.height = height;
603 // Clip rects to frames...
605 // CGRect orig_src_rect = src_rect;
606 CGRect orig_dst_rect = dst_rect;
608 # define CLIP(THIS,THAT,VAL,SIZE) do { \
609 float off = THIS##_rect.origin.VAL; \
612 THIS##_rect.size.SIZE += off; \
613 THAT##_rect.size.SIZE += off; \
614 THIS##_rect.origin.VAL -= off; \
615 THAT##_rect.origin.VAL -= off; \
617 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
618 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
621 THIS##_rect.size.SIZE -= off; \
622 THAT##_rect.size.SIZE -= off; \
625 CLIP (dst, src, x, width);
626 CLIP (dst, src, y, height);
627 CLIP (src, dst, x, width);
628 CLIP (src, dst, y, height);
631 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
634 NSObject *releaseme = 0;
637 BOOL free_cgi_p = NO;
641 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
642 if (src->type == PIXMAP)
646 // If we are copying from a Pixmap to a Pixmap or Window, we must first
647 // copy the bits to an intermediary CGImage object, then copy that to the
648 // destination drawable's CGContext.
650 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
651 // case of copying from a Pixmap back to itself, but I don't think that
652 // happens very often anyway.)
654 // First we get a CGImage out of the pixmap CGContext -- it's the whole
655 // pixmap, but it presumably shares the data pointer instead of copying
656 // it. We then cache that CGImage it inside the Pixmap object. Note:
657 // invalidate_drawable_cache() must be called to discard this any time a
658 // modification is made to the pixmap, or we'll end up re-using old bits.
661 src->cgi = CGBitmapContextCreateImage (src->cgc);
664 // if doing a sub-rect, trim it down.
665 if (src_rect.origin.x != src_frame.origin.x ||
666 src_rect.origin.y != src_frame.origin.y ||
667 src_rect.size.width != src_frame.size.width ||
668 src_rect.size.height != src_frame.size.height) {
669 // #### I don't understand why this is needed...
670 src_rect.origin.y = (src_frame.size.height -
671 src_rect.size.height - src_rect.origin.y);
672 // This does not copy image data, so it should be fast.
673 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
677 if (src->type == PIXMAP && src->pixmap.depth == 1)
681 } else { /* (src->type == WINDOW) */
683 NSRect nsfrom; // NSRect != CGRect on 10.4
684 nsfrom.origin.x = src_rect.origin.x;
685 nsfrom.origin.y = src_rect.origin.y;
686 nsfrom.size.width = src_rect.size.width;
687 nsfrom.size.height = src_rect.size.height;
691 // If we are copying from a window to itself, we can use NSCopyBits()
692 // without first copying the rectangle to an intermediary CGImage.
693 // This is ~28% faster (but I *expected* it to be twice as fast...)
694 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
700 // If we are copying from a Window to a Pixmap, we must first copy
701 // the bits to an intermediary CGImage object, then copy that to the
702 // Pixmap's CGContext.
704 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
705 initWithFocusedViewRect:nsfrom];
706 unsigned char *data = [bm bitmapData];
707 int bps = [bm bitsPerSample];
708 int bpp = [bm bitsPerPixel];
709 int bpl = [bm bytesPerRow];
712 // create a CGImage from those bits.
713 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
714 // but that method didn't exist in 10.4.)
716 CGDataProviderRef prov =
717 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
719 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
722 /* Use whatever default bit ordering we got from
723 initWithFocusedViewRect. I would have assumed
724 that it was (kCGImageAlphaNoneSkipFirst |
725 kCGBitmapByteOrder32Host), but on Intel,
731 NO, /* interpolate */
732 kCGRenderingIntentDefault);
734 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
735 CGDataProviderRelease (prov);
738 # endif // !USE_IPHONE
741 CGContextRef cgc = dst->cgc;
743 if (mask_p) { // src depth == 1
745 push_bg_gc (dst, gc, YES);
747 // fill the destination rectangle with solid background...
748 CGContextFillRect (cgc, orig_dst_rect);
750 Assert (cgc, "no CGC with 1-bit XCopyArea");
752 // then fill in a solid rectangle of the fg color, using the image as an
753 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
754 set_color (cgc, gc->gcv.foreground, gc->depth,
755 gc->gcv.alpha_allowed_p, YES);
756 CGContextClipToMask (cgc, dst_rect, cgi);
757 CGContextFillRect (cgc, dst_rect);
761 } else { // src depth > 1
765 // If either the src or dst rects did not lie within their drawables,
766 // then we have adjusted both the src and dst rects to account for
767 // the clipping; that means we need to first clear to the background,
768 // so that clipped bits end up in the bg color instead of simply not
772 set_color (cgc, gc->gcv.background, gc->depth,
773 gc->gcv.alpha_allowed_p, YES);
774 CGContextFillRect (cgc, orig_dst_rect);
778 // copy the CGImage onto the destination CGContext
779 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
780 CGContextDrawImage (cgc, dst_rect, cgi);
782 // No cgi means src == dst, and both are Windows.
785 abort(); // No NSCopyBits on iOS, but shouldn't be reached anyway.
786 # else // !USE_IPHONE
788 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
789 nsfrom.origin.y = src_rect.origin.y;
790 nsfrom.size.width = src_rect.size.width;
791 nsfrom.size.height = src_rect.size.height;
793 nsto.x = dst_rect.origin.x;
794 nsto.y = dst_rect.origin.y;
795 NSCopyBits (0, nsfrom, nsto);
796 # endif // !USE_IPHONE
802 if (free_cgi_p) CGImageRelease (cgi);
804 if (releaseme) [releaseme release];
805 invalidate_drawable_cache (dst);
811 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
812 int src_x, int src_y,
813 unsigned width, int height,
814 int dest_x, int dest_y, unsigned long plane)
816 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
818 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
819 // not to white/black.
820 return XCopyArea (dpy, src, dest, gc,
821 src_x, src_y, width, height, dest_x, dest_y);
826 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
828 // when drawing a zero-length line, obey line-width and cap-style.
829 if (x1 == x2 && y1 == y2) {
830 int w = gc->gcv.line_width;
833 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
834 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
836 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
839 CGRect wr = d->frame;
841 p.x = wr.origin.x + x1;
842 p.y = wr.origin.y + wr.size.height - y1;
844 push_fg_gc (d, gc, NO);
846 CGContextRef cgc = d->cgc;
847 set_line_mode (cgc, &gc->gcv);
848 CGContextBeginPath (cgc);
849 CGContextMoveToPoint (cgc, p.x, p.y);
850 p.x = wr.origin.x + x2;
851 p.y = wr.origin.y + wr.size.height - y2;
852 CGContextAddLineToPoint (cgc, p.x, p.y);
853 CGContextStrokePath (cgc);
855 invalidate_drawable_cache (d);
860 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
865 CGRect wr = d->frame;
866 push_fg_gc (d, gc, NO);
868 CGContextRef cgc = d->cgc;
870 set_line_mode (cgc, &gc->gcv);
872 // if the first and last points coincide, use closepath to get
873 // the proper line-joining.
874 BOOL closed_p = (points[0].x == points[count-1].x &&
875 points[0].y == points[count-1].y);
876 if (closed_p) count--;
878 p.x = wr.origin.x + points->x;
879 p.y = wr.origin.y + wr.size.height - points->y;
881 CGContextBeginPath (cgc);
882 CGContextMoveToPoint (cgc, p.x, p.y);
883 for (i = 1; i < count; i++) {
884 if (mode == CoordModePrevious) {
888 p.x = wr.origin.x + points->x;
889 p.y = wr.origin.y + wr.size.height - points->y;
891 CGContextAddLineToPoint (cgc, p.x, p.y);
894 if (closed_p) CGContextClosePath (cgc);
895 CGContextStrokePath (cgc);
897 invalidate_drawable_cache (d);
903 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
906 CGRect wr = d->frame;
908 CGContextRef cgc = d->cgc;
910 push_fg_gc (d, gc, NO);
911 set_line_mode (cgc, &gc->gcv);
912 CGContextBeginPath (cgc);
913 for (i = 0; i < count; i++) {
914 CGContextMoveToPoint (cgc,
915 wr.origin.x + segments->x1,
916 wr.origin.y + wr.size.height - segments->y1);
917 CGContextAddLineToPoint (cgc,
918 wr.origin.x + segments->x2,
919 wr.origin.y + wr.size.height - segments->y2);
922 CGContextStrokePath (cgc);
924 invalidate_drawable_cache (d);
930 XClearWindow (Display *dpy, Window win)
932 Assert (win->type == WINDOW, "not a window");
933 CGRect wr = win->frame;
934 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
938 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
940 Assert (w->type == WINDOW, "not a window");
941 validate_pixel (pixel, 32, NO);
942 w->window.background = pixel;
947 draw_rect (Display *dpy, Drawable d, GC gc,
948 int x, int y, unsigned int width, unsigned int height,
949 BOOL foreground_p, BOOL fill_p)
951 CGRect wr = d->frame;
953 r.origin.x = wr.origin.x + x;
954 r.origin.y = wr.origin.y + wr.size.height - y - height;
955 r.size.width = width;
956 r.size.height = height;
960 push_fg_gc (d, gc, fill_p);
962 push_bg_gc (d, gc, fill_p);
965 CGContextRef cgc = d->cgc;
967 CGContextFillRect (cgc, r);
970 set_line_mode (cgc, &gc->gcv);
971 CGContextStrokeRect (cgc, r);
976 invalidate_drawable_cache (d);
981 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
982 unsigned int width, unsigned int height)
984 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
989 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
990 unsigned int width, unsigned int height)
992 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
997 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
999 CGRect wr = d->frame;
1001 CGContextRef cgc = d->cgc;
1002 push_fg_gc (d, gc, YES);
1003 for (i = 0; i < n; i++) {
1005 r.origin.x = wr.origin.x + rects->x;
1006 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1007 r.size.width = rects->width;
1008 r.size.height = rects->height;
1009 CGContextFillRect (cgc, r);
1013 invalidate_drawable_cache (d);
1019 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1021 Assert (win->type == WINDOW, "not a window");
1022 CGContextRef cgc = win->cgc;
1023 set_color (cgc, win->window.background, 32, NO, YES);
1024 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1030 XFillPolygon (Display *dpy, Drawable d, GC gc,
1031 XPoint *points, int npoints, int shape, int mode)
1033 CGRect wr = d->frame;
1035 push_fg_gc (d, gc, YES);
1036 CGContextRef cgc = d->cgc;
1037 CGContextBeginPath (cgc);
1039 for (i = 0; i < npoints; i++) {
1040 if (i > 0 && mode == CoordModePrevious) {
1044 x = wr.origin.x + points[i].x;
1045 y = wr.origin.y + wr.size.height - points[i].y;
1049 CGContextMoveToPoint (cgc, x, y);
1051 CGContextAddLineToPoint (cgc, x, y);
1053 CGContextClosePath (cgc);
1054 if (gc->gcv.fill_rule == EvenOddRule)
1055 CGContextEOFillPath (cgc);
1057 CGContextFillPath (cgc);
1059 invalidate_drawable_cache (d);
1063 #define radians(DEG) ((DEG) * M_PI / 180.0)
1064 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1067 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1068 unsigned int width, unsigned int height, int angle1, int angle2,
1071 CGRect wr = d->frame;
1073 bound.origin.x = wr.origin.x + x;
1074 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1075 bound.size.width = width;
1076 bound.size.height = height;
1079 ctr.x = bound.origin.x + bound.size.width /2;
1080 ctr.y = bound.origin.y + bound.size.height/2;
1082 float r1 = radians (angle1/64.0);
1083 float r2 = radians (angle2/64.0) + r1;
1084 BOOL clockwise = angle2 < 0;
1085 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1087 push_fg_gc (d, gc, fill_p);
1089 CGContextRef cgc = d->cgc;
1090 CGContextBeginPath (cgc);
1092 CGContextSaveGState(cgc);
1093 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1094 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1096 CGContextMoveToPoint (cgc, 0, 0);
1098 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1099 CGContextRestoreGState (cgc); // restore before stroke, for line width
1102 CGContextClosePath (cgc); // for proper line joining
1105 CGContextFillPath (cgc);
1107 set_line_mode (cgc, &gc->gcv);
1108 CGContextStrokePath (cgc);
1112 invalidate_drawable_cache (d);
1117 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1118 unsigned int width, unsigned int height, int angle1, int angle2)
1120 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1124 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1125 unsigned int width, unsigned int height, int angle1, int angle2)
1127 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1131 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1134 for (i = 0; i < narcs; i++)
1135 draw_arc (dpy, d, gc,
1136 arcs[i].x, arcs[i].y,
1137 arcs[i].width, arcs[i].height,
1138 arcs[i].angle1, arcs[i].angle2,
1144 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1147 for (i = 0; i < narcs; i++)
1148 draw_arc (dpy, d, gc,
1149 arcs[i].x, arcs[i].y,
1150 arcs[i].width, arcs[i].height,
1151 arcs[i].angle1, arcs[i].angle2,
1158 gcv_defaults (XGCValues *gcv, int depth)
1160 memset (gcv, 0, sizeof(*gcv));
1161 gcv->function = GXcopy;
1162 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1163 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1164 gcv->line_width = 1;
1165 gcv->cap_style = CapNotLast;
1166 gcv->join_style = JoinMiter;
1167 gcv->fill_rule = EvenOddRule;
1169 gcv->alpha_allowed_p = NO;
1170 gcv->antialias_p = YES;
1174 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1178 if (! from) abort();
1180 if (mask & GCFunction) gc->gcv.function = from->function;
1181 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1182 if (mask & GCBackground) gc->gcv.background = from->background;
1183 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1184 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1185 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1186 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1187 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1188 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1189 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1191 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1192 if (mask & GCFont) XSetFont (0, gc, from->font);
1194 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1195 gc->gcv.alpha_allowed_p);
1196 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1197 gc->gcv.alpha_allowed_p);
1199 if (mask & GCLineStyle) abort();
1200 if (mask & GCPlaneMask) abort();
1201 if (mask & GCFillStyle) abort();
1202 if (mask & GCTile) abort();
1203 if (mask & GCStipple) abort();
1204 if (mask & GCTileStipXOrigin) abort();
1205 if (mask & GCTileStipYOrigin) abort();
1206 if (mask & GCGraphicsExposures) abort();
1207 if (mask & GCDashOffset) abort();
1208 if (mask & GCDashList) abort();
1209 if (mask & GCArcMode) abort();
1214 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1216 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1217 if (d->type == WINDOW) {
1219 } else { /* (d->type == PIXMAP) */
1220 gc->depth = d->pixmap.depth;
1223 gcv_defaults (&gc->gcv, gc->depth);
1224 set_gcv (gc, xgcv, mask);
1229 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1231 set_gcv (gc, gcv, mask);
1237 XFreeGC (Display *dpy, GC gc)
1240 XUnloadFont (dpy, gc->gcv.font);
1242 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1244 if (gc->gcv.clip_mask) {
1245 XFreePixmap (dpy, gc->gcv.clip_mask);
1246 CGImageRelease (gc->clip_mask);
1254 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1256 Assert (w->type == WINDOW, "not a window");
1257 memset (xgwa, 0, sizeof(*xgwa));
1258 xgwa->x = w->frame.origin.x;
1259 xgwa->y = w->frame.origin.y;
1260 xgwa->width = w->frame.size.width;
1261 xgwa->height = w->frame.size.height;
1263 xgwa->screen = dpy->screen;
1264 xgwa->visual = dpy->screen->visual;
1269 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1270 int *x_ret, int *y_ret,
1271 unsigned int *w_ret, unsigned int *h_ret,
1272 unsigned int *bw_ret, unsigned int *d_ret)
1274 *x_ret = d->frame.origin.x;
1275 *y_ret = d->frame.origin.y;
1276 *w_ret = d->frame.size.width;
1277 *h_ret = d->frame.size.height;
1278 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1279 *root_ret = RootWindow (dpy, 0);
1286 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1288 // store 32 bit ARGB in the pixel field.
1289 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1290 color->pixel = (uint32_t)
1292 (((color->red >> 8) & 0xFF) << 16) |
1293 (((color->green >> 8) & 0xFF) << 8) |
1294 (((color->blue >> 8) & 0xFF) ));
1299 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1300 unsigned long *pmret, unsigned int npl,
1301 unsigned long *pxret, unsigned int npx)
1307 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1309 Assert(0, "XStoreColors called");
1314 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1316 Assert(0, "XStoreColor called");
1321 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1322 unsigned long planes)
1328 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1330 unsigned char r=0, g=0, b=0;
1331 if (*spec == '#' && strlen(spec) == 7) {
1332 static unsigned const char hex[] = { // yeah yeah, shoot me.
1333 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,
1334 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,
1335 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,
1336 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,
1337 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,
1338 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,
1339 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,
1340 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};
1341 r = (hex[spec[1]] << 4) | hex[spec[2]];
1342 g = (hex[spec[3]] << 4) | hex[spec[4]];
1343 b = (hex[spec[5]] << 4) | hex[spec[6]];
1344 } else if (!strcasecmp(spec,"black")) {
1346 } else if (!strcasecmp(spec,"white")) {
1348 } else if (!strcasecmp(spec,"red")) {
1350 } else if (!strcasecmp(spec,"green")) {
1352 } else if (!strcasecmp(spec,"blue")) {
1354 } else if (!strcasecmp(spec,"cyan")) {
1356 } else if (!strcasecmp(spec,"magenta")) {
1358 } else if (!strcasecmp(spec,"yellow")) {
1364 ret->red = (r << 8) | r;
1365 ret->green = (g << 8) | g;
1366 ret->blue = (b << 8) | b;
1367 ret->flags = DoRed|DoGreen|DoBlue;
1372 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1373 XColor *screen_ret, XColor *exact_ret)
1375 if (! XParseColor (dpy, cmap, name, screen_ret))
1377 *exact_ret = *screen_ret;
1378 return XAllocColor (dpy, cmap, screen_ret);
1382 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1384 validate_pixel (color->pixel, 32, NO);
1385 unsigned char r = ((color->pixel >> 16) & 0xFF);
1386 unsigned char g = ((color->pixel >> 8) & 0xFF);
1387 unsigned char b = ((color->pixel ) & 0xFF);
1388 color->red = (r << 8) | r;
1389 color->green = (g << 8) | g;
1390 color->blue = (b << 8) | b;
1391 color->flags = DoRed|DoGreen|DoBlue;
1396 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1399 for (i = 0; i < n; i++)
1400 XQueryColor (dpy, cmap, &c[i]);
1405 static unsigned long
1406 ximage_getpixel_1 (XImage *ximage, int x, int y)
1408 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1412 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1415 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1417 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1422 static unsigned long
1423 ximage_getpixel_32 (XImage *ximage, int x, int y)
1425 return ((unsigned long)
1426 *((uint32_t *) ximage->data +
1427 (y * (ximage->bytes_per_line >> 2)) +
1432 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1434 *((uint32_t *) ximage->data +
1435 (y * (ximage->bytes_per_line >> 2)) +
1436 x) = (uint32_t) pixel;
1442 XInitImage (XImage *ximage)
1444 if (!ximage->bytes_per_line)
1445 ximage->bytes_per_line = (ximage->depth == 1
1446 ? (ximage->width + 7) / 8
1447 : ximage->width * 4);
1449 if (ximage->depth == 1) {
1450 ximage->f.put_pixel = ximage_putpixel_1;
1451 ximage->f.get_pixel = ximage_getpixel_1;
1452 } else if (ximage->depth == 32 || ximage->depth == 24) {
1453 ximage->f.put_pixel = ximage_putpixel_32;
1454 ximage->f.get_pixel = ximage_getpixel_32;
1463 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1464 int format, int offset, char *data,
1465 unsigned int width, unsigned int height,
1466 int bitmap_pad, int bytes_per_line)
1468 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1469 ximage->width = width;
1470 ximage->height = height;
1471 ximage->format = format;
1472 ximage->data = data;
1473 ximage->bitmap_unit = 8;
1474 ximage->byte_order = MSBFirst;
1475 ximage->bitmap_bit_order = ximage->byte_order;
1476 ximage->bitmap_pad = bitmap_pad;
1477 ximage->depth = depth;
1478 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1479 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1480 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1481 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1482 ximage->bytes_per_line = bytes_per_line;
1484 XInitImage (ximage);
1489 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1491 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1492 w, h, from->bitmap_pad, 0);
1493 to->data = (char *) malloc (h * to->bytes_per_line);
1495 if (x >= from->width)
1497 else if (x+w > from->width)
1498 w = from->width - x;
1500 if (y >= from->height)
1502 else if (y+h > from->height)
1503 h = from->height - y;
1506 for (ty = 0; ty < h; ty++)
1507 for (tx = 0; tx < w; tx++)
1508 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1513 XPixmapFormatValues *
1514 XListPixmapFormats (Display *dpy, int *n_ret)
1516 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1518 ret[0].bits_per_pixel = 32;
1519 ret[0].scanline_pad = 8;
1521 ret[1].bits_per_pixel = 1;
1522 ret[1].scanline_pad = 8;
1529 XGetPixel (XImage *ximage, int x, int y)
1531 return ximage->f.get_pixel (ximage, x, y);
1536 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1538 return ximage->f.put_pixel (ximage, x, y, pixel);
1542 XDestroyImage (XImage *ximage)
1544 if (ximage->data) free (ximage->data);
1551 flipbits (unsigned const char *in, unsigned char *out, int length)
1553 static const unsigned char table[256] = {
1554 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1555 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1556 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1557 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1558 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1559 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1560 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1561 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1562 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1563 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1564 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1565 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1566 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1567 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1568 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1569 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1570 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1571 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1572 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1573 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1574 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1575 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1576 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1577 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1578 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1579 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1580 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1581 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1582 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1583 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1584 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1585 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1587 while (--length > 0)
1588 *out++ = table[*in++];
1593 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1594 int src_x, int src_y, int dest_x, int dest_y,
1595 unsigned int w, unsigned int h)
1597 CGRect wr = d->frame;
1599 Assert (gc, "no GC");
1600 Assert ((w < 65535), "improbably large width");
1601 Assert ((h < 65535), "improbably large height");
1602 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1603 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1604 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1605 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1607 // Clip width and height to the bounds of the Drawable
1609 if (dest_x + w > wr.size.width) {
1610 if (dest_x > wr.size.width)
1612 w = wr.size.width - dest_x;
1614 if (dest_y + h > wr.size.height) {
1615 if (dest_y > wr.size.height)
1617 h = wr.size.height - dest_y;
1619 if (w <= 0 || h <= 0)
1622 // Clip width and height to the bounds of the XImage
1624 if (src_x + w > ximage->width) {
1625 if (src_x > ximage->width)
1627 w = ximage->width - src_x;
1629 if (src_y + h > ximage->height) {
1630 if (src_y > ximage->height)
1632 h = ximage->height - src_y;
1634 if (w <= 0 || h <= 0)
1637 CGContextRef cgc = d->cgc;
1639 if (gc->gcv.function == GXset ||
1640 gc->gcv.function == GXclear) {
1641 // "set" and "clear" are dumb drawing modes that ignore the source
1642 // bits and just draw solid rectangles.
1643 set_color (cgc, (gc->gcv.function == GXset
1644 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1645 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1646 gc->depth, gc->gcv.alpha_allowed_p, YES);
1647 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1651 int bpl = ximage->bytes_per_line;
1652 int bpp = ximage->bits_per_pixel;
1653 int bsize = bpl * h;
1654 char *data = ximage->data;
1657 r.origin.x = wr.origin.x + dest_x;
1658 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1664 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1665 to create a CGImage from a sub-rectagle of the XImage.
1667 data += (src_y * bpl) + (src_x * 4);
1668 CGDataProviderRef prov =
1669 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1671 CGImageRef cgi = CGImageCreate (w, h,
1674 /* Need this for XPMs to have the right
1675 colors, e.g. the logo in "maze". */
1676 (kCGImageAlphaNoneSkipFirst |
1677 kCGBitmapByteOrder32Host),
1679 NULL, /* decode[] */
1680 NO, /* interpolate */
1681 kCGRenderingIntentDefault);
1682 CGDataProviderRelease (prov);
1683 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1684 CGContextDrawImage (cgc, r, cgi);
1685 CGImageRelease (cgi);
1687 } else { // (bpp == 1)
1689 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1691 #### However, the bit order within a byte in a 1bpp XImage is
1692 the wrong way around from what Quartz expects, so first we
1693 have to copy the data to reverse it. Shit! Maybe it
1694 would be worthwhile to go through the hacks and #ifdef
1695 each one that diddles 1bpp XImage->data directly...
1697 Assert ((src_x % 8) == 0,
1698 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1700 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1701 unsigned char *flipped = (unsigned char *) malloc (bsize);
1703 flipbits ((unsigned char *) data, flipped, bsize);
1705 CGDataProviderRef prov =
1706 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1707 CGImageRef mask = CGImageMaskCreate (w, h,
1710 NULL, /* decode[] */
1711 NO); /* interpolate */
1712 push_fg_gc (d, gc, YES);
1714 CGContextFillRect (cgc, r); // foreground color
1715 CGContextClipToMask (cgc, r, mask);
1716 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1717 CGContextFillRect (cgc, r); // background color
1721 CGDataProviderRelease (prov);
1722 CGImageRelease (mask);
1725 invalidate_drawable_cache (d);
1732 XGetImage (Display *dpy, Drawable d, int x, int y,
1733 unsigned int width, unsigned int height,
1734 unsigned long plane_mask, int format)
1736 const unsigned char *data = 0;
1737 int depth, ibpp, ibpl, alpha_first_p;
1739 NSBitmapImageRep *bm = 0;
1742 Assert ((width < 65535), "improbably large width");
1743 Assert ((height < 65535), "improbably large height");
1744 Assert ((x < 65535 && x > -65535), "improbably large x");
1745 Assert ((y < 65535 && y > -65535), "improbably large y");
1747 CGContextRef cgc = d->cgc;
1750 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
1751 if (d->type == PIXMAP)
1754 depth = (d->type == PIXMAP
1757 // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst.
1758 // If it's an iPhone window, it's the other way around.
1759 alpha_first_p = (d->type == PIXMAP);
1760 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
1761 ibpl = CGBitmapContextGetBytesPerRow (cgc);
1762 data = CGBitmapContextGetData (cgc);
1763 Assert (data, "CGBitmapContextGetData failed");
1766 } else { /* (d->type == WINDOW) */
1768 // get the bits (desired sub-rectangle) out of the NSView
1770 nsfrom.origin.x = x;
1771 nsfrom.origin.y = y;
1772 nsfrom.size.width = width;
1773 nsfrom.size.height = height;
1774 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
1776 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1777 ibpp = [bm bitsPerPixel];
1778 ibpl = [bm bytesPerRow];
1779 data = [bm bitmapData];
1780 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1781 # endif // !USE_IPHONE
1784 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1785 data += (y * ibpl) + (x * (ibpp/8));
1787 format = (depth == 1 ? XYPixmap : ZPixmap);
1788 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1790 image->data = (char *) malloc (height * image->bytes_per_line);
1792 int obpl = image->bytes_per_line;
1794 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1795 means that on Intel it is BGRA when viewed by bytes (And BGR
1796 when using 24bpp packing).
1798 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1799 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1800 indicator of this latest kink.
1804 const unsigned char *iline = data;
1805 for (yy = 0; yy < height; yy++) {
1807 const unsigned char *iline2 = iline;
1808 for (xx = 0; xx < width; xx++) {
1810 iline2++; // ignore R or A or A or B
1811 iline2++; // ignore G or B or R or G
1812 unsigned char r = *iline2++; // use B or G or G or R
1813 if (ibpp == 32) iline2++; // ignore A or R or B or A
1815 XPutPixel (image, xx, yy, (r ? 1 : 0));
1820 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1821 const unsigned char *iline = data;
1822 unsigned char *oline = (unsigned char *) image->data;
1823 for (yy = 0; yy < height; yy++) {
1825 const unsigned char *iline2 = iline;
1826 unsigned char *oline2 = oline;
1828 if (alpha_first_p) // ARGB
1829 for (xx = 0; xx < width; xx++) {
1830 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1831 unsigned char r = *iline2++;
1832 unsigned char g = *iline2++;
1833 unsigned char b = *iline2++;
1834 uint32_t pixel = ((a << 24) |
1838 *((uint32_t *) oline2) = pixel;
1842 for (xx = 0; xx < width; xx++) {
1843 unsigned char r = *iline2++;
1844 unsigned char g = *iline2++;
1845 unsigned char b = *iline2++;
1846 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1847 uint32_t pixel = ((a << 24) |
1851 *((uint32_t *) oline2) = pixel;
1861 if (bm) [bm release];
1869 /* Returns a transformation matrix to do rotation as per the provided
1870 EXIF "Orientation" value.
1872 static CGAffineTransform
1873 exif_rotate (int rot, CGSize rect)
1875 CGAffineTransform trans = CGAffineTransformIdentity;
1877 case 2: // flip horizontal
1878 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1879 trans = CGAffineTransformScale (trans, -1, 1);
1882 case 3: // rotate 180
1883 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1884 trans = CGAffineTransformRotate (trans, M_PI);
1887 case 4: // flip vertical
1888 trans = CGAffineTransformMakeTranslation (0, rect.height);
1889 trans = CGAffineTransformScale (trans, 1, -1);
1892 case 5: // transpose (UL-to-LR axis)
1893 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1894 trans = CGAffineTransformScale (trans, -1, 1);
1895 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1898 case 6: // rotate 90
1899 trans = CGAffineTransformMakeTranslation (0, rect.width);
1900 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1903 case 7: // transverse (UR-to-LL axis)
1904 trans = CGAffineTransformMakeScale (-1, 1);
1905 trans = CGAffineTransformRotate (trans, M_PI / 2);
1908 case 8: // rotate 270
1909 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1910 trans = CGAffineTransformRotate (trans, M_PI / 2);
1922 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1923 Bool nsimg_p, void *img_arg,
1924 XRectangle *geom_ret, int exif_rotation)
1928 CGImageSourceRef cgsrc;
1929 # endif // USE_IPHONE
1932 CGContextRef cgc = d->cgc;
1936 NSImage *nsimg = (NSImage *) img_arg;
1937 imgr = [nsimg size];
1940 // convert the NSImage to a CGImage via the toll-free-bridging
1941 // of NSData and CFData...
1943 NSData *nsdata = [NSBitmapImageRep
1944 TIFFRepresentationOfImageRepsInArray:
1945 [nsimg representations]];
1946 CFDataRef cfdata = (CFDataRef) nsdata;
1947 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1948 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1949 # else // USE_IPHONE
1950 cgi = nsimg.CGImage;
1951 # endif // USE_IPHONE
1954 cgi = (CGImageRef) img_arg;
1955 imgr.width = CGImageGetWidth (cgi);
1956 imgr.height = CGImageGetHeight (cgi);
1959 Bool rot_p = (exif_rotation >= 5);
1962 imgr = NSMakeSize (imgr.height, imgr.width);
1964 CGRect winr = d->frame;
1965 float rw = winr.size.width / imgr.width;
1966 float rh = winr.size.height / imgr.height;
1967 float r = (rw < rh ? rw : rh);
1970 dst.size.width = imgr.width * r;
1971 dst.size.height = imgr.height * r;
1972 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1973 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1975 dst2.origin.x = dst2.origin.y = 0;
1977 dst2.size.width = dst.size.height;
1978 dst2.size.height = dst.size.width;
1980 dst2.size = dst.size;
1983 // Clear the part not covered by the image to background or black.
1985 if (d->type == WINDOW)
1986 XClearWindow (dpy, d);
1988 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
1989 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1992 CGAffineTransform trans =
1993 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1995 CGContextSaveGState (cgc);
1996 CGContextConcatCTM (cgc,
1997 CGAffineTransformMakeTranslation (dst.origin.x,
1999 CGContextConcatCTM (cgc, trans);
2000 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2001 CGContextDrawImage (cgc, dst2, cgi);
2002 CGContextRestoreGState (cgc);
2007 CGImageRelease (cgi);
2009 # endif // USE_IPHONE
2012 geom_ret->x = dst.origin.x;
2013 geom_ret->y = dst.origin.y;
2014 geom_ret->width = dst.size.width;
2015 geom_ret->height = dst.size.height;
2018 invalidate_drawable_cache (d);
2024 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2026 unsigned int w, unsigned int h,
2027 unsigned long fg, unsigned int bg,
2030 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2031 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2032 (char *) data, w, h, 0, 0);
2034 gcv.foreground = fg;
2035 gcv.background = bg;
2036 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2037 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2040 XDestroyImage (image);
2045 XCreatePixmap (Display *dpy, Drawable d,
2046 unsigned int width, unsigned int height, unsigned int depth)
2048 char *data = (char *) malloc (width * height * 4);
2049 if (! data) return 0;
2051 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2053 p->frame.size.width = width;
2054 p->frame.size.height = height;
2055 p->pixmap.depth = depth;
2056 p->pixmap.cgc_buffer = data;
2058 /* Quartz doesn't have a 1bpp image type.
2059 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2060 don't support that! So we always use 32bpp, regardless of depth. */
2062 p->cgc = CGBitmapContextCreate (data, width, height,
2063 8, /* bits per component */
2064 width * 4, /* bpl */
2066 // Without this, it returns 0...
2067 kCGImageAlphaNoneSkipFirst
2069 Assert (p->cgc, "could not create CGBitmapContext");
2075 XFreePixmap (Display *d, Pixmap p)
2077 Assert (p->type == PIXMAP, "not a pixmap");
2078 invalidate_drawable_cache (p);
2079 CGContextRelease (p->cgc);
2080 if (p->pixmap.cgc_buffer)
2081 free (p->pixmap.cgc_buffer);
2088 copy_pixmap (Display *dpy, Pixmap p)
2091 Assert (p->type == PIXMAP, "not a pixmap");
2093 int width = p->frame.size.width;
2094 int height = p->frame.size.height;
2095 char *data = (char *) malloc (width * height * 4);
2096 if (! data) return 0;
2098 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2100 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2103 p2->pixmap.cgc_buffer = data;
2104 p2->cgc = CGBitmapContextCreate (data, width, height,
2105 8, /* bits per component */
2106 width * 4, /* bpl */
2108 // Without this, it returns 0...
2109 kCGImageAlphaNoneSkipFirst
2111 Assert (p2->cgc, "could not create CGBitmapContext");
2117 /* Font metric terminology, as used by X11:
2119 "lbearing" is the distance from the logical origin to the leftmost pixel.
2120 If a character's ink extends to the left of the origin, it is negative.
2122 "rbearing" is the distance from the logical origin to the rightmost pixel.
2124 "descent" is the distance from the logical origin to the bottommost pixel.
2125 For characters with descenders, it is negative.
2127 "ascent" is the distance from the logical origin to the topmost pixel.
2128 It is the number of pixels above the baseline.
2130 "width" is the distance from the logical origin to the position where
2131 the logical origin of the next character should be placed.
2133 If "rbearing" is greater than "width", then this character overlaps the
2134 following character. If smaller, then there is trailing blank space.
2138 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2141 query_font (Font fid)
2143 if (!fid || !fid->nsfont) {
2144 NSLog(@"no NSFont in fid");
2147 if (![fid->nsfont fontName]) {
2148 NSLog(@"broken NSFont in fid");
2155 XFontStruct *f = &fid->metrics;
2156 XCharStruct *min = &f->min_bounds;
2157 XCharStruct *max = &f->max_bounds;
2159 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2162 f->min_char_or_byte2 = first;
2163 f->max_char_or_byte2 = last;
2164 f->default_char = 'M';
2165 f->ascent = CEIL ([fid->nsfont ascender]);
2166 f->descent = -CEIL ([fid->nsfont descender]);
2168 min->width = 255; // set to smaller values in the loop
2171 min->lbearing = 255;
2172 min->rbearing = 255;
2174 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2178 NSBezierPath *bpath = [NSBezierPath bezierPath];
2181 for (i = first; i <= last; i++) {
2182 unsigned char str[2];
2186 NSString *nsstr = [NSString stringWithCString:(char *) str
2187 encoding:NSISOLatin1StringEncoding];
2188 NSPoint advancement;
2193 /* I can't believe we have to go through this bullshit just to
2194 convert a 'char' to an NSGlyph!!
2196 You might think that we could do
2197 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2198 but that doesn't work; my guess is that glyphWithName expects
2199 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2203 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2204 [ts setFont:fid->nsfont];
2205 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2207 /* Without this, the layout manager ends up on a queue somewhere and is
2208 referenced again after we return to the command loop. Since we don't
2209 use this layout manager again, by that time it may have been garbage
2210 collected, and we crash. Setting this seems to cause `lm' to no
2211 longer be referenced once we exit this block. */
2212 [lm setBackgroundLayoutEnabled:NO];
2214 NSTextContainer *tc = [[NSTextContainer alloc] init];
2215 [lm addTextContainer:tc];
2216 [tc release]; // lm retains tc
2217 [ts addLayoutManager:lm];
2218 [lm release]; // ts retains lm
2219 glyph = [lm glyphAtIndex:0];
2223 /* Compute the bounding box and advancement by converting the glyph
2224 to a bezier path. There appears to be *no other way* to find out
2225 the bounding box of a character: [NSFont boundingRectForGlyph] and
2226 [NSString sizeWithAttributes] both return an advancement-sized
2227 rectangle, not a rectangle completely enclosing the glyph's ink.
2229 advancement.x = advancement.y = 0;
2230 [bpath removeAllPoints];
2231 [bpath moveToPoint:advancement];
2232 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2233 advancement = [bpath currentPoint];
2234 bbox = [bpath bounds];
2236 # else // USE_IPHONE
2238 UIFont *ff = fid->nsfont;
2239 CGSize size = [nsstr sizeWithFont:ff];
2241 /* sizeWithFont gives us a character's "width" and "height".
2242 There is no way to get a character's "lbearing", "rbearing",
2243 or "descent". We do have the font's overall "descent", though.
2245 drawAtPoint (to an offscreen CGContext) returns "width" and the
2246 "ascent" of the font (not of the glyph, I think) so that doesn't
2249 CGFontGetGlyphBBoxes might help (if it actually returns a bounding
2250 box and not just the ascent/width again, which I don't know) but
2251 we can't use it anyway because there is no way to map a unichar to
2254 Fuck you sideways, Apple.
2258 bbox.origin.y = [ff descender];
2259 bbox.size.width = size.width;
2260 bbox.size.height = size.height;
2262 advancement.x = size.width;
2265 # endif // USE_IPHONE
2267 /* Now that we know the advancement and bounding box, we can compute
2268 the lbearing and rbearing.
2270 XCharStruct *cs = &f->per_char[i-first];
2272 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2273 cs->descent = CEIL(-bbox.origin.y);
2274 cs->lbearing = CEIL (bbox.origin.x);
2275 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2276 cs->width = CEIL (advancement.x);
2278 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2280 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2283 max->width = MAX (max->width, cs->width);
2284 max->ascent = MAX (max->ascent, cs->ascent);
2285 max->descent = MAX (max->descent, cs->descent);
2286 max->lbearing = MAX (max->lbearing, cs->lbearing);
2287 max->rbearing = MAX (max->rbearing, cs->rbearing);
2289 min->width = MIN (min->width, cs->width);
2290 min->ascent = MIN (min->ascent, cs->ascent);
2291 min->descent = MIN (min->descent, cs->descent);
2292 min->lbearing = MIN (min->lbearing, cs->lbearing);
2293 min->rbearing = MIN (min->rbearing, cs->rbearing);
2298 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2299 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2300 i, i, cs->width, cs->lbearing, cs->rbearing,
2301 cs->ascent, cs->descent,
2302 (int) bbox.size.width, (int) bbox.size.height,
2303 (int) bbox.origin.x, (int) bbox.origin.y,
2304 (int) advancement.x, (int) advancement.y);
2310 // Since 'Font' includes the metrics, this just makes a copy of that.
2313 XQueryFont (Display *dpy, Font fid)
2316 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2319 // copy XCharStruct array
2320 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2321 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2322 memcpy (f->per_char, fid->metrics.per_char,
2323 size * sizeof (XCharStruct));
2330 copy_font (Font fid)
2332 // copy 'Font' struct
2333 Font fid2 = (Font) malloc (sizeof(*fid2));
2336 // copy XCharStruct array
2337 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2338 fid2->metrics.per_char = (XCharStruct *)
2339 malloc ((size + 2) * sizeof (XCharStruct));
2340 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2341 size * sizeof (XCharStruct));
2343 // copy the other pointers
2344 fid2->ps_name = strdup (fid->ps_name);
2345 // [fid2->nsfont retain];
2346 fid2->metrics.fid = fid2;
2353 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2356 Assert (size > 0, "zero font size");
2361 // "Monaco" only exists in plain.
2362 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2364 if (bold && ital) name = "Courier-BoldOblique";
2365 else if (bold) name = "Courier-Bold";
2366 else if (ital) name = "Courier-Oblique";
2367 else name = "Courier";
2371 // "Georgia" looks better than "Times".
2373 if (bold && ital) name = "Georgia-BoldItalic";
2374 else if (bold) name = "Georgia-Bold";
2375 else if (ital) name = "Georgia-Italic";
2376 else name = "Georgia";
2380 // "Geneva" only exists in plain.
2381 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2382 // "Verdana" renders smoother than "Helvetica" for some reason.
2384 if (bold && ital) name = "Verdana-BoldItalic";
2385 else if (bold) name = "Verdana-Bold";
2386 else if (ital) name = "Verdana-Italic";
2387 else name = "Verdana";
2390 NSString *nsname = [NSString stringWithCString:name
2391 encoding:NSUTF8StringEncoding];
2392 NSFont *f = [NSFont fontWithName:nsname size:size];
2394 *name_ret = strdup(name);
2399 try_native_font (const char *name, float scale,
2400 char **name_ret, float *size_ret)
2402 if (!name) return 0;
2403 const char *spc = strrchr (name, ' ');
2406 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2409 if (size <= 4) return 0;
2413 char *name2 = strdup (name);
2414 name2[strlen(name2) - strlen(spc)] = 0;
2415 NSString *nsname = [NSString stringWithCString:name2
2416 encoding:NSUTF8StringEncoding];
2417 NSFont *f = [NSFont fontWithName:nsname size:size];
2429 /* Returns a random font in the given size and face.
2432 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2435 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2436 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2437 NSArray *fonts = [[NSFontManager sharedFontManager]
2438 availableFontNamesWithTraits:mask];
2439 if (!fonts) return 0;
2441 int n = [fonts count];
2442 if (n <= 0) return 0;
2445 for (j = 0; j < n; j++) {
2446 int i = random() % n;
2447 NSString *name = [fonts objectAtIndex:i];
2448 NSFont *f = [NSFont fontWithName:name size:size];
2451 /* Don't use this font if it (probably) doesn't include ASCII characters.
2453 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2454 if (! (enc == NSUTF8StringEncoding ||
2455 enc == NSISOLatin1StringEncoding ||
2456 enc == NSNonLossyASCIIStringEncoding ||
2457 enc == NSISOLatin2StringEncoding ||
2458 enc == NSUnicodeStringEncoding ||
2459 enc == NSWindowsCP1250StringEncoding ||
2460 enc == NSWindowsCP1252StringEncoding ||
2461 enc == NSMacOSRomanStringEncoding)) {
2462 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2465 // NSLog(@"using \"%@\": %d", name, enc);
2467 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2471 // None of the fonts support ASCII?
2474 # else // USE_IPHONE
2476 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2477 NSArray *families = [UIFont familyNames];
2478 NSMutableDictionary *famdict = [NSMutableDictionary
2479 dictionaryWithCapacity:100];
2480 NSObject *y = [NSNumber numberWithBool:YES];
2481 for (NSString *name in families) {
2482 // There are many dups in the families array -- uniquify it.
2483 [famdict setValue:y forKey:name];
2486 for (NSString *name in famdict) {
2487 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2490 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2493 BOOL bb = MATCH(@"Bold");
2494 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2496 if (!bold != !bb) continue;
2497 if (!ital != !ii) continue;
2499 /* Check if it can do ASCII. No good way to accomplish this!
2500 These are fonts present in iPhone Simulator as of June 2012
2501 that don't include ASCII.
2503 if (MATCH(@"AppleGothic") || // Korean
2504 MATCH(@"Dingbats") || // Dingbats
2505 MATCH(@"Emoji") || // Emoticons
2506 MATCH(@"Geeza") || // Arabic
2507 MATCH(@"Hebrew") || // Hebrew
2508 MATCH(@"HiraKaku") || // Japanese
2509 MATCH(@"HiraMin") || // Japanese
2510 MATCH(@"Kailasa") || // Tibetan
2511 MATCH(@"Ornaments") || // Dingbats
2512 MATCH(@"STHeiti") // Chinese
2516 [fonts addObject:fn];
2521 if (! [fonts count]) return 0; // Nothing suitable?
2523 int i = random() % [fonts count];
2524 NSString *name = [fonts objectAtIndex:i];
2525 UIFont *ff = [UIFont fontWithName:name size:size];
2526 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2530 # endif // USE_IPHONE
2535 try_xlfd_font (const char *name, float scale,
2536 char **name_ret, float *size_ret)
2547 const char *s = (name ? name : "");
2549 while (*s && (*s == '*' || *s == '-'))
2552 while (*s2 && (*s2 != '*' && *s2 != '-'))
2558 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2559 else if (CMP ("random")) rand = YES;
2560 else if (CMP ("bold")) bold = YES;
2561 else if (CMP ("i")) ital = YES;
2562 else if (CMP ("o")) ital = YES;
2563 else if (CMP ("courier")) fixed = YES;
2564 else if (CMP ("fixed")) fixed = YES;
2565 else if (CMP ("m")) fixed = YES;
2566 else if (CMP ("times")) serif = YES;
2567 else if (CMP ("6x10")) fixed = YES, size = 8;
2568 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2569 else if (CMP ("9x15")) fixed = YES, size = 12;
2570 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2571 else if (CMP ("vga")) fixed = YES, size = 12;
2572 else if (CMP ("console")) fixed = YES, size = 12;
2573 else if (CMP ("gallant")) fixed = YES, size = 12;
2575 else if (size == 0) {
2577 if (1 == sscanf (s, " %d ", &n))
2584 if (size < 6 || size > 1000)
2590 nsfont = random_font (bold, ital, size, &ps_name);
2593 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2595 // if that didn't work, turn off attibutes until it does
2596 // (e.g., there is no "Monaco-Bold".)
2598 if (!nsfont && serif) {
2600 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2602 if (!nsfont && ital) {
2604 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2606 if (!nsfont && bold) {
2608 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2610 if (!nsfont && fixed) {
2612 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2616 *name_ret = ps_name;
2626 XLoadFont (Display *dpy, const char *name)
2628 Font fid = (Font) calloc (1, sizeof(*fid));
2633 // Scale up fonts on Retina displays.
2634 scale = dpy->main_window->window.view.contentScaleFactor;
2637 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2639 if (!fid->nsfont && name &&
2640 strchr (name, ' ') &&
2641 !strchr (name, '*')) {
2642 // If name contains a space but no stars, it is a native font spec --
2643 // return NULL so that we know it really didn't exist. Else, it is an
2644 // XLFD font, so keep trying.
2645 XUnloadFont (dpy, fid);
2650 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2652 // We should never return NULL for XLFD fonts.
2656 CFRetain (fid->nsfont); // needed for garbage collection?
2658 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2667 XLoadQueryFont (Display *dpy, const char *name)
2669 Font fid = XLoadFont (dpy, name);
2671 return XQueryFont (dpy, fid);
2675 XUnloadFont (Display *dpy, Font fid)
2678 free (fid->ps_name);
2679 if (fid->metrics.per_char)
2680 free (fid->metrics.per_char);
2682 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2683 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2684 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2685 // They're probably not very big...
2687 // [fid->nsfont release];
2688 // CFRelease (fid->nsfont);
2695 XFreeFontInfo (char **names, XFontStruct *info, int n)
2699 for (i = 0; i < n; i++)
2700 if (names[i]) free (names[i]);
2704 for (i = 0; i < n; i++)
2705 if (info[i].per_char)
2706 free (info[i].per_char);
2713 XFreeFont (Display *dpy, XFontStruct *f)
2716 XFreeFontInfo (0, f, 1);
2717 XUnloadFont (dpy, fid);
2723 XSetFont (Display *dpy, GC gc, Font fid)
2726 XUnloadFont (dpy, gc->gcv.font);
2727 gc->gcv.font = copy_font (fid);
2728 [gc->gcv.font->nsfont retain];
2729 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2734 XTextExtents (XFontStruct *f, const char *s, int length,
2735 int *dir_ret, int *ascent_ret, int *descent_ret,
2738 memset (cs, 0, sizeof(*cs));
2740 for (i = 0; i < length; i++) {
2741 unsigned char c = (unsigned char) s[i];
2742 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2743 c = f->default_char;
2744 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2748 cs->ascent = MAX (cs->ascent, cc->ascent);
2749 cs->descent = MAX (cs->descent, cc->descent);
2750 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2751 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2752 cs->width += cc->width;
2756 *ascent_ret = f->ascent;
2757 *descent_ret = f->descent;
2762 XTextWidth (XFontStruct *f, const char *s, int length)
2764 int ascent, descent, dir;
2766 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2772 set_font (Display *dpy, CGContextRef cgc, GC gc)
2774 Font font = gc->gcv.font;
2776 font = XLoadFont (dpy, 0);
2777 gc->gcv.font = font;
2778 [gc->gcv.font->nsfont retain];
2779 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2781 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2782 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2787 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2788 const char *str, int len, BOOL clear_background_p)
2790 if (clear_background_p) {
2791 int ascent, descent, dir;
2793 XTextExtents (&gc->gcv.font->metrics, str, len,
2794 &dir, &ascent, &descent, &cs);
2795 draw_rect (dpy, d, gc,
2796 x + MIN (0, cs.lbearing),
2797 y - MAX (0, ascent),
2798 MAX (MAX (0, cs.rbearing) -
2799 MIN (0, cs.lbearing),
2801 MAX (0, ascent) + MAX (0, descent),
2805 CGRect wr = d->frame;
2808 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2809 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2812 CGContextRef cgc = d->cgc;
2813 push_fg_gc (d, gc, YES);
2814 set_font (dpy, cgc, gc);
2816 CGContextSetTextDrawingMode (cgc, kCGTextFill);
2817 if (gc->gcv.antialias_p)
2818 CGContextSetShouldAntialias (cgc, YES);
2819 CGContextShowTextAtPoint (cgc,
2821 wr.origin.y + wr.size.height - y,
2830 unsigned long argb = gc->gcv.foreground;
2831 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2832 float a = ((argb >> 24) & 0xFF) / 255.0;
2833 float r = ((argb >> 16) & 0xFF) / 255.0;
2834 float g = ((argb >> 8) & 0xFF) / 255.0;
2835 float b = ((argb ) & 0xFF) / 255.0;
2836 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2837 NSDictionary *attr =
2838 [NSDictionary dictionaryWithObjectsAndKeys:
2839 gc->gcv.font->nsfont, NSFontAttributeName,
2840 fg, NSForegroundColorAttributeName,
2842 char *s2 = (char *) malloc (len + 1);
2843 strncpy (s2, str, len);
2845 NSString *nsstr = [NSString stringWithCString:s2
2846 encoding:NSISOLatin1StringEncoding];
2849 pos.x = wr.origin.x + x;
2850 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2851 [nsstr drawAtPoint:pos withAttributes:attr];
2855 invalidate_drawable_cache (d);
2861 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2862 const char *str, int len)
2864 return draw_string (dpy, d, gc, x, y, str, len, NO);
2868 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2869 const char *str, int len)
2871 return draw_string (dpy, d, gc, x, y, str, len, YES);
2876 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2878 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2879 gc->gcv.foreground = fg;
2885 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2887 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2888 gc->gcv.background = bg;
2893 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2895 gc->gcv.alpha_allowed_p = allowed;
2900 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2902 gc->gcv.antialias_p = antialias_p;
2908 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2909 int line_style, int cap_style, int join_style)
2911 gc->gcv.line_width = line_width;
2912 Assert (line_style == LineSolid, "only LineSolid implemented");
2913 // gc->gcv.line_style = line_style;
2914 gc->gcv.cap_style = cap_style;
2915 gc->gcv.join_style = join_style;
2920 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2926 XSetFunction (Display *dpy, GC gc, int which)
2928 gc->gcv.function = which;
2933 XSetSubwindowMode (Display *dpy, GC gc, int which)
2935 gc->gcv.subwindow_mode = which;
2940 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2942 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2944 if (gc->gcv.clip_mask) {
2945 XFreePixmap (dpy, gc->gcv.clip_mask);
2946 CGImageRelease (gc->clip_mask);
2949 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2950 if (gc->gcv.clip_mask)
2952 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2960 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2962 gc->gcv.clip_x_origin = x;
2963 gc->gcv.clip_y_origin = y;
2969 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2970 int *root_x_ret, int *root_y_ret,
2971 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2973 Assert (w->type == WINDOW, "not a window");
2976 int x = w->window.last_mouse_x;
2977 int y = w->window.last_mouse_y;
2978 if (root_x_ret) *root_x_ret = x;
2979 if (root_y_ret) *root_y_ret = y;
2980 if (win_x_ret) *win_x_ret = x;
2981 if (win_y_ret) *win_y_ret = y;
2983 # else // !USE_IPHONE
2985 NSWindow *nsw = [w->window.view window];
2987 // get bottom left of window on screen, from bottom left
2988 wpos.x = wpos.y = 0;
2989 wpos = [nsw convertBaseToScreen:wpos];
2992 // get bottom left of view on window, from bottom left
2993 vpos.x = vpos.y = 0;
2994 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2996 // get bottom left of view on screen, from bottom left
3000 // get top left of view on screen, from bottom left
3001 vpos.y += w->frame.size.height;
3003 // get top left of view on screen, from top left
3004 NSArray *screens = [NSScreen screens];
3005 NSScreen *screen = (screens && [screens count] > 0
3006 ? [screens objectAtIndex:0]
3007 : [NSScreen mainScreen]);
3009 double s = w->window.view.contentScaleFactor;
3013 NSRect srect = [screen frame];
3014 vpos.y = (s * srect.size.height) - vpos.y;
3016 // get the mouse position on window, from bottom left
3017 NSEvent *e = [NSApp currentEvent];
3018 NSPoint p = [e locationInWindow];
3020 // get mouse position on screen, from bottom left
3024 // get mouse position on screen, from top left
3025 p.y = srect.size.height - p.y;
3027 if (root_x_ret) *root_x_ret = (int) p.x;
3028 if (root_y_ret) *root_y_ret = (int) p.y;
3029 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3030 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3031 # endif // !USE_IPHONE
3033 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3034 if (root_ret) *root_ret = 0;
3035 if (child_ret) *child_ret = 0;
3040 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3041 int src_x, int src_y,
3042 int *dest_x_ret, int *dest_y_ret,
3045 Assert (w->type == WINDOW, "not a window");
3053 # else // !USE_IPHONE
3055 NSWindow *nsw = [w->window.view window];
3057 // get bottom left of window on screen, from bottom left
3058 wpos.x = wpos.y = 0;
3059 wpos = [nsw convertBaseToScreen:wpos];
3062 // get bottom left of view on window, from bottom left
3063 vpos.x = vpos.y = 0;
3064 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3066 // get bottom left of view on screen, from bottom left
3070 // get top left of view on screen, from bottom left
3071 vpos.y += w->frame.size.height;
3073 // get top left of view on screen, from top left
3074 NSArray *screens = [NSScreen screens];
3075 NSScreen *screen = (screens && [screens count] > 0
3076 ? [screens objectAtIndex:0]
3077 : [NSScreen mainScreen]);
3079 double s = w->window.view.contentScaleFactor;
3083 NSRect srect = [screen frame];
3084 vpos.y = (s * srect.size.height) - vpos.y;
3086 // point starts out relative to top left of view
3091 // get point relative to top left of screen
3094 # endif // !USE_IPHONE
3105 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3111 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3114 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3115 char c = (char) ks; // could be smarter about modifiers here...
3116 if (k_ret) *k_ret = ks;
3117 if (size > 0) buf[0] = c;
3118 if (size > 1) buf[1] = 0;
3124 XFlush (Display *dpy)
3126 // Just let the event loop take care of this on its own schedule.
3131 XSync (Display *dpy, Bool flush)
3133 return XFlush (dpy);
3137 // declared in utils/visual.h
3139 has_writable_cells (Screen *s, Visual *v)
3145 visual_depth (Screen *s, Visual *v)
3151 visual_cells (Screen *s, Visual *v)
3157 visual_class (Screen *s, Visual *v)
3162 // declared in utils/grabclient.h
3164 use_subwindow_mode_p (Screen *screen, Window window)