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 {
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
103 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
109 float size; // points
111 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
112 // But we need the metrics on both of them, so they go here.
117 /* Instead of calling abort(), throw a real exception, so that
118 XScreenSaverView can catch it and display a dialog.
121 jwxyz_abort (const char *fmt, ...)
129 va_start (args, fmt);
130 vsprintf (s, fmt, args);
133 [[NSException exceptionWithName: NSInternalInconsistencyException
134 reason: [NSString stringWithCString: s
135 encoding:NSUTF8StringEncoding]
138 abort(); // not reached
142 /* We keep a list of all of the Displays that have been created and not
143 yet freed so that they can have sensible display numbers. If three
144 displays are created (0, 1, 2) and then #1 is closed, then the fourth
145 display will be given the now-unused display number 1. (Everything in
146 here assumes a 1:1 Display/Screen mapping.)
148 The size of this array is the most number of live displays at one time.
149 So if it's 20, then we'll blow up if the system has 19 monitors and also
150 has System Preferences open (the small preview window).
152 Note that xlockmore-style savers tend to allocate big structures, so
153 setting this to 1000 will waste a few megabytes. Also some of them assume
154 that the number of screens never changes, so dynamically expanding this
158 static Display *jwxyz_live_displays[20] = { 0, };
163 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
165 CGContextRef cgc = (CGContextRef) cgc_arg;
166 NSView *view = (NSView *) nsview_arg;
167 Assert (view, "no view");
170 Display *d = (Display *) calloc (1, sizeof(*d));
171 d->screen = (Screen *) calloc (1, sizeof(Screen));
175 d->screen->screen_number = 0;
178 // Find the first empty slot in live_displays and plug us in.
179 int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
181 for (i = 0; i < size; i++) {
182 if (! jwxyz_live_displays[i])
185 if (i >= size) abort();
186 jwxyz_live_displays[i] = d;
187 d->screen_count = size;
188 d->screen->screen_number = i;
190 # endif // !USE_IPHONE
192 Visual *v = (Visual *) calloc (1, sizeof(Visual));
193 v->class = TrueColor;
194 v->red_mask = 0x00FF0000;
195 v->green_mask = 0x0000FF00;
196 v->blue_mask = 0x000000FF;
198 d->screen->visual = v;
200 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
203 Window w = (Window) calloc (1, sizeof(*w));
205 w->window.view = view;
206 CFRetain (w->window.view); // needed for garbage collection?
207 w->window.background = BlackPixel(0,0);
214 cgc = [[[view window] graphicsContext] graphicsPort];
220 Assert (cgc, "no CGContext");
225 jwxyz_free_display (Display *dpy)
227 jwxyz_XtRemoveInput_all (dpy);
228 // #### jwxyz_XtRemoveTimeOut_all ();
232 // Find us in live_displays and clear that slot.
233 int size = ScreenCount(dpy);
235 for (i = 0; i < size; i++) {
236 if (dpy == jwxyz_live_displays[i]) {
237 jwxyz_live_displays[i] = 0;
241 if (i >= size) abort();
243 # endif // !USE_IPHONE
245 free (dpy->screen->visual);
247 CFRelease (dpy->main_window->window.view);
248 free (dpy->main_window);
254 jwxyz_window_view (Window w)
256 Assert (w && w->type == WINDOW, "not a window");
257 return w->window.view;
261 /* Call this after any modification to the bits on a Pixmap or Window.
262 Most Pixmaps are used frequently as sources and infrequently as
263 destinations, so it pays to cache the data as a CGImage as needed.
266 invalidate_drawable_cache (Drawable d)
269 CGImageRelease (d->cgi);
275 /* Call this when the View changes size or position.
278 jwxyz_window_resized (Display *dpy, Window w,
279 int new_x, int new_y, int new_width, int new_height,
282 CGContextRef cgc = (CGContextRef) cgc_arg;
283 Assert (w && w->type == WINDOW, "not a window");
284 w->frame.origin.x = new_x;
285 w->frame.origin.y = new_y;
286 w->frame.size.width = new_width;
287 w->frame.size.height = new_height;
289 if (cgc) w->cgc = cgc;
290 Assert (w->cgc, "no CGContext");
293 // Figure out which screen the window is currently on.
296 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
302 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
306 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
308 Assert (dpy->cgdpy, "unable to find CGDisplay");
310 # endif // USE_IPHONE
312 # ifndef USE_BACKBUFFER
313 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
314 // then this one's faster.
317 // Figure out this screen's colorspace, and use that for every CGImage.
319 CMProfileRef profile = 0;
320 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
321 Assert (profile, "unable to find colorspace profile");
322 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
323 Assert (dpy->colorspace, "unable to find colorspace");
325 # else // USE_BACKBUFFER
327 // WTF? It's faster if we *do not* use the screen's colorspace!
329 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
330 # endif // USE_BACKBUFFER
332 invalidate_drawable_cache (w);
338 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
340 Assert (w && w->type == WINDOW, "not a window");
341 w->window.last_mouse_x = x;
342 w->window.last_mouse_y = y;
348 jwxyz_flush_context (Display *dpy)
350 // This is only used when USE_BACKBUFFER is off.
351 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
355 display_sources_data (Display *dpy)
357 return dpy->timers_data;
362 XRootWindow (Display *dpy, int screen)
364 return dpy->main_window;
368 XDefaultScreenOfDisplay (Display *dpy)
374 XDefaultVisualOfScreen (Screen *screen)
376 return screen->visual;
380 XDisplayOfScreen (Screen *s)
386 XDisplayNumberOfScreen (Screen *s)
392 XScreenNumberOfScreen (Screen *s)
394 return s->screen_number;
398 jwxyz_ScreenCount (Display *dpy)
400 return dpy->screen_count;
404 XDisplayWidth (Display *dpy, int screen)
406 return (int) dpy->main_window->frame.size.width;
410 XDisplayHeight (Display *dpy, int screen)
412 return (int) dpy->main_window->frame.size.height;
416 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
419 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
420 else if (!alpha_allowed_p)
421 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
422 "bogus color pixel");
427 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
428 BOOL alpha_allowed_p, BOOL fill_p)
430 validate_pixel (argb, depth, alpha_allowed_p);
433 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
435 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
437 float a = ((argb >> 24) & 0xFF) / 255.0;
438 float r = ((argb >> 16) & 0xFF) / 255.0;
439 float g = ((argb >> 8) & 0xFF) / 255.0;
440 float b = ((argb ) & 0xFF) / 255.0;
442 CGContextSetRGBFillColor (cgc, r, g, b, a);
444 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
449 set_line_mode (CGContextRef cgc, XGCValues *gcv)
451 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
452 CGContextSetLineJoin (cgc,
453 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
454 gcv->join_style == JoinRound ? kCGLineJoinRound :
456 CGContextSetLineCap (cgc,
457 gcv->cap_style == CapNotLast ? kCGLineCapButt :
458 gcv->cap_style == CapButt ? kCGLineCapButt :
459 gcv->cap_style == CapRound ? kCGLineCapRound :
464 set_clip_mask (Drawable d, GC gc)
466 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
468 Pixmap p = gc->gcv.clip_mask;
470 Assert (p->type == PIXMAP, "not a pixmap");
472 CGRect wr = d->frame;
474 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
475 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
476 - p->frame.size.height;
477 to.size.width = p->frame.size.width;
478 to.size.height = p->frame.size.height;
480 CGContextClipToMask (d->cgc, to, gc->clip_mask);
484 /* Pushes a GC context; sets BlendMode and ClipMask.
487 push_gc (Drawable d, GC gc)
489 CGContextRef cgc = d->cgc;
490 CGContextSaveGState (cgc);
492 switch (gc->gcv.function) {
495 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
496 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
497 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
498 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
499 default: Assert(0, "unknown gcv function"); break;
502 if (gc->gcv.clip_mask)
503 set_clip_mask (d, gc);
506 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
509 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
512 push_color_gc (Drawable d, GC gc, unsigned long color,
513 BOOL antialias_p, Bool fill_p)
517 int depth = gc->depth;
518 switch (gc->gcv.function) {
519 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
520 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
523 CGContextRef cgc = d->cgc;
524 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
525 CGContextSetShouldAntialias (cgc, antialias_p);
529 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
532 push_fg_gc (Drawable d, GC gc, Bool fill_p)
534 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
537 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
540 push_bg_gc (Drawable d, GC gc, Bool fill_p)
542 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
547 /* You've got to be fucking kidding me!
549 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
550 with repeated calls to CGContextDrawImage than it is to make a single
551 call to CGContextFillRects() with a list of 1x1 rectangles!
553 I still wouldn't call it *fast*, however...
555 #define XDRAWPOINTS_IMAGES
557 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
558 the bitmap data directly is faster. This only works on Pixmaps, though,
559 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
561 #define XDRAWPOINTS_CGDATA
564 XDrawPoints (Display *dpy, Drawable d, GC gc,
565 XPoint *points, int count, int mode)
568 CGRect wr = d->frame;
570 push_fg_gc (d, gc, YES);
572 # ifdef XDRAWPOINTS_CGDATA
574 # ifdef USE_BACKBUFFER
575 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
577 if (d->type == PIXMAP)
580 CGContextRef cgc = d->cgc;
581 void *data = CGBitmapContextGetData (cgc);
582 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
583 size_t w = CGBitmapContextGetWidth (cgc);
584 size_t h = CGBitmapContextGetHeight (cgc);
586 Assert (data, "no bitmap data in Drawable");
588 unsigned long argb = gc->gcv.foreground;
589 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
591 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
593 CGFloat x0 = wr.origin.x;
594 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
596 // It's uglier, but faster, to hoist the conditional out of the loop.
597 if (mode == CoordModePrevious) {
598 CGFloat x = x0, y = y0;
599 for (i = 0; i < count; i++, points++) {
603 if (x >= 0 && x < w && y >= 0 && y < h) {
604 unsigned int *p = (unsigned int *)
605 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
606 *p = (unsigned int) argb;
610 for (i = 0; i < count; i++, points++) {
611 CGFloat x = x0 + points->x;
612 CGFloat y = y0 + points->y;
614 if (x >= 0 && x < w && y >= 0 && y < h) {
615 unsigned int *p = (unsigned int *)
616 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
617 *p = (unsigned int) argb;
622 } else /* d->type == WINDOW */
624 # endif /* XDRAWPOINTS_CGDATA */
627 # ifdef XDRAWPOINTS_IMAGES
629 unsigned int argb = gc->gcv.foreground;
630 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
632 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
634 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
636 CGImageRef cgi = CGImageCreate (1, 1,
639 /* Host-ordered, since we're using the
640 address of an int as the color data. */
641 (kCGImageAlphaNoneSkipFirst |
642 kCGBitmapByteOrder32Host),
645 NO, /* interpolate */
646 kCGRenderingIntentDefault);
647 CGDataProviderRelease (prov);
649 CGContextRef cgc = d->cgc;
651 rect.size.width = rect.size.height = 1;
652 for (i = 0; i < count; i++) {
653 if (i > 0 && mode == CoordModePrevious) {
654 rect.origin.x += points->x;
655 rect.origin.x -= points->y;
657 rect.origin.x = wr.origin.x + points->x;
658 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
661 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
662 CGContextDrawImage (cgc, rect, cgi);
666 CGImageRelease (cgi);
668 # else /* ! XDRAWPOINTS_IMAGES */
670 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
673 for (i = 0; i < count; i++) {
674 r->size.width = r->size.height = 1;
675 if (i > 0 && mode == CoordModePrevious) {
676 r->origin.x = r[-1].origin.x + points->x;
677 r->origin.y = r[-1].origin.x - points->y;
679 r->origin.x = wr.origin.x + points->x;
680 r->origin.y = wr.origin.y + wr.size.height - points->y;
686 CGContextFillRects (d->cgc, rects, count);
689 # endif /* ! XDRAWPOINTS_IMAGES */
693 invalidate_drawable_cache (d);
700 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
705 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
709 static void draw_rect (Display *, Drawable, GC,
710 int x, int y, unsigned int width, unsigned int height,
711 BOOL foreground_p, BOOL fill_p);
714 bitmap_context_p (Drawable d)
716 # ifdef USE_BACKBUFFER
719 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
720 return d->type == PIXMAP;
725 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
726 size_t fill_width, size_t fill_height)
728 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
729 while (fill_height) {
730 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
731 wmemset (dst, fill_data, fill_width);
733 dst = (char *) dst + dst_pitch;
738 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
740 return (char *)dst + dst_pitch * y + x * 4;
744 drawable_depth (Drawable d)
746 return (d->type == WINDOW
747 ? visual_depth (NULL, NULL)
753 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
754 int src_x, int src_y,
755 unsigned int width, unsigned int height,
756 int dst_x, int dst_y)
758 Assert (gc, "no GC");
759 Assert ((width < 65535), "improbably large width");
760 Assert ((height < 65535), "improbably large height");
761 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
762 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
763 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
764 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
766 if (width == 0 || height == 0)
769 if (gc->gcv.function == GXset ||
770 gc->gcv.function == GXclear) {
771 // "set" and "clear" are dumb drawing modes that ignore the source
772 // bits and just draw solid rectangles.
774 (gc->gcv.function == GXset
775 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
776 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
777 gc->depth, gc->gcv.alpha_allowed_p, YES);
778 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
782 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
783 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
784 // bounds of their drawables.
785 BOOL clipped = NO; // Whether we did any clipping of the rects.
787 src_frame = src->frame;
788 dst_frame = dst->frame;
790 // Initialize src_rect...
792 src_rect.origin.x = src_frame.origin.x + src_x;
793 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
795 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
796 src_rect.size.width = width;
797 src_rect.size.height = height;
799 // Initialize dst_rect...
801 dst_rect.origin.x = dst_frame.origin.x + dst_x;
802 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
804 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
805 dst_rect.size.width = width;
806 dst_rect.size.height = height;
808 // Clip rects to frames...
811 # define CLIP(THIS,THAT,VAL,SIZE) do { \
812 float off = THIS##_rect.origin.VAL; \
815 THIS##_rect.size.SIZE += off; \
816 THAT##_rect.size.SIZE += off; \
817 THIS##_rect.origin.VAL -= off; \
818 THAT##_rect.origin.VAL -= off; \
820 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
821 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
824 THIS##_rect.size.SIZE -= off; \
825 THAT##_rect.size.SIZE -= off; \
828 CLIP (dst, src, x, width);
829 CLIP (dst, src, y, height);
831 // Not actually the original dst_rect, just the one before it's clipped to
833 CGRect orig_dst_rect = dst_rect;
835 CLIP (src, dst, x, width);
836 CLIP (src, dst, y, height);
839 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
842 // Sort-of-special case where no pixels can be grabbed from the source,
843 // and the whole destination is filled with the background color.
844 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
846 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
847 (int)src_rect.size.height == (int)dst_rect.size.height,
850 src_rect.size.width = 0;
851 src_rect.size.height = 0;
852 dst_rect.size.width = 0;
853 dst_rect.size.height = 0;
856 NSObject *releaseme = 0;
859 BOOL free_cgi_p = NO;
862 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
863 going on with clipping masks or depths or anything, optimize it by
864 just doing a memcpy instead of going through a CGI.
866 if (bitmap_context_p (src)) {
868 if (bitmap_context_p (dst) &&
869 gc->gcv.function == GXcopy &&
870 !gc->gcv.clip_mask &&
871 drawable_depth (src) == drawable_depth (dst)) {
873 Assert(!(int)src_frame.origin.x &&
874 !(int)src_frame.origin.y &&
875 !(int)dst_frame.origin.x &&
876 !(int)dst_frame.origin.y,
877 "unexpected non-zero origin");
879 char *src_data = CGBitmapContextGetData(src->cgc);
880 char *dst_data = CGBitmapContextGetData(dst->cgc);
881 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
882 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
884 // Int to float and back again. It's not very safe, but it seems to work.
885 int src_x0 = src_rect.origin.x;
886 int dst_x0 = dst_rect.origin.x;
888 // Flip the Y-axis a second time.
889 int src_y0 = (src_frame.origin.y + src_frame.size.height -
890 src_rect.size.height - src_rect.origin.y);
891 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
892 dst_rect.size.height - dst_rect.origin.y);
894 unsigned width0 = (int) src_rect.size.width;
895 unsigned height0 = (int) src_rect.size.height;
897 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
898 (int)src_rect.size.height == (int)dst_rect.size.height,
901 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
902 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
903 size_t src_pitch0 = src_pitch;
904 size_t dst_pitch0 = dst_pitch;
905 size_t bytes = width0 * 4;
907 if (src == dst && dst_y0 > src_y0) {
908 // Copy upwards if the areas might overlap.
909 src_data0 += src_pitch0 * (height0 - 1);
910 dst_data0 += dst_pitch0 * (height0 - 1);
911 src_pitch0 = -src_pitch0;
912 dst_pitch0 = -dst_pitch0;
915 size_t lines0 = height0;
917 // memcpy is an alias for memmove on OS X.
918 memmove(dst_data0, src_data0, bytes);
919 src_data0 += src_pitch0;
920 dst_data0 += dst_pitch0;
926 int orig_dst_x = orig_dst_rect.origin.x;
927 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
928 orig_dst_rect.origin.y - orig_dst_rect.size.height);
929 int orig_width = orig_dst_rect.size.width;
930 int orig_height = orig_dst_rect.size.height;
932 Assert (orig_dst_x >= 0 &&
933 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
935 orig_dst_y + orig_height <= (int) dst_frame.size.height,
938 if (orig_dst_y < dst_y0) {
939 fill_rect_memset (seek_xy (dst_data, dst_pitch,
940 orig_dst_x, orig_dst_y), dst_pitch,
941 (uint32_t) gc->gcv.background, orig_width,
942 dst_y0 - orig_dst_y);
945 if (orig_dst_y + orig_height > dst_y0 + height0) {
946 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
949 (uint32_t) gc->gcv.background, orig_width,
950 orig_dst_y + orig_height - dst_y0 - height0);
953 if (orig_dst_x < dst_x0) {
954 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
955 dst_pitch, (uint32_t) gc->gcv.background,
956 dst_x0 - orig_dst_x, height0);
959 if (dst_x0 + width0 < orig_dst_x + orig_width) {
960 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
962 dst_pitch, (uint32_t) gc->gcv.background,
963 orig_dst_x + orig_width - dst_x0 - width0,
968 invalidate_drawable_cache (dst);
973 // If we are copying from a Pixmap to a Pixmap or Window, we must first
974 // copy the bits to an intermediary CGImage object, then copy that to the
975 // destination drawable's CGContext.
977 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
978 // case of copying from a Pixmap back to itself, but I don't think that
979 // happens very often anyway.)
981 // First we get a CGImage out of the pixmap CGContext -- it's the whole
982 // pixmap, but it presumably shares the data pointer instead of copying
983 // it. We then cache that CGImage it inside the Pixmap object. Note:
984 // invalidate_drawable_cache() must be called to discard this any time a
985 // modification is made to the pixmap, or we'll end up re-using old bits.
988 src->cgi = CGBitmapContextCreateImage (src->cgc);
991 // if doing a sub-rect, trim it down.
992 if (src_rect.origin.x != src_frame.origin.x ||
993 src_rect.origin.y != src_frame.origin.y ||
994 src_rect.size.width != src_frame.size.width ||
995 src_rect.size.height != src_frame.size.height) {
996 // #### I don't understand why this is needed...
997 src_rect.origin.y = (src_frame.size.height -
998 src_rect.size.height - src_rect.origin.y);
999 // This does not copy image data, so it should be fast.
1000 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1004 if (src->type == PIXMAP && src->pixmap.depth == 1)
1007 # ifndef USE_BACKBUFFER
1008 } else { /* (src->type == WINDOW) */
1010 NSRect nsfrom; // NSRect != CGRect on 10.4
1011 nsfrom.origin.x = src_rect.origin.x;
1012 nsfrom.origin.y = src_rect.origin.y;
1013 nsfrom.size.width = src_rect.size.width;
1014 nsfrom.size.height = src_rect.size.height;
1018 // If we are copying from a window to itself, we can use NSCopyBits()
1019 // without first copying the rectangle to an intermediary CGImage.
1020 // This is ~28% faster (but I *expected* it to be twice as fast...)
1021 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1027 // If we are copying from a Window to a Pixmap, we must first copy
1028 // the bits to an intermediary CGImage object, then copy that to the
1029 // Pixmap's CGContext.
1031 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1032 initWithFocusedViewRect:nsfrom];
1033 unsigned char *data = [bm bitmapData];
1034 int bps = [bm bitsPerSample];
1035 int bpp = [bm bitsPerPixel];
1036 int bpl = [bm bytesPerRow];
1039 // create a CGImage from those bits.
1040 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1041 // but that method didn't exist in 10.4.)
1043 CGDataProviderRef prov =
1044 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1046 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1049 /* Use whatever default bit ordering we got from
1050 initWithFocusedViewRect. I would have assumed
1051 that it was (kCGImageAlphaNoneSkipFirst |
1052 kCGBitmapByteOrder32Host), but on Intel,
1057 NULL, /* decode[] */
1058 NO, /* interpolate */
1059 kCGRenderingIntentDefault);
1061 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1062 CGDataProviderRelease (prov);
1065 # endif // !USE_BACKBUFFER
1068 CGContextRef cgc = dst->cgc;
1070 if (mask_p) { // src depth == 1
1072 push_bg_gc (dst, gc, YES);
1074 // fill the destination rectangle with solid background...
1075 CGContextFillRect (cgc, orig_dst_rect);
1077 Assert (cgc, "no CGC with 1-bit XCopyArea");
1079 // then fill in a solid rectangle of the fg color, using the image as an
1080 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1081 set_color (cgc, gc->gcv.foreground, gc->depth,
1082 gc->gcv.alpha_allowed_p, YES);
1083 CGContextClipToMask (cgc, dst_rect, cgi);
1084 CGContextFillRect (cgc, dst_rect);
1088 } else { // src depth > 1
1092 // If either the src or dst rects did not lie within their drawables,
1093 // then we have adjusted both the src and dst rects to account for
1094 // the clipping; that means we need to first clear to the background,
1095 // so that clipped bits end up in the bg color instead of simply not
1099 set_color (cgc, gc->gcv.background, gc->depth,
1100 gc->gcv.alpha_allowed_p, YES);
1101 CGContextFillRect (cgc, orig_dst_rect);
1105 // copy the CGImage onto the destination CGContext
1106 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1107 CGContextDrawImage (cgc, dst_rect, cgi);
1109 // No cgi means src == dst, and both are Windows.
1111 # ifdef USE_BACKBUFFER
1112 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1114 # else // !USE_BACKBUFFER
1116 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1117 nsfrom.origin.y = src_rect.origin.y;
1118 nsfrom.size.width = src_rect.size.width;
1119 nsfrom.size.height = src_rect.size.height;
1121 nsto.x = dst_rect.origin.x;
1122 nsto.y = dst_rect.origin.y;
1123 NSCopyBits (0, nsfrom, nsto);
1124 # endif // !USE_BACKBUFFER
1130 if (free_cgi_p) CGImageRelease (cgi);
1132 if (releaseme) [releaseme release];
1133 invalidate_drawable_cache (dst);
1139 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1140 int src_x, int src_y,
1141 unsigned width, int height,
1142 int dest_x, int dest_y, unsigned long plane)
1144 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1146 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1147 // not to white/black.
1148 return XCopyArea (dpy, src, dest, gc,
1149 src_x, src_y, width, height, dest_x, dest_y);
1154 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1156 // when drawing a zero-length line, obey line-width and cap-style.
1157 if (x1 == x2 && y1 == y2) {
1158 int w = gc->gcv.line_width;
1161 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1162 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1164 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1167 CGRect wr = d->frame;
1169 p.x = wr.origin.x + x1;
1170 p.y = wr.origin.y + wr.size.height - y1;
1172 push_fg_gc (d, gc, NO);
1174 CGContextRef cgc = d->cgc;
1175 set_line_mode (cgc, &gc->gcv);
1176 CGContextBeginPath (cgc);
1177 CGContextMoveToPoint (cgc, p.x, p.y);
1178 p.x = wr.origin.x + x2;
1179 p.y = wr.origin.y + wr.size.height - y2;
1180 CGContextAddLineToPoint (cgc, p.x, p.y);
1181 CGContextStrokePath (cgc);
1183 invalidate_drawable_cache (d);
1188 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1193 CGRect wr = d->frame;
1194 push_fg_gc (d, gc, NO);
1196 CGContextRef cgc = d->cgc;
1198 set_line_mode (cgc, &gc->gcv);
1200 // if the first and last points coincide, use closepath to get
1201 // the proper line-joining.
1202 BOOL closed_p = (points[0].x == points[count-1].x &&
1203 points[0].y == points[count-1].y);
1204 if (closed_p) count--;
1206 p.x = wr.origin.x + points->x;
1207 p.y = wr.origin.y + wr.size.height - points->y;
1209 CGContextBeginPath (cgc);
1210 CGContextMoveToPoint (cgc, p.x, p.y);
1211 for (i = 1; i < count; i++) {
1212 if (mode == CoordModePrevious) {
1216 p.x = wr.origin.x + points->x;
1217 p.y = wr.origin.y + wr.size.height - points->y;
1219 CGContextAddLineToPoint (cgc, p.x, p.y);
1222 if (closed_p) CGContextClosePath (cgc);
1223 CGContextStrokePath (cgc);
1225 invalidate_drawable_cache (d);
1231 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1234 CGRect wr = d->frame;
1236 CGContextRef cgc = d->cgc;
1238 push_fg_gc (d, gc, NO);
1239 set_line_mode (cgc, &gc->gcv);
1240 CGContextBeginPath (cgc);
1241 for (i = 0; i < count; i++) {
1242 CGContextMoveToPoint (cgc,
1243 wr.origin.x + segments->x1,
1244 wr.origin.y + wr.size.height - segments->y1);
1245 CGContextAddLineToPoint (cgc,
1246 wr.origin.x + segments->x2,
1247 wr.origin.y + wr.size.height - segments->y2);
1250 CGContextStrokePath (cgc);
1252 invalidate_drawable_cache (d);
1258 XClearWindow (Display *dpy, Window win)
1260 Assert (win && win->type == WINDOW, "not a window");
1261 CGRect wr = win->frame;
1262 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1266 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1268 Assert (w && w->type == WINDOW, "not a window");
1269 validate_pixel (pixel, 32, NO);
1270 w->window.background = pixel;
1275 draw_rect (Display *dpy, Drawable d, GC gc,
1276 int x, int y, unsigned int width, unsigned int height,
1277 BOOL foreground_p, BOOL fill_p)
1279 CGRect wr = d->frame;
1281 r.origin.x = wr.origin.x + x;
1282 r.origin.y = wr.origin.y + wr.size.height - y - height;
1283 r.size.width = width;
1284 r.size.height = height;
1288 push_fg_gc (d, gc, fill_p);
1290 push_bg_gc (d, gc, fill_p);
1293 CGContextRef cgc = d->cgc;
1295 CGContextFillRect (cgc, r);
1298 set_line_mode (cgc, &gc->gcv);
1299 CGContextStrokeRect (cgc, r);
1304 invalidate_drawable_cache (d);
1309 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1310 unsigned int width, unsigned int height)
1312 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1317 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1318 unsigned int width, unsigned int height)
1320 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1325 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1327 CGRect wr = d->frame;
1329 CGContextRef cgc = d->cgc;
1330 push_fg_gc (d, gc, YES);
1331 for (i = 0; i < n; i++) {
1333 r.origin.x = wr.origin.x + rects->x;
1334 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1335 r.size.width = rects->width;
1336 r.size.height = rects->height;
1337 CGContextFillRect (cgc, r);
1341 invalidate_drawable_cache (d);
1347 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1349 Assert (win && win->type == WINDOW, "not a window");
1350 CGContextRef cgc = win->cgc;
1351 set_color (cgc, win->window.background, 32, NO, YES);
1352 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1358 XFillPolygon (Display *dpy, Drawable d, GC gc,
1359 XPoint *points, int npoints, int shape, int mode)
1361 CGRect wr = d->frame;
1363 push_fg_gc (d, gc, YES);
1364 CGContextRef cgc = d->cgc;
1365 CGContextBeginPath (cgc);
1367 for (i = 0; i < npoints; i++) {
1368 if (i > 0 && mode == CoordModePrevious) {
1372 x = wr.origin.x + points[i].x;
1373 y = wr.origin.y + wr.size.height - points[i].y;
1377 CGContextMoveToPoint (cgc, x, y);
1379 CGContextAddLineToPoint (cgc, x, y);
1381 CGContextClosePath (cgc);
1382 if (gc->gcv.fill_rule == EvenOddRule)
1383 CGContextEOFillPath (cgc);
1385 CGContextFillPath (cgc);
1387 invalidate_drawable_cache (d);
1391 #define radians(DEG) ((DEG) * M_PI / 180.0)
1392 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1395 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1396 unsigned int width, unsigned int height, int angle1, int angle2,
1399 CGRect wr = d->frame;
1401 bound.origin.x = wr.origin.x + x;
1402 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1403 bound.size.width = width;
1404 bound.size.height = height;
1407 ctr.x = bound.origin.x + bound.size.width /2;
1408 ctr.y = bound.origin.y + bound.size.height/2;
1410 float r1 = radians (angle1/64.0);
1411 float r2 = radians (angle2/64.0) + r1;
1412 BOOL clockwise = angle2 < 0;
1413 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1415 push_fg_gc (d, gc, fill_p);
1417 CGContextRef cgc = d->cgc;
1418 CGContextBeginPath (cgc);
1420 CGContextSaveGState(cgc);
1421 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1422 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1424 CGContextMoveToPoint (cgc, 0, 0);
1426 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1427 CGContextRestoreGState (cgc); // restore before stroke, for line width
1430 CGContextClosePath (cgc); // for proper line joining
1433 CGContextFillPath (cgc);
1435 set_line_mode (cgc, &gc->gcv);
1436 CGContextStrokePath (cgc);
1440 invalidate_drawable_cache (d);
1445 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1446 unsigned int width, unsigned int height, int angle1, int angle2)
1448 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1452 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1453 unsigned int width, unsigned int height, int angle1, int angle2)
1455 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1459 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1462 for (i = 0; i < narcs; i++)
1463 draw_arc (dpy, d, gc,
1464 arcs[i].x, arcs[i].y,
1465 arcs[i].width, arcs[i].height,
1466 arcs[i].angle1, arcs[i].angle2,
1472 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1475 for (i = 0; i < narcs; i++)
1476 draw_arc (dpy, d, gc,
1477 arcs[i].x, arcs[i].y,
1478 arcs[i].width, arcs[i].height,
1479 arcs[i].angle1, arcs[i].angle2,
1486 gcv_defaults (XGCValues *gcv, int depth)
1488 memset (gcv, 0, sizeof(*gcv));
1489 gcv->function = GXcopy;
1490 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1491 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1492 gcv->line_width = 1;
1493 gcv->cap_style = CapNotLast;
1494 gcv->join_style = JoinMiter;
1495 gcv->fill_rule = EvenOddRule;
1497 gcv->alpha_allowed_p = NO;
1498 gcv->antialias_p = YES;
1502 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1505 Assert (gc && from, "no gc");
1506 if (!gc || !from) return;
1508 if (mask & GCFunction) gc->gcv.function = from->function;
1509 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1510 if (mask & GCBackground) gc->gcv.background = from->background;
1511 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1512 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1513 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1514 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1515 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1516 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1517 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1519 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1520 if (mask & GCFont) XSetFont (0, gc, from->font);
1522 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1523 gc->gcv.alpha_allowed_p);
1524 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1525 gc->gcv.alpha_allowed_p);
1527 Assert ((! (mask & (GCLineStyle |
1534 GCGraphicsExposures |
1538 "unimplemented gcvalues mask");
1543 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1545 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1546 if (d->type == WINDOW) {
1548 } else { /* (d->type == PIXMAP) */
1549 gc->depth = d->pixmap.depth;
1552 gcv_defaults (&gc->gcv, gc->depth);
1553 set_gcv (gc, xgcv, mask);
1558 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1560 set_gcv (gc, gcv, mask);
1566 XFreeGC (Display *dpy, GC gc)
1569 XUnloadFont (dpy, gc->gcv.font);
1571 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1573 if (gc->gcv.clip_mask) {
1574 XFreePixmap (dpy, gc->gcv.clip_mask);
1575 CGImageRelease (gc->clip_mask);
1583 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1585 Assert (w && w->type == WINDOW, "not a window");
1586 memset (xgwa, 0, sizeof(*xgwa));
1587 xgwa->x = w->frame.origin.x;
1588 xgwa->y = w->frame.origin.y;
1589 xgwa->width = w->frame.size.width;
1590 xgwa->height = w->frame.size.height;
1592 xgwa->screen = dpy->screen;
1593 xgwa->visual = dpy->screen->visual;
1598 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1599 int *x_ret, int *y_ret,
1600 unsigned int *w_ret, unsigned int *h_ret,
1601 unsigned int *bw_ret, unsigned int *d_ret)
1603 *x_ret = d->frame.origin.x;
1604 *y_ret = d->frame.origin.y;
1605 *w_ret = d->frame.size.width;
1606 *h_ret = d->frame.size.height;
1607 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1608 *root_ret = RootWindow (dpy, 0);
1615 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1617 // store 32 bit ARGB in the pixel field.
1618 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1619 color->pixel = (uint32_t)
1621 (((color->red >> 8) & 0xFF) << 16) |
1622 (((color->green >> 8) & 0xFF) << 8) |
1623 (((color->blue >> 8) & 0xFF) ));
1628 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1629 unsigned long *pmret, unsigned int npl,
1630 unsigned long *pxret, unsigned int npx)
1636 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1638 Assert(0, "XStoreColors called");
1643 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1645 Assert(0, "XStoreColor called");
1650 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1651 unsigned long planes)
1657 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1659 unsigned char r=0, g=0, b=0;
1660 if (*spec == '#' && strlen(spec) == 7) {
1661 static unsigned const char hex[] = { // yeah yeah, shoot me.
1662 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,
1663 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,
1664 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,
1665 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,
1666 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,
1667 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,
1668 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,
1669 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};
1670 r = (hex[spec[1]] << 4) | hex[spec[2]];
1671 g = (hex[spec[3]] << 4) | hex[spec[4]];
1672 b = (hex[spec[5]] << 4) | hex[spec[6]];
1673 } else if (!strcasecmp(spec,"black")) {
1675 } else if (!strcasecmp(spec,"white")) {
1677 } else if (!strcasecmp(spec,"red")) {
1679 } else if (!strcasecmp(spec,"green")) {
1681 } else if (!strcasecmp(spec,"blue")) {
1683 } else if (!strcasecmp(spec,"cyan")) {
1685 } else if (!strcasecmp(spec,"magenta")) {
1687 } else if (!strcasecmp(spec,"yellow")) {
1693 ret->red = (r << 8) | r;
1694 ret->green = (g << 8) | g;
1695 ret->blue = (b << 8) | b;
1696 ret->flags = DoRed|DoGreen|DoBlue;
1701 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1702 XColor *screen_ret, XColor *exact_ret)
1704 if (! XParseColor (dpy, cmap, name, screen_ret))
1706 *exact_ret = *screen_ret;
1707 return XAllocColor (dpy, cmap, screen_ret);
1711 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1713 validate_pixel (color->pixel, 32, NO);
1714 unsigned char r = ((color->pixel >> 16) & 0xFF);
1715 unsigned char g = ((color->pixel >> 8) & 0xFF);
1716 unsigned char b = ((color->pixel ) & 0xFF);
1717 color->red = (r << 8) | r;
1718 color->green = (g << 8) | g;
1719 color->blue = (b << 8) | b;
1720 color->flags = DoRed|DoGreen|DoBlue;
1725 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1728 for (i = 0; i < n; i++)
1729 XQueryColor (dpy, cmap, &c[i]);
1734 static unsigned long
1735 ximage_getpixel_1 (XImage *ximage, int x, int y)
1737 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1741 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1744 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1746 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1751 static unsigned long
1752 ximage_getpixel_32 (XImage *ximage, int x, int y)
1754 return ((unsigned long)
1755 *((uint32_t *) ximage->data +
1756 (y * (ximage->bytes_per_line >> 2)) +
1761 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1763 *((uint32_t *) ximage->data +
1764 (y * (ximage->bytes_per_line >> 2)) +
1765 x) = (uint32_t) pixel;
1771 XInitImage (XImage *ximage)
1773 if (!ximage->bytes_per_line)
1774 ximage->bytes_per_line = (ximage->depth == 1
1775 ? (ximage->width + 7) / 8
1776 : ximage->width * 4);
1778 if (ximage->depth == 1) {
1779 ximage->f.put_pixel = ximage_putpixel_1;
1780 ximage->f.get_pixel = ximage_getpixel_1;
1781 } else if (ximage->depth == 32 || ximage->depth == 24) {
1782 ximage->f.put_pixel = ximage_putpixel_32;
1783 ximage->f.get_pixel = ximage_getpixel_32;
1785 Assert (0, "unknown depth");
1792 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1793 int format, int offset, char *data,
1794 unsigned int width, unsigned int height,
1795 int bitmap_pad, int bytes_per_line)
1797 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1798 ximage->width = width;
1799 ximage->height = height;
1800 ximage->format = format;
1801 ximage->data = data;
1802 ximage->bitmap_unit = 8;
1803 ximage->byte_order = LSBFirst;
1804 ximage->bitmap_bit_order = ximage->byte_order;
1805 ximage->bitmap_pad = bitmap_pad;
1806 ximage->depth = depth;
1807 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1808 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1809 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1810 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1811 ximage->bytes_per_line = bytes_per_line;
1813 XInitImage (ximage);
1818 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1820 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1821 w, h, from->bitmap_pad, 0);
1822 to->data = (char *) malloc (h * to->bytes_per_line);
1824 if (x >= from->width)
1826 else if (x+w > from->width)
1827 w = from->width - x;
1829 if (y >= from->height)
1831 else if (y+h > from->height)
1832 h = from->height - y;
1835 for (ty = 0; ty < h; ty++)
1836 for (tx = 0; tx < w; tx++)
1837 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1842 XPixmapFormatValues *
1843 XListPixmapFormats (Display *dpy, int *n_ret)
1845 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1847 ret[0].bits_per_pixel = 32;
1848 ret[0].scanline_pad = 8;
1850 ret[1].bits_per_pixel = 1;
1851 ret[1].scanline_pad = 8;
1858 XGetPixel (XImage *ximage, int x, int y)
1860 return ximage->f.get_pixel (ximage, x, y);
1865 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1867 return ximage->f.put_pixel (ximage, x, y, pixel);
1871 XDestroyImage (XImage *ximage)
1873 if (ximage->data) free (ximage->data);
1880 flipbits (unsigned const char *in, unsigned char *out, int length)
1882 static const unsigned char table[256] = {
1883 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1884 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1885 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1886 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1887 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1888 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1889 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1890 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1891 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1892 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1893 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1894 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1895 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1896 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1897 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1898 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1899 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1900 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1901 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1902 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1903 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1904 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1905 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1906 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1907 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1908 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1909 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1910 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1911 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1912 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1913 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1914 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1916 while (--length > 0)
1917 *out++ = table[*in++];
1922 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1923 int src_x, int src_y, int dest_x, int dest_y,
1924 unsigned int w, unsigned int h)
1926 CGRect wr = d->frame;
1928 Assert (gc, "no GC");
1929 Assert ((w < 65535), "improbably large width");
1930 Assert ((h < 65535), "improbably large height");
1931 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1932 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1933 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1934 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1936 // Clip width and height to the bounds of the Drawable
1938 if (dest_x + w > wr.size.width) {
1939 if (dest_x > wr.size.width)
1941 w = wr.size.width - dest_x;
1943 if (dest_y + h > wr.size.height) {
1944 if (dest_y > wr.size.height)
1946 h = wr.size.height - dest_y;
1948 if (w <= 0 || h <= 0)
1951 // Clip width and height to the bounds of the XImage
1953 if (src_x + w > ximage->width) {
1954 if (src_x > ximage->width)
1956 w = ximage->width - src_x;
1958 if (src_y + h > ximage->height) {
1959 if (src_y > ximage->height)
1961 h = ximage->height - src_y;
1963 if (w <= 0 || h <= 0)
1966 CGContextRef cgc = d->cgc;
1968 if (gc->gcv.function == GXset ||
1969 gc->gcv.function == GXclear) {
1970 // "set" and "clear" are dumb drawing modes that ignore the source
1971 // bits and just draw solid rectangles.
1972 set_color (cgc, (gc->gcv.function == GXset
1973 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1974 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1975 gc->depth, gc->gcv.alpha_allowed_p, YES);
1976 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1980 int bpl = ximage->bytes_per_line;
1981 int bpp = ximage->bits_per_pixel;
1982 int bsize = bpl * h;
1983 char *data = ximage->data;
1986 r.origin.x = wr.origin.x + dest_x;
1987 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1993 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1994 to create a CGImage from a sub-rectagle of the XImage.
1996 data += (src_y * bpl) + (src_x * 4);
1997 CGDataProviderRef prov =
1998 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2000 CGImageRef cgi = CGImageCreate (w, h,
2003 /* Need this for XPMs to have the right
2004 colors, e.g. the logo in "maze". */
2005 (kCGImageAlphaNoneSkipFirst |
2006 kCGBitmapByteOrder32Host),
2008 NULL, /* decode[] */
2009 NO, /* interpolate */
2010 kCGRenderingIntentDefault);
2011 CGDataProviderRelease (prov);
2012 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2013 CGContextDrawImage (cgc, r, cgi);
2014 CGImageRelease (cgi);
2016 } else { // (bpp == 1)
2018 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2020 #### However, the bit order within a byte in a 1bpp XImage is
2021 the wrong way around from what Quartz expects, so first we
2022 have to copy the data to reverse it. Shit! Maybe it
2023 would be worthwhile to go through the hacks and #ifdef
2024 each one that diddles 1bpp XImage->data directly...
2026 Assert ((src_x % 8) == 0,
2027 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2029 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
2030 unsigned char *flipped = (unsigned char *) malloc (bsize);
2032 flipbits ((unsigned char *) data, flipped, bsize);
2034 CGDataProviderRef prov =
2035 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2036 CGImageRef mask = CGImageMaskCreate (w, h,
2039 NULL, /* decode[] */
2040 NO); /* interpolate */
2041 push_fg_gc (d, gc, YES);
2043 CGContextFillRect (cgc, r); // foreground color
2044 CGContextClipToMask (cgc, r, mask);
2045 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
2046 CGContextFillRect (cgc, r); // background color
2050 CGDataProviderRelease (prov);
2051 CGImageRelease (mask);
2054 invalidate_drawable_cache (d);
2061 XGetImage (Display *dpy, Drawable d, int x, int y,
2062 unsigned int width, unsigned int height,
2063 unsigned long plane_mask, int format)
2065 const unsigned char *data = 0;
2066 size_t depth, ibpp, ibpl;
2067 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2068 # ifndef USE_BACKBUFFER
2069 NSBitmapImageRep *bm = 0;
2072 Assert ((width < 65535), "improbably large width");
2073 Assert ((height < 65535), "improbably large height");
2074 Assert ((x < 65535 && x > -65535), "improbably large x");
2075 Assert ((y < 65535 && y > -65535), "improbably large y");
2077 CGContextRef cgc = d->cgc;
2079 #ifndef USE_BACKBUFFER
2080 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2081 if (d->type == PIXMAP)
2084 depth = (d->type == PIXMAP
2087 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2088 src_format = BGRA; // #### Should this be ARGB on PPC?
2089 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2090 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2091 data = CGBitmapContextGetData (cgc);
2092 Assert (data, "CGBitmapContextGetData failed");
2094 # ifndef USE_BACKBUFFER
2095 } else { /* (d->type == WINDOW) */
2097 // get the bits (desired sub-rectangle) out of the NSView
2099 nsfrom.origin.x = x;
2100 // nsfrom.origin.y = y;
2101 nsfrom.origin.y = d->frame.size.height - height - y;
2102 nsfrom.size.width = width;
2103 nsfrom.size.height = height;
2104 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2106 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2107 ibpp = [bm bitsPerPixel];
2108 ibpl = [bm bytesPerRow];
2109 data = [bm bitmapData];
2110 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2111 # endif // !USE_BACKBUFFER
2114 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2115 data += (y * ibpl) + (x * (ibpp/8));
2117 format = (depth == 1 ? XYPixmap : ZPixmap);
2118 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2119 format, 0, 0, width, height, 0, 0);
2120 image->data = (char *) malloc (height * image->bytes_per_line);
2122 int obpl = image->bytes_per_line;
2124 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2125 means that on Intel it is BGRA when viewed by bytes (And BGR
2126 when using 24bpp packing).
2128 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2129 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2130 indicator of this latest kink.
2134 const unsigned char *iline = data;
2135 for (yy = 0; yy < height; yy++) {
2137 const unsigned char *iline2 = iline;
2138 for (xx = 0; xx < width; xx++) {
2140 iline2++; // ignore R or A or A or B
2141 iline2++; // ignore G or B or R or G
2142 unsigned char r = *iline2++; // use B or G or G or R
2143 if (ibpp == 32) iline2++; // ignore A or R or B or A
2145 XPutPixel (image, xx, yy, (r ? 1 : 0));
2150 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2151 const unsigned char *iline = data;
2152 unsigned char *oline = (unsigned char *) image->data;
2153 for (yy = 0; yy < height; yy++) {
2155 const unsigned char *iline2 = iline;
2156 unsigned char *oline2 = oline;
2158 switch (src_format) {
2160 for (xx = 0; xx < width; xx++) {
2161 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2162 unsigned char r = *iline2++;
2163 unsigned char g = *iline2++;
2164 unsigned char b = *iline2++;
2165 uint32_t pixel = ((a << 24) |
2169 *((uint32_t *) oline2) = pixel;
2174 for (xx = 0; xx < width; xx++) {
2175 unsigned char r = *iline2++;
2176 unsigned char g = *iline2++;
2177 unsigned char b = *iline2++;
2178 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2179 uint32_t pixel = ((a << 24) |
2183 *((uint32_t *) oline2) = pixel;
2188 for (xx = 0; xx < width; xx++) {
2189 unsigned char b = *iline2++;
2190 unsigned char g = *iline2++;
2191 unsigned char r = *iline2++;
2192 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2193 uint32_t pixel = ((a << 24) |
2197 *((uint32_t *) oline2) = pixel;
2211 # ifndef USE_BACKBUFFER
2212 if (bm) [bm release];
2220 /* Returns a transformation matrix to do rotation as per the provided
2221 EXIF "Orientation" value.
2223 static CGAffineTransform
2224 exif_rotate (int rot, CGSize rect)
2226 CGAffineTransform trans = CGAffineTransformIdentity;
2228 case 2: // flip horizontal
2229 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2230 trans = CGAffineTransformScale (trans, -1, 1);
2233 case 3: // rotate 180
2234 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2235 trans = CGAffineTransformRotate (trans, M_PI);
2238 case 4: // flip vertical
2239 trans = CGAffineTransformMakeTranslation (0, rect.height);
2240 trans = CGAffineTransformScale (trans, 1, -1);
2243 case 5: // transpose (UL-to-LR axis)
2244 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2245 trans = CGAffineTransformScale (trans, -1, 1);
2246 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2249 case 6: // rotate 90
2250 trans = CGAffineTransformMakeTranslation (0, rect.width);
2251 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2254 case 7: // transverse (UR-to-LL axis)
2255 trans = CGAffineTransformMakeScale (-1, 1);
2256 trans = CGAffineTransformRotate (trans, M_PI / 2);
2259 case 8: // rotate 270
2260 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2261 trans = CGAffineTransformRotate (trans, M_PI / 2);
2273 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2274 Bool nsimg_p, void *img_arg,
2275 XRectangle *geom_ret, int exif_rotation)
2279 CGImageSourceRef cgsrc;
2280 # endif // USE_IPHONE
2283 CGContextRef cgc = d->cgc;
2287 NSImage *nsimg = (NSImage *) img_arg;
2288 imgr = [nsimg size];
2291 // convert the NSImage to a CGImage via the toll-free-bridging
2292 // of NSData and CFData...
2294 NSData *nsdata = [NSBitmapImageRep
2295 TIFFRepresentationOfImageRepsInArray:
2296 [nsimg representations]];
2297 CFDataRef cfdata = (CFDataRef) nsdata;
2298 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2299 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2300 # else // USE_IPHONE
2301 cgi = nsimg.CGImage;
2302 # endif // USE_IPHONE
2305 cgi = (CGImageRef) img_arg;
2306 imgr.width = CGImageGetWidth (cgi);
2307 imgr.height = CGImageGetHeight (cgi);
2310 Bool rot_p = (exif_rotation >= 5);
2313 imgr = NSMakeSize (imgr.height, imgr.width);
2315 CGRect winr = d->frame;
2316 float rw = winr.size.width / imgr.width;
2317 float rh = winr.size.height / imgr.height;
2318 float r = (rw < rh ? rw : rh);
2321 dst.size.width = imgr.width * r;
2322 dst.size.height = imgr.height * r;
2323 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2324 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2326 dst2.origin.x = dst2.origin.y = 0;
2328 dst2.size.width = dst.size.height;
2329 dst2.size.height = dst.size.width;
2331 dst2.size = dst.size;
2334 // Clear the part not covered by the image to background or black.
2336 if (d->type == WINDOW)
2337 XClearWindow (dpy, d);
2339 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2340 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2343 CGAffineTransform trans =
2344 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2346 CGContextSaveGState (cgc);
2347 CGContextConcatCTM (cgc,
2348 CGAffineTransformMakeTranslation (dst.origin.x,
2350 CGContextConcatCTM (cgc, trans);
2351 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2352 CGContextDrawImage (cgc, dst2, cgi);
2353 CGContextRestoreGState (cgc);
2358 CGImageRelease (cgi);
2360 # endif // USE_IPHONE
2363 geom_ret->x = dst.origin.x;
2364 geom_ret->y = dst.origin.y;
2365 geom_ret->width = dst.size.width;
2366 geom_ret->height = dst.size.height;
2369 invalidate_drawable_cache (d);
2375 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2377 unsigned int w, unsigned int h,
2378 unsigned long fg, unsigned int bg,
2381 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2382 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2383 (char *) data, w, h, 0, 0);
2385 gcv.foreground = fg;
2386 gcv.background = bg;
2387 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2388 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2391 XDestroyImage (image);
2396 XCreatePixmap (Display *dpy, Drawable d,
2397 unsigned int width, unsigned int height, unsigned int depth)
2399 char *data = (char *) malloc (width * height * 4);
2400 if (! data) return 0;
2402 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2404 p->frame.size.width = width;
2405 p->frame.size.height = height;
2406 p->pixmap.depth = depth;
2407 p->pixmap.cgc_buffer = data;
2409 /* Quartz doesn't have a 1bpp image type.
2410 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2411 don't support that! So we always use 32bpp, regardless of depth. */
2413 p->cgc = CGBitmapContextCreate (data, width, height,
2414 8, /* bits per component */
2415 width * 4, /* bpl */
2417 // Without this, it returns 0...
2418 (kCGImageAlphaNoneSkipFirst |
2419 kCGBitmapByteOrder32Host)
2421 Assert (p->cgc, "could not create CGBitmapContext");
2427 XFreePixmap (Display *d, Pixmap p)
2429 Assert (p && p->type == PIXMAP, "not a pixmap");
2430 invalidate_drawable_cache (p);
2431 CGContextRelease (p->cgc);
2432 if (p->pixmap.cgc_buffer)
2433 free (p->pixmap.cgc_buffer);
2440 copy_pixmap (Display *dpy, Pixmap p)
2443 Assert (p->type == PIXMAP, "not a pixmap");
2445 int width = p->frame.size.width;
2446 int height = p->frame.size.height;
2447 char *data = (char *) malloc (width * height * 4);
2448 if (! data) return 0;
2450 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2452 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2455 p2->pixmap.cgc_buffer = data;
2456 p2->cgc = CGBitmapContextCreate (data, width, height,
2457 8, /* bits per component */
2458 width * 4, /* bpl */
2460 // Without this, it returns 0...
2461 (kCGImageAlphaNoneSkipFirst |
2462 kCGBitmapByteOrder32Host)
2464 Assert (p2->cgc, "could not create CGBitmapContext");
2470 /* Font metric terminology, as used by X11:
2472 "lbearing" is the distance from the logical origin to the leftmost pixel.
2473 If a character's ink extends to the left of the origin, it is negative.
2475 "rbearing" is the distance from the logical origin to the rightmost pixel.
2477 "descent" is the distance from the logical origin to the bottommost pixel.
2478 For characters with descenders, it is negative.
2480 "ascent" is the distance from the logical origin to the topmost pixel.
2481 It is the number of pixels above the baseline.
2483 "width" is the distance from the logical origin to the position where
2484 the logical origin of the next character should be placed.
2486 If "rbearing" is greater than "width", then this character overlaps the
2487 following character. If smaller, then there is trailing blank space.
2491 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2494 query_font (Font fid)
2496 if (!fid || !fid->nsfont) {
2497 Assert (0, "no NSFont in fid");
2500 if (![fid->nsfont fontName]) {
2501 Assert(0, @"broken NSFont in fid");
2508 XFontStruct *f = &fid->metrics;
2509 XCharStruct *min = &f->min_bounds;
2510 XCharStruct *max = &f->max_bounds;
2512 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2515 f->min_char_or_byte2 = first;
2516 f->max_char_or_byte2 = last;
2517 f->default_char = 'M';
2518 f->ascent = CEIL ([fid->nsfont ascender]);
2519 f->descent = -CEIL ([fid->nsfont descender]);
2521 min->width = 255; // set to smaller values in the loop
2524 min->lbearing = 255;
2525 min->rbearing = 255;
2527 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2531 NSBezierPath *bpath = [NSBezierPath bezierPath];
2532 # else // USE_IPHONE
2534 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2535 [fid->nsfont pointSize],
2537 Assert (ctfont, @"no CTFontRef for UIFont");
2538 # endif // USE_IPHONE
2540 for (i = first; i <= last; i++) {
2541 unsigned char str[2];
2545 NSString *nsstr = [NSString stringWithCString:(char *) str
2546 encoding:NSISOLatin1StringEncoding];
2547 NSPoint advancement = { 0, };
2548 NSRect bbox = {{ 0, }, };
2552 /* I can't believe we have to go through this bullshit just to
2553 convert a 'char' to an NSGlyph!!
2555 You might think that we could do
2556 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2557 but that doesn't work; my guess is that glyphWithName expects
2558 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2562 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2563 [ts setFont:fid->nsfont];
2564 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2566 /* Without this, the layout manager ends up on a queue somewhere and is
2567 referenced again after we return to the command loop. Since we don't
2568 use this layout manager again, by that time it may have been garbage
2569 collected, and we crash. Setting this seems to cause `lm' to no
2570 longer be referenced once we exit this block. */
2571 [lm setBackgroundLayoutEnabled:NO];
2573 NSTextContainer *tc = [[NSTextContainer alloc] init];
2574 [lm addTextContainer:tc];
2575 [tc release]; // lm retains tc
2576 [ts addLayoutManager:lm];
2577 [lm release]; // ts retains lm
2578 glyph = [lm glyphAtIndex:0];
2582 /* Compute the bounding box and advancement by converting the glyph
2583 to a bezier path. There appears to be *no other way* to find out
2584 the bounding box of a character: [NSFont boundingRectForGlyph] and
2585 [NSString sizeWithAttributes] both return an advancement-sized
2586 rectangle, not a rectangle completely enclosing the glyph's ink.
2588 advancement.x = advancement.y = 0;
2589 [bpath removeAllPoints];
2590 [bpath moveToPoint:advancement];
2591 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2592 advancement = [bpath currentPoint];
2593 bbox = [bpath bounds];
2595 # else // USE_IPHONE
2597 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2598 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2599 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2600 width of the character and the ascent of the font.
2602 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2603 the CoreText library, but there's no non-CoreText way to turn a
2604 unichar into a CGGlyph.
2606 UniChar uchar = [nsstr characterAtIndex: 0];
2607 CGGlyph cgglyph = 0;
2609 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2611 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2612 kCTFontDefaultOrientation,
2614 CGSize adv = { 0, };
2615 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2617 advancement.x = adv.width;
2618 advancement.y = adv.height;
2620 /* A bug that existed was that the GL FPS display was truncating
2621 characters slightly: commas looked like periods.
2623 At one point, I believed the bounding box was being rounded
2624 wrong and we needed to add padding to it here.
2626 I think what was actually going on was, I was computing rbearing
2627 wrong. Also there was an off-by-one error in texfont.c, displaying
2628 too little of the bitmap.
2630 Adding arbitrarily large padding to the bbox is fine in fontglide
2631 and FPS display, but screws up BSOD. Increasing bbox width makes
2632 inverted text print too wide; decreasing origin makes characters
2635 I think that all 3 states are correct now with the new lbearing
2636 computation plus the texfont fix.
2640 bbox.origin.x -= kludge;
2641 bbox.origin.y -= kludge;
2642 bbox.size.width += kludge;
2643 bbox.size.height += kludge;
2646 # endif // USE_IPHONE
2648 /* Now that we know the advancement and bounding box, we can compute
2649 the lbearing and rbearing.
2651 XCharStruct *cs = &f->per_char[i-first];
2653 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2654 cs->descent = CEIL(-bbox.origin.y);
2655 cs->lbearing = floor (bbox.origin.x);
2656 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2657 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
2658 cs->width = CEIL (advancement.x);
2660 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2662 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2665 max->width = MAX (max->width, cs->width);
2666 max->ascent = MAX (max->ascent, cs->ascent);
2667 max->descent = MAX (max->descent, cs->descent);
2668 max->lbearing = MAX (max->lbearing, cs->lbearing);
2669 max->rbearing = MAX (max->rbearing, cs->rbearing);
2671 min->width = MIN (min->width, cs->width);
2672 min->ascent = MIN (min->ascent, cs->ascent);
2673 min->descent = MIN (min->descent, cs->descent);
2674 min->lbearing = MIN (min->lbearing, cs->lbearing);
2675 min->rbearing = MIN (min->rbearing, cs->rbearing);
2680 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2681 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2682 i, i, cs->width, cs->lbearing, cs->rbearing,
2683 cs->ascent, cs->descent,
2684 bbox.size.width, bbox.size.height,
2685 bbox.origin.x, bbox.origin.y,
2686 advancement.x, advancement.y);
2696 // Since 'Font' includes the metrics, this just makes a copy of that.
2699 XQueryFont (Display *dpy, Font fid)
2702 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2705 // copy XCharStruct array
2706 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2707 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2708 memcpy (f->per_char, fid->metrics.per_char,
2709 size * sizeof (XCharStruct));
2716 copy_font (Font fid)
2718 // copy 'Font' struct
2719 Font fid2 = (Font) malloc (sizeof(*fid2));
2722 // copy XCharStruct array
2723 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2724 fid2->metrics.per_char = (XCharStruct *)
2725 malloc ((size + 2) * sizeof (XCharStruct));
2726 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2727 size * sizeof (XCharStruct));
2729 // copy the other pointers
2730 fid2->ps_name = strdup (fid->ps_name);
2731 // [fid2->nsfont retain];
2732 fid2->metrics.fid = fid2;
2739 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2742 Assert (size > 0, "zero font size");
2747 // "Monaco" only exists in plain.
2748 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2750 if (bold && ital) name = "Courier-BoldOblique";
2751 else if (bold) name = "Courier-Bold";
2752 else if (ital) name = "Courier-Oblique";
2753 else name = "Courier";
2757 // "Georgia" looks better than "Times".
2759 if (bold && ital) name = "Georgia-BoldItalic";
2760 else if (bold) name = "Georgia-Bold";
2761 else if (ital) name = "Georgia-Italic";
2762 else name = "Georgia";
2766 // "Geneva" only exists in plain.
2767 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2768 // "Verdana" renders smoother than "Helvetica" for some reason.
2770 if (bold && ital) name = "Verdana-BoldItalic";
2771 else if (bold) name = "Verdana-Bold";
2772 else if (ital) name = "Verdana-Italic";
2773 else name = "Verdana";
2776 NSString *nsname = [NSString stringWithCString:name
2777 encoding:NSUTF8StringEncoding];
2778 NSFont *f = [NSFont fontWithName:nsname size:size];
2780 *name_ret = strdup(name);
2785 try_native_font (const char *name, float scale,
2786 char **name_ret, float *size_ret)
2788 if (!name) return 0;
2789 const char *spc = strrchr (name, ' ');
2792 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2795 if (size <= 4) return 0;
2799 char *name2 = strdup (name);
2800 name2[strlen(name2) - strlen(spc)] = 0;
2801 NSString *nsname = [NSString stringWithCString:name2
2802 encoding:NSUTF8StringEncoding];
2803 NSFont *f = [NSFont fontWithName:nsname size:size];
2815 /* Returns a random font in the given size and face.
2818 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2821 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2822 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2823 NSArray *fonts = [[NSFontManager sharedFontManager]
2824 availableFontNamesWithTraits:mask];
2825 if (!fonts) return 0;
2827 int n = [fonts count];
2828 if (n <= 0) return 0;
2831 for (j = 0; j < n; j++) {
2832 int i = random() % n;
2833 NSString *name = [fonts objectAtIndex:i];
2834 NSFont *f = [NSFont fontWithName:name size:size];
2837 /* Don't use this font if it (probably) doesn't include ASCII characters.
2839 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2840 if (! (enc == NSUTF8StringEncoding ||
2841 enc == NSISOLatin1StringEncoding ||
2842 enc == NSNonLossyASCIIStringEncoding ||
2843 enc == NSISOLatin2StringEncoding ||
2844 enc == NSUnicodeStringEncoding ||
2845 enc == NSWindowsCP1250StringEncoding ||
2846 enc == NSWindowsCP1252StringEncoding ||
2847 enc == NSMacOSRomanStringEncoding)) {
2848 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2851 // NSLog(@"using \"%@\": %d", name, enc);
2853 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2857 // None of the fonts support ASCII?
2860 # else // USE_IPHONE
2862 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2863 NSArray *families = [UIFont familyNames];
2864 NSMutableDictionary *famdict = [NSMutableDictionary
2865 dictionaryWithCapacity:100];
2866 NSObject *y = [NSNumber numberWithBool:YES];
2867 for (NSString *name in families) {
2868 // There are many dups in the families array -- uniquify it.
2869 [famdict setValue:y forKey:name];
2872 for (NSString *name in famdict) {
2873 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2876 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2879 BOOL bb = MATCH(@"Bold");
2880 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2882 if (!bold != !bb) continue;
2883 if (!ital != !ii) continue;
2885 /* Check if it can do ASCII. No good way to accomplish this!
2886 These are fonts present in iPhone Simulator as of June 2012
2887 that don't include ASCII.
2889 if (MATCH(@"AppleGothic") || // Korean
2890 MATCH(@"Dingbats") || // Dingbats
2891 MATCH(@"Emoji") || // Emoticons
2892 MATCH(@"Geeza") || // Arabic
2893 MATCH(@"Hebrew") || // Hebrew
2894 MATCH(@"HiraKaku") || // Japanese
2895 MATCH(@"HiraMin") || // Japanese
2896 MATCH(@"Kailasa") || // Tibetan
2897 MATCH(@"Ornaments") || // Dingbats
2898 MATCH(@"STHeiti") // Chinese
2902 [fonts addObject:fn];
2907 if (! [fonts count]) return 0; // Nothing suitable?
2909 int i = random() % [fonts count];
2910 NSString *name = [fonts objectAtIndex:i];
2911 UIFont *ff = [UIFont fontWithName:name size:size];
2912 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2916 # endif // USE_IPHONE
2921 try_xlfd_font (const char *name, float scale,
2922 char **name_ret, float *size_ret)
2933 const char *s = (name ? name : "");
2935 while (*s && (*s == '*' || *s == '-'))
2938 while (*s2 && (*s2 != '*' && *s2 != '-'))
2941 unsigned long L = s2-s;
2944 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2945 else if (CMP ("random")) rand = YES;
2946 else if (CMP ("bold")) bold = YES;
2947 else if (CMP ("i")) ital = YES;
2948 else if (CMP ("o")) ital = YES;
2949 else if (CMP ("courier")) fixed = YES;
2950 else if (CMP ("fixed")) fixed = YES;
2951 else if (CMP ("m")) fixed = YES;
2952 else if (CMP ("times")) serif = YES;
2953 else if (CMP ("6x10")) fixed = YES, size = 8;
2954 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2955 else if (CMP ("9x15")) fixed = YES, size = 12;
2956 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2957 else if (CMP ("vga")) fixed = YES, size = 12;
2958 else if (CMP ("console")) fixed = YES, size = 12;
2959 else if (CMP ("gallant")) fixed = YES, size = 12;
2961 else if (size == 0) {
2963 if (1 == sscanf (s, " %d ", &n))
2970 if (size < 6 || size > 1000)
2976 nsfont = random_font (bold, ital, size, &ps_name);
2979 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2981 // if that didn't work, turn off attibutes until it does
2982 // (e.g., there is no "Monaco-Bold".)
2984 if (!nsfont && serif) {
2986 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2988 if (!nsfont && ital) {
2990 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2992 if (!nsfont && bold) {
2994 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2996 if (!nsfont && fixed) {
2998 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
3002 *name_ret = ps_name;
3012 XLoadFont (Display *dpy, const char *name)
3014 Font fid = (Font) calloc (1, sizeof(*fid));
3019 // Scale up fonts on Retina displays.
3020 scale = dpy->main_window->window.view.contentScaleFactor;
3023 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
3025 if (!fid->nsfont && name &&
3026 strchr (name, ' ') &&
3027 !strchr (name, '*')) {
3028 // If name contains a space but no stars, it is a native font spec --
3029 // return NULL so that we know it really didn't exist. Else, it is an
3030 // XLFD font, so keep trying.
3031 XUnloadFont (dpy, fid);
3036 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
3038 // We should never return NULL for XLFD fonts.
3040 Assert (0, "no font");
3043 CFRetain (fid->nsfont); // needed for garbage collection?
3045 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
3054 XLoadQueryFont (Display *dpy, const char *name)
3056 Font fid = XLoadFont (dpy, name);
3058 return XQueryFont (dpy, fid);
3062 XUnloadFont (Display *dpy, Font fid)
3065 free (fid->ps_name);
3066 if (fid->metrics.per_char)
3067 free (fid->metrics.per_char);
3069 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3070 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3071 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3072 // They're probably not very big...
3074 // [fid->nsfont release];
3075 // CFRelease (fid->nsfont);
3082 XFreeFontInfo (char **names, XFontStruct *info, int n)
3086 for (i = 0; i < n; i++)
3087 if (names[i]) free (names[i]);
3091 for (i = 0; i < n; i++)
3092 if (info[i].per_char)
3093 free (info[i].per_char);
3100 XFreeFont (Display *dpy, XFontStruct *f)
3103 XFreeFontInfo (0, f, 1);
3104 XUnloadFont (dpy, fid);
3110 XSetFont (Display *dpy, GC gc, Font fid)
3113 XUnloadFont (dpy, gc->gcv.font);
3114 gc->gcv.font = copy_font (fid);
3115 [gc->gcv.font->nsfont retain];
3116 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3121 XTextExtents (XFontStruct *f, const char *s, int length,
3122 int *dir_ret, int *ascent_ret, int *descent_ret,
3125 memset (cs, 0, sizeof(*cs));
3127 for (i = 0; i < length; i++) {
3128 unsigned char c = (unsigned char) s[i];
3129 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3130 c = f->default_char;
3131 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3135 cs->ascent = MAX (cs->ascent, cc->ascent);
3136 cs->descent = MAX (cs->descent, cc->descent);
3137 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3138 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3139 cs->width += cc->width;
3143 *ascent_ret = f->ascent;
3144 *descent_ret = f->descent;
3149 XTextWidth (XFontStruct *f, const char *s, int length)
3151 int ascent, descent, dir;
3153 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3159 set_font (Display *dpy, CGContextRef cgc, GC gc)
3161 Font font = gc->gcv.font;
3163 font = XLoadFont (dpy, 0);
3164 gc->gcv.font = font;
3165 [gc->gcv.font->nsfont retain];
3166 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3168 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
3169 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
3174 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3175 const char *str, int len, BOOL clear_background_p)
3177 if (clear_background_p) {
3178 int ascent, descent, dir;
3180 XTextExtents (&gc->gcv.font->metrics, str, len,
3181 &dir, &ascent, &descent, &cs);
3182 draw_rect (dpy, d, gc,
3183 x + MIN (0, cs.lbearing),
3184 y - MAX (0, ascent),
3185 MAX (MAX (0, cs.rbearing) -
3186 MIN (0, cs.lbearing),
3188 MAX (0, ascent) + MAX (0, descent),
3192 CGRect wr = d->frame;
3195 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
3196 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
3199 CGContextRef cgc = d->cgc;
3200 push_fg_gc (d, gc, YES);
3201 set_font (dpy, cgc, gc);
3203 CGContextSetTextDrawingMode (cgc, kCGTextFill);
3204 if (gc->gcv.antialias_p)
3205 CGContextSetShouldAntialias (cgc, YES);
3206 CGContextShowTextAtPoint (cgc,
3208 wr.origin.y + wr.size.height - y,
3217 unsigned long argb = gc->gcv.foreground;
3218 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3219 float a = ((argb >> 24) & 0xFF) / 255.0;
3220 float r = ((argb >> 16) & 0xFF) / 255.0;
3221 float g = ((argb >> 8) & 0xFF) / 255.0;
3222 float b = ((argb ) & 0xFF) / 255.0;
3223 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3224 NSDictionary *attr =
3225 [NSDictionary dictionaryWithObjectsAndKeys:
3226 gc->gcv.font->nsfont, NSFontAttributeName,
3227 fg, NSForegroundColorAttributeName,
3229 char *s2 = (char *) malloc (len + 1);
3230 strncpy (s2, str, len);
3232 NSString *nsstr = [NSString stringWithCString:s2
3233 encoding:NSISOLatin1StringEncoding];
3236 pos.x = wr.origin.x + x;
3237 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
3238 [nsstr drawAtPoint:pos withAttributes:attr];
3242 invalidate_drawable_cache (d);
3248 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3249 const char *str, int len)
3251 return draw_string (dpy, d, gc, x, y, str, len, NO);
3255 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3256 const char *str, int len)
3258 return draw_string (dpy, d, gc, x, y, str, len, YES);
3263 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3265 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3266 gc->gcv.foreground = fg;
3272 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3274 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3275 gc->gcv.background = bg;
3280 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3282 gc->gcv.alpha_allowed_p = allowed;
3287 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3289 gc->gcv.antialias_p = antialias_p;
3295 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3296 int line_style, int cap_style, int join_style)
3298 gc->gcv.line_width = line_width;
3299 Assert (line_style == LineSolid, "only LineSolid implemented");
3300 // gc->gcv.line_style = line_style;
3301 gc->gcv.cap_style = cap_style;
3302 gc->gcv.join_style = join_style;
3307 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3313 XSetFunction (Display *dpy, GC gc, int which)
3315 gc->gcv.function = which;
3320 XSetSubwindowMode (Display *dpy, GC gc, int which)
3322 gc->gcv.subwindow_mode = which;
3327 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3329 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3331 if (gc->gcv.clip_mask) {
3332 XFreePixmap (dpy, gc->gcv.clip_mask);
3333 CGImageRelease (gc->clip_mask);
3336 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3337 if (gc->gcv.clip_mask)
3339 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3347 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3349 gc->gcv.clip_x_origin = x;
3350 gc->gcv.clip_y_origin = y;
3356 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3357 int *root_x_ret, int *root_y_ret,
3358 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3360 Assert (w && w->type == WINDOW, "not a window");
3363 int x = w->window.last_mouse_x;
3364 int y = w->window.last_mouse_y;
3365 if (root_x_ret) *root_x_ret = x;
3366 if (root_y_ret) *root_y_ret = y;
3367 if (win_x_ret) *win_x_ret = x;
3368 if (win_y_ret) *win_y_ret = y;
3370 # else // !USE_IPHONE
3372 NSWindow *nsw = [w->window.view window];
3374 // get bottom left of window on screen, from bottom left
3375 wpos.x = wpos.y = 0;
3376 wpos = [nsw convertBaseToScreen:wpos];
3379 // get bottom left of view on window, from bottom left
3380 vpos.x = vpos.y = 0;
3381 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3383 // get bottom left of view on screen, from bottom left
3387 // get top left of view on screen, from bottom left
3388 vpos.y += w->frame.size.height;
3390 // get top left of view on screen, from top left
3391 NSArray *screens = [NSScreen screens];
3392 NSScreen *screen = (screens && [screens count] > 0
3393 ? [screens objectAtIndex:0]
3394 : [NSScreen mainScreen]);
3396 double s = w->window.view.contentScaleFactor;
3400 NSRect srect = [screen frame];
3401 vpos.y = (s * srect.size.height) - vpos.y;
3403 // get the mouse position on window, from bottom left
3404 NSEvent *e = [NSApp currentEvent];
3405 NSPoint p = [e locationInWindow];
3407 // get mouse position on screen, from bottom left
3411 // get mouse position on screen, from top left
3412 p.y = srect.size.height - p.y;
3414 if (root_x_ret) *root_x_ret = (int) p.x;
3415 if (root_y_ret) *root_y_ret = (int) p.y;
3416 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3417 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3418 # endif // !USE_IPHONE
3420 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3421 if (root_ret) *root_ret = 0;
3422 if (child_ret) *child_ret = 0;
3427 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3428 int src_x, int src_y,
3429 int *dest_x_ret, int *dest_y_ret,
3432 Assert (w && w->type == WINDOW, "not a window");
3440 # else // !USE_IPHONE
3442 NSWindow *nsw = [w->window.view window];
3444 // get bottom left of window on screen, from bottom left
3445 wpos.x = wpos.y = 0;
3446 wpos = [nsw convertBaseToScreen:wpos];
3449 // get bottom left of view on window, from bottom left
3450 vpos.x = vpos.y = 0;
3451 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3453 // get bottom left of view on screen, from bottom left
3457 // get top left of view on screen, from bottom left
3458 vpos.y += w->frame.size.height;
3460 // get top left of view on screen, from top left
3461 NSArray *screens = [NSScreen screens];
3462 NSScreen *screen = (screens && [screens count] > 0
3463 ? [screens objectAtIndex:0]
3464 : [NSScreen mainScreen]);
3466 double s = w->window.view.contentScaleFactor;
3470 NSRect srect = [screen frame];
3471 vpos.y = (s * srect.size.height) - vpos.y;
3473 // point starts out relative to top left of view
3478 // get point relative to top left of screen
3481 # endif // !USE_IPHONE
3492 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3498 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3501 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3503 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3504 if ((unsigned int) ks <= 255)
3507 // Put control characters in the string. Not meta.
3508 if (e->state & ControlMask) {
3509 if (c >= 'a' && c <= 'z') // Upcase control.
3511 if (c >= '@' && c <= '_') // Shift to control page.
3513 if (c == ' ') // C-SPC is NULL.
3517 if (k_ret) *k_ret = ks;
3518 if (size > 0) buf[0] = c;
3519 if (size > 1) buf[1] = 0;
3520 return (size > 0 ? 1 : 0);
3525 XFlush (Display *dpy)
3527 // Just let the event loop take care of this on its own schedule.
3532 XSync (Display *dpy, Bool flush)
3534 return XFlush (dpy);
3538 // declared in utils/visual.h
3540 has_writable_cells (Screen *s, Visual *v)
3546 visual_depth (Screen *s, Visual *v)
3552 visual_cells (Screen *s, Visual *v)
3558 visual_class (Screen *s, Visual *v)
3564 get_bits_per_pixel (Display *dpy, int depth)
3566 Assert (depth == 32 || depth == 1, "unexpected depth");
3570 // declared in utils/grabclient.h
3572 use_subwindow_mode_p (Screen *screen, Window window)