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 # import <CoreText/CTFont.h>
27 # define NSView UIView
28 # define NSRect CGRect
29 # define NSPoint CGPoint
30 # define NSSize CGSize
31 # define NSColor UIColor
32 # define NSImage UIImage
33 # define NSEvent UIEvent
34 # define NSFont UIFont
35 # define NSGlyph CGGlyph
36 # define NSWindow UIWindow
37 # define NSMakeSize CGSizeMake
38 # define NSBezierPath UIBezierPath
40 # import <Cocoa/Cocoa.h>
44 #import "jwxyz-timers.h"
48 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
52 # define MAX(a,b) ((a)>(b)?(a):(b))
53 # define MIN(a,b) ((a)<(b)?(a):(b))
56 struct jwxyz_Drawable {
57 enum { WINDOW, PIXMAP } type;
64 unsigned long background;
65 int last_mouse_x, last_mouse_y;
69 void *cgc_buffer; // the bits to which CGContextRef renders
74 struct jwxyz_Display {
77 struct jwxyz_sources_data *timers_data;
80 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
81 This can change if the window is dragged to
82 a different screen. */
85 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
86 our images with this to avoid translation
98 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
104 float size; // points
106 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
107 // But we need the metrics on both of them, so they go here.
112 /* Instead of calling abort(), throw a real exception, so that
113 XScreenSaverView can catch it and display a dialog.
116 jwxyz_abort (const char *fmt, ...)
124 va_start (args, fmt);
125 vsprintf (s, fmt, args);
128 [[NSException exceptionWithName: NSInternalInconsistencyException
129 reason: [NSString stringWithCString: s
130 encoding:NSUTF8StringEncoding]
133 abort(); // not reached
138 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
140 CGContextRef cgc = (CGContextRef) cgc_arg;
141 NSView *view = (NSView *) nsview_arg;
142 Assert (view, "no view");
145 Display *d = (Display *) calloc (1, sizeof(*d));
146 d->screen = (Screen *) calloc (1, sizeof(Screen));
149 Visual *v = (Visual *) calloc (1, sizeof(Visual));
150 v->class = TrueColor;
151 v->red_mask = 0x00FF0000;
152 v->green_mask = 0x0000FF00;
153 v->blue_mask = 0x000000FF;
155 d->screen->visual = v;
157 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
160 Window w = (Window) calloc (1, sizeof(*w));
162 w->window.view = view;
163 CFRetain (w->window.view); // needed for garbage collection?
164 w->window.background = BlackPixel(0,0);
171 cgc = [[[view window] graphicsContext] graphicsPort];
177 Assert (cgc, "no CGContext");
182 jwxyz_free_display (Display *dpy)
184 jwxyz_XtRemoveInput_all (dpy);
185 // #### jwxyz_XtRemoveTimeOut_all ();
187 free (dpy->screen->visual);
189 free (dpy->main_window);
195 jwxyz_window_view (Window w)
197 Assert (w->type == WINDOW, "not a window");
198 return w->window.view;
202 /* Call this after any modification to the bits on a Pixmap or Window.
203 Most Pixmaps are used frequently as sources and infrequently as
204 destinations, so it pays to cache the data as a CGImage as needed.
207 invalidate_drawable_cache (Drawable d)
210 CGImageRelease (d->cgi);
216 /* Call this when the View changes size or position.
219 jwxyz_window_resized (Display *dpy, Window w,
220 int new_x, int new_y, int new_width, int new_height,
223 CGContextRef cgc = (CGContextRef) cgc_arg;
224 Assert (w->type == WINDOW, "not a window");
225 w->frame.origin.x = new_x;
226 w->frame.origin.y = new_y;
227 w->frame.size.width = new_width;
228 w->frame.size.height = new_height;
230 if (cgc) w->cgc = cgc;
231 Assert (w->cgc, "no CGContext");
234 // Figure out which screen the window is currently on.
237 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
243 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
244 Assert (dpy->cgdpy, "unable to find CGDisplay");
246 # endif // USE_IPHONE
250 // Figure out this screen's colorspace, and use that for every CGImage.
252 CMProfileRef profile = 0;
253 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
254 Assert (profile, "unable to find colorspace profile");
255 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
256 Assert (dpy->colorspace, "unable to find colorspace");
260 // WTF? It's faster if we *do not* use the screen's colorspace!
262 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
265 invalidate_drawable_cache (w);
271 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
273 Assert (w->type == WINDOW, "not a window");
274 w->window.last_mouse_x = x;
275 w->window.last_mouse_y = y;
282 display_sources_data (Display *dpy)
284 return dpy->timers_data;
289 XRootWindow (Display *dpy, int screen)
291 return dpy->main_window;
295 XDefaultScreenOfDisplay (Display *dpy)
301 XDefaultVisualOfScreen (Screen *screen)
303 return screen->visual;
307 XDisplayOfScreen (Screen *s)
313 XDisplayNumberOfScreen (Screen *s)
319 XScreenNumberOfScreen (Screen *s)
325 XDisplayWidth (Display *dpy, int screen)
327 return (int) dpy->main_window->frame.size.width;
331 XDisplayHeight (Display *dpy, int screen)
333 return (int) dpy->main_window->frame.size.height;
337 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
340 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
341 else if (!alpha_allowed_p)
342 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
343 "bogus color pixel");
348 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
349 BOOL alpha_allowed_p, BOOL fill_p)
351 validate_pixel (argb, depth, alpha_allowed_p);
354 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
356 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
358 float a = ((argb >> 24) & 0xFF) / 255.0;
359 float r = ((argb >> 16) & 0xFF) / 255.0;
360 float g = ((argb >> 8) & 0xFF) / 255.0;
361 float b = ((argb ) & 0xFF) / 255.0;
363 CGContextSetRGBFillColor (cgc, r, g, b, a);
365 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
370 set_line_mode (CGContextRef cgc, XGCValues *gcv)
372 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
373 CGContextSetLineJoin (cgc,
374 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
375 gcv->join_style == JoinRound ? kCGLineJoinRound :
377 CGContextSetLineCap (cgc,
378 gcv->cap_style == CapNotLast ? kCGLineCapButt :
379 gcv->cap_style == CapButt ? kCGLineCapButt :
380 gcv->cap_style == CapRound ? kCGLineCapRound :
385 set_clip_mask (Drawable d, GC gc)
387 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
389 Pixmap p = gc->gcv.clip_mask;
391 Assert (p->type == PIXMAP, "not a pixmap");
393 CGRect wr = d->frame;
395 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
396 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
397 - p->frame.size.height;
398 to.size.width = p->frame.size.width;
399 to.size.height = p->frame.size.height;
401 CGContextClipToMask (d->cgc, to, gc->clip_mask);
405 /* Pushes a GC context; sets BlendMode and ClipMask.
408 push_gc (Drawable d, GC gc)
410 CGContextRef cgc = d->cgc;
411 CGContextSaveGState (cgc);
413 switch (gc->gcv.function) {
416 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
417 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
418 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
419 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
420 default: Assert(0, "unknown gcv function"); break;
423 if (gc->gcv.clip_mask)
424 set_clip_mask (d, gc);
427 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
430 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
433 push_color_gc (Drawable d, GC gc, unsigned long color,
434 BOOL antialias_p, Bool fill_p)
438 int depth = gc->depth;
439 switch (gc->gcv.function) {
440 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
441 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
444 CGContextRef cgc = d->cgc;
445 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
446 CGContextSetShouldAntialias (cgc, antialias_p);
450 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
453 push_fg_gc (Drawable d, GC gc, Bool fill_p)
455 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
458 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
461 push_bg_gc (Drawable d, GC gc, Bool fill_p)
463 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
468 /* You've got to be fucking kidding me!
470 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
471 with repeated calls to CGContextDrawImage than it is to make a single
472 call to CGContextFillRects() with a list of 1x1 rectangles!
474 I still wouldn't call it *fast*, however...
476 #define XDRAWPOINTS_IMAGES
479 XDrawPoints (Display *dpy, Drawable d, GC gc,
480 XPoint *points, int count, int mode)
483 CGRect wr = d->frame;
485 push_fg_gc (d, gc, YES);
487 # ifdef XDRAWPOINTS_IMAGES
489 unsigned int argb = gc->gcv.foreground;
490 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
492 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
494 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
495 CGImageRef cgi = CGImageCreate (1, 1,
498 /* Host-ordered, since we're using the
499 address of an int as the color data. */
500 (kCGImageAlphaNoneSkipFirst |
501 kCGBitmapByteOrder32Host),
504 NO, /* interpolate */
505 kCGRenderingIntentDefault);
506 CGDataProviderRelease (prov);
508 CGContextRef cgc = d->cgc;
510 rect.size.width = rect.size.height = 1;
511 for (i = 0; i < count; i++) {
512 if (i > 0 && mode == CoordModePrevious) {
513 rect.origin.x += points->x;
514 rect.origin.x -= points->y;
516 rect.origin.x = wr.origin.x + points->x;
517 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
520 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
521 CGContextDrawImage (cgc, rect, cgi);
525 CGImageRelease (cgi);
527 # else /* ! XDRAWPOINTS_IMAGES */
529 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
532 for (i = 0; i < count; i++) {
533 r->size.width = r->size.height = 1;
534 if (i > 0 && mode == CoordModePrevious) {
535 r->origin.x = r[-1].origin.x + points->x;
536 r->origin.y = r[-1].origin.x - points->y;
538 r->origin.x = wr.origin.x + points->x;
539 r->origin.y = wr.origin.y + wr.size.height - points->y;
545 CGContextFillRects (d->cgc, rects, count);
548 # endif /* ! XDRAWPOINTS_IMAGES */
551 invalidate_drawable_cache (d);
558 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
563 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
567 static void draw_rect (Display *, Drawable, GC,
568 int x, int y, unsigned int width, unsigned int height,
569 BOOL foreground_p, BOOL fill_p);
572 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
573 int src_x, int src_y,
574 unsigned int width, unsigned int height,
575 int dst_x, int dst_y)
577 Assert (gc, "no GC");
578 Assert ((width < 65535), "improbably large width");
579 Assert ((height < 65535), "improbably large height");
580 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
581 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
582 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
583 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
585 if (width == 0 || height == 0)
588 if (gc->gcv.function == GXset ||
589 gc->gcv.function == GXclear) {
590 // "set" and "clear" are dumb drawing modes that ignore the source
591 // bits and just draw solid rectangles.
593 (gc->gcv.function == GXset
594 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
595 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
596 gc->depth, gc->gcv.alpha_allowed_p, YES);
597 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
601 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
602 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
603 // bounds of their drawables.
604 BOOL clipped = NO; // Whether we did any clipping of the rects.
606 src_frame = src->frame;
607 dst_frame = dst->frame;
609 // Initialize src_rect...
611 src_rect.origin.x = src_frame.origin.x + src_x;
612 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
614 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
615 src_rect.size.width = width;
616 src_rect.size.height = height;
618 // Initialize dst_rect...
620 dst_rect.origin.x = dst_frame.origin.x + dst_x;
621 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
623 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
624 dst_rect.size.width = width;
625 dst_rect.size.height = height;
627 // Clip rects to frames...
629 // CGRect orig_src_rect = src_rect;
630 CGRect orig_dst_rect = dst_rect;
632 # define CLIP(THIS,THAT,VAL,SIZE) do { \
633 float off = THIS##_rect.origin.VAL; \
636 THIS##_rect.size.SIZE += off; \
637 THAT##_rect.size.SIZE += off; \
638 THIS##_rect.origin.VAL -= off; \
639 THAT##_rect.origin.VAL -= off; \
641 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
642 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
645 THIS##_rect.size.SIZE -= off; \
646 THAT##_rect.size.SIZE -= off; \
649 CLIP (dst, src, x, width);
650 CLIP (dst, src, y, height);
651 CLIP (src, dst, x, width);
652 CLIP (src, dst, y, height);
655 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
658 NSObject *releaseme = 0;
661 BOOL free_cgi_p = NO;
665 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
666 if (src->type == PIXMAP)
670 // If we are copying from a Pixmap to a Pixmap or Window, we must first
671 // copy the bits to an intermediary CGImage object, then copy that to the
672 // destination drawable's CGContext.
674 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
675 // case of copying from a Pixmap back to itself, but I don't think that
676 // happens very often anyway.)
678 // First we get a CGImage out of the pixmap CGContext -- it's the whole
679 // pixmap, but it presumably shares the data pointer instead of copying
680 // it. We then cache that CGImage it inside the Pixmap object. Note:
681 // invalidate_drawable_cache() must be called to discard this any time a
682 // modification is made to the pixmap, or we'll end up re-using old bits.
685 src->cgi = CGBitmapContextCreateImage (src->cgc);
688 // if doing a sub-rect, trim it down.
689 if (src_rect.origin.x != src_frame.origin.x ||
690 src_rect.origin.y != src_frame.origin.y ||
691 src_rect.size.width != src_frame.size.width ||
692 src_rect.size.height != src_frame.size.height) {
693 // #### I don't understand why this is needed...
694 src_rect.origin.y = (src_frame.size.height -
695 src_rect.size.height - src_rect.origin.y);
696 // This does not copy image data, so it should be fast.
697 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
701 if (src->type == PIXMAP && src->pixmap.depth == 1)
705 } else { /* (src->type == WINDOW) */
707 NSRect nsfrom; // NSRect != CGRect on 10.4
708 nsfrom.origin.x = src_rect.origin.x;
709 nsfrom.origin.y = src_rect.origin.y;
710 nsfrom.size.width = src_rect.size.width;
711 nsfrom.size.height = src_rect.size.height;
715 // If we are copying from a window to itself, we can use NSCopyBits()
716 // without first copying the rectangle to an intermediary CGImage.
717 // This is ~28% faster (but I *expected* it to be twice as fast...)
718 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
724 // If we are copying from a Window to a Pixmap, we must first copy
725 // the bits to an intermediary CGImage object, then copy that to the
726 // Pixmap's CGContext.
728 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
729 initWithFocusedViewRect:nsfrom];
730 unsigned char *data = [bm bitmapData];
731 int bps = [bm bitsPerSample];
732 int bpp = [bm bitsPerPixel];
733 int bpl = [bm bytesPerRow];
736 // create a CGImage from those bits.
737 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
738 // but that method didn't exist in 10.4.)
740 CGDataProviderRef prov =
741 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
743 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
746 /* Use whatever default bit ordering we got from
747 initWithFocusedViewRect. I would have assumed
748 that it was (kCGImageAlphaNoneSkipFirst |
749 kCGBitmapByteOrder32Host), but on Intel,
755 NO, /* interpolate */
756 kCGRenderingIntentDefault);
758 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
759 CGDataProviderRelease (prov);
762 # endif // !USE_IPHONE
765 CGContextRef cgc = dst->cgc;
767 if (mask_p) { // src depth == 1
769 push_bg_gc (dst, gc, YES);
771 // fill the destination rectangle with solid background...
772 CGContextFillRect (cgc, orig_dst_rect);
774 Assert (cgc, "no CGC with 1-bit XCopyArea");
776 // then fill in a solid rectangle of the fg color, using the image as an
777 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
778 set_color (cgc, gc->gcv.foreground, gc->depth,
779 gc->gcv.alpha_allowed_p, YES);
780 CGContextClipToMask (cgc, dst_rect, cgi);
781 CGContextFillRect (cgc, dst_rect);
785 } else { // src depth > 1
789 // If either the src or dst rects did not lie within their drawables,
790 // then we have adjusted both the src and dst rects to account for
791 // the clipping; that means we need to first clear to the background,
792 // so that clipped bits end up in the bg color instead of simply not
796 set_color (cgc, gc->gcv.background, gc->depth,
797 gc->gcv.alpha_allowed_p, YES);
798 CGContextFillRect (cgc, orig_dst_rect);
802 // copy the CGImage onto the destination CGContext
803 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
804 CGContextDrawImage (cgc, dst_rect, cgi);
806 // No cgi means src == dst, and both are Windows.
809 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
811 # else // !USE_IPHONE
813 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
814 nsfrom.origin.y = src_rect.origin.y;
815 nsfrom.size.width = src_rect.size.width;
816 nsfrom.size.height = src_rect.size.height;
818 nsto.x = dst_rect.origin.x;
819 nsto.y = dst_rect.origin.y;
820 NSCopyBits (0, nsfrom, nsto);
821 # endif // !USE_IPHONE
827 if (free_cgi_p) CGImageRelease (cgi);
829 if (releaseme) [releaseme release];
830 invalidate_drawable_cache (dst);
836 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
837 int src_x, int src_y,
838 unsigned width, int height,
839 int dest_x, int dest_y, unsigned long plane)
841 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
843 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
844 // not to white/black.
845 return XCopyArea (dpy, src, dest, gc,
846 src_x, src_y, width, height, dest_x, dest_y);
851 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
853 // when drawing a zero-length line, obey line-width and cap-style.
854 if (x1 == x2 && y1 == y2) {
855 int w = gc->gcv.line_width;
858 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
859 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
861 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
864 CGRect wr = d->frame;
866 p.x = wr.origin.x + x1;
867 p.y = wr.origin.y + wr.size.height - y1;
869 push_fg_gc (d, gc, NO);
871 CGContextRef cgc = d->cgc;
872 set_line_mode (cgc, &gc->gcv);
873 CGContextBeginPath (cgc);
874 CGContextMoveToPoint (cgc, p.x, p.y);
875 p.x = wr.origin.x + x2;
876 p.y = wr.origin.y + wr.size.height - y2;
877 CGContextAddLineToPoint (cgc, p.x, p.y);
878 CGContextStrokePath (cgc);
880 invalidate_drawable_cache (d);
885 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
890 CGRect wr = d->frame;
891 push_fg_gc (d, gc, NO);
893 CGContextRef cgc = d->cgc;
895 set_line_mode (cgc, &gc->gcv);
897 // if the first and last points coincide, use closepath to get
898 // the proper line-joining.
899 BOOL closed_p = (points[0].x == points[count-1].x &&
900 points[0].y == points[count-1].y);
901 if (closed_p) count--;
903 p.x = wr.origin.x + points->x;
904 p.y = wr.origin.y + wr.size.height - points->y;
906 CGContextBeginPath (cgc);
907 CGContextMoveToPoint (cgc, p.x, p.y);
908 for (i = 1; i < count; i++) {
909 if (mode == CoordModePrevious) {
913 p.x = wr.origin.x + points->x;
914 p.y = wr.origin.y + wr.size.height - points->y;
916 CGContextAddLineToPoint (cgc, p.x, p.y);
919 if (closed_p) CGContextClosePath (cgc);
920 CGContextStrokePath (cgc);
922 invalidate_drawable_cache (d);
928 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
931 CGRect wr = d->frame;
933 CGContextRef cgc = d->cgc;
935 push_fg_gc (d, gc, NO);
936 set_line_mode (cgc, &gc->gcv);
937 CGContextBeginPath (cgc);
938 for (i = 0; i < count; i++) {
939 CGContextMoveToPoint (cgc,
940 wr.origin.x + segments->x1,
941 wr.origin.y + wr.size.height - segments->y1);
942 CGContextAddLineToPoint (cgc,
943 wr.origin.x + segments->x2,
944 wr.origin.y + wr.size.height - segments->y2);
947 CGContextStrokePath (cgc);
949 invalidate_drawable_cache (d);
955 XClearWindow (Display *dpy, Window win)
957 Assert (win->type == WINDOW, "not a window");
958 CGRect wr = win->frame;
959 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
963 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
965 Assert (w->type == WINDOW, "not a window");
966 validate_pixel (pixel, 32, NO);
967 w->window.background = pixel;
972 draw_rect (Display *dpy, Drawable d, GC gc,
973 int x, int y, unsigned int width, unsigned int height,
974 BOOL foreground_p, BOOL fill_p)
976 CGRect wr = d->frame;
978 r.origin.x = wr.origin.x + x;
979 r.origin.y = wr.origin.y + wr.size.height - y - height;
980 r.size.width = width;
981 r.size.height = height;
985 push_fg_gc (d, gc, fill_p);
987 push_bg_gc (d, gc, fill_p);
990 CGContextRef cgc = d->cgc;
992 CGContextFillRect (cgc, r);
995 set_line_mode (cgc, &gc->gcv);
996 CGContextStrokeRect (cgc, r);
1001 invalidate_drawable_cache (d);
1006 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1007 unsigned int width, unsigned int height)
1009 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1014 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1015 unsigned int width, unsigned int height)
1017 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1022 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1024 CGRect wr = d->frame;
1026 CGContextRef cgc = d->cgc;
1027 push_fg_gc (d, gc, YES);
1028 for (i = 0; i < n; i++) {
1030 r.origin.x = wr.origin.x + rects->x;
1031 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1032 r.size.width = rects->width;
1033 r.size.height = rects->height;
1034 CGContextFillRect (cgc, r);
1038 invalidate_drawable_cache (d);
1044 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1046 Assert (win->type == WINDOW, "not a window");
1047 CGContextRef cgc = win->cgc;
1048 set_color (cgc, win->window.background, 32, NO, YES);
1049 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1055 XFillPolygon (Display *dpy, Drawable d, GC gc,
1056 XPoint *points, int npoints, int shape, int mode)
1058 CGRect wr = d->frame;
1060 push_fg_gc (d, gc, YES);
1061 CGContextRef cgc = d->cgc;
1062 CGContextBeginPath (cgc);
1064 for (i = 0; i < npoints; i++) {
1065 if (i > 0 && mode == CoordModePrevious) {
1069 x = wr.origin.x + points[i].x;
1070 y = wr.origin.y + wr.size.height - points[i].y;
1074 CGContextMoveToPoint (cgc, x, y);
1076 CGContextAddLineToPoint (cgc, x, y);
1078 CGContextClosePath (cgc);
1079 if (gc->gcv.fill_rule == EvenOddRule)
1080 CGContextEOFillPath (cgc);
1082 CGContextFillPath (cgc);
1084 invalidate_drawable_cache (d);
1088 #define radians(DEG) ((DEG) * M_PI / 180.0)
1089 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1092 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1093 unsigned int width, unsigned int height, int angle1, int angle2,
1096 CGRect wr = d->frame;
1098 bound.origin.x = wr.origin.x + x;
1099 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1100 bound.size.width = width;
1101 bound.size.height = height;
1104 ctr.x = bound.origin.x + bound.size.width /2;
1105 ctr.y = bound.origin.y + bound.size.height/2;
1107 float r1 = radians (angle1/64.0);
1108 float r2 = radians (angle2/64.0) + r1;
1109 BOOL clockwise = angle2 < 0;
1110 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1112 push_fg_gc (d, gc, fill_p);
1114 CGContextRef cgc = d->cgc;
1115 CGContextBeginPath (cgc);
1117 CGContextSaveGState(cgc);
1118 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1119 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1121 CGContextMoveToPoint (cgc, 0, 0);
1123 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1124 CGContextRestoreGState (cgc); // restore before stroke, for line width
1127 CGContextClosePath (cgc); // for proper line joining
1130 CGContextFillPath (cgc);
1132 set_line_mode (cgc, &gc->gcv);
1133 CGContextStrokePath (cgc);
1137 invalidate_drawable_cache (d);
1142 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1143 unsigned int width, unsigned int height, int angle1, int angle2)
1145 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1149 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1150 unsigned int width, unsigned int height, int angle1, int angle2)
1152 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1156 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1159 for (i = 0; i < narcs; i++)
1160 draw_arc (dpy, d, gc,
1161 arcs[i].x, arcs[i].y,
1162 arcs[i].width, arcs[i].height,
1163 arcs[i].angle1, arcs[i].angle2,
1169 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1172 for (i = 0; i < narcs; i++)
1173 draw_arc (dpy, d, gc,
1174 arcs[i].x, arcs[i].y,
1175 arcs[i].width, arcs[i].height,
1176 arcs[i].angle1, arcs[i].angle2,
1183 gcv_defaults (XGCValues *gcv, int depth)
1185 memset (gcv, 0, sizeof(*gcv));
1186 gcv->function = GXcopy;
1187 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1188 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1189 gcv->line_width = 1;
1190 gcv->cap_style = CapNotLast;
1191 gcv->join_style = JoinMiter;
1192 gcv->fill_rule = EvenOddRule;
1194 gcv->alpha_allowed_p = NO;
1195 gcv->antialias_p = YES;
1199 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1202 Assert (gc && from, "no gc");
1203 if (!gc || !from) return;
1205 if (mask & GCFunction) gc->gcv.function = from->function;
1206 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1207 if (mask & GCBackground) gc->gcv.background = from->background;
1208 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1209 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1210 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1211 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1212 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1213 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1214 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1216 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1217 if (mask & GCFont) XSetFont (0, gc, from->font);
1219 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1220 gc->gcv.alpha_allowed_p);
1221 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1222 gc->gcv.alpha_allowed_p);
1224 Assert ((! (mask & (GCLineStyle |
1231 GCGraphicsExposures |
1235 "unimplemented gcvalues mask");
1240 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1242 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1243 if (d->type == WINDOW) {
1245 } else { /* (d->type == PIXMAP) */
1246 gc->depth = d->pixmap.depth;
1249 gcv_defaults (&gc->gcv, gc->depth);
1250 set_gcv (gc, xgcv, mask);
1255 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1257 set_gcv (gc, gcv, mask);
1263 XFreeGC (Display *dpy, GC gc)
1266 XUnloadFont (dpy, gc->gcv.font);
1268 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1270 if (gc->gcv.clip_mask) {
1271 XFreePixmap (dpy, gc->gcv.clip_mask);
1272 CGImageRelease (gc->clip_mask);
1280 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1282 Assert (w->type == WINDOW, "not a window");
1283 memset (xgwa, 0, sizeof(*xgwa));
1284 xgwa->x = w->frame.origin.x;
1285 xgwa->y = w->frame.origin.y;
1286 xgwa->width = w->frame.size.width;
1287 xgwa->height = w->frame.size.height;
1289 xgwa->screen = dpy->screen;
1290 xgwa->visual = dpy->screen->visual;
1295 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1296 int *x_ret, int *y_ret,
1297 unsigned int *w_ret, unsigned int *h_ret,
1298 unsigned int *bw_ret, unsigned int *d_ret)
1300 *x_ret = d->frame.origin.x;
1301 *y_ret = d->frame.origin.y;
1302 *w_ret = d->frame.size.width;
1303 *h_ret = d->frame.size.height;
1304 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1305 *root_ret = RootWindow (dpy, 0);
1312 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1314 // store 32 bit ARGB in the pixel field.
1315 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1316 color->pixel = (uint32_t)
1318 (((color->red >> 8) & 0xFF) << 16) |
1319 (((color->green >> 8) & 0xFF) << 8) |
1320 (((color->blue >> 8) & 0xFF) ));
1325 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1326 unsigned long *pmret, unsigned int npl,
1327 unsigned long *pxret, unsigned int npx)
1333 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1335 Assert(0, "XStoreColors called");
1340 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1342 Assert(0, "XStoreColor called");
1347 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1348 unsigned long planes)
1354 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1356 unsigned char r=0, g=0, b=0;
1357 if (*spec == '#' && strlen(spec) == 7) {
1358 static unsigned const char hex[] = { // yeah yeah, shoot me.
1359 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,
1360 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,
1361 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,
1362 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,
1363 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,
1364 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,
1365 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,
1366 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};
1367 r = (hex[spec[1]] << 4) | hex[spec[2]];
1368 g = (hex[spec[3]] << 4) | hex[spec[4]];
1369 b = (hex[spec[5]] << 4) | hex[spec[6]];
1370 } else if (!strcasecmp(spec,"black")) {
1372 } else if (!strcasecmp(spec,"white")) {
1374 } else if (!strcasecmp(spec,"red")) {
1376 } else if (!strcasecmp(spec,"green")) {
1378 } else if (!strcasecmp(spec,"blue")) {
1380 } else if (!strcasecmp(spec,"cyan")) {
1382 } else if (!strcasecmp(spec,"magenta")) {
1384 } else if (!strcasecmp(spec,"yellow")) {
1390 ret->red = (r << 8) | r;
1391 ret->green = (g << 8) | g;
1392 ret->blue = (b << 8) | b;
1393 ret->flags = DoRed|DoGreen|DoBlue;
1398 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1399 XColor *screen_ret, XColor *exact_ret)
1401 if (! XParseColor (dpy, cmap, name, screen_ret))
1403 *exact_ret = *screen_ret;
1404 return XAllocColor (dpy, cmap, screen_ret);
1408 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1410 validate_pixel (color->pixel, 32, NO);
1411 unsigned char r = ((color->pixel >> 16) & 0xFF);
1412 unsigned char g = ((color->pixel >> 8) & 0xFF);
1413 unsigned char b = ((color->pixel ) & 0xFF);
1414 color->red = (r << 8) | r;
1415 color->green = (g << 8) | g;
1416 color->blue = (b << 8) | b;
1417 color->flags = DoRed|DoGreen|DoBlue;
1422 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1425 for (i = 0; i < n; i++)
1426 XQueryColor (dpy, cmap, &c[i]);
1431 static unsigned long
1432 ximage_getpixel_1 (XImage *ximage, int x, int y)
1434 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1438 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1441 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1443 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1448 static unsigned long
1449 ximage_getpixel_32 (XImage *ximage, int x, int y)
1451 return ((unsigned long)
1452 *((uint32_t *) ximage->data +
1453 (y * (ximage->bytes_per_line >> 2)) +
1458 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1460 *((uint32_t *) ximage->data +
1461 (y * (ximage->bytes_per_line >> 2)) +
1462 x) = (uint32_t) pixel;
1468 XInitImage (XImage *ximage)
1470 if (!ximage->bytes_per_line)
1471 ximage->bytes_per_line = (ximage->depth == 1
1472 ? (ximage->width + 7) / 8
1473 : ximage->width * 4);
1475 if (ximage->depth == 1) {
1476 ximage->f.put_pixel = ximage_putpixel_1;
1477 ximage->f.get_pixel = ximage_getpixel_1;
1478 } else if (ximage->depth == 32 || ximage->depth == 24) {
1479 ximage->f.put_pixel = ximage_putpixel_32;
1480 ximage->f.get_pixel = ximage_getpixel_32;
1482 Assert (0, "unknown depth");
1489 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1490 int format, int offset, char *data,
1491 unsigned int width, unsigned int height,
1492 int bitmap_pad, int bytes_per_line)
1494 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1495 ximage->width = width;
1496 ximage->height = height;
1497 ximage->format = format;
1498 ximage->data = data;
1499 ximage->bitmap_unit = 8;
1500 ximage->byte_order = MSBFirst;
1501 ximage->bitmap_bit_order = ximage->byte_order;
1502 ximage->bitmap_pad = bitmap_pad;
1503 ximage->depth = depth;
1504 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1505 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1506 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1507 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1508 ximage->bytes_per_line = bytes_per_line;
1510 XInitImage (ximage);
1515 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1517 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1518 w, h, from->bitmap_pad, 0);
1519 to->data = (char *) malloc (h * to->bytes_per_line);
1521 if (x >= from->width)
1523 else if (x+w > from->width)
1524 w = from->width - x;
1526 if (y >= from->height)
1528 else if (y+h > from->height)
1529 h = from->height - y;
1532 for (ty = 0; ty < h; ty++)
1533 for (tx = 0; tx < w; tx++)
1534 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1539 XPixmapFormatValues *
1540 XListPixmapFormats (Display *dpy, int *n_ret)
1542 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1544 ret[0].bits_per_pixel = 32;
1545 ret[0].scanline_pad = 8;
1547 ret[1].bits_per_pixel = 1;
1548 ret[1].scanline_pad = 8;
1555 XGetPixel (XImage *ximage, int x, int y)
1557 return ximage->f.get_pixel (ximage, x, y);
1562 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1564 return ximage->f.put_pixel (ximage, x, y, pixel);
1568 XDestroyImage (XImage *ximage)
1570 if (ximage->data) free (ximage->data);
1577 flipbits (unsigned const char *in, unsigned char *out, int length)
1579 static const unsigned char table[256] = {
1580 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1581 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1582 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1583 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1584 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1585 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1586 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1587 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1588 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1589 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1590 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1591 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1592 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1593 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1594 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1595 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1596 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1597 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1598 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1599 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1600 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1601 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1602 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1603 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1604 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1605 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1606 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1607 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1608 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1609 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1610 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1611 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1613 while (--length > 0)
1614 *out++ = table[*in++];
1619 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1620 int src_x, int src_y, int dest_x, int dest_y,
1621 unsigned int w, unsigned int h)
1623 CGRect wr = d->frame;
1625 Assert (gc, "no GC");
1626 Assert ((w < 65535), "improbably large width");
1627 Assert ((h < 65535), "improbably large height");
1628 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1629 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1630 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1631 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1633 // Clip width and height to the bounds of the Drawable
1635 if (dest_x + w > wr.size.width) {
1636 if (dest_x > wr.size.width)
1638 w = wr.size.width - dest_x;
1640 if (dest_y + h > wr.size.height) {
1641 if (dest_y > wr.size.height)
1643 h = wr.size.height - dest_y;
1645 if (w <= 0 || h <= 0)
1648 // Clip width and height to the bounds of the XImage
1650 if (src_x + w > ximage->width) {
1651 if (src_x > ximage->width)
1653 w = ximage->width - src_x;
1655 if (src_y + h > ximage->height) {
1656 if (src_y > ximage->height)
1658 h = ximage->height - src_y;
1660 if (w <= 0 || h <= 0)
1663 CGContextRef cgc = d->cgc;
1665 if (gc->gcv.function == GXset ||
1666 gc->gcv.function == GXclear) {
1667 // "set" and "clear" are dumb drawing modes that ignore the source
1668 // bits and just draw solid rectangles.
1669 set_color (cgc, (gc->gcv.function == GXset
1670 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1671 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1672 gc->depth, gc->gcv.alpha_allowed_p, YES);
1673 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1677 int bpl = ximage->bytes_per_line;
1678 int bpp = ximage->bits_per_pixel;
1679 int bsize = bpl * h;
1680 char *data = ximage->data;
1683 r.origin.x = wr.origin.x + dest_x;
1684 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1690 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1691 to create a CGImage from a sub-rectagle of the XImage.
1693 data += (src_y * bpl) + (src_x * 4);
1694 CGDataProviderRef prov =
1695 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1697 CGImageRef cgi = CGImageCreate (w, h,
1700 /* Need this for XPMs to have the right
1701 colors, e.g. the logo in "maze". */
1702 (kCGImageAlphaNoneSkipFirst |
1703 kCGBitmapByteOrder32Host),
1705 NULL, /* decode[] */
1706 NO, /* interpolate */
1707 kCGRenderingIntentDefault);
1708 CGDataProviderRelease (prov);
1709 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1710 CGContextDrawImage (cgc, r, cgi);
1711 CGImageRelease (cgi);
1713 } else { // (bpp == 1)
1715 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1717 #### However, the bit order within a byte in a 1bpp XImage is
1718 the wrong way around from what Quartz expects, so first we
1719 have to copy the data to reverse it. Shit! Maybe it
1720 would be worthwhile to go through the hacks and #ifdef
1721 each one that diddles 1bpp XImage->data directly...
1723 Assert ((src_x % 8) == 0,
1724 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1726 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1727 unsigned char *flipped = (unsigned char *) malloc (bsize);
1729 flipbits ((unsigned char *) data, flipped, bsize);
1731 CGDataProviderRef prov =
1732 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1733 CGImageRef mask = CGImageMaskCreate (w, h,
1736 NULL, /* decode[] */
1737 NO); /* interpolate */
1738 push_fg_gc (d, gc, YES);
1740 CGContextFillRect (cgc, r); // foreground color
1741 CGContextClipToMask (cgc, r, mask);
1742 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1743 CGContextFillRect (cgc, r); // background color
1747 CGDataProviderRelease (prov);
1748 CGImageRelease (mask);
1751 invalidate_drawable_cache (d);
1758 XGetImage (Display *dpy, Drawable d, int x, int y,
1759 unsigned int width, unsigned int height,
1760 unsigned long plane_mask, int format)
1762 const unsigned char *data = 0;
1763 int depth, ibpp, ibpl, alpha_first_p;
1765 NSBitmapImageRep *bm = 0;
1768 Assert ((width < 65535), "improbably large width");
1769 Assert ((height < 65535), "improbably large height");
1770 Assert ((x < 65535 && x > -65535), "improbably large x");
1771 Assert ((y < 65535 && y > -65535), "improbably large y");
1773 CGContextRef cgc = d->cgc;
1776 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
1777 if (d->type == PIXMAP)
1780 depth = (d->type == PIXMAP
1783 // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst.
1784 // If it's an iPhone window, it's the other way around.
1785 alpha_first_p = (d->type == PIXMAP);
1786 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
1787 ibpl = CGBitmapContextGetBytesPerRow (cgc);
1788 data = CGBitmapContextGetData (cgc);
1789 Assert (data, "CGBitmapContextGetData failed");
1792 } else { /* (d->type == WINDOW) */
1794 // get the bits (desired sub-rectangle) out of the NSView
1796 nsfrom.origin.x = x;
1797 nsfrom.origin.y = y;
1798 nsfrom.size.width = width;
1799 nsfrom.size.height = height;
1800 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
1802 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1803 ibpp = [bm bitsPerPixel];
1804 ibpl = [bm bytesPerRow];
1805 data = [bm bitmapData];
1806 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1807 # endif // !USE_IPHONE
1810 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1811 data += (y * ibpl) + (x * (ibpp/8));
1813 format = (depth == 1 ? XYPixmap : ZPixmap);
1814 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1816 image->data = (char *) malloc (height * image->bytes_per_line);
1818 int obpl = image->bytes_per_line;
1820 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1821 means that on Intel it is BGRA when viewed by bytes (And BGR
1822 when using 24bpp packing).
1824 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1825 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1826 indicator of this latest kink.
1830 const unsigned char *iline = data;
1831 for (yy = 0; yy < height; yy++) {
1833 const unsigned char *iline2 = iline;
1834 for (xx = 0; xx < width; xx++) {
1836 iline2++; // ignore R or A or A or B
1837 iline2++; // ignore G or B or R or G
1838 unsigned char r = *iline2++; // use B or G or G or R
1839 if (ibpp == 32) iline2++; // ignore A or R or B or A
1841 XPutPixel (image, xx, yy, (r ? 1 : 0));
1846 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1847 const unsigned char *iline = data;
1848 unsigned char *oline = (unsigned char *) image->data;
1849 for (yy = 0; yy < height; yy++) {
1851 const unsigned char *iline2 = iline;
1852 unsigned char *oline2 = oline;
1854 if (alpha_first_p) // ARGB
1855 for (xx = 0; xx < width; xx++) {
1856 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1857 unsigned char r = *iline2++;
1858 unsigned char g = *iline2++;
1859 unsigned char b = *iline2++;
1860 uint32_t pixel = ((a << 24) |
1864 *((uint32_t *) oline2) = pixel;
1868 for (xx = 0; xx < width; xx++) {
1869 unsigned char r = *iline2++;
1870 unsigned char g = *iline2++;
1871 unsigned char b = *iline2++;
1872 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1873 uint32_t pixel = ((a << 24) |
1877 *((uint32_t *) oline2) = pixel;
1887 if (bm) [bm release];
1895 /* Returns a transformation matrix to do rotation as per the provided
1896 EXIF "Orientation" value.
1898 static CGAffineTransform
1899 exif_rotate (int rot, CGSize rect)
1901 CGAffineTransform trans = CGAffineTransformIdentity;
1903 case 2: // flip horizontal
1904 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1905 trans = CGAffineTransformScale (trans, -1, 1);
1908 case 3: // rotate 180
1909 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1910 trans = CGAffineTransformRotate (trans, M_PI);
1913 case 4: // flip vertical
1914 trans = CGAffineTransformMakeTranslation (0, rect.height);
1915 trans = CGAffineTransformScale (trans, 1, -1);
1918 case 5: // transpose (UL-to-LR axis)
1919 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1920 trans = CGAffineTransformScale (trans, -1, 1);
1921 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1924 case 6: // rotate 90
1925 trans = CGAffineTransformMakeTranslation (0, rect.width);
1926 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1929 case 7: // transverse (UR-to-LL axis)
1930 trans = CGAffineTransformMakeScale (-1, 1);
1931 trans = CGAffineTransformRotate (trans, M_PI / 2);
1934 case 8: // rotate 270
1935 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1936 trans = CGAffineTransformRotate (trans, M_PI / 2);
1948 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
1949 Bool nsimg_p, void *img_arg,
1950 XRectangle *geom_ret, int exif_rotation)
1954 CGImageSourceRef cgsrc;
1955 # endif // USE_IPHONE
1958 CGContextRef cgc = d->cgc;
1962 NSImage *nsimg = (NSImage *) img_arg;
1963 imgr = [nsimg size];
1966 // convert the NSImage to a CGImage via the toll-free-bridging
1967 // of NSData and CFData...
1969 NSData *nsdata = [NSBitmapImageRep
1970 TIFFRepresentationOfImageRepsInArray:
1971 [nsimg representations]];
1972 CFDataRef cfdata = (CFDataRef) nsdata;
1973 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1974 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1975 # else // USE_IPHONE
1976 cgi = nsimg.CGImage;
1977 # endif // USE_IPHONE
1980 cgi = (CGImageRef) img_arg;
1981 imgr.width = CGImageGetWidth (cgi);
1982 imgr.height = CGImageGetHeight (cgi);
1985 Bool rot_p = (exif_rotation >= 5);
1988 imgr = NSMakeSize (imgr.height, imgr.width);
1990 CGRect winr = d->frame;
1991 float rw = winr.size.width / imgr.width;
1992 float rh = winr.size.height / imgr.height;
1993 float r = (rw < rh ? rw : rh);
1996 dst.size.width = imgr.width * r;
1997 dst.size.height = imgr.height * r;
1998 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1999 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2001 dst2.origin.x = dst2.origin.y = 0;
2003 dst2.size.width = dst.size.height;
2004 dst2.size.height = dst.size.width;
2006 dst2.size = dst.size;
2009 // Clear the part not covered by the image to background or black.
2011 if (d->type == WINDOW)
2012 XClearWindow (dpy, d);
2014 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2015 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2018 CGAffineTransform trans =
2019 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2021 CGContextSaveGState (cgc);
2022 CGContextConcatCTM (cgc,
2023 CGAffineTransformMakeTranslation (dst.origin.x,
2025 CGContextConcatCTM (cgc, trans);
2026 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2027 CGContextDrawImage (cgc, dst2, cgi);
2028 CGContextRestoreGState (cgc);
2033 CGImageRelease (cgi);
2035 # endif // USE_IPHONE
2038 geom_ret->x = dst.origin.x;
2039 geom_ret->y = dst.origin.y;
2040 geom_ret->width = dst.size.width;
2041 geom_ret->height = dst.size.height;
2044 invalidate_drawable_cache (d);
2050 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2052 unsigned int w, unsigned int h,
2053 unsigned long fg, unsigned int bg,
2056 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2057 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2058 (char *) data, w, h, 0, 0);
2060 gcv.foreground = fg;
2061 gcv.background = bg;
2062 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2063 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2066 XDestroyImage (image);
2071 XCreatePixmap (Display *dpy, Drawable d,
2072 unsigned int width, unsigned int height, unsigned int depth)
2074 char *data = (char *) malloc (width * height * 4);
2075 if (! data) return 0;
2077 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2079 p->frame.size.width = width;
2080 p->frame.size.height = height;
2081 p->pixmap.depth = depth;
2082 p->pixmap.cgc_buffer = data;
2084 /* Quartz doesn't have a 1bpp image type.
2085 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2086 don't support that! So we always use 32bpp, regardless of depth. */
2088 p->cgc = CGBitmapContextCreate (data, width, height,
2089 8, /* bits per component */
2090 width * 4, /* bpl */
2092 // Without this, it returns 0...
2093 kCGImageAlphaNoneSkipFirst
2095 Assert (p->cgc, "could not create CGBitmapContext");
2101 XFreePixmap (Display *d, Pixmap p)
2103 Assert (p->type == PIXMAP, "not a pixmap");
2104 invalidate_drawable_cache (p);
2105 CGContextRelease (p->cgc);
2106 if (p->pixmap.cgc_buffer)
2107 free (p->pixmap.cgc_buffer);
2114 copy_pixmap (Display *dpy, Pixmap p)
2117 Assert (p->type == PIXMAP, "not a pixmap");
2119 int width = p->frame.size.width;
2120 int height = p->frame.size.height;
2121 char *data = (char *) malloc (width * height * 4);
2122 if (! data) return 0;
2124 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2126 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2129 p2->pixmap.cgc_buffer = data;
2130 p2->cgc = CGBitmapContextCreate (data, width, height,
2131 8, /* bits per component */
2132 width * 4, /* bpl */
2134 // Without this, it returns 0...
2135 kCGImageAlphaNoneSkipFirst
2137 Assert (p2->cgc, "could not create CGBitmapContext");
2143 /* Font metric terminology, as used by X11:
2145 "lbearing" is the distance from the logical origin to the leftmost pixel.
2146 If a character's ink extends to the left of the origin, it is negative.
2148 "rbearing" is the distance from the logical origin to the rightmost pixel.
2150 "descent" is the distance from the logical origin to the bottommost pixel.
2151 For characters with descenders, it is negative.
2153 "ascent" is the distance from the logical origin to the topmost pixel.
2154 It is the number of pixels above the baseline.
2156 "width" is the distance from the logical origin to the position where
2157 the logical origin of the next character should be placed.
2159 If "rbearing" is greater than "width", then this character overlaps the
2160 following character. If smaller, then there is trailing blank space.
2164 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2167 query_font (Font fid)
2169 if (!fid || !fid->nsfont) {
2170 Assert (0, "no NSFont in fid");
2173 if (![fid->nsfont fontName]) {
2174 Assert(0, @"broken NSFont in fid");
2181 XFontStruct *f = &fid->metrics;
2182 XCharStruct *min = &f->min_bounds;
2183 XCharStruct *max = &f->max_bounds;
2185 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2188 f->min_char_or_byte2 = first;
2189 f->max_char_or_byte2 = last;
2190 f->default_char = 'M';
2191 f->ascent = CEIL ([fid->nsfont ascender]);
2192 f->descent = -CEIL ([fid->nsfont descender]);
2194 min->width = 255; // set to smaller values in the loop
2197 min->lbearing = 255;
2198 min->rbearing = 255;
2200 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2204 NSBezierPath *bpath = [NSBezierPath bezierPath];
2205 # else // USE_IPHONE
2207 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2208 [fid->nsfont pointSize],
2210 Assert (ctfont, @"no CTFontRef for UIFont");
2211 # endif // USE_IPHONE
2213 for (i = first; i <= last; i++) {
2214 unsigned char str[2];
2218 NSString *nsstr = [NSString stringWithCString:(char *) str
2219 encoding:NSISOLatin1StringEncoding];
2220 NSPoint advancement = { 0, };
2221 NSRect bbox = {{ 0, }, };
2225 /* I can't believe we have to go through this bullshit just to
2226 convert a 'char' to an NSGlyph!!
2228 You might think that we could do
2229 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2230 but that doesn't work; my guess is that glyphWithName expects
2231 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2235 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2236 [ts setFont:fid->nsfont];
2237 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2239 /* Without this, the layout manager ends up on a queue somewhere and is
2240 referenced again after we return to the command loop. Since we don't
2241 use this layout manager again, by that time it may have been garbage
2242 collected, and we crash. Setting this seems to cause `lm' to no
2243 longer be referenced once we exit this block. */
2244 [lm setBackgroundLayoutEnabled:NO];
2246 NSTextContainer *tc = [[NSTextContainer alloc] init];
2247 [lm addTextContainer:tc];
2248 [tc release]; // lm retains tc
2249 [ts addLayoutManager:lm];
2250 [lm release]; // ts retains lm
2251 glyph = [lm glyphAtIndex:0];
2255 /* Compute the bounding box and advancement by converting the glyph
2256 to a bezier path. There appears to be *no other way* to find out
2257 the bounding box of a character: [NSFont boundingRectForGlyph] and
2258 [NSString sizeWithAttributes] both return an advancement-sized
2259 rectangle, not a rectangle completely enclosing the glyph's ink.
2261 advancement.x = advancement.y = 0;
2262 [bpath removeAllPoints];
2263 [bpath moveToPoint:advancement];
2264 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2265 advancement = [bpath currentPoint];
2266 bbox = [bpath bounds];
2268 # else // USE_IPHONE
2270 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2271 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2272 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2273 width of the character and the ascent of the font.
2275 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2276 the CoreText library, but there's no non-CoreText way to turn a
2277 unichar into a CGGlyph.
2279 UniChar uchar = [nsstr characterAtIndex: 0];
2280 CGGlyph cgglyph = 0;
2282 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2284 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2285 kCTFontDefaultOrientation,
2287 CGSize adv = { 0, };
2288 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2290 advancement.x = adv.width;
2291 advancement.y = adv.height;
2293 // Seems to be clipping by a pixel or two. Add a margin to be safe.
2296 bbox.size.width += 4;
2297 bbox.size.height += 4;
2299 # endif // USE_IPHONE
2301 /* Now that we know the advancement and bounding box, we can compute
2302 the lbearing and rbearing.
2304 XCharStruct *cs = &f->per_char[i-first];
2306 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2307 cs->descent = CEIL(-bbox.origin.y);
2308 cs->lbearing = CEIL (bbox.origin.x);
2309 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2310 cs->width = CEIL (advancement.x);
2312 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2314 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2317 max->width = MAX (max->width, cs->width);
2318 max->ascent = MAX (max->ascent, cs->ascent);
2319 max->descent = MAX (max->descent, cs->descent);
2320 max->lbearing = MAX (max->lbearing, cs->lbearing);
2321 max->rbearing = MAX (max->rbearing, cs->rbearing);
2323 min->width = MIN (min->width, cs->width);
2324 min->ascent = MIN (min->ascent, cs->ascent);
2325 min->descent = MIN (min->descent, cs->descent);
2326 min->lbearing = MIN (min->lbearing, cs->lbearing);
2327 min->rbearing = MIN (min->rbearing, cs->rbearing);
2332 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2333 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2334 i, i, cs->width, cs->lbearing, cs->rbearing,
2335 cs->ascent, cs->descent,
2336 (int) bbox.size.width, (int) bbox.size.height,
2337 (int) bbox.origin.x, (int) bbox.origin.y,
2338 (int) advancement.x, (int) advancement.y);
2348 // Since 'Font' includes the metrics, this just makes a copy of that.
2351 XQueryFont (Display *dpy, Font fid)
2354 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2357 // copy XCharStruct array
2358 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2359 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2360 memcpy (f->per_char, fid->metrics.per_char,
2361 size * sizeof (XCharStruct));
2368 copy_font (Font fid)
2370 // copy 'Font' struct
2371 Font fid2 = (Font) malloc (sizeof(*fid2));
2374 // copy XCharStruct array
2375 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2376 fid2->metrics.per_char = (XCharStruct *)
2377 malloc ((size + 2) * sizeof (XCharStruct));
2378 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2379 size * sizeof (XCharStruct));
2381 // copy the other pointers
2382 fid2->ps_name = strdup (fid->ps_name);
2383 // [fid2->nsfont retain];
2384 fid2->metrics.fid = fid2;
2391 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2394 Assert (size > 0, "zero font size");
2399 // "Monaco" only exists in plain.
2400 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2402 if (bold && ital) name = "Courier-BoldOblique";
2403 else if (bold) name = "Courier-Bold";
2404 else if (ital) name = "Courier-Oblique";
2405 else name = "Courier";
2409 // "Georgia" looks better than "Times".
2411 if (bold && ital) name = "Georgia-BoldItalic";
2412 else if (bold) name = "Georgia-Bold";
2413 else if (ital) name = "Georgia-Italic";
2414 else name = "Georgia";
2418 // "Geneva" only exists in plain.
2419 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2420 // "Verdana" renders smoother than "Helvetica" for some reason.
2422 if (bold && ital) name = "Verdana-BoldItalic";
2423 else if (bold) name = "Verdana-Bold";
2424 else if (ital) name = "Verdana-Italic";
2425 else name = "Verdana";
2428 NSString *nsname = [NSString stringWithCString:name
2429 encoding:NSUTF8StringEncoding];
2430 NSFont *f = [NSFont fontWithName:nsname size:size];
2432 *name_ret = strdup(name);
2437 try_native_font (const char *name, float scale,
2438 char **name_ret, float *size_ret)
2440 if (!name) return 0;
2441 const char *spc = strrchr (name, ' ');
2444 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2447 if (size <= 4) return 0;
2451 char *name2 = strdup (name);
2452 name2[strlen(name2) - strlen(spc)] = 0;
2453 NSString *nsname = [NSString stringWithCString:name2
2454 encoding:NSUTF8StringEncoding];
2455 NSFont *f = [NSFont fontWithName:nsname size:size];
2467 /* Returns a random font in the given size and face.
2470 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2473 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2474 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2475 NSArray *fonts = [[NSFontManager sharedFontManager]
2476 availableFontNamesWithTraits:mask];
2477 if (!fonts) return 0;
2479 int n = [fonts count];
2480 if (n <= 0) return 0;
2483 for (j = 0; j < n; j++) {
2484 int i = random() % n;
2485 NSString *name = [fonts objectAtIndex:i];
2486 NSFont *f = [NSFont fontWithName:name size:size];
2489 /* Don't use this font if it (probably) doesn't include ASCII characters.
2491 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2492 if (! (enc == NSUTF8StringEncoding ||
2493 enc == NSISOLatin1StringEncoding ||
2494 enc == NSNonLossyASCIIStringEncoding ||
2495 enc == NSISOLatin2StringEncoding ||
2496 enc == NSUnicodeStringEncoding ||
2497 enc == NSWindowsCP1250StringEncoding ||
2498 enc == NSWindowsCP1252StringEncoding ||
2499 enc == NSMacOSRomanStringEncoding)) {
2500 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2503 // NSLog(@"using \"%@\": %d", name, enc);
2505 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2509 // None of the fonts support ASCII?
2512 # else // USE_IPHONE
2514 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2515 NSArray *families = [UIFont familyNames];
2516 NSMutableDictionary *famdict = [NSMutableDictionary
2517 dictionaryWithCapacity:100];
2518 NSObject *y = [NSNumber numberWithBool:YES];
2519 for (NSString *name in families) {
2520 // There are many dups in the families array -- uniquify it.
2521 [famdict setValue:y forKey:name];
2524 for (NSString *name in famdict) {
2525 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2528 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2531 BOOL bb = MATCH(@"Bold");
2532 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2534 if (!bold != !bb) continue;
2535 if (!ital != !ii) continue;
2537 /* Check if it can do ASCII. No good way to accomplish this!
2538 These are fonts present in iPhone Simulator as of June 2012
2539 that don't include ASCII.
2541 if (MATCH(@"AppleGothic") || // Korean
2542 MATCH(@"Dingbats") || // Dingbats
2543 MATCH(@"Emoji") || // Emoticons
2544 MATCH(@"Geeza") || // Arabic
2545 MATCH(@"Hebrew") || // Hebrew
2546 MATCH(@"HiraKaku") || // Japanese
2547 MATCH(@"HiraMin") || // Japanese
2548 MATCH(@"Kailasa") || // Tibetan
2549 MATCH(@"Ornaments") || // Dingbats
2550 MATCH(@"STHeiti") // Chinese
2554 [fonts addObject:fn];
2559 if (! [fonts count]) return 0; // Nothing suitable?
2561 int i = random() % [fonts count];
2562 NSString *name = [fonts objectAtIndex:i];
2563 UIFont *ff = [UIFont fontWithName:name size:size];
2564 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2568 # endif // USE_IPHONE
2573 try_xlfd_font (const char *name, float scale,
2574 char **name_ret, float *size_ret)
2585 const char *s = (name ? name : "");
2587 while (*s && (*s == '*' || *s == '-'))
2590 while (*s2 && (*s2 != '*' && *s2 != '-'))
2596 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2597 else if (CMP ("random")) rand = YES;
2598 else if (CMP ("bold")) bold = YES;
2599 else if (CMP ("i")) ital = YES;
2600 else if (CMP ("o")) ital = YES;
2601 else if (CMP ("courier")) fixed = YES;
2602 else if (CMP ("fixed")) fixed = YES;
2603 else if (CMP ("m")) fixed = YES;
2604 else if (CMP ("times")) serif = YES;
2605 else if (CMP ("6x10")) fixed = YES, size = 8;
2606 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2607 else if (CMP ("9x15")) fixed = YES, size = 12;
2608 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2609 else if (CMP ("vga")) fixed = YES, size = 12;
2610 else if (CMP ("console")) fixed = YES, size = 12;
2611 else if (CMP ("gallant")) fixed = YES, size = 12;
2613 else if (size == 0) {
2615 if (1 == sscanf (s, " %d ", &n))
2622 if (size < 6 || size > 1000)
2628 nsfont = random_font (bold, ital, size, &ps_name);
2631 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2633 // if that didn't work, turn off attibutes until it does
2634 // (e.g., there is no "Monaco-Bold".)
2636 if (!nsfont && serif) {
2638 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2640 if (!nsfont && ital) {
2642 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2644 if (!nsfont && bold) {
2646 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2648 if (!nsfont && fixed) {
2650 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2654 *name_ret = ps_name;
2664 XLoadFont (Display *dpy, const char *name)
2666 Font fid = (Font) calloc (1, sizeof(*fid));
2671 // Scale up fonts on Retina displays.
2672 scale = dpy->main_window->window.view.contentScaleFactor;
2675 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2677 if (!fid->nsfont && name &&
2678 strchr (name, ' ') &&
2679 !strchr (name, '*')) {
2680 // If name contains a space but no stars, it is a native font spec --
2681 // return NULL so that we know it really didn't exist. Else, it is an
2682 // XLFD font, so keep trying.
2683 XUnloadFont (dpy, fid);
2688 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2690 // We should never return NULL for XLFD fonts.
2692 Assert (0, "no font");
2695 CFRetain (fid->nsfont); // needed for garbage collection?
2697 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2706 XLoadQueryFont (Display *dpy, const char *name)
2708 Font fid = XLoadFont (dpy, name);
2710 return XQueryFont (dpy, fid);
2714 XUnloadFont (Display *dpy, Font fid)
2717 free (fid->ps_name);
2718 if (fid->metrics.per_char)
2719 free (fid->metrics.per_char);
2721 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2722 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2723 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2724 // They're probably not very big...
2726 // [fid->nsfont release];
2727 // CFRelease (fid->nsfont);
2734 XFreeFontInfo (char **names, XFontStruct *info, int n)
2738 for (i = 0; i < n; i++)
2739 if (names[i]) free (names[i]);
2743 for (i = 0; i < n; i++)
2744 if (info[i].per_char)
2745 free (info[i].per_char);
2752 XFreeFont (Display *dpy, XFontStruct *f)
2755 XFreeFontInfo (0, f, 1);
2756 XUnloadFont (dpy, fid);
2762 XSetFont (Display *dpy, GC gc, Font fid)
2765 XUnloadFont (dpy, gc->gcv.font);
2766 gc->gcv.font = copy_font (fid);
2767 [gc->gcv.font->nsfont retain];
2768 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2773 XTextExtents (XFontStruct *f, const char *s, int length,
2774 int *dir_ret, int *ascent_ret, int *descent_ret,
2777 memset (cs, 0, sizeof(*cs));
2779 for (i = 0; i < length; i++) {
2780 unsigned char c = (unsigned char) s[i];
2781 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2782 c = f->default_char;
2783 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2787 cs->ascent = MAX (cs->ascent, cc->ascent);
2788 cs->descent = MAX (cs->descent, cc->descent);
2789 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2790 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2791 cs->width += cc->width;
2795 *ascent_ret = f->ascent;
2796 *descent_ret = f->descent;
2801 XTextWidth (XFontStruct *f, const char *s, int length)
2803 int ascent, descent, dir;
2805 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2811 set_font (Display *dpy, CGContextRef cgc, GC gc)
2813 Font font = gc->gcv.font;
2815 font = XLoadFont (dpy, 0);
2816 gc->gcv.font = font;
2817 [gc->gcv.font->nsfont retain];
2818 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2820 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2821 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2826 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2827 const char *str, int len, BOOL clear_background_p)
2829 if (clear_background_p) {
2830 int ascent, descent, dir;
2832 XTextExtents (&gc->gcv.font->metrics, str, len,
2833 &dir, &ascent, &descent, &cs);
2834 draw_rect (dpy, d, gc,
2835 x + MIN (0, cs.lbearing),
2836 y - MAX (0, ascent),
2837 MAX (MAX (0, cs.rbearing) -
2838 MIN (0, cs.lbearing),
2840 MAX (0, ascent) + MAX (0, descent),
2844 CGRect wr = d->frame;
2847 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2848 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2851 CGContextRef cgc = d->cgc;
2852 push_fg_gc (d, gc, YES);
2853 set_font (dpy, cgc, gc);
2855 CGContextSetTextDrawingMode (cgc, kCGTextFill);
2856 if (gc->gcv.antialias_p)
2857 CGContextSetShouldAntialias (cgc, YES);
2858 CGContextShowTextAtPoint (cgc,
2860 wr.origin.y + wr.size.height - y,
2869 unsigned long argb = gc->gcv.foreground;
2870 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2871 float a = ((argb >> 24) & 0xFF) / 255.0;
2872 float r = ((argb >> 16) & 0xFF) / 255.0;
2873 float g = ((argb >> 8) & 0xFF) / 255.0;
2874 float b = ((argb ) & 0xFF) / 255.0;
2875 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2876 NSDictionary *attr =
2877 [NSDictionary dictionaryWithObjectsAndKeys:
2878 gc->gcv.font->nsfont, NSFontAttributeName,
2879 fg, NSForegroundColorAttributeName,
2881 char *s2 = (char *) malloc (len + 1);
2882 strncpy (s2, str, len);
2884 NSString *nsstr = [NSString stringWithCString:s2
2885 encoding:NSISOLatin1StringEncoding];
2888 pos.x = wr.origin.x + x;
2889 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2890 [nsstr drawAtPoint:pos withAttributes:attr];
2894 invalidate_drawable_cache (d);
2900 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2901 const char *str, int len)
2903 return draw_string (dpy, d, gc, x, y, str, len, NO);
2907 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2908 const char *str, int len)
2910 return draw_string (dpy, d, gc, x, y, str, len, YES);
2915 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2917 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2918 gc->gcv.foreground = fg;
2924 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2926 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2927 gc->gcv.background = bg;
2932 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2934 gc->gcv.alpha_allowed_p = allowed;
2939 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2941 gc->gcv.antialias_p = antialias_p;
2947 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2948 int line_style, int cap_style, int join_style)
2950 gc->gcv.line_width = line_width;
2951 Assert (line_style == LineSolid, "only LineSolid implemented");
2952 // gc->gcv.line_style = line_style;
2953 gc->gcv.cap_style = cap_style;
2954 gc->gcv.join_style = join_style;
2959 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2965 XSetFunction (Display *dpy, GC gc, int which)
2967 gc->gcv.function = which;
2972 XSetSubwindowMode (Display *dpy, GC gc, int which)
2974 gc->gcv.subwindow_mode = which;
2979 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2981 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2983 if (gc->gcv.clip_mask) {
2984 XFreePixmap (dpy, gc->gcv.clip_mask);
2985 CGImageRelease (gc->clip_mask);
2988 gc->gcv.clip_mask = copy_pixmap (dpy, m);
2989 if (gc->gcv.clip_mask)
2991 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2999 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3001 gc->gcv.clip_x_origin = x;
3002 gc->gcv.clip_y_origin = y;
3008 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3009 int *root_x_ret, int *root_y_ret,
3010 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3012 Assert (w->type == WINDOW, "not a window");
3015 int x = w->window.last_mouse_x;
3016 int y = w->window.last_mouse_y;
3017 if (root_x_ret) *root_x_ret = x;
3018 if (root_y_ret) *root_y_ret = y;
3019 if (win_x_ret) *win_x_ret = x;
3020 if (win_y_ret) *win_y_ret = y;
3022 # else // !USE_IPHONE
3024 NSWindow *nsw = [w->window.view window];
3026 // get bottom left of window on screen, from bottom left
3027 wpos.x = wpos.y = 0;
3028 wpos = [nsw convertBaseToScreen:wpos];
3031 // get bottom left of view on window, from bottom left
3032 vpos.x = vpos.y = 0;
3033 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3035 // get bottom left of view on screen, from bottom left
3039 // get top left of view on screen, from bottom left
3040 vpos.y += w->frame.size.height;
3042 // get top left of view on screen, from top left
3043 NSArray *screens = [NSScreen screens];
3044 NSScreen *screen = (screens && [screens count] > 0
3045 ? [screens objectAtIndex:0]
3046 : [NSScreen mainScreen]);
3048 double s = w->window.view.contentScaleFactor;
3052 NSRect srect = [screen frame];
3053 vpos.y = (s * srect.size.height) - vpos.y;
3055 // get the mouse position on window, from bottom left
3056 NSEvent *e = [NSApp currentEvent];
3057 NSPoint p = [e locationInWindow];
3059 // get mouse position on screen, from bottom left
3063 // get mouse position on screen, from top left
3064 p.y = srect.size.height - p.y;
3066 if (root_x_ret) *root_x_ret = (int) p.x;
3067 if (root_y_ret) *root_y_ret = (int) p.y;
3068 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3069 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3070 # endif // !USE_IPHONE
3072 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3073 if (root_ret) *root_ret = 0;
3074 if (child_ret) *child_ret = 0;
3079 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3080 int src_x, int src_y,
3081 int *dest_x_ret, int *dest_y_ret,
3084 Assert (w->type == WINDOW, "not a window");
3092 # else // !USE_IPHONE
3094 NSWindow *nsw = [w->window.view window];
3096 // get bottom left of window on screen, from bottom left
3097 wpos.x = wpos.y = 0;
3098 wpos = [nsw convertBaseToScreen:wpos];
3101 // get bottom left of view on window, from bottom left
3102 vpos.x = vpos.y = 0;
3103 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3105 // get bottom left of view on screen, from bottom left
3109 // get top left of view on screen, from bottom left
3110 vpos.y += w->frame.size.height;
3112 // get top left of view on screen, from top left
3113 NSArray *screens = [NSScreen screens];
3114 NSScreen *screen = (screens && [screens count] > 0
3115 ? [screens objectAtIndex:0]
3116 : [NSScreen mainScreen]);
3118 double s = w->window.view.contentScaleFactor;
3122 NSRect srect = [screen frame];
3123 vpos.y = (s * srect.size.height) - vpos.y;
3125 // point starts out relative to top left of view
3130 // get point relative to top left of screen
3133 # endif // !USE_IPHONE
3144 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3150 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3153 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3154 char c = (char) ks; // could be smarter about modifiers here...
3155 if (k_ret) *k_ret = ks;
3156 if (size > 0) buf[0] = c;
3157 if (size > 1) buf[1] = 0;
3163 XFlush (Display *dpy)
3165 // Just let the event loop take care of this on its own schedule.
3170 XSync (Display *dpy, Bool flush)
3172 return XFlush (dpy);
3176 // declared in utils/visual.h
3178 has_writable_cells (Screen *s, Visual *v)
3184 visual_depth (Screen *s, Visual *v)
3190 visual_cells (Screen *s, Visual *v)
3196 visual_class (Screen *s, Visual *v)
3201 // declared in utils/grabclient.h
3203 use_subwindow_mode_p (Screen *screen, Window window)