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 USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
52 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
56 # define MAX(a,b) ((a)>(b)?(a):(b))
57 # define MIN(a,b) ((a)<(b)?(a):(b))
60 struct jwxyz_Drawable {
61 enum { WINDOW, PIXMAP } type;
68 unsigned long background;
69 int last_mouse_x, last_mouse_y;
73 void *cgc_buffer; // the bits to which CGContextRef renders
78 struct jwxyz_Display {
81 struct jwxyz_sources_data *timers_data;
84 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
85 This can change if the window is dragged to
86 a different screen. */
89 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
90 our images with this to avoid translation
102 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
108 float size; // points
110 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
111 // But we need the metrics on both of them, so they go here.
116 /* Instead of calling abort(), throw a real exception, so that
117 XScreenSaverView can catch it and display a dialog.
120 jwxyz_abort (const char *fmt, ...)
128 va_start (args, fmt);
129 vsprintf (s, fmt, args);
132 [[NSException exceptionWithName: NSInternalInconsistencyException
133 reason: [NSString stringWithCString: s
134 encoding:NSUTF8StringEncoding]
137 abort(); // not reached
142 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
144 CGContextRef cgc = (CGContextRef) cgc_arg;
145 NSView *view = (NSView *) nsview_arg;
146 Assert (view, "no view");
149 Display *d = (Display *) calloc (1, sizeof(*d));
150 d->screen = (Screen *) calloc (1, sizeof(Screen));
153 Visual *v = (Visual *) calloc (1, sizeof(Visual));
154 v->class = TrueColor;
155 v->red_mask = 0x00FF0000;
156 v->green_mask = 0x0000FF00;
157 v->blue_mask = 0x000000FF;
159 d->screen->visual = v;
161 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
164 Window w = (Window) calloc (1, sizeof(*w));
166 w->window.view = view;
167 CFRetain (w->window.view); // needed for garbage collection?
168 w->window.background = BlackPixel(0,0);
175 cgc = [[[view window] graphicsContext] graphicsPort];
181 Assert (cgc, "no CGContext");
186 jwxyz_free_display (Display *dpy)
188 jwxyz_XtRemoveInput_all (dpy);
189 // #### jwxyz_XtRemoveTimeOut_all ();
191 free (dpy->screen->visual);
193 free (dpy->main_window);
199 jwxyz_window_view (Window w)
201 Assert (w && w->type == WINDOW, "not a window");
202 return w->window.view;
206 /* Call this after any modification to the bits on a Pixmap or Window.
207 Most Pixmaps are used frequently as sources and infrequently as
208 destinations, so it pays to cache the data as a CGImage as needed.
211 invalidate_drawable_cache (Drawable d)
214 CGImageRelease (d->cgi);
220 /* Call this when the View changes size or position.
223 jwxyz_window_resized (Display *dpy, Window w,
224 int new_x, int new_y, int new_width, int new_height,
227 CGContextRef cgc = (CGContextRef) cgc_arg;
228 Assert (w && w->type == WINDOW, "not a window");
229 w->frame.origin.x = new_x;
230 w->frame.origin.y = new_y;
231 w->frame.size.width = new_width;
232 w->frame.size.height = new_height;
234 if (cgc) w->cgc = cgc;
235 Assert (w->cgc, "no CGContext");
238 // Figure out which screen the window is currently on.
241 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
247 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
248 Assert (dpy->cgdpy, "unable to find CGDisplay");
250 # endif // USE_IPHONE
254 // Figure out this screen's colorspace, and use that for every CGImage.
256 CMProfileRef profile = 0;
257 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
258 Assert (profile, "unable to find colorspace profile");
259 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
260 Assert (dpy->colorspace, "unable to find colorspace");
264 // WTF? It's faster if we *do not* use the screen's colorspace!
266 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
269 invalidate_drawable_cache (w);
275 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
277 Assert (w && w->type == WINDOW, "not a window");
278 w->window.last_mouse_x = x;
279 w->window.last_mouse_y = y;
286 display_sources_data (Display *dpy)
288 return dpy->timers_data;
293 XRootWindow (Display *dpy, int screen)
295 return dpy->main_window;
299 XDefaultScreenOfDisplay (Display *dpy)
305 XDefaultVisualOfScreen (Screen *screen)
307 return screen->visual;
311 XDisplayOfScreen (Screen *s)
317 XDisplayNumberOfScreen (Screen *s)
323 XScreenNumberOfScreen (Screen *s)
329 XDisplayWidth (Display *dpy, int screen)
331 return (int) dpy->main_window->frame.size.width;
335 XDisplayHeight (Display *dpy, int screen)
337 return (int) dpy->main_window->frame.size.height;
341 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
344 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
345 else if (!alpha_allowed_p)
346 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
347 "bogus color pixel");
352 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
353 BOOL alpha_allowed_p, BOOL fill_p)
355 validate_pixel (argb, depth, alpha_allowed_p);
358 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
360 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
362 float a = ((argb >> 24) & 0xFF) / 255.0;
363 float r = ((argb >> 16) & 0xFF) / 255.0;
364 float g = ((argb >> 8) & 0xFF) / 255.0;
365 float b = ((argb ) & 0xFF) / 255.0;
367 CGContextSetRGBFillColor (cgc, r, g, b, a);
369 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
374 set_line_mode (CGContextRef cgc, XGCValues *gcv)
376 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
377 CGContextSetLineJoin (cgc,
378 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
379 gcv->join_style == JoinRound ? kCGLineJoinRound :
381 CGContextSetLineCap (cgc,
382 gcv->cap_style == CapNotLast ? kCGLineCapButt :
383 gcv->cap_style == CapButt ? kCGLineCapButt :
384 gcv->cap_style == CapRound ? kCGLineCapRound :
389 set_clip_mask (Drawable d, GC gc)
391 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
393 Pixmap p = gc->gcv.clip_mask;
395 Assert (p->type == PIXMAP, "not a pixmap");
397 CGRect wr = d->frame;
399 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
400 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
401 - p->frame.size.height;
402 to.size.width = p->frame.size.width;
403 to.size.height = p->frame.size.height;
405 CGContextClipToMask (d->cgc, to, gc->clip_mask);
409 /* Pushes a GC context; sets BlendMode and ClipMask.
412 push_gc (Drawable d, GC gc)
414 CGContextRef cgc = d->cgc;
415 CGContextSaveGState (cgc);
417 switch (gc->gcv.function) {
420 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
421 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
422 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
423 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
424 default: Assert(0, "unknown gcv function"); break;
427 if (gc->gcv.clip_mask)
428 set_clip_mask (d, gc);
431 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
434 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
437 push_color_gc (Drawable d, GC gc, unsigned long color,
438 BOOL antialias_p, Bool fill_p)
442 int depth = gc->depth;
443 switch (gc->gcv.function) {
444 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
445 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
448 CGContextRef cgc = d->cgc;
449 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
450 CGContextSetShouldAntialias (cgc, antialias_p);
454 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
457 push_fg_gc (Drawable d, GC gc, Bool fill_p)
459 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
462 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
465 push_bg_gc (Drawable d, GC gc, Bool fill_p)
467 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
472 /* You've got to be fucking kidding me!
474 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
475 with repeated calls to CGContextDrawImage than it is to make a single
476 call to CGContextFillRects() with a list of 1x1 rectangles!
478 I still wouldn't call it *fast*, however...
480 #define XDRAWPOINTS_IMAGES
482 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
483 the bitmap data directly is faster. This only works on Pixmaps, though,
484 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
486 #define XDRAWPOINTS_CGDATA
489 XDrawPoints (Display *dpy, Drawable d, GC gc,
490 XPoint *points, int count, int mode)
493 CGRect wr = d->frame;
495 push_fg_gc (d, gc, YES);
497 # ifdef XDRAWPOINTS_CGDATA
499 # ifdef USE_BACKBUFFER
500 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
502 if (d->type == PIXMAP)
505 CGContextRef cgc = d->cgc;
506 void *data = CGBitmapContextGetData (cgc);
507 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
508 size_t w = CGBitmapContextGetWidth (cgc);
509 size_t h = CGBitmapContextGetHeight (cgc);
511 Assert (data, "no bitmap data in Drawable");
513 unsigned int argb = gc->gcv.foreground;
514 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
516 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
518 CGFloat x0 = wr.origin.x;
519 CGFloat y0 = wr.origin.y + wr.size.height;
521 // It's uglier, but faster, to hoist the conditional out of the loop.
522 if (mode == CoordModePrevious) {
523 CGFloat x = x0, y = y0;
524 for (i = 0; i < count; i++, points++) {
528 if (0 <= x && x < w && 0 <= y && y < h) {
529 unsigned int *p = (unsigned int *)
530 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
535 for (i = 0; i < count; i++, points++) {
536 CGFloat x = x0 + points->x;
537 CGFloat y = y0 - points->y;
539 if (0 <= x && x < w && 0 <= y && y < h) {
540 unsigned int *p = (unsigned int *)
541 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
547 } else /* d->type == WINDOW */
549 # endif /* XDRAWPOINTS_CGDATA */
552 # ifdef XDRAWPOINTS_IMAGES
554 unsigned int argb = gc->gcv.foreground;
555 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
557 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
559 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
561 CGImageRef cgi = CGImageCreate (1, 1,
564 /* Host-ordered, since we're using the
565 address of an int as the color data. */
566 (kCGImageAlphaNoneSkipFirst |
567 kCGBitmapByteOrder32Host),
570 NO, /* interpolate */
571 kCGRenderingIntentDefault);
572 CGDataProviderRelease (prov);
574 CGContextRef cgc = d->cgc;
576 rect.size.width = rect.size.height = 1;
577 for (i = 0; i < count; i++) {
578 if (i > 0 && mode == CoordModePrevious) {
579 rect.origin.x += points->x;
580 rect.origin.x -= points->y;
582 rect.origin.x = wr.origin.x + points->x;
583 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
586 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
587 CGContextDrawImage (cgc, rect, cgi);
591 CGImageRelease (cgi);
593 # else /* ! XDRAWPOINTS_IMAGES */
595 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
598 for (i = 0; i < count; i++) {
599 r->size.width = r->size.height = 1;
600 if (i > 0 && mode == CoordModePrevious) {
601 r->origin.x = r[-1].origin.x + points->x;
602 r->origin.y = r[-1].origin.x - points->y;
604 r->origin.x = wr.origin.x + points->x;
605 r->origin.y = wr.origin.y + wr.size.height - points->y;
611 CGContextFillRects (d->cgc, rects, count);
614 # endif /* ! XDRAWPOINTS_IMAGES */
618 invalidate_drawable_cache (d);
625 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
630 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
634 static void draw_rect (Display *, Drawable, GC,
635 int x, int y, unsigned int width, unsigned int height,
636 BOOL foreground_p, BOOL fill_p);
639 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
640 int src_x, int src_y,
641 unsigned int width, unsigned int height,
642 int dst_x, int dst_y)
644 Assert (gc, "no GC");
645 Assert ((width < 65535), "improbably large width");
646 Assert ((height < 65535), "improbably large height");
647 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
648 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
649 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
650 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
652 if (width == 0 || height == 0)
655 if (gc->gcv.function == GXset ||
656 gc->gcv.function == GXclear) {
657 // "set" and "clear" are dumb drawing modes that ignore the source
658 // bits and just draw solid rectangles.
660 (gc->gcv.function == GXset
661 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
662 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
663 gc->depth, gc->gcv.alpha_allowed_p, YES);
664 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
668 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
669 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
670 // bounds of their drawables.
671 BOOL clipped = NO; // Whether we did any clipping of the rects.
673 src_frame = src->frame;
674 dst_frame = dst->frame;
676 // Initialize src_rect...
678 src_rect.origin.x = src_frame.origin.x + src_x;
679 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
681 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
682 src_rect.size.width = width;
683 src_rect.size.height = height;
685 // Initialize dst_rect...
687 dst_rect.origin.x = dst_frame.origin.x + dst_x;
688 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
690 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
691 dst_rect.size.width = width;
692 dst_rect.size.height = height;
694 // Clip rects to frames...
696 // CGRect orig_src_rect = src_rect;
697 CGRect orig_dst_rect = dst_rect;
699 # define CLIP(THIS,THAT,VAL,SIZE) do { \
700 float off = THIS##_rect.origin.VAL; \
703 THIS##_rect.size.SIZE += off; \
704 THAT##_rect.size.SIZE += off; \
705 THIS##_rect.origin.VAL -= off; \
706 THAT##_rect.origin.VAL -= off; \
708 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
709 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
712 THIS##_rect.size.SIZE -= off; \
713 THAT##_rect.size.SIZE -= off; \
716 CLIP (dst, src, x, width);
717 CLIP (dst, src, y, height);
718 CLIP (src, dst, x, width);
719 CLIP (src, dst, y, height);
722 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
725 NSObject *releaseme = 0;
728 BOOL free_cgi_p = NO;
731 #ifndef USE_BACKBUFFER
732 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
733 if (src->type == PIXMAP)
737 // If we are copying from a Pixmap to a Pixmap or Window, we must first
738 // copy the bits to an intermediary CGImage object, then copy that to the
739 // destination drawable's CGContext.
741 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
742 // case of copying from a Pixmap back to itself, but I don't think that
743 // happens very often anyway.)
745 // First we get a CGImage out of the pixmap CGContext -- it's the whole
746 // pixmap, but it presumably shares the data pointer instead of copying
747 // it. We then cache that CGImage it inside the Pixmap object. Note:
748 // invalidate_drawable_cache() must be called to discard this any time a
749 // modification is made to the pixmap, or we'll end up re-using old bits.
752 src->cgi = CGBitmapContextCreateImage (src->cgc);
755 // if doing a sub-rect, trim it down.
756 if (src_rect.origin.x != src_frame.origin.x ||
757 src_rect.origin.y != src_frame.origin.y ||
758 src_rect.size.width != src_frame.size.width ||
759 src_rect.size.height != src_frame.size.height) {
760 // #### I don't understand why this is needed...
761 src_rect.origin.y = (src_frame.size.height -
762 src_rect.size.height - src_rect.origin.y);
763 // This does not copy image data, so it should be fast.
764 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
768 if (src->type == PIXMAP && src->pixmap.depth == 1)
771 # ifndef USE_BACKBUFFER
772 } else { /* (src->type == WINDOW) */
774 NSRect nsfrom; // NSRect != CGRect on 10.4
775 nsfrom.origin.x = src_rect.origin.x;
776 nsfrom.origin.y = src_rect.origin.y;
777 nsfrom.size.width = src_rect.size.width;
778 nsfrom.size.height = src_rect.size.height;
782 // If we are copying from a window to itself, we can use NSCopyBits()
783 // without first copying the rectangle to an intermediary CGImage.
784 // This is ~28% faster (but I *expected* it to be twice as fast...)
785 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
791 // If we are copying from a Window to a Pixmap, we must first copy
792 // the bits to an intermediary CGImage object, then copy that to the
793 // Pixmap's CGContext.
795 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
796 initWithFocusedViewRect:nsfrom];
797 unsigned char *data = [bm bitmapData];
798 int bps = [bm bitsPerSample];
799 int bpp = [bm bitsPerPixel];
800 int bpl = [bm bytesPerRow];
803 // create a CGImage from those bits.
804 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
805 // but that method didn't exist in 10.4.)
807 CGDataProviderRef prov =
808 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
810 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
813 /* Use whatever default bit ordering we got from
814 initWithFocusedViewRect. I would have assumed
815 that it was (kCGImageAlphaNoneSkipFirst |
816 kCGBitmapByteOrder32Host), but on Intel,
822 NO, /* interpolate */
823 kCGRenderingIntentDefault);
825 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
826 CGDataProviderRelease (prov);
829 # endif // !USE_BACKBUFFER
832 CGContextRef cgc = dst->cgc;
834 if (mask_p) { // src depth == 1
836 push_bg_gc (dst, gc, YES);
838 // fill the destination rectangle with solid background...
839 CGContextFillRect (cgc, orig_dst_rect);
841 Assert (cgc, "no CGC with 1-bit XCopyArea");
843 // then fill in a solid rectangle of the fg color, using the image as an
844 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
845 set_color (cgc, gc->gcv.foreground, gc->depth,
846 gc->gcv.alpha_allowed_p, YES);
847 CGContextClipToMask (cgc, dst_rect, cgi);
848 CGContextFillRect (cgc, dst_rect);
852 } else { // src depth > 1
856 // If either the src or dst rects did not lie within their drawables,
857 // then we have adjusted both the src and dst rects to account for
858 // the clipping; that means we need to first clear to the background,
859 // so that clipped bits end up in the bg color instead of simply not
863 set_color (cgc, gc->gcv.background, gc->depth,
864 gc->gcv.alpha_allowed_p, YES);
865 CGContextFillRect (cgc, orig_dst_rect);
869 // copy the CGImage onto the destination CGContext
870 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
871 CGContextDrawImage (cgc, dst_rect, cgi);
873 // No cgi means src == dst, and both are Windows.
875 # ifdef USE_BACKBUFFER
876 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
878 # else // !USE_BACKBUFFER
880 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
881 nsfrom.origin.y = src_rect.origin.y;
882 nsfrom.size.width = src_rect.size.width;
883 nsfrom.size.height = src_rect.size.height;
885 nsto.x = dst_rect.origin.x;
886 nsto.y = dst_rect.origin.y;
887 NSCopyBits (0, nsfrom, nsto);
888 # endif // !USE_BACKBUFFER
894 if (free_cgi_p) CGImageRelease (cgi);
896 if (releaseme) [releaseme release];
897 invalidate_drawable_cache (dst);
903 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
904 int src_x, int src_y,
905 unsigned width, int height,
906 int dest_x, int dest_y, unsigned long plane)
908 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
910 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
911 // not to white/black.
912 return XCopyArea (dpy, src, dest, gc,
913 src_x, src_y, width, height, dest_x, dest_y);
918 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
920 // when drawing a zero-length line, obey line-width and cap-style.
921 if (x1 == x2 && y1 == y2) {
922 int w = gc->gcv.line_width;
925 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
926 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
928 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
931 CGRect wr = d->frame;
933 p.x = wr.origin.x + x1;
934 p.y = wr.origin.y + wr.size.height - y1;
936 push_fg_gc (d, gc, NO);
938 CGContextRef cgc = d->cgc;
939 set_line_mode (cgc, &gc->gcv);
940 CGContextBeginPath (cgc);
941 CGContextMoveToPoint (cgc, p.x, p.y);
942 p.x = wr.origin.x + x2;
943 p.y = wr.origin.y + wr.size.height - y2;
944 CGContextAddLineToPoint (cgc, p.x, p.y);
945 CGContextStrokePath (cgc);
947 invalidate_drawable_cache (d);
952 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
957 CGRect wr = d->frame;
958 push_fg_gc (d, gc, NO);
960 CGContextRef cgc = d->cgc;
962 set_line_mode (cgc, &gc->gcv);
964 // if the first and last points coincide, use closepath to get
965 // the proper line-joining.
966 BOOL closed_p = (points[0].x == points[count-1].x &&
967 points[0].y == points[count-1].y);
968 if (closed_p) count--;
970 p.x = wr.origin.x + points->x;
971 p.y = wr.origin.y + wr.size.height - points->y;
973 CGContextBeginPath (cgc);
974 CGContextMoveToPoint (cgc, p.x, p.y);
975 for (i = 1; i < count; i++) {
976 if (mode == CoordModePrevious) {
980 p.x = wr.origin.x + points->x;
981 p.y = wr.origin.y + wr.size.height - points->y;
983 CGContextAddLineToPoint (cgc, p.x, p.y);
986 if (closed_p) CGContextClosePath (cgc);
987 CGContextStrokePath (cgc);
989 invalidate_drawable_cache (d);
995 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
998 CGRect wr = d->frame;
1000 CGContextRef cgc = d->cgc;
1002 push_fg_gc (d, gc, NO);
1003 set_line_mode (cgc, &gc->gcv);
1004 CGContextBeginPath (cgc);
1005 for (i = 0; i < count; i++) {
1006 CGContextMoveToPoint (cgc,
1007 wr.origin.x + segments->x1,
1008 wr.origin.y + wr.size.height - segments->y1);
1009 CGContextAddLineToPoint (cgc,
1010 wr.origin.x + segments->x2,
1011 wr.origin.y + wr.size.height - segments->y2);
1014 CGContextStrokePath (cgc);
1016 invalidate_drawable_cache (d);
1022 XClearWindow (Display *dpy, Window win)
1024 Assert (win && win->type == WINDOW, "not a window");
1025 CGRect wr = win->frame;
1026 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1030 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1032 Assert (w && w->type == WINDOW, "not a window");
1033 validate_pixel (pixel, 32, NO);
1034 w->window.background = pixel;
1039 draw_rect (Display *dpy, Drawable d, GC gc,
1040 int x, int y, unsigned int width, unsigned int height,
1041 BOOL foreground_p, BOOL fill_p)
1043 CGRect wr = d->frame;
1045 r.origin.x = wr.origin.x + x;
1046 r.origin.y = wr.origin.y + wr.size.height - y - height;
1047 r.size.width = width;
1048 r.size.height = height;
1052 push_fg_gc (d, gc, fill_p);
1054 push_bg_gc (d, gc, fill_p);
1057 CGContextRef cgc = d->cgc;
1059 CGContextFillRect (cgc, r);
1062 set_line_mode (cgc, &gc->gcv);
1063 CGContextStrokeRect (cgc, r);
1068 invalidate_drawable_cache (d);
1073 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1074 unsigned int width, unsigned int height)
1076 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1081 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1082 unsigned int width, unsigned int height)
1084 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1089 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1091 CGRect wr = d->frame;
1093 CGContextRef cgc = d->cgc;
1094 push_fg_gc (d, gc, YES);
1095 for (i = 0; i < n; i++) {
1097 r.origin.x = wr.origin.x + rects->x;
1098 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1099 r.size.width = rects->width;
1100 r.size.height = rects->height;
1101 CGContextFillRect (cgc, r);
1105 invalidate_drawable_cache (d);
1111 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1113 Assert (win && win->type == WINDOW, "not a window");
1114 CGContextRef cgc = win->cgc;
1115 set_color (cgc, win->window.background, 32, NO, YES);
1116 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1122 XFillPolygon (Display *dpy, Drawable d, GC gc,
1123 XPoint *points, int npoints, int shape, int mode)
1125 CGRect wr = d->frame;
1127 push_fg_gc (d, gc, YES);
1128 CGContextRef cgc = d->cgc;
1129 CGContextBeginPath (cgc);
1131 for (i = 0; i < npoints; i++) {
1132 if (i > 0 && mode == CoordModePrevious) {
1136 x = wr.origin.x + points[i].x;
1137 y = wr.origin.y + wr.size.height - points[i].y;
1141 CGContextMoveToPoint (cgc, x, y);
1143 CGContextAddLineToPoint (cgc, x, y);
1145 CGContextClosePath (cgc);
1146 if (gc->gcv.fill_rule == EvenOddRule)
1147 CGContextEOFillPath (cgc);
1149 CGContextFillPath (cgc);
1151 invalidate_drawable_cache (d);
1155 #define radians(DEG) ((DEG) * M_PI / 180.0)
1156 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1159 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1160 unsigned int width, unsigned int height, int angle1, int angle2,
1163 CGRect wr = d->frame;
1165 bound.origin.x = wr.origin.x + x;
1166 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1167 bound.size.width = width;
1168 bound.size.height = height;
1171 ctr.x = bound.origin.x + bound.size.width /2;
1172 ctr.y = bound.origin.y + bound.size.height/2;
1174 float r1 = radians (angle1/64.0);
1175 float r2 = radians (angle2/64.0) + r1;
1176 BOOL clockwise = angle2 < 0;
1177 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1179 push_fg_gc (d, gc, fill_p);
1181 CGContextRef cgc = d->cgc;
1182 CGContextBeginPath (cgc);
1184 CGContextSaveGState(cgc);
1185 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1186 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1188 CGContextMoveToPoint (cgc, 0, 0);
1190 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1191 CGContextRestoreGState (cgc); // restore before stroke, for line width
1194 CGContextClosePath (cgc); // for proper line joining
1197 CGContextFillPath (cgc);
1199 set_line_mode (cgc, &gc->gcv);
1200 CGContextStrokePath (cgc);
1204 invalidate_drawable_cache (d);
1209 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1210 unsigned int width, unsigned int height, int angle1, int angle2)
1212 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1216 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1217 unsigned int width, unsigned int height, int angle1, int angle2)
1219 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1223 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1226 for (i = 0; i < narcs; i++)
1227 draw_arc (dpy, d, gc,
1228 arcs[i].x, arcs[i].y,
1229 arcs[i].width, arcs[i].height,
1230 arcs[i].angle1, arcs[i].angle2,
1236 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1239 for (i = 0; i < narcs; i++)
1240 draw_arc (dpy, d, gc,
1241 arcs[i].x, arcs[i].y,
1242 arcs[i].width, arcs[i].height,
1243 arcs[i].angle1, arcs[i].angle2,
1250 gcv_defaults (XGCValues *gcv, int depth)
1252 memset (gcv, 0, sizeof(*gcv));
1253 gcv->function = GXcopy;
1254 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1255 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1256 gcv->line_width = 1;
1257 gcv->cap_style = CapNotLast;
1258 gcv->join_style = JoinMiter;
1259 gcv->fill_rule = EvenOddRule;
1261 gcv->alpha_allowed_p = NO;
1262 gcv->antialias_p = YES;
1266 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1269 Assert (gc && from, "no gc");
1270 if (!gc || !from) return;
1272 if (mask & GCFunction) gc->gcv.function = from->function;
1273 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1274 if (mask & GCBackground) gc->gcv.background = from->background;
1275 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1276 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1277 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1278 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1279 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1280 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1281 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1283 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1284 if (mask & GCFont) XSetFont (0, gc, from->font);
1286 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1287 gc->gcv.alpha_allowed_p);
1288 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1289 gc->gcv.alpha_allowed_p);
1291 Assert ((! (mask & (GCLineStyle |
1298 GCGraphicsExposures |
1302 "unimplemented gcvalues mask");
1307 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1309 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1310 if (d->type == WINDOW) {
1312 } else { /* (d->type == PIXMAP) */
1313 gc->depth = d->pixmap.depth;
1316 gcv_defaults (&gc->gcv, gc->depth);
1317 set_gcv (gc, xgcv, mask);
1322 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1324 set_gcv (gc, gcv, mask);
1330 XFreeGC (Display *dpy, GC gc)
1333 XUnloadFont (dpy, gc->gcv.font);
1335 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1337 if (gc->gcv.clip_mask) {
1338 XFreePixmap (dpy, gc->gcv.clip_mask);
1339 CGImageRelease (gc->clip_mask);
1347 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1349 Assert (w && w->type == WINDOW, "not a window");
1350 memset (xgwa, 0, sizeof(*xgwa));
1351 xgwa->x = w->frame.origin.x;
1352 xgwa->y = w->frame.origin.y;
1353 xgwa->width = w->frame.size.width;
1354 xgwa->height = w->frame.size.height;
1356 xgwa->screen = dpy->screen;
1357 xgwa->visual = dpy->screen->visual;
1362 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1363 int *x_ret, int *y_ret,
1364 unsigned int *w_ret, unsigned int *h_ret,
1365 unsigned int *bw_ret, unsigned int *d_ret)
1367 *x_ret = d->frame.origin.x;
1368 *y_ret = d->frame.origin.y;
1369 *w_ret = d->frame.size.width;
1370 *h_ret = d->frame.size.height;
1371 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1372 *root_ret = RootWindow (dpy, 0);
1379 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1381 // store 32 bit ARGB in the pixel field.
1382 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1383 color->pixel = (uint32_t)
1385 (((color->red >> 8) & 0xFF) << 16) |
1386 (((color->green >> 8) & 0xFF) << 8) |
1387 (((color->blue >> 8) & 0xFF) ));
1392 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1393 unsigned long *pmret, unsigned int npl,
1394 unsigned long *pxret, unsigned int npx)
1400 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1402 Assert(0, "XStoreColors called");
1407 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1409 Assert(0, "XStoreColor called");
1414 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1415 unsigned long planes)
1421 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1423 unsigned char r=0, g=0, b=0;
1424 if (*spec == '#' && strlen(spec) == 7) {
1425 static unsigned const char hex[] = { // yeah yeah, shoot me.
1426 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,
1427 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,
1428 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,
1429 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,
1430 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,
1431 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,
1432 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,
1433 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};
1434 r = (hex[spec[1]] << 4) | hex[spec[2]];
1435 g = (hex[spec[3]] << 4) | hex[spec[4]];
1436 b = (hex[spec[5]] << 4) | hex[spec[6]];
1437 } else if (!strcasecmp(spec,"black")) {
1439 } else if (!strcasecmp(spec,"white")) {
1441 } else if (!strcasecmp(spec,"red")) {
1443 } else if (!strcasecmp(spec,"green")) {
1445 } else if (!strcasecmp(spec,"blue")) {
1447 } else if (!strcasecmp(spec,"cyan")) {
1449 } else if (!strcasecmp(spec,"magenta")) {
1451 } else if (!strcasecmp(spec,"yellow")) {
1457 ret->red = (r << 8) | r;
1458 ret->green = (g << 8) | g;
1459 ret->blue = (b << 8) | b;
1460 ret->flags = DoRed|DoGreen|DoBlue;
1465 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1466 XColor *screen_ret, XColor *exact_ret)
1468 if (! XParseColor (dpy, cmap, name, screen_ret))
1470 *exact_ret = *screen_ret;
1471 return XAllocColor (dpy, cmap, screen_ret);
1475 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1477 validate_pixel (color->pixel, 32, NO);
1478 unsigned char r = ((color->pixel >> 16) & 0xFF);
1479 unsigned char g = ((color->pixel >> 8) & 0xFF);
1480 unsigned char b = ((color->pixel ) & 0xFF);
1481 color->red = (r << 8) | r;
1482 color->green = (g << 8) | g;
1483 color->blue = (b << 8) | b;
1484 color->flags = DoRed|DoGreen|DoBlue;
1489 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1492 for (i = 0; i < n; i++)
1493 XQueryColor (dpy, cmap, &c[i]);
1498 static unsigned long
1499 ximage_getpixel_1 (XImage *ximage, int x, int y)
1501 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1505 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1508 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1510 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1515 static unsigned long
1516 ximage_getpixel_32 (XImage *ximage, int x, int y)
1518 return ((unsigned long)
1519 *((uint32_t *) ximage->data +
1520 (y * (ximage->bytes_per_line >> 2)) +
1525 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1527 *((uint32_t *) ximage->data +
1528 (y * (ximage->bytes_per_line >> 2)) +
1529 x) = (uint32_t) pixel;
1535 XInitImage (XImage *ximage)
1537 if (!ximage->bytes_per_line)
1538 ximage->bytes_per_line = (ximage->depth == 1
1539 ? (ximage->width + 7) / 8
1540 : ximage->width * 4);
1542 if (ximage->depth == 1) {
1543 ximage->f.put_pixel = ximage_putpixel_1;
1544 ximage->f.get_pixel = ximage_getpixel_1;
1545 } else if (ximage->depth == 32 || ximage->depth == 24) {
1546 ximage->f.put_pixel = ximage_putpixel_32;
1547 ximage->f.get_pixel = ximage_getpixel_32;
1549 Assert (0, "unknown depth");
1556 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1557 int format, int offset, char *data,
1558 unsigned int width, unsigned int height,
1559 int bitmap_pad, int bytes_per_line)
1561 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1562 ximage->width = width;
1563 ximage->height = height;
1564 ximage->format = format;
1565 ximage->data = data;
1566 ximage->bitmap_unit = 8;
1567 ximage->byte_order = MSBFirst;
1568 ximage->bitmap_bit_order = ximage->byte_order;
1569 ximage->bitmap_pad = bitmap_pad;
1570 ximage->depth = depth;
1571 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1572 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1573 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1574 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1575 ximage->bytes_per_line = bytes_per_line;
1577 XInitImage (ximage);
1582 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1584 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1585 w, h, from->bitmap_pad, 0);
1586 to->data = (char *) malloc (h * to->bytes_per_line);
1588 if (x >= from->width)
1590 else if (x+w > from->width)
1591 w = from->width - x;
1593 if (y >= from->height)
1595 else if (y+h > from->height)
1596 h = from->height - y;
1599 for (ty = 0; ty < h; ty++)
1600 for (tx = 0; tx < w; tx++)
1601 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1606 XPixmapFormatValues *
1607 XListPixmapFormats (Display *dpy, int *n_ret)
1609 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1611 ret[0].bits_per_pixel = 32;
1612 ret[0].scanline_pad = 8;
1614 ret[1].bits_per_pixel = 1;
1615 ret[1].scanline_pad = 8;
1622 XGetPixel (XImage *ximage, int x, int y)
1624 return ximage->f.get_pixel (ximage, x, y);
1629 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1631 return ximage->f.put_pixel (ximage, x, y, pixel);
1635 XDestroyImage (XImage *ximage)
1637 if (ximage->data) free (ximage->data);
1644 flipbits (unsigned const char *in, unsigned char *out, int length)
1646 static const unsigned char table[256] = {
1647 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1648 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1649 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1650 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1651 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1652 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1653 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1654 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1655 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1656 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1657 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1658 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1659 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1660 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1661 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1662 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1663 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1664 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1665 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1666 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1667 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1668 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1669 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1670 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1671 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1672 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1673 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1674 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1675 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1676 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1677 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1678 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1680 while (--length > 0)
1681 *out++ = table[*in++];
1686 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1687 int src_x, int src_y, int dest_x, int dest_y,
1688 unsigned int w, unsigned int h)
1690 CGRect wr = d->frame;
1692 Assert (gc, "no GC");
1693 Assert ((w < 65535), "improbably large width");
1694 Assert ((h < 65535), "improbably large height");
1695 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1696 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1697 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1698 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1700 // Clip width and height to the bounds of the Drawable
1702 if (dest_x + w > wr.size.width) {
1703 if (dest_x > wr.size.width)
1705 w = wr.size.width - dest_x;
1707 if (dest_y + h > wr.size.height) {
1708 if (dest_y > wr.size.height)
1710 h = wr.size.height - dest_y;
1712 if (w <= 0 || h <= 0)
1715 // Clip width and height to the bounds of the XImage
1717 if (src_x + w > ximage->width) {
1718 if (src_x > ximage->width)
1720 w = ximage->width - src_x;
1722 if (src_y + h > ximage->height) {
1723 if (src_y > ximage->height)
1725 h = ximage->height - src_y;
1727 if (w <= 0 || h <= 0)
1730 CGContextRef cgc = d->cgc;
1732 if (gc->gcv.function == GXset ||
1733 gc->gcv.function == GXclear) {
1734 // "set" and "clear" are dumb drawing modes that ignore the source
1735 // bits and just draw solid rectangles.
1736 set_color (cgc, (gc->gcv.function == GXset
1737 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1738 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1739 gc->depth, gc->gcv.alpha_allowed_p, YES);
1740 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1744 int bpl = ximage->bytes_per_line;
1745 int bpp = ximage->bits_per_pixel;
1746 int bsize = bpl * h;
1747 char *data = ximage->data;
1750 r.origin.x = wr.origin.x + dest_x;
1751 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1757 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1758 to create a CGImage from a sub-rectagle of the XImage.
1760 data += (src_y * bpl) + (src_x * 4);
1761 CGDataProviderRef prov =
1762 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1764 CGImageRef cgi = CGImageCreate (w, h,
1767 /* Need this for XPMs to have the right
1768 colors, e.g. the logo in "maze". */
1769 (kCGImageAlphaNoneSkipFirst |
1770 kCGBitmapByteOrder32Host),
1772 NULL, /* decode[] */
1773 NO, /* interpolate */
1774 kCGRenderingIntentDefault);
1775 CGDataProviderRelease (prov);
1776 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1777 CGContextDrawImage (cgc, r, cgi);
1778 CGImageRelease (cgi);
1780 } else { // (bpp == 1)
1782 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1784 #### However, the bit order within a byte in a 1bpp XImage is
1785 the wrong way around from what Quartz expects, so first we
1786 have to copy the data to reverse it. Shit! Maybe it
1787 would be worthwhile to go through the hacks and #ifdef
1788 each one that diddles 1bpp XImage->data directly...
1790 Assert ((src_x % 8) == 0,
1791 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1793 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1794 unsigned char *flipped = (unsigned char *) malloc (bsize);
1796 flipbits ((unsigned char *) data, flipped, bsize);
1798 CGDataProviderRef prov =
1799 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1800 CGImageRef mask = CGImageMaskCreate (w, h,
1803 NULL, /* decode[] */
1804 NO); /* interpolate */
1805 push_fg_gc (d, gc, YES);
1807 CGContextFillRect (cgc, r); // foreground color
1808 CGContextClipToMask (cgc, r, mask);
1809 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1810 CGContextFillRect (cgc, r); // background color
1814 CGDataProviderRelease (prov);
1815 CGImageRelease (mask);
1818 invalidate_drawable_cache (d);
1825 XGetImage (Display *dpy, Drawable d, int x, int y,
1826 unsigned int width, unsigned int height,
1827 unsigned long plane_mask, int format)
1829 const unsigned char *data = 0;
1830 int depth, ibpp, ibpl, alpha_first_p;
1831 # ifndef USE_BACKBUFFER
1832 NSBitmapImageRep *bm = 0;
1835 Assert ((width < 65535), "improbably large width");
1836 Assert ((height < 65535), "improbably large height");
1837 Assert ((x < 65535 && x > -65535), "improbably large x");
1838 Assert ((y < 65535 && y > -65535), "improbably large y");
1840 CGContextRef cgc = d->cgc;
1842 #ifndef USE_BACKBUFFER
1843 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
1844 if (d->type == PIXMAP)
1847 depth = (d->type == PIXMAP
1850 // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst.
1851 // If it's an iPhone window, it's the other way around.
1852 alpha_first_p = (d->type == PIXMAP);
1853 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
1854 ibpl = CGBitmapContextGetBytesPerRow (cgc);
1855 data = CGBitmapContextGetData (cgc);
1856 Assert (data, "CGBitmapContextGetData failed");
1858 # ifndef USE_BACKBUFFER
1859 } else { /* (d->type == WINDOW) */
1861 // get the bits (desired sub-rectangle) out of the NSView
1863 nsfrom.origin.x = x;
1864 nsfrom.origin.y = y;
1865 nsfrom.size.width = width;
1866 nsfrom.size.height = height;
1867 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
1869 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1870 ibpp = [bm bitsPerPixel];
1871 ibpl = [bm bytesPerRow];
1872 data = [bm bitmapData];
1873 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1874 # endif // !USE_BACKBUFFER
1877 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1878 data += (y * ibpl) + (x * (ibpp/8));
1880 format = (depth == 1 ? XYPixmap : ZPixmap);
1881 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1883 image->data = (char *) malloc (height * image->bytes_per_line);
1885 int obpl = image->bytes_per_line;
1887 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1888 means that on Intel it is BGRA when viewed by bytes (And BGR
1889 when using 24bpp packing).
1891 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1892 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1893 indicator of this latest kink.
1897 const unsigned char *iline = data;
1898 for (yy = 0; yy < height; yy++) {
1900 const unsigned char *iline2 = iline;
1901 for (xx = 0; xx < width; xx++) {
1903 iline2++; // ignore R or A or A or B
1904 iline2++; // ignore G or B or R or G
1905 unsigned char r = *iline2++; // use B or G or G or R
1906 if (ibpp == 32) iline2++; // ignore A or R or B or A
1908 XPutPixel (image, xx, yy, (r ? 1 : 0));
1913 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1914 const unsigned char *iline = data;
1915 unsigned char *oline = (unsigned char *) image->data;
1916 for (yy = 0; yy < height; yy++) {
1918 const unsigned char *iline2 = iline;
1919 unsigned char *oline2 = oline;
1921 if (alpha_first_p) // ARGB
1922 for (xx = 0; xx < width; xx++) {
1923 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1924 unsigned char r = *iline2++;
1925 unsigned char g = *iline2++;
1926 unsigned char b = *iline2++;
1927 uint32_t pixel = ((a << 24) |
1931 *((uint32_t *) oline2) = pixel;
1935 for (xx = 0; xx < width; xx++) {
1936 unsigned char r = *iline2++;
1937 unsigned char g = *iline2++;
1938 unsigned char b = *iline2++;
1939 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1940 uint32_t pixel = ((a << 24) |
1944 *((uint32_t *) oline2) = pixel;
1953 # ifndef USE_BACKBUFFER
1954 if (bm) [bm release];
1962 /* Returns a transformation matrix to do rotation as per the provided
1963 EXIF "Orientation" value.
1965 static CGAffineTransform
1966 exif_rotate (int rot, CGSize rect)
1968 CGAffineTransform trans = CGAffineTransformIdentity;
1970 case 2: // flip horizontal
1971 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1972 trans = CGAffineTransformScale (trans, -1, 1);
1975 case 3: // rotate 180
1976 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1977 trans = CGAffineTransformRotate (trans, M_PI);
1980 case 4: // flip vertical
1981 trans = CGAffineTransformMakeTranslation (0, rect.height);
1982 trans = CGAffineTransformScale (trans, 1, -1);
1985 case 5: // transpose (UL-to-LR axis)
1986 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1987 trans = CGAffineTransformScale (trans, -1, 1);
1988 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1991 case 6: // rotate 90
1992 trans = CGAffineTransformMakeTranslation (0, rect.width);
1993 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1996 case 7: // transverse (UR-to-LL axis)
1997 trans = CGAffineTransformMakeScale (-1, 1);
1998 trans = CGAffineTransformRotate (trans, M_PI / 2);
2001 case 8: // rotate 270
2002 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2003 trans = CGAffineTransformRotate (trans, M_PI / 2);
2015 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2016 Bool nsimg_p, void *img_arg,
2017 XRectangle *geom_ret, int exif_rotation)
2021 CGImageSourceRef cgsrc;
2022 # endif // USE_IPHONE
2025 CGContextRef cgc = d->cgc;
2029 NSImage *nsimg = (NSImage *) img_arg;
2030 imgr = [nsimg size];
2033 // convert the NSImage to a CGImage via the toll-free-bridging
2034 // of NSData and CFData...
2036 NSData *nsdata = [NSBitmapImageRep
2037 TIFFRepresentationOfImageRepsInArray:
2038 [nsimg representations]];
2039 CFDataRef cfdata = (CFDataRef) nsdata;
2040 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2041 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2042 # else // USE_IPHONE
2043 cgi = nsimg.CGImage;
2044 # endif // USE_IPHONE
2047 cgi = (CGImageRef) img_arg;
2048 imgr.width = CGImageGetWidth (cgi);
2049 imgr.height = CGImageGetHeight (cgi);
2052 Bool rot_p = (exif_rotation >= 5);
2055 imgr = NSMakeSize (imgr.height, imgr.width);
2057 CGRect winr = d->frame;
2058 float rw = winr.size.width / imgr.width;
2059 float rh = winr.size.height / imgr.height;
2060 float r = (rw < rh ? rw : rh);
2063 dst.size.width = imgr.width * r;
2064 dst.size.height = imgr.height * r;
2065 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2066 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2068 dst2.origin.x = dst2.origin.y = 0;
2070 dst2.size.width = dst.size.height;
2071 dst2.size.height = dst.size.width;
2073 dst2.size = dst.size;
2076 // Clear the part not covered by the image to background or black.
2078 if (d->type == WINDOW)
2079 XClearWindow (dpy, d);
2081 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2082 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2085 CGAffineTransform trans =
2086 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2088 CGContextSaveGState (cgc);
2089 CGContextConcatCTM (cgc,
2090 CGAffineTransformMakeTranslation (dst.origin.x,
2092 CGContextConcatCTM (cgc, trans);
2093 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2094 CGContextDrawImage (cgc, dst2, cgi);
2095 CGContextRestoreGState (cgc);
2100 CGImageRelease (cgi);
2102 # endif // USE_IPHONE
2105 geom_ret->x = dst.origin.x;
2106 geom_ret->y = dst.origin.y;
2107 geom_ret->width = dst.size.width;
2108 geom_ret->height = dst.size.height;
2111 invalidate_drawable_cache (d);
2117 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2119 unsigned int w, unsigned int h,
2120 unsigned long fg, unsigned int bg,
2123 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2124 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2125 (char *) data, w, h, 0, 0);
2127 gcv.foreground = fg;
2128 gcv.background = bg;
2129 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2130 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2133 XDestroyImage (image);
2138 XCreatePixmap (Display *dpy, Drawable d,
2139 unsigned int width, unsigned int height, unsigned int depth)
2141 char *data = (char *) malloc (width * height * 4);
2142 if (! data) return 0;
2144 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2146 p->frame.size.width = width;
2147 p->frame.size.height = height;
2148 p->pixmap.depth = depth;
2149 p->pixmap.cgc_buffer = data;
2151 /* Quartz doesn't have a 1bpp image type.
2152 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2153 don't support that! So we always use 32bpp, regardless of depth. */
2155 p->cgc = CGBitmapContextCreate (data, width, height,
2156 8, /* bits per component */
2157 width * 4, /* bpl */
2159 // Without this, it returns 0...
2160 kCGImageAlphaNoneSkipFirst
2162 Assert (p->cgc, "could not create CGBitmapContext");
2168 XFreePixmap (Display *d, Pixmap p)
2170 Assert (p && p->type == PIXMAP, "not a pixmap");
2171 invalidate_drawable_cache (p);
2172 CGContextRelease (p->cgc);
2173 if (p->pixmap.cgc_buffer)
2174 free (p->pixmap.cgc_buffer);
2181 copy_pixmap (Display *dpy, Pixmap p)
2184 Assert (p->type == PIXMAP, "not a pixmap");
2186 int width = p->frame.size.width;
2187 int height = p->frame.size.height;
2188 char *data = (char *) malloc (width * height * 4);
2189 if (! data) return 0;
2191 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2193 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2196 p2->pixmap.cgc_buffer = data;
2197 p2->cgc = CGBitmapContextCreate (data, width, height,
2198 8, /* bits per component */
2199 width * 4, /* bpl */
2201 // Without this, it returns 0...
2202 kCGImageAlphaNoneSkipFirst
2204 Assert (p2->cgc, "could not create CGBitmapContext");
2210 /* Font metric terminology, as used by X11:
2212 "lbearing" is the distance from the logical origin to the leftmost pixel.
2213 If a character's ink extends to the left of the origin, it is negative.
2215 "rbearing" is the distance from the logical origin to the rightmost pixel.
2217 "descent" is the distance from the logical origin to the bottommost pixel.
2218 For characters with descenders, it is negative.
2220 "ascent" is the distance from the logical origin to the topmost pixel.
2221 It is the number of pixels above the baseline.
2223 "width" is the distance from the logical origin to the position where
2224 the logical origin of the next character should be placed.
2226 If "rbearing" is greater than "width", then this character overlaps the
2227 following character. If smaller, then there is trailing blank space.
2231 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2234 query_font (Font fid)
2236 if (!fid || !fid->nsfont) {
2237 Assert (0, "no NSFont in fid");
2240 if (![fid->nsfont fontName]) {
2241 Assert(0, @"broken NSFont in fid");
2248 XFontStruct *f = &fid->metrics;
2249 XCharStruct *min = &f->min_bounds;
2250 XCharStruct *max = &f->max_bounds;
2252 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2255 f->min_char_or_byte2 = first;
2256 f->max_char_or_byte2 = last;
2257 f->default_char = 'M';
2258 f->ascent = CEIL ([fid->nsfont ascender]);
2259 f->descent = -CEIL ([fid->nsfont descender]);
2261 min->width = 255; // set to smaller values in the loop
2264 min->lbearing = 255;
2265 min->rbearing = 255;
2267 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2271 NSBezierPath *bpath = [NSBezierPath bezierPath];
2272 # else // USE_IPHONE
2274 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2275 [fid->nsfont pointSize],
2277 Assert (ctfont, @"no CTFontRef for UIFont");
2278 # endif // USE_IPHONE
2280 for (i = first; i <= last; i++) {
2281 unsigned char str[2];
2285 NSString *nsstr = [NSString stringWithCString:(char *) str
2286 encoding:NSISOLatin1StringEncoding];
2287 NSPoint advancement = { 0, };
2288 NSRect bbox = {{ 0, }, };
2292 /* I can't believe we have to go through this bullshit just to
2293 convert a 'char' to an NSGlyph!!
2295 You might think that we could do
2296 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2297 but that doesn't work; my guess is that glyphWithName expects
2298 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2302 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2303 [ts setFont:fid->nsfont];
2304 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2306 /* Without this, the layout manager ends up on a queue somewhere and is
2307 referenced again after we return to the command loop. Since we don't
2308 use this layout manager again, by that time it may have been garbage
2309 collected, and we crash. Setting this seems to cause `lm' to no
2310 longer be referenced once we exit this block. */
2311 [lm setBackgroundLayoutEnabled:NO];
2313 NSTextContainer *tc = [[NSTextContainer alloc] init];
2314 [lm addTextContainer:tc];
2315 [tc release]; // lm retains tc
2316 [ts addLayoutManager:lm];
2317 [lm release]; // ts retains lm
2318 glyph = [lm glyphAtIndex:0];
2322 /* Compute the bounding box and advancement by converting the glyph
2323 to a bezier path. There appears to be *no other way* to find out
2324 the bounding box of a character: [NSFont boundingRectForGlyph] and
2325 [NSString sizeWithAttributes] both return an advancement-sized
2326 rectangle, not a rectangle completely enclosing the glyph's ink.
2328 advancement.x = advancement.y = 0;
2329 [bpath removeAllPoints];
2330 [bpath moveToPoint:advancement];
2331 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2332 advancement = [bpath currentPoint];
2333 bbox = [bpath bounds];
2335 # else // USE_IPHONE
2337 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2338 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2339 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2340 width of the character and the ascent of the font.
2342 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2343 the CoreText library, but there's no non-CoreText way to turn a
2344 unichar into a CGGlyph.
2346 UniChar uchar = [nsstr characterAtIndex: 0];
2347 CGGlyph cgglyph = 0;
2349 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2351 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2352 kCTFontDefaultOrientation,
2354 CGSize adv = { 0, };
2355 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2357 advancement.x = adv.width;
2358 advancement.y = adv.height;
2360 // Seems to be clipping by a pixel or two. Add a margin to be safe.
2363 bbox.size.width += 4;
2364 bbox.size.height += 4;
2366 # endif // USE_IPHONE
2368 /* Now that we know the advancement and bounding box, we can compute
2369 the lbearing and rbearing.
2371 XCharStruct *cs = &f->per_char[i-first];
2373 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2374 cs->descent = CEIL(-bbox.origin.y);
2375 cs->lbearing = CEIL (bbox.origin.x);
2376 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2377 cs->width = CEIL (advancement.x);
2379 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2381 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2384 max->width = MAX (max->width, cs->width);
2385 max->ascent = MAX (max->ascent, cs->ascent);
2386 max->descent = MAX (max->descent, cs->descent);
2387 max->lbearing = MAX (max->lbearing, cs->lbearing);
2388 max->rbearing = MAX (max->rbearing, cs->rbearing);
2390 min->width = MIN (min->width, cs->width);
2391 min->ascent = MIN (min->ascent, cs->ascent);
2392 min->descent = MIN (min->descent, cs->descent);
2393 min->lbearing = MIN (min->lbearing, cs->lbearing);
2394 min->rbearing = MIN (min->rbearing, cs->rbearing);
2399 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2400 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2401 i, i, cs->width, cs->lbearing, cs->rbearing,
2402 cs->ascent, cs->descent,
2403 (int) bbox.size.width, (int) bbox.size.height,
2404 (int) bbox.origin.x, (int) bbox.origin.y,
2405 (int) advancement.x, (int) advancement.y);
2415 // Since 'Font' includes the metrics, this just makes a copy of that.
2418 XQueryFont (Display *dpy, Font fid)
2421 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2424 // copy XCharStruct array
2425 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2426 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2427 memcpy (f->per_char, fid->metrics.per_char,
2428 size * sizeof (XCharStruct));
2435 copy_font (Font fid)
2437 // copy 'Font' struct
2438 Font fid2 = (Font) malloc (sizeof(*fid2));
2441 // copy XCharStruct array
2442 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2443 fid2->metrics.per_char = (XCharStruct *)
2444 malloc ((size + 2) * sizeof (XCharStruct));
2445 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2446 size * sizeof (XCharStruct));
2448 // copy the other pointers
2449 fid2->ps_name = strdup (fid->ps_name);
2450 // [fid2->nsfont retain];
2451 fid2->metrics.fid = fid2;
2458 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2461 Assert (size > 0, "zero font size");
2466 // "Monaco" only exists in plain.
2467 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2469 if (bold && ital) name = "Courier-BoldOblique";
2470 else if (bold) name = "Courier-Bold";
2471 else if (ital) name = "Courier-Oblique";
2472 else name = "Courier";
2476 // "Georgia" looks better than "Times".
2478 if (bold && ital) name = "Georgia-BoldItalic";
2479 else if (bold) name = "Georgia-Bold";
2480 else if (ital) name = "Georgia-Italic";
2481 else name = "Georgia";
2485 // "Geneva" only exists in plain.
2486 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2487 // "Verdana" renders smoother than "Helvetica" for some reason.
2489 if (bold && ital) name = "Verdana-BoldItalic";
2490 else if (bold) name = "Verdana-Bold";
2491 else if (ital) name = "Verdana-Italic";
2492 else name = "Verdana";
2495 NSString *nsname = [NSString stringWithCString:name
2496 encoding:NSUTF8StringEncoding];
2497 NSFont *f = [NSFont fontWithName:nsname size:size];
2499 *name_ret = strdup(name);
2504 try_native_font (const char *name, float scale,
2505 char **name_ret, float *size_ret)
2507 if (!name) return 0;
2508 const char *spc = strrchr (name, ' ');
2511 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2514 if (size <= 4) return 0;
2518 char *name2 = strdup (name);
2519 name2[strlen(name2) - strlen(spc)] = 0;
2520 NSString *nsname = [NSString stringWithCString:name2
2521 encoding:NSUTF8StringEncoding];
2522 NSFont *f = [NSFont fontWithName:nsname size:size];
2534 /* Returns a random font in the given size and face.
2537 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2540 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2541 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2542 NSArray *fonts = [[NSFontManager sharedFontManager]
2543 availableFontNamesWithTraits:mask];
2544 if (!fonts) return 0;
2546 int n = [fonts count];
2547 if (n <= 0) return 0;
2550 for (j = 0; j < n; j++) {
2551 int i = random() % n;
2552 NSString *name = [fonts objectAtIndex:i];
2553 NSFont *f = [NSFont fontWithName:name size:size];
2556 /* Don't use this font if it (probably) doesn't include ASCII characters.
2558 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2559 if (! (enc == NSUTF8StringEncoding ||
2560 enc == NSISOLatin1StringEncoding ||
2561 enc == NSNonLossyASCIIStringEncoding ||
2562 enc == NSISOLatin2StringEncoding ||
2563 enc == NSUnicodeStringEncoding ||
2564 enc == NSWindowsCP1250StringEncoding ||
2565 enc == NSWindowsCP1252StringEncoding ||
2566 enc == NSMacOSRomanStringEncoding)) {
2567 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2570 // NSLog(@"using \"%@\": %d", name, enc);
2572 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2576 // None of the fonts support ASCII?
2579 # else // USE_IPHONE
2581 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2582 NSArray *families = [UIFont familyNames];
2583 NSMutableDictionary *famdict = [NSMutableDictionary
2584 dictionaryWithCapacity:100];
2585 NSObject *y = [NSNumber numberWithBool:YES];
2586 for (NSString *name in families) {
2587 // There are many dups in the families array -- uniquify it.
2588 [famdict setValue:y forKey:name];
2591 for (NSString *name in famdict) {
2592 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2595 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2598 BOOL bb = MATCH(@"Bold");
2599 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2601 if (!bold != !bb) continue;
2602 if (!ital != !ii) continue;
2604 /* Check if it can do ASCII. No good way to accomplish this!
2605 These are fonts present in iPhone Simulator as of June 2012
2606 that don't include ASCII.
2608 if (MATCH(@"AppleGothic") || // Korean
2609 MATCH(@"Dingbats") || // Dingbats
2610 MATCH(@"Emoji") || // Emoticons
2611 MATCH(@"Geeza") || // Arabic
2612 MATCH(@"Hebrew") || // Hebrew
2613 MATCH(@"HiraKaku") || // Japanese
2614 MATCH(@"HiraMin") || // Japanese
2615 MATCH(@"Kailasa") || // Tibetan
2616 MATCH(@"Ornaments") || // Dingbats
2617 MATCH(@"STHeiti") // Chinese
2621 [fonts addObject:fn];
2626 if (! [fonts count]) return 0; // Nothing suitable?
2628 int i = random() % [fonts count];
2629 NSString *name = [fonts objectAtIndex:i];
2630 UIFont *ff = [UIFont fontWithName:name size:size];
2631 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2635 # endif // USE_IPHONE
2640 try_xlfd_font (const char *name, float scale,
2641 char **name_ret, float *size_ret)
2652 const char *s = (name ? name : "");
2654 while (*s && (*s == '*' || *s == '-'))
2657 while (*s2 && (*s2 != '*' && *s2 != '-'))
2663 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2664 else if (CMP ("random")) rand = YES;
2665 else if (CMP ("bold")) bold = YES;
2666 else if (CMP ("i")) ital = YES;
2667 else if (CMP ("o")) ital = YES;
2668 else if (CMP ("courier")) fixed = YES;
2669 else if (CMP ("fixed")) fixed = YES;
2670 else if (CMP ("m")) fixed = YES;
2671 else if (CMP ("times")) serif = YES;
2672 else if (CMP ("6x10")) fixed = YES, size = 8;
2673 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2674 else if (CMP ("9x15")) fixed = YES, size = 12;
2675 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2676 else if (CMP ("vga")) fixed = YES, size = 12;
2677 else if (CMP ("console")) fixed = YES, size = 12;
2678 else if (CMP ("gallant")) fixed = YES, size = 12;
2680 else if (size == 0) {
2682 if (1 == sscanf (s, " %d ", &n))
2689 if (size < 6 || size > 1000)
2695 nsfont = random_font (bold, ital, size, &ps_name);
2698 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2700 // if that didn't work, turn off attibutes until it does
2701 // (e.g., there is no "Monaco-Bold".)
2703 if (!nsfont && serif) {
2705 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2707 if (!nsfont && ital) {
2709 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2711 if (!nsfont && bold) {
2713 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2715 if (!nsfont && fixed) {
2717 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2721 *name_ret = ps_name;
2731 XLoadFont (Display *dpy, const char *name)
2733 Font fid = (Font) calloc (1, sizeof(*fid));
2738 // Scale up fonts on Retina displays.
2739 scale = dpy->main_window->window.view.contentScaleFactor;
2742 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2744 if (!fid->nsfont && name &&
2745 strchr (name, ' ') &&
2746 !strchr (name, '*')) {
2747 // If name contains a space but no stars, it is a native font spec --
2748 // return NULL so that we know it really didn't exist. Else, it is an
2749 // XLFD font, so keep trying.
2750 XUnloadFont (dpy, fid);
2755 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2757 // We should never return NULL for XLFD fonts.
2759 Assert (0, "no font");
2762 CFRetain (fid->nsfont); // needed for garbage collection?
2764 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2773 XLoadQueryFont (Display *dpy, const char *name)
2775 Font fid = XLoadFont (dpy, name);
2777 return XQueryFont (dpy, fid);
2781 XUnloadFont (Display *dpy, Font fid)
2784 free (fid->ps_name);
2785 if (fid->metrics.per_char)
2786 free (fid->metrics.per_char);
2788 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2789 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2790 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2791 // They're probably not very big...
2793 // [fid->nsfont release];
2794 // CFRelease (fid->nsfont);
2801 XFreeFontInfo (char **names, XFontStruct *info, int n)
2805 for (i = 0; i < n; i++)
2806 if (names[i]) free (names[i]);
2810 for (i = 0; i < n; i++)
2811 if (info[i].per_char)
2812 free (info[i].per_char);
2819 XFreeFont (Display *dpy, XFontStruct *f)
2822 XFreeFontInfo (0, f, 1);
2823 XUnloadFont (dpy, fid);
2829 XSetFont (Display *dpy, GC gc, Font fid)
2832 XUnloadFont (dpy, gc->gcv.font);
2833 gc->gcv.font = copy_font (fid);
2834 [gc->gcv.font->nsfont retain];
2835 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2840 XTextExtents (XFontStruct *f, const char *s, int length,
2841 int *dir_ret, int *ascent_ret, int *descent_ret,
2844 memset (cs, 0, sizeof(*cs));
2846 for (i = 0; i < length; i++) {
2847 unsigned char c = (unsigned char) s[i];
2848 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2849 c = f->default_char;
2850 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2854 cs->ascent = MAX (cs->ascent, cc->ascent);
2855 cs->descent = MAX (cs->descent, cc->descent);
2856 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2857 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2858 cs->width += cc->width;
2862 *ascent_ret = f->ascent;
2863 *descent_ret = f->descent;
2868 XTextWidth (XFontStruct *f, const char *s, int length)
2870 int ascent, descent, dir;
2872 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2878 set_font (Display *dpy, CGContextRef cgc, GC gc)
2880 Font font = gc->gcv.font;
2882 font = XLoadFont (dpy, 0);
2883 gc->gcv.font = font;
2884 [gc->gcv.font->nsfont retain];
2885 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
2887 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2888 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2893 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2894 const char *str, int len, BOOL clear_background_p)
2896 if (clear_background_p) {
2897 int ascent, descent, dir;
2899 XTextExtents (&gc->gcv.font->metrics, str, len,
2900 &dir, &ascent, &descent, &cs);
2901 draw_rect (dpy, d, gc,
2902 x + MIN (0, cs.lbearing),
2903 y - MAX (0, ascent),
2904 MAX (MAX (0, cs.rbearing) -
2905 MIN (0, cs.lbearing),
2907 MAX (0, ascent) + MAX (0, descent),
2911 CGRect wr = d->frame;
2914 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2915 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2918 CGContextRef cgc = d->cgc;
2919 push_fg_gc (d, gc, YES);
2920 set_font (dpy, cgc, gc);
2922 CGContextSetTextDrawingMode (cgc, kCGTextFill);
2923 if (gc->gcv.antialias_p)
2924 CGContextSetShouldAntialias (cgc, YES);
2925 CGContextShowTextAtPoint (cgc,
2927 wr.origin.y + wr.size.height - y,
2936 unsigned long argb = gc->gcv.foreground;
2937 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2938 float a = ((argb >> 24) & 0xFF) / 255.0;
2939 float r = ((argb >> 16) & 0xFF) / 255.0;
2940 float g = ((argb >> 8) & 0xFF) / 255.0;
2941 float b = ((argb ) & 0xFF) / 255.0;
2942 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2943 NSDictionary *attr =
2944 [NSDictionary dictionaryWithObjectsAndKeys:
2945 gc->gcv.font->nsfont, NSFontAttributeName,
2946 fg, NSForegroundColorAttributeName,
2948 char *s2 = (char *) malloc (len + 1);
2949 strncpy (s2, str, len);
2951 NSString *nsstr = [NSString stringWithCString:s2
2952 encoding:NSISOLatin1StringEncoding];
2955 pos.x = wr.origin.x + x;
2956 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2957 [nsstr drawAtPoint:pos withAttributes:attr];
2961 invalidate_drawable_cache (d);
2967 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2968 const char *str, int len)
2970 return draw_string (dpy, d, gc, x, y, str, len, NO);
2974 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2975 const char *str, int len)
2977 return draw_string (dpy, d, gc, x, y, str, len, YES);
2982 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2984 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2985 gc->gcv.foreground = fg;
2991 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2993 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2994 gc->gcv.background = bg;
2999 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3001 gc->gcv.alpha_allowed_p = allowed;
3006 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3008 gc->gcv.antialias_p = antialias_p;
3014 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3015 int line_style, int cap_style, int join_style)
3017 gc->gcv.line_width = line_width;
3018 Assert (line_style == LineSolid, "only LineSolid implemented");
3019 // gc->gcv.line_style = line_style;
3020 gc->gcv.cap_style = cap_style;
3021 gc->gcv.join_style = join_style;
3026 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3032 XSetFunction (Display *dpy, GC gc, int which)
3034 gc->gcv.function = which;
3039 XSetSubwindowMode (Display *dpy, GC gc, int which)
3041 gc->gcv.subwindow_mode = which;
3046 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3048 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3050 if (gc->gcv.clip_mask) {
3051 XFreePixmap (dpy, gc->gcv.clip_mask);
3052 CGImageRelease (gc->clip_mask);
3055 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3056 if (gc->gcv.clip_mask)
3058 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3066 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3068 gc->gcv.clip_x_origin = x;
3069 gc->gcv.clip_y_origin = y;
3075 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3076 int *root_x_ret, int *root_y_ret,
3077 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3079 Assert (w && w->type == WINDOW, "not a window");
3082 int x = w->window.last_mouse_x;
3083 int y = w->window.last_mouse_y;
3084 if (root_x_ret) *root_x_ret = x;
3085 if (root_y_ret) *root_y_ret = y;
3086 if (win_x_ret) *win_x_ret = x;
3087 if (win_y_ret) *win_y_ret = y;
3089 # else // !USE_IPHONE
3091 NSWindow *nsw = [w->window.view window];
3093 // get bottom left of window on screen, from bottom left
3094 wpos.x = wpos.y = 0;
3095 wpos = [nsw convertBaseToScreen:wpos];
3098 // get bottom left of view on window, from bottom left
3099 vpos.x = vpos.y = 0;
3100 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3102 // get bottom left of view on screen, from bottom left
3106 // get top left of view on screen, from bottom left
3107 vpos.y += w->frame.size.height;
3109 // get top left of view on screen, from top left
3110 NSArray *screens = [NSScreen screens];
3111 NSScreen *screen = (screens && [screens count] > 0
3112 ? [screens objectAtIndex:0]
3113 : [NSScreen mainScreen]);
3115 double s = w->window.view.contentScaleFactor;
3119 NSRect srect = [screen frame];
3120 vpos.y = (s * srect.size.height) - vpos.y;
3122 // get the mouse position on window, from bottom left
3123 NSEvent *e = [NSApp currentEvent];
3124 NSPoint p = [e locationInWindow];
3126 // get mouse position on screen, from bottom left
3130 // get mouse position on screen, from top left
3131 p.y = srect.size.height - p.y;
3133 if (root_x_ret) *root_x_ret = (int) p.x;
3134 if (root_y_ret) *root_y_ret = (int) p.y;
3135 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3136 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3137 # endif // !USE_IPHONE
3139 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3140 if (root_ret) *root_ret = 0;
3141 if (child_ret) *child_ret = 0;
3146 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3147 int src_x, int src_y,
3148 int *dest_x_ret, int *dest_y_ret,
3151 Assert (w && w->type == WINDOW, "not a window");
3159 # else // !USE_IPHONE
3161 NSWindow *nsw = [w->window.view window];
3163 // get bottom left of window on screen, from bottom left
3164 wpos.x = wpos.y = 0;
3165 wpos = [nsw convertBaseToScreen:wpos];
3168 // get bottom left of view on window, from bottom left
3169 vpos.x = vpos.y = 0;
3170 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3172 // get bottom left of view on screen, from bottom left
3176 // get top left of view on screen, from bottom left
3177 vpos.y += w->frame.size.height;
3179 // get top left of view on screen, from top left
3180 NSArray *screens = [NSScreen screens];
3181 NSScreen *screen = (screens && [screens count] > 0
3182 ? [screens objectAtIndex:0]
3183 : [NSScreen mainScreen]);
3185 double s = w->window.view.contentScaleFactor;
3189 NSRect srect = [screen frame];
3190 vpos.y = (s * srect.size.height) - vpos.y;
3192 // point starts out relative to top left of view
3197 // get point relative to top left of screen
3200 # endif // !USE_IPHONE
3211 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3217 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3220 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3221 char c = (char) ks; // could be smarter about modifiers here...
3222 if (k_ret) *k_ret = ks;
3223 if (size > 0) buf[0] = c;
3224 if (size > 1) buf[1] = 0;
3230 XFlush (Display *dpy)
3232 // Just let the event loop take care of this on its own schedule.
3237 XSync (Display *dpy, Bool flush)
3239 return XFlush (dpy);
3243 // declared in utils/visual.h
3245 has_writable_cells (Screen *s, Visual *v)
3251 visual_depth (Screen *s, Visual *v)
3257 visual_cells (Screen *s, Visual *v)
3263 visual_class (Screen *s, Visual *v)
3268 // declared in utils/grabclient.h
3270 use_subwindow_mode_p (Screen *screen, Window window)