1 /* xscreensaver, Copyright (c) 1991-2014 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.
24 # import <UIKit/UIKit.h>
25 # import <UIKit/UIScreen.h>
26 # import <QuartzCore/QuartzCore.h>
27 # import <CoreText/CTFont.h>
28 # define NSView UIView
29 # define NSRect CGRect
30 # define NSPoint CGPoint
31 # define NSSize CGSize
32 # define NSColor UIColor
33 # define NSImage UIImage
34 # define NSEvent UIEvent
35 # define NSFont UIFont
36 # define NSGlyph CGGlyph
37 # define NSWindow UIWindow
38 # define NSMakeSize CGSizeMake
39 # define NSBezierPath UIBezierPath
41 # import <Cocoa/Cocoa.h>
45 #import "jwxyz-timers.h"
48 # define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
51 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
55 # define MAX(a,b) ((a)>(b)?(a):(b))
56 # define MIN(a,b) ((a)<(b)?(a):(b))
59 struct jwxyz_Drawable {
60 enum { WINDOW, PIXMAP } type;
67 unsigned long background;
68 int last_mouse_x, last_mouse_y;
72 void *cgc_buffer; // the bits to which CGContextRef renders
77 struct jwxyz_Display {
80 struct jwxyz_sources_data *timers_data;
83 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
84 This can change if the window is dragged to
85 a different screen. */
88 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
89 our images with this to avoid translation
101 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
107 float size; // points
109 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
110 // But we need the metrics on both of them, so they go here.
115 /* Instead of calling abort(), throw a real exception, so that
116 XScreenSaverView can catch it and display a dialog.
119 jwxyz_abort (const char *fmt, ...)
127 va_start (args, fmt);
128 vsprintf (s, fmt, args);
131 [[NSException exceptionWithName: NSInternalInconsistencyException
132 reason: [NSString stringWithCString: s
133 encoding:NSUTF8StringEncoding]
136 abort(); // not reached
141 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
143 CGContextRef cgc = (CGContextRef) cgc_arg;
144 NSView *view = (NSView *) nsview_arg;
145 Assert (view, "no view");
148 Display *d = (Display *) calloc (1, sizeof(*d));
149 d->screen = (Screen *) calloc (1, sizeof(Screen));
152 Visual *v = (Visual *) calloc (1, sizeof(Visual));
153 v->class = TrueColor;
154 v->red_mask = 0x00FF0000;
155 v->green_mask = 0x0000FF00;
156 v->blue_mask = 0x000000FF;
158 d->screen->visual = v;
160 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
163 Window w = (Window) calloc (1, sizeof(*w));
165 w->window.view = view;
166 CFRetain (w->window.view); // needed for garbage collection?
167 w->window.background = BlackPixel(0,0);
174 cgc = [[[view window] graphicsContext] graphicsPort];
180 Assert (cgc, "no CGContext");
185 jwxyz_free_display (Display *dpy)
187 jwxyz_XtRemoveInput_all (dpy);
188 // #### jwxyz_XtRemoveTimeOut_all ();
190 free (dpy->screen->visual);
192 CFRelease (dpy->main_window->window.view);
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);
251 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
253 Assert (dpy->cgdpy, "unable to find CGDisplay");
255 # endif // USE_IPHONE
257 # ifndef USE_BACKBUFFER
258 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
259 // then this one's faster.
262 // Figure out this screen's colorspace, and use that for every CGImage.
264 CMProfileRef profile = 0;
265 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
266 Assert (profile, "unable to find colorspace profile");
267 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
268 Assert (dpy->colorspace, "unable to find colorspace");
270 # else // USE_BACKBUFFER
272 // WTF? It's faster if we *do not* use the screen's colorspace!
274 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
275 # endif // USE_BACKBUFFER
277 invalidate_drawable_cache (w);
283 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
285 Assert (w && w->type == WINDOW, "not a window");
286 w->window.last_mouse_x = x;
287 w->window.last_mouse_y = y;
293 jwxyz_flush_context (Display *dpy)
295 // This is only used when USE_BACKBUFFER is off.
296 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
300 display_sources_data (Display *dpy)
302 return dpy->timers_data;
307 XRootWindow (Display *dpy, int screen)
309 return dpy->main_window;
313 XDefaultScreenOfDisplay (Display *dpy)
319 XDefaultVisualOfScreen (Screen *screen)
321 return screen->visual;
325 XDisplayOfScreen (Screen *s)
331 XDisplayNumberOfScreen (Screen *s)
337 XScreenNumberOfScreen (Screen *s)
343 XDisplayWidth (Display *dpy, int screen)
345 return (int) dpy->main_window->frame.size.width;
349 XDisplayHeight (Display *dpy, int screen)
351 return (int) dpy->main_window->frame.size.height;
355 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
358 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
359 else if (!alpha_allowed_p)
360 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
361 "bogus color pixel");
366 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
367 BOOL alpha_allowed_p, BOOL fill_p)
369 validate_pixel (argb, depth, alpha_allowed_p);
372 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
374 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
376 float a = ((argb >> 24) & 0xFF) / 255.0;
377 float r = ((argb >> 16) & 0xFF) / 255.0;
378 float g = ((argb >> 8) & 0xFF) / 255.0;
379 float b = ((argb ) & 0xFF) / 255.0;
381 CGContextSetRGBFillColor (cgc, r, g, b, a);
383 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
388 set_line_mode (CGContextRef cgc, XGCValues *gcv)
390 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
391 CGContextSetLineJoin (cgc,
392 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
393 gcv->join_style == JoinRound ? kCGLineJoinRound :
395 CGContextSetLineCap (cgc,
396 gcv->cap_style == CapNotLast ? kCGLineCapButt :
397 gcv->cap_style == CapButt ? kCGLineCapButt :
398 gcv->cap_style == CapRound ? kCGLineCapRound :
403 set_clip_mask (Drawable d, GC gc)
405 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
407 Pixmap p = gc->gcv.clip_mask;
409 Assert (p->type == PIXMAP, "not a pixmap");
411 CGRect wr = d->frame;
413 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
414 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
415 - p->frame.size.height;
416 to.size.width = p->frame.size.width;
417 to.size.height = p->frame.size.height;
419 CGContextClipToMask (d->cgc, to, gc->clip_mask);
423 /* Pushes a GC context; sets BlendMode and ClipMask.
426 push_gc (Drawable d, GC gc)
428 CGContextRef cgc = d->cgc;
429 CGContextSaveGState (cgc);
431 switch (gc->gcv.function) {
434 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
435 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
436 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
437 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
438 default: Assert(0, "unknown gcv function"); break;
441 if (gc->gcv.clip_mask)
442 set_clip_mask (d, gc);
445 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
448 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
451 push_color_gc (Drawable d, GC gc, unsigned long color,
452 BOOL antialias_p, Bool fill_p)
456 int depth = gc->depth;
457 switch (gc->gcv.function) {
458 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
459 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
462 CGContextRef cgc = d->cgc;
463 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
464 CGContextSetShouldAntialias (cgc, antialias_p);
468 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
471 push_fg_gc (Drawable d, GC gc, Bool fill_p)
473 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
476 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
479 push_bg_gc (Drawable d, GC gc, Bool fill_p)
481 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
486 /* You've got to be fucking kidding me!
488 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
489 with repeated calls to CGContextDrawImage than it is to make a single
490 call to CGContextFillRects() with a list of 1x1 rectangles!
492 I still wouldn't call it *fast*, however...
494 #define XDRAWPOINTS_IMAGES
496 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
497 the bitmap data directly is faster. This only works on Pixmaps, though,
498 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
500 #define XDRAWPOINTS_CGDATA
503 XDrawPoints (Display *dpy, Drawable d, GC gc,
504 XPoint *points, int count, int mode)
507 CGRect wr = d->frame;
509 push_fg_gc (d, gc, YES);
511 # ifdef XDRAWPOINTS_CGDATA
513 # ifdef USE_BACKBUFFER
514 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
516 if (d->type == PIXMAP)
519 CGContextRef cgc = d->cgc;
520 void *data = CGBitmapContextGetData (cgc);
521 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
522 size_t w = CGBitmapContextGetWidth (cgc);
523 size_t h = CGBitmapContextGetHeight (cgc);
525 Assert (data, "no bitmap data in Drawable");
527 unsigned long argb = gc->gcv.foreground;
528 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
530 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
532 CGFloat x0 = wr.origin.x;
533 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
535 // It's uglier, but faster, to hoist the conditional out of the loop.
536 if (mode == CoordModePrevious) {
537 CGFloat x = x0, y = y0;
538 for (i = 0; i < count; i++, points++) {
542 if (x >= 0 && x < w && y >= 0 && y < h) {
543 unsigned int *p = (unsigned int *)
544 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
545 *p = (unsigned int) argb;
549 for (i = 0; i < count; i++, points++) {
550 CGFloat x = x0 + points->x;
551 CGFloat y = y0 + points->y;
553 if (x >= 0 && x < w && y >= 0 && y < h) {
554 unsigned int *p = (unsigned int *)
555 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
556 *p = (unsigned int) argb;
561 } else /* d->type == WINDOW */
563 # endif /* XDRAWPOINTS_CGDATA */
566 # ifdef XDRAWPOINTS_IMAGES
568 unsigned int argb = gc->gcv.foreground;
569 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
571 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
573 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
575 CGImageRef cgi = CGImageCreate (1, 1,
578 /* Host-ordered, since we're using the
579 address of an int as the color data. */
580 (kCGImageAlphaNoneSkipFirst |
581 kCGBitmapByteOrder32Host),
584 NO, /* interpolate */
585 kCGRenderingIntentDefault);
586 CGDataProviderRelease (prov);
588 CGContextRef cgc = d->cgc;
590 rect.size.width = rect.size.height = 1;
591 for (i = 0; i < count; i++) {
592 if (i > 0 && mode == CoordModePrevious) {
593 rect.origin.x += points->x;
594 rect.origin.x -= points->y;
596 rect.origin.x = wr.origin.x + points->x;
597 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
600 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
601 CGContextDrawImage (cgc, rect, cgi);
605 CGImageRelease (cgi);
607 # else /* ! XDRAWPOINTS_IMAGES */
609 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
612 for (i = 0; i < count; i++) {
613 r->size.width = r->size.height = 1;
614 if (i > 0 && mode == CoordModePrevious) {
615 r->origin.x = r[-1].origin.x + points->x;
616 r->origin.y = r[-1].origin.x - points->y;
618 r->origin.x = wr.origin.x + points->x;
619 r->origin.y = wr.origin.y + wr.size.height - points->y;
625 CGContextFillRects (d->cgc, rects, count);
628 # endif /* ! XDRAWPOINTS_IMAGES */
632 invalidate_drawable_cache (d);
639 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
644 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
648 static void draw_rect (Display *, Drawable, GC,
649 int x, int y, unsigned int width, unsigned int height,
650 BOOL foreground_p, BOOL fill_p);
653 bitmap_context_p (Drawable d)
655 # ifdef USE_BACKBUFFER
658 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
659 return d->type == PIXMAP;
664 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
665 size_t fill_width, size_t fill_height)
667 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
668 while (fill_height) {
669 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
670 wmemset (dst, fill_data, fill_width);
672 dst = (char *) dst + dst_pitch;
677 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
679 return (char *)dst + dst_pitch * y + x * 4;
683 drawable_depth (Drawable d)
685 return (d->type == WINDOW
686 ? visual_depth (NULL, NULL)
692 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
693 int src_x, int src_y,
694 unsigned int width, unsigned int height,
695 int dst_x, int dst_y)
697 Assert (gc, "no GC");
698 Assert ((width < 65535), "improbably large width");
699 Assert ((height < 65535), "improbably large height");
700 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
701 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
702 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
703 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
705 if (width == 0 || height == 0)
708 if (gc->gcv.function == GXset ||
709 gc->gcv.function == GXclear) {
710 // "set" and "clear" are dumb drawing modes that ignore the source
711 // bits and just draw solid rectangles.
713 (gc->gcv.function == GXset
714 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
715 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
716 gc->depth, gc->gcv.alpha_allowed_p, YES);
717 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
721 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
722 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
723 // bounds of their drawables.
724 BOOL clipped = NO; // Whether we did any clipping of the rects.
726 src_frame = src->frame;
727 dst_frame = dst->frame;
729 // Initialize src_rect...
731 src_rect.origin.x = src_frame.origin.x + src_x;
732 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
734 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
735 src_rect.size.width = width;
736 src_rect.size.height = height;
738 // Initialize dst_rect...
740 dst_rect.origin.x = dst_frame.origin.x + dst_x;
741 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
743 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
744 dst_rect.size.width = width;
745 dst_rect.size.height = height;
747 // Clip rects to frames...
750 # define CLIP(THIS,THAT,VAL,SIZE) do { \
751 float off = THIS##_rect.origin.VAL; \
754 THIS##_rect.size.SIZE += off; \
755 THAT##_rect.size.SIZE += off; \
756 THIS##_rect.origin.VAL -= off; \
757 THAT##_rect.origin.VAL -= off; \
759 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
760 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
763 THIS##_rect.size.SIZE -= off; \
764 THAT##_rect.size.SIZE -= off; \
767 CLIP (dst, src, x, width);
768 CLIP (dst, src, y, height);
770 // Not actually the original dst_rect, just the one before it's clipped to
772 CGRect orig_dst_rect = dst_rect;
774 CLIP (src, dst, x, width);
775 CLIP (src, dst, y, height);
778 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
781 // Sort-of-special case where no pixels can be grabbed from the source,
782 // and the whole destination is filled with the background color.
783 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
785 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
786 (int)src_rect.size.height == (int)dst_rect.size.height,
789 src_rect.size.width = 0;
790 src_rect.size.height = 0;
791 dst_rect.size.width = 0;
792 dst_rect.size.height = 0;
795 NSObject *releaseme = 0;
798 BOOL free_cgi_p = NO;
801 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
802 going on with clipping masks or depths or anything, optimize it by
803 just doing a memcpy instead of going through a CGI.
805 if (bitmap_context_p (src)) {
807 if (bitmap_context_p (dst) &&
808 gc->gcv.function == GXcopy &&
809 !gc->gcv.clip_mask &&
810 drawable_depth (src) == drawable_depth (dst)) {
812 Assert(!(int)src_frame.origin.x &&
813 !(int)src_frame.origin.y &&
814 !(int)dst_frame.origin.x &&
815 !(int)dst_frame.origin.y,
816 "unexpected non-zero origin");
818 char *src_data = CGBitmapContextGetData(src->cgc);
819 char *dst_data = CGBitmapContextGetData(dst->cgc);
820 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
821 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
823 // Int to float and back again. It's not very safe, but it seems to work.
824 int src_x0 = src_rect.origin.x;
825 int dst_x0 = dst_rect.origin.x;
827 // Flip the Y-axis a second time.
828 int src_y0 = (src_frame.origin.y + src_frame.size.height -
829 src_rect.size.height - src_rect.origin.y);
830 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
831 dst_rect.size.height - dst_rect.origin.y);
833 unsigned width0 = (int) src_rect.size.width;
834 unsigned height0 = (int) src_rect.size.height;
836 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
837 (int)src_rect.size.height == (int)dst_rect.size.height,
840 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
841 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
842 size_t src_pitch0 = src_pitch;
843 size_t dst_pitch0 = dst_pitch;
844 size_t bytes = width0 * 4;
846 if (src == dst && dst_y0 > src_y0) {
847 // Copy upwards if the areas might overlap.
848 src_data0 += src_pitch0 * (height0 - 1);
849 dst_data0 += dst_pitch0 * (height0 - 1);
850 src_pitch0 = -src_pitch0;
851 dst_pitch0 = -dst_pitch0;
854 size_t lines0 = height0;
856 // memcpy is an alias for memmove on OS X.
857 memmove(dst_data0, src_data0, bytes);
858 src_data0 += src_pitch0;
859 dst_data0 += dst_pitch0;
865 int orig_dst_x = orig_dst_rect.origin.x;
866 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
867 orig_dst_rect.origin.y - orig_dst_rect.size.height);
868 int orig_width = orig_dst_rect.size.width;
869 int orig_height = orig_dst_rect.size.height;
871 Assert (orig_dst_x >= 0 &&
872 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
874 orig_dst_y + orig_height <= (int) dst_frame.size.height,
877 if (orig_dst_y < dst_y0) {
878 fill_rect_memset (seek_xy (dst_data, dst_pitch,
879 orig_dst_x, orig_dst_y), dst_pitch,
880 (uint32_t) gc->gcv.background, orig_width,
881 dst_y0 - orig_dst_y);
884 if (orig_dst_y + orig_height > dst_y0 + height0) {
885 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
888 (uint32_t) gc->gcv.background, orig_width,
889 orig_dst_y + orig_height - dst_y0 - height0);
892 if (orig_dst_x < dst_x0) {
893 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
894 dst_pitch, (uint32_t) gc->gcv.background,
895 dst_x0 - orig_dst_x, height0);
898 if (dst_x0 + width0 < orig_dst_x + orig_width) {
899 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
901 dst_pitch, (uint32_t) gc->gcv.background,
902 orig_dst_x + orig_width - dst_x0 - width0,
907 invalidate_drawable_cache (dst);
912 // If we are copying from a Pixmap to a Pixmap or Window, we must first
913 // copy the bits to an intermediary CGImage object, then copy that to the
914 // destination drawable's CGContext.
916 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
917 // case of copying from a Pixmap back to itself, but I don't think that
918 // happens very often anyway.)
920 // First we get a CGImage out of the pixmap CGContext -- it's the whole
921 // pixmap, but it presumably shares the data pointer instead of copying
922 // it. We then cache that CGImage it inside the Pixmap object. Note:
923 // invalidate_drawable_cache() must be called to discard this any time a
924 // modification is made to the pixmap, or we'll end up re-using old bits.
927 src->cgi = CGBitmapContextCreateImage (src->cgc);
930 // if doing a sub-rect, trim it down.
931 if (src_rect.origin.x != src_frame.origin.x ||
932 src_rect.origin.y != src_frame.origin.y ||
933 src_rect.size.width != src_frame.size.width ||
934 src_rect.size.height != src_frame.size.height) {
935 // #### I don't understand why this is needed...
936 src_rect.origin.y = (src_frame.size.height -
937 src_rect.size.height - src_rect.origin.y);
938 // This does not copy image data, so it should be fast.
939 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
943 if (src->type == PIXMAP && src->pixmap.depth == 1)
946 # ifndef USE_BACKBUFFER
947 } else { /* (src->type == WINDOW) */
949 NSRect nsfrom; // NSRect != CGRect on 10.4
950 nsfrom.origin.x = src_rect.origin.x;
951 nsfrom.origin.y = src_rect.origin.y;
952 nsfrom.size.width = src_rect.size.width;
953 nsfrom.size.height = src_rect.size.height;
957 // If we are copying from a window to itself, we can use NSCopyBits()
958 // without first copying the rectangle to an intermediary CGImage.
959 // This is ~28% faster (but I *expected* it to be twice as fast...)
960 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
966 // If we are copying from a Window to a Pixmap, we must first copy
967 // the bits to an intermediary CGImage object, then copy that to the
968 // Pixmap's CGContext.
970 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
971 initWithFocusedViewRect:nsfrom];
972 unsigned char *data = [bm bitmapData];
973 int bps = [bm bitsPerSample];
974 int bpp = [bm bitsPerPixel];
975 int bpl = [bm bytesPerRow];
978 // create a CGImage from those bits.
979 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
980 // but that method didn't exist in 10.4.)
982 CGDataProviderRef prov =
983 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
985 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
988 /* Use whatever default bit ordering we got from
989 initWithFocusedViewRect. I would have assumed
990 that it was (kCGImageAlphaNoneSkipFirst |
991 kCGBitmapByteOrder32Host), but on Intel,
997 NO, /* interpolate */
998 kCGRenderingIntentDefault);
1000 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1001 CGDataProviderRelease (prov);
1004 # endif // !USE_BACKBUFFER
1007 CGContextRef cgc = dst->cgc;
1009 if (mask_p) { // src depth == 1
1011 push_bg_gc (dst, gc, YES);
1013 // fill the destination rectangle with solid background...
1014 CGContextFillRect (cgc, orig_dst_rect);
1016 Assert (cgc, "no CGC with 1-bit XCopyArea");
1018 // then fill in a solid rectangle of the fg color, using the image as an
1019 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1020 set_color (cgc, gc->gcv.foreground, gc->depth,
1021 gc->gcv.alpha_allowed_p, YES);
1022 CGContextClipToMask (cgc, dst_rect, cgi);
1023 CGContextFillRect (cgc, dst_rect);
1027 } else { // src depth > 1
1031 // If either the src or dst rects did not lie within their drawables,
1032 // then we have adjusted both the src and dst rects to account for
1033 // the clipping; that means we need to first clear to the background,
1034 // so that clipped bits end up in the bg color instead of simply not
1038 set_color (cgc, gc->gcv.background, gc->depth,
1039 gc->gcv.alpha_allowed_p, YES);
1040 CGContextFillRect (cgc, orig_dst_rect);
1044 // copy the CGImage onto the destination CGContext
1045 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1046 CGContextDrawImage (cgc, dst_rect, cgi);
1048 // No cgi means src == dst, and both are Windows.
1050 # ifdef USE_BACKBUFFER
1051 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1053 # else // !USE_BACKBUFFER
1055 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1056 nsfrom.origin.y = src_rect.origin.y;
1057 nsfrom.size.width = src_rect.size.width;
1058 nsfrom.size.height = src_rect.size.height;
1060 nsto.x = dst_rect.origin.x;
1061 nsto.y = dst_rect.origin.y;
1062 NSCopyBits (0, nsfrom, nsto);
1063 # endif // !USE_BACKBUFFER
1069 if (free_cgi_p) CGImageRelease (cgi);
1071 if (releaseme) [releaseme release];
1072 invalidate_drawable_cache (dst);
1078 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1079 int src_x, int src_y,
1080 unsigned width, int height,
1081 int dest_x, int dest_y, unsigned long plane)
1083 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1085 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1086 // not to white/black.
1087 return XCopyArea (dpy, src, dest, gc,
1088 src_x, src_y, width, height, dest_x, dest_y);
1093 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1095 // when drawing a zero-length line, obey line-width and cap-style.
1096 if (x1 == x2 && y1 == y2) {
1097 int w = gc->gcv.line_width;
1100 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1101 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1103 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1106 CGRect wr = d->frame;
1108 p.x = wr.origin.x + x1;
1109 p.y = wr.origin.y + wr.size.height - y1;
1111 push_fg_gc (d, gc, NO);
1113 CGContextRef cgc = d->cgc;
1114 set_line_mode (cgc, &gc->gcv);
1115 CGContextBeginPath (cgc);
1116 CGContextMoveToPoint (cgc, p.x, p.y);
1117 p.x = wr.origin.x + x2;
1118 p.y = wr.origin.y + wr.size.height - y2;
1119 CGContextAddLineToPoint (cgc, p.x, p.y);
1120 CGContextStrokePath (cgc);
1122 invalidate_drawable_cache (d);
1127 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1132 CGRect wr = d->frame;
1133 push_fg_gc (d, gc, NO);
1135 CGContextRef cgc = d->cgc;
1137 set_line_mode (cgc, &gc->gcv);
1139 // if the first and last points coincide, use closepath to get
1140 // the proper line-joining.
1141 BOOL closed_p = (points[0].x == points[count-1].x &&
1142 points[0].y == points[count-1].y);
1143 if (closed_p) count--;
1145 p.x = wr.origin.x + points->x;
1146 p.y = wr.origin.y + wr.size.height - points->y;
1148 CGContextBeginPath (cgc);
1149 CGContextMoveToPoint (cgc, p.x, p.y);
1150 for (i = 1; i < count; i++) {
1151 if (mode == CoordModePrevious) {
1155 p.x = wr.origin.x + points->x;
1156 p.y = wr.origin.y + wr.size.height - points->y;
1158 CGContextAddLineToPoint (cgc, p.x, p.y);
1161 if (closed_p) CGContextClosePath (cgc);
1162 CGContextStrokePath (cgc);
1164 invalidate_drawable_cache (d);
1170 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1173 CGRect wr = d->frame;
1175 CGContextRef cgc = d->cgc;
1177 push_fg_gc (d, gc, NO);
1178 set_line_mode (cgc, &gc->gcv);
1179 CGContextBeginPath (cgc);
1180 for (i = 0; i < count; i++) {
1181 CGContextMoveToPoint (cgc,
1182 wr.origin.x + segments->x1,
1183 wr.origin.y + wr.size.height - segments->y1);
1184 CGContextAddLineToPoint (cgc,
1185 wr.origin.x + segments->x2,
1186 wr.origin.y + wr.size.height - segments->y2);
1189 CGContextStrokePath (cgc);
1191 invalidate_drawable_cache (d);
1197 XClearWindow (Display *dpy, Window win)
1199 Assert (win && win->type == WINDOW, "not a window");
1200 CGRect wr = win->frame;
1201 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1205 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1207 Assert (w && w->type == WINDOW, "not a window");
1208 validate_pixel (pixel, 32, NO);
1209 w->window.background = pixel;
1214 draw_rect (Display *dpy, Drawable d, GC gc,
1215 int x, int y, unsigned int width, unsigned int height,
1216 BOOL foreground_p, BOOL fill_p)
1218 CGRect wr = d->frame;
1220 r.origin.x = wr.origin.x + x;
1221 r.origin.y = wr.origin.y + wr.size.height - y - height;
1222 r.size.width = width;
1223 r.size.height = height;
1227 push_fg_gc (d, gc, fill_p);
1229 push_bg_gc (d, gc, fill_p);
1232 CGContextRef cgc = d->cgc;
1234 CGContextFillRect (cgc, r);
1237 set_line_mode (cgc, &gc->gcv);
1238 CGContextStrokeRect (cgc, r);
1243 invalidate_drawable_cache (d);
1248 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1249 unsigned int width, unsigned int height)
1251 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1256 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1257 unsigned int width, unsigned int height)
1259 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1264 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1266 CGRect wr = d->frame;
1268 CGContextRef cgc = d->cgc;
1269 push_fg_gc (d, gc, YES);
1270 for (i = 0; i < n; i++) {
1272 r.origin.x = wr.origin.x + rects->x;
1273 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1274 r.size.width = rects->width;
1275 r.size.height = rects->height;
1276 CGContextFillRect (cgc, r);
1280 invalidate_drawable_cache (d);
1286 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1288 Assert (win && win->type == WINDOW, "not a window");
1289 CGContextRef cgc = win->cgc;
1290 set_color (cgc, win->window.background, 32, NO, YES);
1291 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1297 XFillPolygon (Display *dpy, Drawable d, GC gc,
1298 XPoint *points, int npoints, int shape, int mode)
1300 CGRect wr = d->frame;
1302 push_fg_gc (d, gc, YES);
1303 CGContextRef cgc = d->cgc;
1304 CGContextBeginPath (cgc);
1306 for (i = 0; i < npoints; i++) {
1307 if (i > 0 && mode == CoordModePrevious) {
1311 x = wr.origin.x + points[i].x;
1312 y = wr.origin.y + wr.size.height - points[i].y;
1316 CGContextMoveToPoint (cgc, x, y);
1318 CGContextAddLineToPoint (cgc, x, y);
1320 CGContextClosePath (cgc);
1321 if (gc->gcv.fill_rule == EvenOddRule)
1322 CGContextEOFillPath (cgc);
1324 CGContextFillPath (cgc);
1326 invalidate_drawable_cache (d);
1330 #define radians(DEG) ((DEG) * M_PI / 180.0)
1331 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1334 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1335 unsigned int width, unsigned int height, int angle1, int angle2,
1338 CGRect wr = d->frame;
1340 bound.origin.x = wr.origin.x + x;
1341 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1342 bound.size.width = width;
1343 bound.size.height = height;
1346 ctr.x = bound.origin.x + bound.size.width /2;
1347 ctr.y = bound.origin.y + bound.size.height/2;
1349 float r1 = radians (angle1/64.0);
1350 float r2 = radians (angle2/64.0) + r1;
1351 BOOL clockwise = angle2 < 0;
1352 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1354 push_fg_gc (d, gc, fill_p);
1356 CGContextRef cgc = d->cgc;
1357 CGContextBeginPath (cgc);
1359 CGContextSaveGState(cgc);
1360 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1361 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1363 CGContextMoveToPoint (cgc, 0, 0);
1365 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1366 CGContextRestoreGState (cgc); // restore before stroke, for line width
1369 CGContextClosePath (cgc); // for proper line joining
1372 CGContextFillPath (cgc);
1374 set_line_mode (cgc, &gc->gcv);
1375 CGContextStrokePath (cgc);
1379 invalidate_drawable_cache (d);
1384 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1385 unsigned int width, unsigned int height, int angle1, int angle2)
1387 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1391 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1392 unsigned int width, unsigned int height, int angle1, int angle2)
1394 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1398 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1401 for (i = 0; i < narcs; i++)
1402 draw_arc (dpy, d, gc,
1403 arcs[i].x, arcs[i].y,
1404 arcs[i].width, arcs[i].height,
1405 arcs[i].angle1, arcs[i].angle2,
1411 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1414 for (i = 0; i < narcs; i++)
1415 draw_arc (dpy, d, gc,
1416 arcs[i].x, arcs[i].y,
1417 arcs[i].width, arcs[i].height,
1418 arcs[i].angle1, arcs[i].angle2,
1425 gcv_defaults (XGCValues *gcv, int depth)
1427 memset (gcv, 0, sizeof(*gcv));
1428 gcv->function = GXcopy;
1429 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1430 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1431 gcv->line_width = 1;
1432 gcv->cap_style = CapNotLast;
1433 gcv->join_style = JoinMiter;
1434 gcv->fill_rule = EvenOddRule;
1436 gcv->alpha_allowed_p = NO;
1437 gcv->antialias_p = YES;
1441 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1444 Assert (gc && from, "no gc");
1445 if (!gc || !from) return;
1447 if (mask & GCFunction) gc->gcv.function = from->function;
1448 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1449 if (mask & GCBackground) gc->gcv.background = from->background;
1450 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1451 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1452 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1453 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1454 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1455 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1456 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1458 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1459 if (mask & GCFont) XSetFont (0, gc, from->font);
1461 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1462 gc->gcv.alpha_allowed_p);
1463 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1464 gc->gcv.alpha_allowed_p);
1466 Assert ((! (mask & (GCLineStyle |
1473 GCGraphicsExposures |
1477 "unimplemented gcvalues mask");
1482 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1484 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1485 if (d->type == WINDOW) {
1487 } else { /* (d->type == PIXMAP) */
1488 gc->depth = d->pixmap.depth;
1491 gcv_defaults (&gc->gcv, gc->depth);
1492 set_gcv (gc, xgcv, mask);
1497 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1499 set_gcv (gc, gcv, mask);
1505 XFreeGC (Display *dpy, GC gc)
1508 XUnloadFont (dpy, gc->gcv.font);
1510 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1512 if (gc->gcv.clip_mask) {
1513 XFreePixmap (dpy, gc->gcv.clip_mask);
1514 CGImageRelease (gc->clip_mask);
1522 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1524 Assert (w && w->type == WINDOW, "not a window");
1525 memset (xgwa, 0, sizeof(*xgwa));
1526 xgwa->x = w->frame.origin.x;
1527 xgwa->y = w->frame.origin.y;
1528 xgwa->width = w->frame.size.width;
1529 xgwa->height = w->frame.size.height;
1531 xgwa->screen = dpy->screen;
1532 xgwa->visual = dpy->screen->visual;
1537 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1538 int *x_ret, int *y_ret,
1539 unsigned int *w_ret, unsigned int *h_ret,
1540 unsigned int *bw_ret, unsigned int *d_ret)
1542 *x_ret = d->frame.origin.x;
1543 *y_ret = d->frame.origin.y;
1544 *w_ret = d->frame.size.width;
1545 *h_ret = d->frame.size.height;
1546 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1547 *root_ret = RootWindow (dpy, 0);
1554 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1556 // store 32 bit ARGB in the pixel field.
1557 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1558 color->pixel = (uint32_t)
1560 (((color->red >> 8) & 0xFF) << 16) |
1561 (((color->green >> 8) & 0xFF) << 8) |
1562 (((color->blue >> 8) & 0xFF) ));
1567 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1568 unsigned long *pmret, unsigned int npl,
1569 unsigned long *pxret, unsigned int npx)
1575 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1577 Assert(0, "XStoreColors called");
1582 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1584 Assert(0, "XStoreColor called");
1589 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1590 unsigned long planes)
1596 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1598 unsigned char r=0, g=0, b=0;
1599 if (*spec == '#' && strlen(spec) == 7) {
1600 static unsigned const char hex[] = { // yeah yeah, shoot me.
1601 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,
1602 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,
1603 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,
1604 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,
1605 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,
1606 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,
1607 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,
1608 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};
1609 r = (hex[spec[1]] << 4) | hex[spec[2]];
1610 g = (hex[spec[3]] << 4) | hex[spec[4]];
1611 b = (hex[spec[5]] << 4) | hex[spec[6]];
1612 } else if (!strcasecmp(spec,"black")) {
1614 } else if (!strcasecmp(spec,"white")) {
1616 } else if (!strcasecmp(spec,"red")) {
1618 } else if (!strcasecmp(spec,"green")) {
1620 } else if (!strcasecmp(spec,"blue")) {
1622 } else if (!strcasecmp(spec,"cyan")) {
1624 } else if (!strcasecmp(spec,"magenta")) {
1626 } else if (!strcasecmp(spec,"yellow")) {
1632 ret->red = (r << 8) | r;
1633 ret->green = (g << 8) | g;
1634 ret->blue = (b << 8) | b;
1635 ret->flags = DoRed|DoGreen|DoBlue;
1640 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1641 XColor *screen_ret, XColor *exact_ret)
1643 if (! XParseColor (dpy, cmap, name, screen_ret))
1645 *exact_ret = *screen_ret;
1646 return XAllocColor (dpy, cmap, screen_ret);
1650 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1652 validate_pixel (color->pixel, 32, NO);
1653 unsigned char r = ((color->pixel >> 16) & 0xFF);
1654 unsigned char g = ((color->pixel >> 8) & 0xFF);
1655 unsigned char b = ((color->pixel ) & 0xFF);
1656 color->red = (r << 8) | r;
1657 color->green = (g << 8) | g;
1658 color->blue = (b << 8) | b;
1659 color->flags = DoRed|DoGreen|DoBlue;
1664 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1667 for (i = 0; i < n; i++)
1668 XQueryColor (dpy, cmap, &c[i]);
1673 static unsigned long
1674 ximage_getpixel_1 (XImage *ximage, int x, int y)
1676 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1680 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1683 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1685 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1690 static unsigned long
1691 ximage_getpixel_32 (XImage *ximage, int x, int y)
1693 return ((unsigned long)
1694 *((uint32_t *) ximage->data +
1695 (y * (ximage->bytes_per_line >> 2)) +
1700 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1702 *((uint32_t *) ximage->data +
1703 (y * (ximage->bytes_per_line >> 2)) +
1704 x) = (uint32_t) pixel;
1710 XInitImage (XImage *ximage)
1712 if (!ximage->bytes_per_line)
1713 ximage->bytes_per_line = (ximage->depth == 1
1714 ? (ximage->width + 7) / 8
1715 : ximage->width * 4);
1717 if (ximage->depth == 1) {
1718 ximage->f.put_pixel = ximage_putpixel_1;
1719 ximage->f.get_pixel = ximage_getpixel_1;
1720 } else if (ximage->depth == 32 || ximage->depth == 24) {
1721 ximage->f.put_pixel = ximage_putpixel_32;
1722 ximage->f.get_pixel = ximage_getpixel_32;
1724 Assert (0, "unknown depth");
1731 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1732 int format, int offset, char *data,
1733 unsigned int width, unsigned int height,
1734 int bitmap_pad, int bytes_per_line)
1736 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1737 ximage->width = width;
1738 ximage->height = height;
1739 ximage->format = format;
1740 ximage->data = data;
1741 ximage->bitmap_unit = 8;
1742 ximage->byte_order = LSBFirst;
1743 ximage->bitmap_bit_order = ximage->byte_order;
1744 ximage->bitmap_pad = bitmap_pad;
1745 ximage->depth = depth;
1746 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1747 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1748 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1749 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1750 ximage->bytes_per_line = bytes_per_line;
1752 XInitImage (ximage);
1757 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1759 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1760 w, h, from->bitmap_pad, 0);
1761 to->data = (char *) malloc (h * to->bytes_per_line);
1763 if (x >= from->width)
1765 else if (x+w > from->width)
1766 w = from->width - x;
1768 if (y >= from->height)
1770 else if (y+h > from->height)
1771 h = from->height - y;
1774 for (ty = 0; ty < h; ty++)
1775 for (tx = 0; tx < w; tx++)
1776 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1781 XPixmapFormatValues *
1782 XListPixmapFormats (Display *dpy, int *n_ret)
1784 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1786 ret[0].bits_per_pixel = 32;
1787 ret[0].scanline_pad = 8;
1789 ret[1].bits_per_pixel = 1;
1790 ret[1].scanline_pad = 8;
1797 XGetPixel (XImage *ximage, int x, int y)
1799 return ximage->f.get_pixel (ximage, x, y);
1804 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1806 return ximage->f.put_pixel (ximage, x, y, pixel);
1810 XDestroyImage (XImage *ximage)
1812 if (ximage->data) free (ximage->data);
1819 flipbits (unsigned const char *in, unsigned char *out, int length)
1821 static const unsigned char table[256] = {
1822 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1823 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1824 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1825 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1826 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1827 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1828 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1829 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1830 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1831 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1832 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1833 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1834 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1835 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1836 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1837 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1838 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1839 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1840 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1841 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1842 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1843 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1844 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1845 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1846 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1847 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1848 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1849 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1850 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1851 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1852 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1853 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1855 while (--length > 0)
1856 *out++ = table[*in++];
1861 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1862 int src_x, int src_y, int dest_x, int dest_y,
1863 unsigned int w, unsigned int h)
1865 CGRect wr = d->frame;
1867 Assert (gc, "no GC");
1868 Assert ((w < 65535), "improbably large width");
1869 Assert ((h < 65535), "improbably large height");
1870 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1871 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1872 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1873 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1875 // Clip width and height to the bounds of the Drawable
1877 if (dest_x + w > wr.size.width) {
1878 if (dest_x > wr.size.width)
1880 w = wr.size.width - dest_x;
1882 if (dest_y + h > wr.size.height) {
1883 if (dest_y > wr.size.height)
1885 h = wr.size.height - dest_y;
1887 if (w <= 0 || h <= 0)
1890 // Clip width and height to the bounds of the XImage
1892 if (src_x + w > ximage->width) {
1893 if (src_x > ximage->width)
1895 w = ximage->width - src_x;
1897 if (src_y + h > ximage->height) {
1898 if (src_y > ximage->height)
1900 h = ximage->height - src_y;
1902 if (w <= 0 || h <= 0)
1905 CGContextRef cgc = d->cgc;
1907 if (gc->gcv.function == GXset ||
1908 gc->gcv.function == GXclear) {
1909 // "set" and "clear" are dumb drawing modes that ignore the source
1910 // bits and just draw solid rectangles.
1911 set_color (cgc, (gc->gcv.function == GXset
1912 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1913 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1914 gc->depth, gc->gcv.alpha_allowed_p, YES);
1915 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1919 int bpl = ximage->bytes_per_line;
1920 int bpp = ximage->bits_per_pixel;
1921 int bsize = bpl * h;
1922 char *data = ximage->data;
1925 r.origin.x = wr.origin.x + dest_x;
1926 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1932 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1933 to create a CGImage from a sub-rectagle of the XImage.
1935 data += (src_y * bpl) + (src_x * 4);
1936 CGDataProviderRef prov =
1937 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1939 CGImageRef cgi = CGImageCreate (w, h,
1942 /* Need this for XPMs to have the right
1943 colors, e.g. the logo in "maze". */
1944 (kCGImageAlphaNoneSkipFirst |
1945 kCGBitmapByteOrder32Host),
1947 NULL, /* decode[] */
1948 NO, /* interpolate */
1949 kCGRenderingIntentDefault);
1950 CGDataProviderRelease (prov);
1951 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1952 CGContextDrawImage (cgc, r, cgi);
1953 CGImageRelease (cgi);
1955 } else { // (bpp == 1)
1957 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1959 #### However, the bit order within a byte in a 1bpp XImage is
1960 the wrong way around from what Quartz expects, so first we
1961 have to copy the data to reverse it. Shit! Maybe it
1962 would be worthwhile to go through the hacks and #ifdef
1963 each one that diddles 1bpp XImage->data directly...
1965 Assert ((src_x % 8) == 0,
1966 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1968 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1969 unsigned char *flipped = (unsigned char *) malloc (bsize);
1971 flipbits ((unsigned char *) data, flipped, bsize);
1973 CGDataProviderRef prov =
1974 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1975 CGImageRef mask = CGImageMaskCreate (w, h,
1978 NULL, /* decode[] */
1979 NO); /* interpolate */
1980 push_fg_gc (d, gc, YES);
1982 CGContextFillRect (cgc, r); // foreground color
1983 CGContextClipToMask (cgc, r, mask);
1984 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1985 CGContextFillRect (cgc, r); // background color
1989 CGDataProviderRelease (prov);
1990 CGImageRelease (mask);
1993 invalidate_drawable_cache (d);
2000 XGetImage (Display *dpy, Drawable d, int x, int y,
2001 unsigned int width, unsigned int height,
2002 unsigned long plane_mask, int format)
2004 const unsigned char *data = 0;
2005 size_t depth, ibpp, ibpl;
2006 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2007 # ifndef USE_BACKBUFFER
2008 NSBitmapImageRep *bm = 0;
2011 Assert ((width < 65535), "improbably large width");
2012 Assert ((height < 65535), "improbably large height");
2013 Assert ((x < 65535 && x > -65535), "improbably large x");
2014 Assert ((y < 65535 && y > -65535), "improbably large y");
2016 CGContextRef cgc = d->cgc;
2018 #ifndef USE_BACKBUFFER
2019 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2020 if (d->type == PIXMAP)
2023 depth = (d->type == PIXMAP
2026 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2027 src_format = BGRA; // #### Should this be ARGB on PPC?
2028 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2029 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2030 data = CGBitmapContextGetData (cgc);
2031 Assert (data, "CGBitmapContextGetData failed");
2033 # ifndef USE_BACKBUFFER
2034 } else { /* (d->type == WINDOW) */
2036 // get the bits (desired sub-rectangle) out of the NSView
2038 nsfrom.origin.x = x;
2039 // nsfrom.origin.y = y;
2040 nsfrom.origin.y = d->frame.size.height - height - y;
2041 nsfrom.size.width = width;
2042 nsfrom.size.height = height;
2043 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2045 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2046 ibpp = [bm bitsPerPixel];
2047 ibpl = [bm bytesPerRow];
2048 data = [bm bitmapData];
2049 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2050 # endif // !USE_BACKBUFFER
2053 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2054 data += (y * ibpl) + (x * (ibpp/8));
2056 format = (depth == 1 ? XYPixmap : ZPixmap);
2057 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2058 format, 0, 0, width, height, 0, 0);
2059 image->data = (char *) malloc (height * image->bytes_per_line);
2061 int obpl = image->bytes_per_line;
2063 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2064 means that on Intel it is BGRA when viewed by bytes (And BGR
2065 when using 24bpp packing).
2067 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2068 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2069 indicator of this latest kink.
2073 const unsigned char *iline = data;
2074 for (yy = 0; yy < height; yy++) {
2076 const unsigned char *iline2 = iline;
2077 for (xx = 0; xx < width; xx++) {
2079 iline2++; // ignore R or A or A or B
2080 iline2++; // ignore G or B or R or G
2081 unsigned char r = *iline2++; // use B or G or G or R
2082 if (ibpp == 32) iline2++; // ignore A or R or B or A
2084 XPutPixel (image, xx, yy, (r ? 1 : 0));
2089 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2090 const unsigned char *iline = data;
2091 unsigned char *oline = (unsigned char *) image->data;
2092 for (yy = 0; yy < height; yy++) {
2094 const unsigned char *iline2 = iline;
2095 unsigned char *oline2 = oline;
2097 switch (src_format) {
2099 for (xx = 0; xx < width; xx++) {
2100 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2101 unsigned char r = *iline2++;
2102 unsigned char g = *iline2++;
2103 unsigned char b = *iline2++;
2104 uint32_t pixel = ((a << 24) |
2108 *((uint32_t *) oline2) = pixel;
2113 for (xx = 0; xx < width; xx++) {
2114 unsigned char r = *iline2++;
2115 unsigned char g = *iline2++;
2116 unsigned char b = *iline2++;
2117 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2118 uint32_t pixel = ((a << 24) |
2122 *((uint32_t *) oline2) = pixel;
2127 for (xx = 0; xx < width; xx++) {
2128 unsigned char b = *iline2++;
2129 unsigned char g = *iline2++;
2130 unsigned char r = *iline2++;
2131 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2132 uint32_t pixel = ((a << 24) |
2136 *((uint32_t *) oline2) = pixel;
2150 # ifndef USE_BACKBUFFER
2151 if (bm) [bm release];
2159 /* Returns a transformation matrix to do rotation as per the provided
2160 EXIF "Orientation" value.
2162 static CGAffineTransform
2163 exif_rotate (int rot, CGSize rect)
2165 CGAffineTransform trans = CGAffineTransformIdentity;
2167 case 2: // flip horizontal
2168 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2169 trans = CGAffineTransformScale (trans, -1, 1);
2172 case 3: // rotate 180
2173 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2174 trans = CGAffineTransformRotate (trans, M_PI);
2177 case 4: // flip vertical
2178 trans = CGAffineTransformMakeTranslation (0, rect.height);
2179 trans = CGAffineTransformScale (trans, 1, -1);
2182 case 5: // transpose (UL-to-LR axis)
2183 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2184 trans = CGAffineTransformScale (trans, -1, 1);
2185 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2188 case 6: // rotate 90
2189 trans = CGAffineTransformMakeTranslation (0, rect.width);
2190 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2193 case 7: // transverse (UR-to-LL axis)
2194 trans = CGAffineTransformMakeScale (-1, 1);
2195 trans = CGAffineTransformRotate (trans, M_PI / 2);
2198 case 8: // rotate 270
2199 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2200 trans = CGAffineTransformRotate (trans, M_PI / 2);
2212 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2213 Bool nsimg_p, void *img_arg,
2214 XRectangle *geom_ret, int exif_rotation)
2218 CGImageSourceRef cgsrc;
2219 # endif // USE_IPHONE
2222 CGContextRef cgc = d->cgc;
2226 NSImage *nsimg = (NSImage *) img_arg;
2227 imgr = [nsimg size];
2230 // convert the NSImage to a CGImage via the toll-free-bridging
2231 // of NSData and CFData...
2233 NSData *nsdata = [NSBitmapImageRep
2234 TIFFRepresentationOfImageRepsInArray:
2235 [nsimg representations]];
2236 CFDataRef cfdata = (CFDataRef) nsdata;
2237 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2238 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2239 # else // USE_IPHONE
2240 cgi = nsimg.CGImage;
2241 # endif // USE_IPHONE
2244 cgi = (CGImageRef) img_arg;
2245 imgr.width = CGImageGetWidth (cgi);
2246 imgr.height = CGImageGetHeight (cgi);
2249 Bool rot_p = (exif_rotation >= 5);
2252 imgr = NSMakeSize (imgr.height, imgr.width);
2254 CGRect winr = d->frame;
2255 float rw = winr.size.width / imgr.width;
2256 float rh = winr.size.height / imgr.height;
2257 float r = (rw < rh ? rw : rh);
2260 dst.size.width = imgr.width * r;
2261 dst.size.height = imgr.height * r;
2262 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2263 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2265 dst2.origin.x = dst2.origin.y = 0;
2267 dst2.size.width = dst.size.height;
2268 dst2.size.height = dst.size.width;
2270 dst2.size = dst.size;
2273 // Clear the part not covered by the image to background or black.
2275 if (d->type == WINDOW)
2276 XClearWindow (dpy, d);
2278 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2279 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2282 CGAffineTransform trans =
2283 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2285 CGContextSaveGState (cgc);
2286 CGContextConcatCTM (cgc,
2287 CGAffineTransformMakeTranslation (dst.origin.x,
2289 CGContextConcatCTM (cgc, trans);
2290 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2291 CGContextDrawImage (cgc, dst2, cgi);
2292 CGContextRestoreGState (cgc);
2297 CGImageRelease (cgi);
2299 # endif // USE_IPHONE
2302 geom_ret->x = dst.origin.x;
2303 geom_ret->y = dst.origin.y;
2304 geom_ret->width = dst.size.width;
2305 geom_ret->height = dst.size.height;
2308 invalidate_drawable_cache (d);
2314 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2316 unsigned int w, unsigned int h,
2317 unsigned long fg, unsigned int bg,
2320 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2321 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2322 (char *) data, w, h, 0, 0);
2324 gcv.foreground = fg;
2325 gcv.background = bg;
2326 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2327 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2330 XDestroyImage (image);
2335 XCreatePixmap (Display *dpy, Drawable d,
2336 unsigned int width, unsigned int height, unsigned int depth)
2338 char *data = (char *) malloc (width * height * 4);
2339 if (! data) return 0;
2341 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2343 p->frame.size.width = width;
2344 p->frame.size.height = height;
2345 p->pixmap.depth = depth;
2346 p->pixmap.cgc_buffer = data;
2348 /* Quartz doesn't have a 1bpp image type.
2349 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2350 don't support that! So we always use 32bpp, regardless of depth. */
2352 p->cgc = CGBitmapContextCreate (data, width, height,
2353 8, /* bits per component */
2354 width * 4, /* bpl */
2356 // Without this, it returns 0...
2357 (kCGImageAlphaNoneSkipFirst |
2358 kCGBitmapByteOrder32Host)
2360 Assert (p->cgc, "could not create CGBitmapContext");
2366 XFreePixmap (Display *d, Pixmap p)
2368 Assert (p && p->type == PIXMAP, "not a pixmap");
2369 invalidate_drawable_cache (p);
2370 CGContextRelease (p->cgc);
2371 if (p->pixmap.cgc_buffer)
2372 free (p->pixmap.cgc_buffer);
2379 copy_pixmap (Display *dpy, Pixmap p)
2382 Assert (p->type == PIXMAP, "not a pixmap");
2384 int width = p->frame.size.width;
2385 int height = p->frame.size.height;
2386 char *data = (char *) malloc (width * height * 4);
2387 if (! data) return 0;
2389 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2391 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2394 p2->pixmap.cgc_buffer = data;
2395 p2->cgc = CGBitmapContextCreate (data, width, height,
2396 8, /* bits per component */
2397 width * 4, /* bpl */
2399 // Without this, it returns 0...
2400 (kCGImageAlphaNoneSkipFirst |
2401 kCGBitmapByteOrder32Host)
2403 Assert (p2->cgc, "could not create CGBitmapContext");
2409 /* Font metric terminology, as used by X11:
2411 "lbearing" is the distance from the logical origin to the leftmost pixel.
2412 If a character's ink extends to the left of the origin, it is negative.
2414 "rbearing" is the distance from the logical origin to the rightmost pixel.
2416 "descent" is the distance from the logical origin to the bottommost pixel.
2417 For characters with descenders, it is negative.
2419 "ascent" is the distance from the logical origin to the topmost pixel.
2420 It is the number of pixels above the baseline.
2422 "width" is the distance from the logical origin to the position where
2423 the logical origin of the next character should be placed.
2425 If "rbearing" is greater than "width", then this character overlaps the
2426 following character. If smaller, then there is trailing blank space.
2430 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2433 query_font (Font fid)
2435 if (!fid || !fid->nsfont) {
2436 Assert (0, "no NSFont in fid");
2439 if (![fid->nsfont fontName]) {
2440 Assert(0, @"broken NSFont in fid");
2447 XFontStruct *f = &fid->metrics;
2448 XCharStruct *min = &f->min_bounds;
2449 XCharStruct *max = &f->max_bounds;
2451 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2454 f->min_char_or_byte2 = first;
2455 f->max_char_or_byte2 = last;
2456 f->default_char = 'M';
2457 f->ascent = CEIL ([fid->nsfont ascender]);
2458 f->descent = -CEIL ([fid->nsfont descender]);
2460 min->width = 255; // set to smaller values in the loop
2463 min->lbearing = 255;
2464 min->rbearing = 255;
2466 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2470 NSBezierPath *bpath = [NSBezierPath bezierPath];
2471 # else // USE_IPHONE
2473 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2474 [fid->nsfont pointSize],
2476 Assert (ctfont, @"no CTFontRef for UIFont");
2477 # endif // USE_IPHONE
2479 for (i = first; i <= last; i++) {
2480 unsigned char str[2];
2484 NSString *nsstr = [NSString stringWithCString:(char *) str
2485 encoding:NSISOLatin1StringEncoding];
2486 NSPoint advancement = { 0, };
2487 NSRect bbox = {{ 0, }, };
2491 /* I can't believe we have to go through this bullshit just to
2492 convert a 'char' to an NSGlyph!!
2494 You might think that we could do
2495 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2496 but that doesn't work; my guess is that glyphWithName expects
2497 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2501 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2502 [ts setFont:fid->nsfont];
2503 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2505 /* Without this, the layout manager ends up on a queue somewhere and is
2506 referenced again after we return to the command loop. Since we don't
2507 use this layout manager again, by that time it may have been garbage
2508 collected, and we crash. Setting this seems to cause `lm' to no
2509 longer be referenced once we exit this block. */
2510 [lm setBackgroundLayoutEnabled:NO];
2512 NSTextContainer *tc = [[NSTextContainer alloc] init];
2513 [lm addTextContainer:tc];
2514 [tc release]; // lm retains tc
2515 [ts addLayoutManager:lm];
2516 [lm release]; // ts retains lm
2517 glyph = [lm glyphAtIndex:0];
2521 /* Compute the bounding box and advancement by converting the glyph
2522 to a bezier path. There appears to be *no other way* to find out
2523 the bounding box of a character: [NSFont boundingRectForGlyph] and
2524 [NSString sizeWithAttributes] both return an advancement-sized
2525 rectangle, not a rectangle completely enclosing the glyph's ink.
2527 advancement.x = advancement.y = 0;
2528 [bpath removeAllPoints];
2529 [bpath moveToPoint:advancement];
2530 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2531 advancement = [bpath currentPoint];
2532 bbox = [bpath bounds];
2534 # else // USE_IPHONE
2536 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2537 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2538 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2539 width of the character and the ascent of the font.
2541 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2542 the CoreText library, but there's no non-CoreText way to turn a
2543 unichar into a CGGlyph.
2545 UniChar uchar = [nsstr characterAtIndex: 0];
2546 CGGlyph cgglyph = 0;
2548 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2550 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2551 kCTFontDefaultOrientation,
2553 CGSize adv = { 0, };
2554 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2556 advancement.x = adv.width;
2557 advancement.y = adv.height;
2559 /* A bug that existed was that the GL FPS display was truncating
2560 characters slightly: commas looked like periods.
2562 At one point, I believed the bounding box was being rounded
2563 wrong and we needed to add padding to it here.
2565 I think what was actually going on was, I was computing rbearing
2566 wrong. Also there was an off-by-one error in texfont.c, displaying
2567 too little of the bitmap.
2569 Adding arbitrarily large padding to the bbox is fine in fontglide
2570 and FPS display, but screws up BSOD. Increasing bbox width makes
2571 inverted text print too wide; decreasing origin makes characters
2574 I think that all 3 states are correct now with the new lbearing
2575 computation plus the texfont fix.
2579 bbox.origin.x -= kludge;
2580 bbox.origin.y -= kludge;
2581 bbox.size.width += kludge;
2582 bbox.size.height += kludge;
2585 # endif // USE_IPHONE
2587 /* Now that we know the advancement and bounding box, we can compute
2588 the lbearing and rbearing.
2590 XCharStruct *cs = &f->per_char[i-first];
2592 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2593 cs->descent = CEIL(-bbox.origin.y);
2594 cs->lbearing = floor (bbox.origin.x);
2595 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2596 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
2597 cs->width = CEIL (advancement.x);
2599 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2601 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2604 max->width = MAX (max->width, cs->width);
2605 max->ascent = MAX (max->ascent, cs->ascent);
2606 max->descent = MAX (max->descent, cs->descent);
2607 max->lbearing = MAX (max->lbearing, cs->lbearing);
2608 max->rbearing = MAX (max->rbearing, cs->rbearing);
2610 min->width = MIN (min->width, cs->width);
2611 min->ascent = MIN (min->ascent, cs->ascent);
2612 min->descent = MIN (min->descent, cs->descent);
2613 min->lbearing = MIN (min->lbearing, cs->lbearing);
2614 min->rbearing = MIN (min->rbearing, cs->rbearing);
2619 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2620 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2621 i, i, cs->width, cs->lbearing, cs->rbearing,
2622 cs->ascent, cs->descent,
2623 bbox.size.width, bbox.size.height,
2624 bbox.origin.x, bbox.origin.y,
2625 advancement.x, advancement.y);
2635 // Since 'Font' includes the metrics, this just makes a copy of that.
2638 XQueryFont (Display *dpy, Font fid)
2641 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2644 // copy XCharStruct array
2645 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2646 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2647 memcpy (f->per_char, fid->metrics.per_char,
2648 size * sizeof (XCharStruct));
2655 copy_font (Font fid)
2657 // copy 'Font' struct
2658 Font fid2 = (Font) malloc (sizeof(*fid2));
2661 // copy XCharStruct array
2662 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2663 fid2->metrics.per_char = (XCharStruct *)
2664 malloc ((size + 2) * sizeof (XCharStruct));
2665 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2666 size * sizeof (XCharStruct));
2668 // copy the other pointers
2669 fid2->ps_name = strdup (fid->ps_name);
2670 // [fid2->nsfont retain];
2671 fid2->metrics.fid = fid2;
2678 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2681 Assert (size > 0, "zero font size");
2686 // "Monaco" only exists in plain.
2687 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2689 if (bold && ital) name = "Courier-BoldOblique";
2690 else if (bold) name = "Courier-Bold";
2691 else if (ital) name = "Courier-Oblique";
2692 else name = "Courier";
2696 // "Georgia" looks better than "Times".
2698 if (bold && ital) name = "Georgia-BoldItalic";
2699 else if (bold) name = "Georgia-Bold";
2700 else if (ital) name = "Georgia-Italic";
2701 else name = "Georgia";
2705 // "Geneva" only exists in plain.
2706 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2707 // "Verdana" renders smoother than "Helvetica" for some reason.
2709 if (bold && ital) name = "Verdana-BoldItalic";
2710 else if (bold) name = "Verdana-Bold";
2711 else if (ital) name = "Verdana-Italic";
2712 else name = "Verdana";
2715 NSString *nsname = [NSString stringWithCString:name
2716 encoding:NSUTF8StringEncoding];
2717 NSFont *f = [NSFont fontWithName:nsname size:size];
2719 *name_ret = strdup(name);
2724 try_native_font (const char *name, float scale,
2725 char **name_ret, float *size_ret)
2727 if (!name) return 0;
2728 const char *spc = strrchr (name, ' ');
2731 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2734 if (size <= 4) return 0;
2738 char *name2 = strdup (name);
2739 name2[strlen(name2) - strlen(spc)] = 0;
2740 NSString *nsname = [NSString stringWithCString:name2
2741 encoding:NSUTF8StringEncoding];
2742 NSFont *f = [NSFont fontWithName:nsname size:size];
2754 /* Returns a random font in the given size and face.
2757 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2760 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2761 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2762 NSArray *fonts = [[NSFontManager sharedFontManager]
2763 availableFontNamesWithTraits:mask];
2764 if (!fonts) return 0;
2766 int n = [fonts count];
2767 if (n <= 0) return 0;
2770 for (j = 0; j < n; j++) {
2771 int i = random() % n;
2772 NSString *name = [fonts objectAtIndex:i];
2773 NSFont *f = [NSFont fontWithName:name size:size];
2776 /* Don't use this font if it (probably) doesn't include ASCII characters.
2778 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2779 if (! (enc == NSUTF8StringEncoding ||
2780 enc == NSISOLatin1StringEncoding ||
2781 enc == NSNonLossyASCIIStringEncoding ||
2782 enc == NSISOLatin2StringEncoding ||
2783 enc == NSUnicodeStringEncoding ||
2784 enc == NSWindowsCP1250StringEncoding ||
2785 enc == NSWindowsCP1252StringEncoding ||
2786 enc == NSMacOSRomanStringEncoding)) {
2787 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2790 // NSLog(@"using \"%@\": %d", name, enc);
2792 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2796 // None of the fonts support ASCII?
2799 # else // USE_IPHONE
2801 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2802 NSArray *families = [UIFont familyNames];
2803 NSMutableDictionary *famdict = [NSMutableDictionary
2804 dictionaryWithCapacity:100];
2805 NSObject *y = [NSNumber numberWithBool:YES];
2806 for (NSString *name in families) {
2807 // There are many dups in the families array -- uniquify it.
2808 [famdict setValue:y forKey:name];
2811 for (NSString *name in famdict) {
2812 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2815 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2818 BOOL bb = MATCH(@"Bold");
2819 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2821 if (!bold != !bb) continue;
2822 if (!ital != !ii) continue;
2824 /* Check if it can do ASCII. No good way to accomplish this!
2825 These are fonts present in iPhone Simulator as of June 2012
2826 that don't include ASCII.
2828 if (MATCH(@"AppleGothic") || // Korean
2829 MATCH(@"Dingbats") || // Dingbats
2830 MATCH(@"Emoji") || // Emoticons
2831 MATCH(@"Geeza") || // Arabic
2832 MATCH(@"Hebrew") || // Hebrew
2833 MATCH(@"HiraKaku") || // Japanese
2834 MATCH(@"HiraMin") || // Japanese
2835 MATCH(@"Kailasa") || // Tibetan
2836 MATCH(@"Ornaments") || // Dingbats
2837 MATCH(@"STHeiti") // Chinese
2841 [fonts addObject:fn];
2846 if (! [fonts count]) return 0; // Nothing suitable?
2848 int i = random() % [fonts count];
2849 NSString *name = [fonts objectAtIndex:i];
2850 UIFont *ff = [UIFont fontWithName:name size:size];
2851 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2855 # endif // USE_IPHONE
2860 try_xlfd_font (const char *name, float scale,
2861 char **name_ret, float *size_ret)
2872 const char *s = (name ? name : "");
2874 while (*s && (*s == '*' || *s == '-'))
2877 while (*s2 && (*s2 != '*' && *s2 != '-'))
2880 unsigned long L = s2-s;
2883 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2884 else if (CMP ("random")) rand = YES;
2885 else if (CMP ("bold")) bold = YES;
2886 else if (CMP ("i")) ital = YES;
2887 else if (CMP ("o")) ital = YES;
2888 else if (CMP ("courier")) fixed = YES;
2889 else if (CMP ("fixed")) fixed = YES;
2890 else if (CMP ("m")) fixed = YES;
2891 else if (CMP ("times")) serif = YES;
2892 else if (CMP ("6x10")) fixed = YES, size = 8;
2893 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2894 else if (CMP ("9x15")) fixed = YES, size = 12;
2895 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2896 else if (CMP ("vga")) fixed = YES, size = 12;
2897 else if (CMP ("console")) fixed = YES, size = 12;
2898 else if (CMP ("gallant")) fixed = YES, size = 12;
2900 else if (size == 0) {
2902 if (1 == sscanf (s, " %d ", &n))
2909 if (size < 6 || size > 1000)
2915 nsfont = random_font (bold, ital, size, &ps_name);
2918 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2920 // if that didn't work, turn off attibutes until it does
2921 // (e.g., there is no "Monaco-Bold".)
2923 if (!nsfont && serif) {
2925 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2927 if (!nsfont && ital) {
2929 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2931 if (!nsfont && bold) {
2933 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2935 if (!nsfont && fixed) {
2937 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2941 *name_ret = ps_name;
2951 XLoadFont (Display *dpy, const char *name)
2953 Font fid = (Font) calloc (1, sizeof(*fid));
2958 // Scale up fonts on Retina displays.
2959 scale = dpy->main_window->window.view.contentScaleFactor;
2962 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2964 if (!fid->nsfont && name &&
2965 strchr (name, ' ') &&
2966 !strchr (name, '*')) {
2967 // If name contains a space but no stars, it is a native font spec --
2968 // return NULL so that we know it really didn't exist. Else, it is an
2969 // XLFD font, so keep trying.
2970 XUnloadFont (dpy, fid);
2975 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2977 // We should never return NULL for XLFD fonts.
2979 Assert (0, "no font");
2982 CFRetain (fid->nsfont); // needed for garbage collection?
2984 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2993 XLoadQueryFont (Display *dpy, const char *name)
2995 Font fid = XLoadFont (dpy, name);
2997 return XQueryFont (dpy, fid);
3001 XUnloadFont (Display *dpy, Font fid)
3004 free (fid->ps_name);
3005 if (fid->metrics.per_char)
3006 free (fid->metrics.per_char);
3008 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3009 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3010 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3011 // They're probably not very big...
3013 // [fid->nsfont release];
3014 // CFRelease (fid->nsfont);
3021 XFreeFontInfo (char **names, XFontStruct *info, int n)
3025 for (i = 0; i < n; i++)
3026 if (names[i]) free (names[i]);
3030 for (i = 0; i < n; i++)
3031 if (info[i].per_char)
3032 free (info[i].per_char);
3039 XFreeFont (Display *dpy, XFontStruct *f)
3042 XFreeFontInfo (0, f, 1);
3043 XUnloadFont (dpy, fid);
3049 XSetFont (Display *dpy, GC gc, Font fid)
3052 XUnloadFont (dpy, gc->gcv.font);
3053 gc->gcv.font = copy_font (fid);
3054 [gc->gcv.font->nsfont retain];
3055 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3060 XTextExtents (XFontStruct *f, const char *s, int length,
3061 int *dir_ret, int *ascent_ret, int *descent_ret,
3064 memset (cs, 0, sizeof(*cs));
3066 for (i = 0; i < length; i++) {
3067 unsigned char c = (unsigned char) s[i];
3068 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3069 c = f->default_char;
3070 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3074 cs->ascent = MAX (cs->ascent, cc->ascent);
3075 cs->descent = MAX (cs->descent, cc->descent);
3076 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3077 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3078 cs->width += cc->width;
3082 *ascent_ret = f->ascent;
3083 *descent_ret = f->descent;
3088 XTextWidth (XFontStruct *f, const char *s, int length)
3090 int ascent, descent, dir;
3092 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3098 set_font (Display *dpy, CGContextRef cgc, GC gc)
3100 Font font = gc->gcv.font;
3102 font = XLoadFont (dpy, 0);
3103 gc->gcv.font = font;
3104 [gc->gcv.font->nsfont retain];
3105 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3107 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
3108 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
3113 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3114 const char *str, int len, BOOL clear_background_p)
3116 if (clear_background_p) {
3117 int ascent, descent, dir;
3119 XTextExtents (&gc->gcv.font->metrics, str, len,
3120 &dir, &ascent, &descent, &cs);
3121 draw_rect (dpy, d, gc,
3122 x + MIN (0, cs.lbearing),
3123 y - MAX (0, ascent),
3124 MAX (MAX (0, cs.rbearing) -
3125 MIN (0, cs.lbearing),
3127 MAX (0, ascent) + MAX (0, descent),
3131 CGRect wr = d->frame;
3134 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
3135 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
3138 CGContextRef cgc = d->cgc;
3139 push_fg_gc (d, gc, YES);
3140 set_font (dpy, cgc, gc);
3142 CGContextSetTextDrawingMode (cgc, kCGTextFill);
3143 if (gc->gcv.antialias_p)
3144 CGContextSetShouldAntialias (cgc, YES);
3145 CGContextShowTextAtPoint (cgc,
3147 wr.origin.y + wr.size.height - y,
3156 unsigned long argb = gc->gcv.foreground;
3157 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3158 float a = ((argb >> 24) & 0xFF) / 255.0;
3159 float r = ((argb >> 16) & 0xFF) / 255.0;
3160 float g = ((argb >> 8) & 0xFF) / 255.0;
3161 float b = ((argb ) & 0xFF) / 255.0;
3162 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3163 NSDictionary *attr =
3164 [NSDictionary dictionaryWithObjectsAndKeys:
3165 gc->gcv.font->nsfont, NSFontAttributeName,
3166 fg, NSForegroundColorAttributeName,
3168 char *s2 = (char *) malloc (len + 1);
3169 strncpy (s2, str, len);
3171 NSString *nsstr = [NSString stringWithCString:s2
3172 encoding:NSISOLatin1StringEncoding];
3175 pos.x = wr.origin.x + x;
3176 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
3177 [nsstr drawAtPoint:pos withAttributes:attr];
3181 invalidate_drawable_cache (d);
3187 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3188 const char *str, int len)
3190 return draw_string (dpy, d, gc, x, y, str, len, NO);
3194 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3195 const char *str, int len)
3197 return draw_string (dpy, d, gc, x, y, str, len, YES);
3202 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3204 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3205 gc->gcv.foreground = fg;
3211 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3213 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3214 gc->gcv.background = bg;
3219 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3221 gc->gcv.alpha_allowed_p = allowed;
3226 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3228 gc->gcv.antialias_p = antialias_p;
3234 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3235 int line_style, int cap_style, int join_style)
3237 gc->gcv.line_width = line_width;
3238 Assert (line_style == LineSolid, "only LineSolid implemented");
3239 // gc->gcv.line_style = line_style;
3240 gc->gcv.cap_style = cap_style;
3241 gc->gcv.join_style = join_style;
3246 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3252 XSetFunction (Display *dpy, GC gc, int which)
3254 gc->gcv.function = which;
3259 XSetSubwindowMode (Display *dpy, GC gc, int which)
3261 gc->gcv.subwindow_mode = which;
3266 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3268 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3270 if (gc->gcv.clip_mask) {
3271 XFreePixmap (dpy, gc->gcv.clip_mask);
3272 CGImageRelease (gc->clip_mask);
3275 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3276 if (gc->gcv.clip_mask)
3278 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3286 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3288 gc->gcv.clip_x_origin = x;
3289 gc->gcv.clip_y_origin = y;
3295 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3296 int *root_x_ret, int *root_y_ret,
3297 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3299 Assert (w && w->type == WINDOW, "not a window");
3302 int x = w->window.last_mouse_x;
3303 int y = w->window.last_mouse_y;
3304 if (root_x_ret) *root_x_ret = x;
3305 if (root_y_ret) *root_y_ret = y;
3306 if (win_x_ret) *win_x_ret = x;
3307 if (win_y_ret) *win_y_ret = y;
3309 # else // !USE_IPHONE
3311 NSWindow *nsw = [w->window.view window];
3313 // get bottom left of window on screen, from bottom left
3314 wpos.x = wpos.y = 0;
3315 wpos = [nsw convertBaseToScreen:wpos];
3318 // get bottom left of view on window, from bottom left
3319 vpos.x = vpos.y = 0;
3320 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3322 // get bottom left of view on screen, from bottom left
3326 // get top left of view on screen, from bottom left
3327 vpos.y += w->frame.size.height;
3329 // get top left of view on screen, from top left
3330 NSArray *screens = [NSScreen screens];
3331 NSScreen *screen = (screens && [screens count] > 0
3332 ? [screens objectAtIndex:0]
3333 : [NSScreen mainScreen]);
3335 double s = w->window.view.contentScaleFactor;
3339 NSRect srect = [screen frame];
3340 vpos.y = (s * srect.size.height) - vpos.y;
3342 // get the mouse position on window, from bottom left
3343 NSEvent *e = [NSApp currentEvent];
3344 NSPoint p = [e locationInWindow];
3346 // get mouse position on screen, from bottom left
3350 // get mouse position on screen, from top left
3351 p.y = srect.size.height - p.y;
3353 if (root_x_ret) *root_x_ret = (int) p.x;
3354 if (root_y_ret) *root_y_ret = (int) p.y;
3355 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3356 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3357 # endif // !USE_IPHONE
3359 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3360 if (root_ret) *root_ret = 0;
3361 if (child_ret) *child_ret = 0;
3366 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3367 int src_x, int src_y,
3368 int *dest_x_ret, int *dest_y_ret,
3371 Assert (w && w->type == WINDOW, "not a window");
3379 # else // !USE_IPHONE
3381 NSWindow *nsw = [w->window.view window];
3383 // get bottom left of window on screen, from bottom left
3384 wpos.x = wpos.y = 0;
3385 wpos = [nsw convertBaseToScreen:wpos];
3388 // get bottom left of view on window, from bottom left
3389 vpos.x = vpos.y = 0;
3390 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3392 // get bottom left of view on screen, from bottom left
3396 // get top left of view on screen, from bottom left
3397 vpos.y += w->frame.size.height;
3399 // get top left of view on screen, from top left
3400 NSArray *screens = [NSScreen screens];
3401 NSScreen *screen = (screens && [screens count] > 0
3402 ? [screens objectAtIndex:0]
3403 : [NSScreen mainScreen]);
3405 double s = w->window.view.contentScaleFactor;
3409 NSRect srect = [screen frame];
3410 vpos.y = (s * srect.size.height) - vpos.y;
3412 // point starts out relative to top left of view
3417 // get point relative to top left of screen
3420 # endif // !USE_IPHONE
3431 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3437 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3440 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3442 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3443 if ((unsigned int) ks <= 255)
3446 // Put control characters in the string. Not meta.
3447 if (e->state & ControlMask) {
3448 if (c >= 'a' && c <= 'z') // Upcase control.
3450 if (c >= '@' && c <= '_') // Shift to control page.
3452 if (c == ' ') // C-SPC is NULL.
3456 if (k_ret) *k_ret = ks;
3457 if (size > 0) buf[0] = c;
3458 if (size > 1) buf[1] = 0;
3459 return (size > 0 ? 1 : 0);
3464 XFlush (Display *dpy)
3466 // Just let the event loop take care of this on its own schedule.
3471 XSync (Display *dpy, Bool flush)
3473 return XFlush (dpy);
3477 // declared in utils/visual.h
3479 has_writable_cells (Screen *s, Visual *v)
3485 visual_depth (Screen *s, Visual *v)
3491 visual_cells (Screen *s, Visual *v)
3497 visual_class (Screen *s, Visual *v)
3503 get_bits_per_pixel (Display *dpy, int depth)
3505 Assert (depth == 32 || depth == 1, "unexpected depth");
3509 // declared in utils/grabclient.h
3511 use_subwindow_mode_p (Screen *screen, Window window)