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 # define NSView UIView
28 # define NSRect CGRect
29 # define NSPoint CGPoint
30 # define NSSize CGSize
31 # define NSColor UIColor
32 # define NSImage UIImage
33 # define NSEvent UIEvent
34 # define NSFont UIFont
35 # define NSGlyph CGGlyph
36 # define NSWindow UIWindow
37 # define NSMakeSize CGSizeMake
38 # define NSBezierPath UIBezierPath
39 # define colorWithDeviceRed colorWithRed
41 # import <Cocoa/Cocoa.h>
44 #import <CoreText/CTFont.h>
45 #import <CoreText/CTLine.h>
48 #import "jwxyz-timers.h"
51 # define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
54 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
58 # define MAX(a,b) ((a)>(b)?(a):(b))
59 # define MIN(a,b) ((a)<(b)?(a):(b))
62 struct jwxyz_Drawable {
63 enum { WINDOW, PIXMAP } type;
70 unsigned long background;
71 int last_mouse_x, last_mouse_y;
75 void *cgc_buffer; // the bits to which CGContextRef renders
80 struct jwxyz_Display {
84 struct jwxyz_sources_data *timers_data;
87 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
88 This can change if the window is dragged to
89 a different screen. */
92 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
93 our images with this to avoid translation
106 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
112 float size; // points
114 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
115 // But we need the metrics on both of them, so they go here.
120 /* Instead of calling abort(), throw a real exception, so that
121 XScreenSaverView can catch it and display a dialog.
124 jwxyz_abort (const char *fmt, ...)
132 va_start (args, fmt);
133 vsprintf (s, fmt, args);
136 [[NSException exceptionWithName: NSInternalInconsistencyException
137 reason: [NSString stringWithCString: s
138 encoding:NSUTF8StringEncoding]
141 abort(); // not reached
145 /* We keep a list of all of the Displays that have been created and not
146 yet freed so that they can have sensible display numbers. If three
147 displays are created (0, 1, 2) and then #1 is closed, then the fourth
148 display will be given the now-unused display number 1. (Everything in
149 here assumes a 1:1 Display/Screen mapping.)
151 The size of this array is the most number of live displays at one time.
152 So if it's 20, then we'll blow up if the system has 19 monitors and also
153 has System Preferences open (the small preview window).
155 Note that xlockmore-style savers tend to allocate big structures, so
156 setting this to 1000 will waste a few megabytes. Also some of them assume
157 that the number of screens never changes, so dynamically expanding this
161 static Display *jwxyz_live_displays[20] = { 0, };
166 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
168 CGContextRef cgc = (CGContextRef) cgc_arg;
169 NSView *view = (NSView *) nsview_arg;
170 Assert (view, "no view");
173 Display *d = (Display *) calloc (1, sizeof(*d));
174 d->screen = (Screen *) calloc (1, sizeof(Screen));
178 d->screen->screen_number = 0;
181 // Find the first empty slot in live_displays and plug us in.
182 int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
184 for (i = 0; i < size; i++) {
185 if (! jwxyz_live_displays[i])
188 if (i >= size) abort();
189 jwxyz_live_displays[i] = d;
190 d->screen_count = size;
191 d->screen->screen_number = i;
193 # endif // !USE_IPHONE
195 Visual *v = (Visual *) calloc (1, sizeof(Visual));
196 v->class = TrueColor;
197 v->red_mask = 0x00FF0000;
198 v->green_mask = 0x0000FF00;
199 v->blue_mask = 0x000000FF;
201 d->screen->visual = v;
203 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
206 Window w = (Window) calloc (1, sizeof(*w));
208 w->window.view = view;
209 CFRetain (w->window.view); // needed for garbage collection?
210 w->window.background = BlackPixel(0,0);
217 cgc = [[[view window] graphicsContext] graphicsPort];
223 Assert (cgc, "no CGContext");
228 jwxyz_free_display (Display *dpy)
230 jwxyz_sources_free (dpy->timers_data);
234 // Find us in live_displays and clear that slot.
235 int size = ScreenCount(dpy);
237 for (i = 0; i < size; i++) {
238 if (dpy == jwxyz_live_displays[i]) {
239 jwxyz_live_displays[i] = 0;
243 if (i >= size) abort();
245 # endif // !USE_IPHONE
247 free (dpy->screen->visual);
249 CFRelease (dpy->main_window->window.view);
250 free (dpy->main_window);
256 jwxyz_window_view (Window w)
258 Assert (w && w->type == WINDOW, "not a window");
259 return w->window.view;
263 /* Call this after any modification to the bits on a Pixmap or Window.
264 Most Pixmaps are used frequently as sources and infrequently as
265 destinations, so it pays to cache the data as a CGImage as needed.
268 invalidate_drawable_cache (Drawable d)
271 CGImageRelease (d->cgi);
277 /* Call this when the View changes size or position.
280 jwxyz_window_resized (Display *dpy, Window w,
281 int new_x, int new_y, int new_width, int new_height,
284 CGContextRef cgc = (CGContextRef) cgc_arg;
285 Assert (w && w->type == WINDOW, "not a window");
286 w->frame.origin.x = new_x;
287 w->frame.origin.y = new_y;
288 w->frame.size.width = new_width;
289 w->frame.size.height = new_height;
291 if (cgc) w->cgc = cgc;
292 Assert (w->cgc, "no CGContext");
295 // Figure out which screen the window is currently on.
298 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
304 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
308 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
310 Assert (dpy->cgdpy, "unable to find CGDisplay");
312 # endif // USE_IPHONE
314 # ifndef USE_BACKBUFFER
315 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
316 // then this one's faster.
319 // Figure out this screen's colorspace, and use that for every CGImage.
321 CMProfileRef profile = 0;
322 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
323 Assert (profile, "unable to find colorspace profile");
324 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
325 Assert (dpy->colorspace, "unable to find colorspace");
327 # else // USE_BACKBUFFER
329 // WTF? It's faster if we *do not* use the screen's colorspace!
331 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
332 # endif // USE_BACKBUFFER
334 invalidate_drawable_cache (w);
340 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
342 Assert (w && w->type == WINDOW, "not a window");
343 w->window.last_mouse_x = x;
344 w->window.last_mouse_y = y;
350 jwxyz_flush_context (Display *dpy)
352 // This is only used when USE_BACKBUFFER is off.
353 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
357 display_sources_data (Display *dpy)
359 return dpy->timers_data;
364 XRootWindow (Display *dpy, int screen)
366 return dpy->main_window;
370 XDefaultScreenOfDisplay (Display *dpy)
376 XDefaultVisualOfScreen (Screen *screen)
378 return screen->visual;
382 XDisplayOfScreen (Screen *s)
388 XDisplayNumberOfScreen (Screen *s)
394 XScreenNumberOfScreen (Screen *s)
396 return s->screen_number;
400 jwxyz_ScreenCount (Display *dpy)
402 return dpy->screen_count;
406 XDisplayWidth (Display *dpy, int screen)
408 return (int) dpy->main_window->frame.size.width;
412 XDisplayHeight (Display *dpy, int screen)
414 return (int) dpy->main_window->frame.size.height;
418 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
421 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
422 else if (!alpha_allowed_p)
423 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
424 "bogus color pixel");
429 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
430 BOOL alpha_allowed_p, BOOL fill_p)
432 validate_pixel (argb, depth, alpha_allowed_p);
435 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
437 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
439 float a = ((argb >> 24) & 0xFF) / 255.0;
440 float r = ((argb >> 16) & 0xFF) / 255.0;
441 float g = ((argb >> 8) & 0xFF) / 255.0;
442 float b = ((argb ) & 0xFF) / 255.0;
444 CGContextSetRGBFillColor (cgc, r, g, b, a);
446 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
451 set_line_mode (CGContextRef cgc, XGCValues *gcv)
453 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
454 CGContextSetLineJoin (cgc,
455 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
456 gcv->join_style == JoinRound ? kCGLineJoinRound :
458 CGContextSetLineCap (cgc,
459 gcv->cap_style == CapNotLast ? kCGLineCapButt :
460 gcv->cap_style == CapButt ? kCGLineCapButt :
461 gcv->cap_style == CapRound ? kCGLineCapRound :
466 set_clip_mask (Drawable d, GC gc)
468 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
470 Pixmap p = gc->gcv.clip_mask;
472 Assert (p->type == PIXMAP, "not a pixmap");
474 CGRect wr = d->frame;
476 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
477 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
478 - p->frame.size.height;
479 to.size.width = p->frame.size.width;
480 to.size.height = p->frame.size.height;
482 CGContextClipToMask (d->cgc, to, gc->clip_mask);
486 /* Pushes a GC context; sets BlendMode and ClipMask.
489 push_gc (Drawable d, GC gc)
491 CGContextRef cgc = d->cgc;
492 CGContextSaveGState (cgc);
494 switch (gc->gcv.function) {
497 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
498 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
499 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
500 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
501 default: Assert(0, "unknown gcv function"); break;
504 if (gc->gcv.clip_mask)
505 set_clip_mask (d, gc);
508 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
511 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
514 push_color_gc (Drawable d, GC gc, unsigned long color,
515 BOOL antialias_p, Bool fill_p)
519 int depth = gc->depth;
520 switch (gc->gcv.function) {
521 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
522 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
525 CGContextRef cgc = d->cgc;
526 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
527 CGContextSetShouldAntialias (cgc, antialias_p);
531 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
534 push_fg_gc (Drawable d, GC gc, Bool fill_p)
536 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
539 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
542 push_bg_gc (Drawable d, GC gc, Bool fill_p)
544 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
549 /* You've got to be fucking kidding me!
551 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
552 with repeated calls to CGContextDrawImage than it is to make a single
553 call to CGContextFillRects() with a list of 1x1 rectangles!
555 I still wouldn't call it *fast*, however...
557 #define XDRAWPOINTS_IMAGES
559 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
560 the bitmap data directly is faster. This only works on Pixmaps, though,
561 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
563 #define XDRAWPOINTS_CGDATA
566 XDrawPoints (Display *dpy, Drawable d, GC gc,
567 XPoint *points, int count, int mode)
570 CGRect wr = d->frame;
572 push_fg_gc (d, gc, YES);
574 # ifdef XDRAWPOINTS_CGDATA
576 # ifdef USE_BACKBUFFER
577 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
579 if (d->type == PIXMAP)
582 CGContextRef cgc = d->cgc;
583 void *data = CGBitmapContextGetData (cgc);
584 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
585 size_t w = CGBitmapContextGetWidth (cgc);
586 size_t h = CGBitmapContextGetHeight (cgc);
588 Assert (data, "no bitmap data in Drawable");
590 unsigned long argb = gc->gcv.foreground;
591 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
593 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
595 CGFloat x0 = wr.origin.x;
596 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
598 // It's uglier, but faster, to hoist the conditional out of the loop.
599 if (mode == CoordModePrevious) {
600 CGFloat x = x0, y = y0;
601 for (i = 0; i < count; i++, points++) {
605 if (x >= 0 && x < w && y >= 0 && y < h) {
606 unsigned int *p = (unsigned int *)
607 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
608 *p = (unsigned int) argb;
612 for (i = 0; i < count; i++, points++) {
613 CGFloat x = x0 + points->x;
614 CGFloat y = y0 + points->y;
616 if (x >= 0 && x < w && y >= 0 && y < h) {
617 unsigned int *p = (unsigned int *)
618 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
619 *p = (unsigned int) argb;
624 } else /* d->type == WINDOW */
626 # endif /* XDRAWPOINTS_CGDATA */
629 # ifdef XDRAWPOINTS_IMAGES
631 unsigned int argb = gc->gcv.foreground;
632 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
634 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
636 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
638 CGImageRef cgi = CGImageCreate (1, 1,
641 /* Host-ordered, since we're using the
642 address of an int as the color data. */
643 (kCGImageAlphaNoneSkipFirst |
644 kCGBitmapByteOrder32Host),
647 NO, /* interpolate */
648 kCGRenderingIntentDefault);
649 CGDataProviderRelease (prov);
651 CGContextRef cgc = d->cgc;
653 rect.size.width = rect.size.height = 1;
654 for (i = 0; i < count; i++) {
655 if (i > 0 && mode == CoordModePrevious) {
656 rect.origin.x += points->x;
657 rect.origin.x -= points->y;
659 rect.origin.x = wr.origin.x + points->x;
660 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
663 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
664 CGContextDrawImage (cgc, rect, cgi);
668 CGImageRelease (cgi);
670 # else /* ! XDRAWPOINTS_IMAGES */
672 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
675 for (i = 0; i < count; i++) {
676 r->size.width = r->size.height = 1;
677 if (i > 0 && mode == CoordModePrevious) {
678 r->origin.x = r[-1].origin.x + points->x;
679 r->origin.y = r[-1].origin.x - points->y;
681 r->origin.x = wr.origin.x + points->x;
682 r->origin.y = wr.origin.y + wr.size.height - points->y;
688 CGContextFillRects (d->cgc, rects, count);
691 # endif /* ! XDRAWPOINTS_IMAGES */
695 invalidate_drawable_cache (d);
702 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
707 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
711 static void draw_rect (Display *, Drawable, GC,
712 int x, int y, unsigned int width, unsigned int height,
713 BOOL foreground_p, BOOL fill_p);
716 bitmap_context_p (Drawable d)
718 # ifdef USE_BACKBUFFER
721 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
722 return d->type == PIXMAP;
727 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
728 size_t fill_width, size_t fill_height)
730 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
731 while (fill_height) {
732 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
733 wmemset (dst, fill_data, fill_width);
735 dst = (char *) dst + dst_pitch;
740 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
742 return (char *)dst + dst_pitch * y + x * 4;
746 drawable_depth (Drawable d)
748 return (d->type == WINDOW
749 ? visual_depth (NULL, NULL)
755 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
756 int src_x, int src_y,
757 unsigned int width, unsigned int height,
758 int dst_x, int dst_y)
760 Assert (gc, "no GC");
761 Assert ((width < 65535), "improbably large width");
762 Assert ((height < 65535), "improbably large height");
763 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
764 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
765 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
766 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
768 if (width == 0 || height == 0)
771 if (gc->gcv.function == GXset ||
772 gc->gcv.function == GXclear) {
773 // "set" and "clear" are dumb drawing modes that ignore the source
774 // bits and just draw solid rectangles.
776 (gc->gcv.function == GXset
777 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
778 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
779 gc->depth, gc->gcv.alpha_allowed_p, YES);
780 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
784 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
785 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
786 // bounds of their drawables.
787 BOOL clipped = NO; // Whether we did any clipping of the rects.
789 src_frame = src->frame;
790 dst_frame = dst->frame;
792 // Initialize src_rect...
794 src_rect.origin.x = src_frame.origin.x + src_x;
795 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
797 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
798 src_rect.size.width = width;
799 src_rect.size.height = height;
801 // Initialize dst_rect...
803 dst_rect.origin.x = dst_frame.origin.x + dst_x;
804 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
806 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
807 dst_rect.size.width = width;
808 dst_rect.size.height = height;
810 // Clip rects to frames...
813 # define CLIP(THIS,THAT,VAL,SIZE) do { \
814 float off = THIS##_rect.origin.VAL; \
817 THIS##_rect.size.SIZE += off; \
818 THAT##_rect.size.SIZE += off; \
819 THIS##_rect.origin.VAL -= off; \
820 THAT##_rect.origin.VAL -= off; \
822 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
823 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
826 THIS##_rect.size.SIZE -= off; \
827 THAT##_rect.size.SIZE -= off; \
830 CLIP (dst, src, x, width);
831 CLIP (dst, src, y, height);
833 // Not actually the original dst_rect, just the one before it's clipped to
835 CGRect orig_dst_rect = dst_rect;
837 CLIP (src, dst, x, width);
838 CLIP (src, dst, y, height);
841 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
844 // Sort-of-special case where no pixels can be grabbed from the source,
845 // and the whole destination is filled with the background color.
846 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
848 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
849 (int)src_rect.size.height == (int)dst_rect.size.height,
852 src_rect.size.width = 0;
853 src_rect.size.height = 0;
854 dst_rect.size.width = 0;
855 dst_rect.size.height = 0;
858 NSObject *releaseme = 0;
861 BOOL free_cgi_p = NO;
864 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
865 going on with clipping masks or depths or anything, optimize it by
866 just doing a memcpy instead of going through a CGI.
868 if (bitmap_context_p (src)) {
870 if (bitmap_context_p (dst) &&
871 gc->gcv.function == GXcopy &&
872 !gc->gcv.clip_mask &&
873 drawable_depth (src) == drawable_depth (dst)) {
875 Assert(!(int)src_frame.origin.x &&
876 !(int)src_frame.origin.y &&
877 !(int)dst_frame.origin.x &&
878 !(int)dst_frame.origin.y,
879 "unexpected non-zero origin");
881 char *src_data = CGBitmapContextGetData(src->cgc);
882 char *dst_data = CGBitmapContextGetData(dst->cgc);
883 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
884 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
886 // Int to float and back again. It's not very safe, but it seems to work.
887 int src_x0 = src_rect.origin.x;
888 int dst_x0 = dst_rect.origin.x;
890 // Flip the Y-axis a second time.
891 int src_y0 = (src_frame.origin.y + src_frame.size.height -
892 src_rect.size.height - src_rect.origin.y);
893 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
894 dst_rect.size.height - dst_rect.origin.y);
896 unsigned width0 = (int) src_rect.size.width;
897 unsigned height0 = (int) src_rect.size.height;
899 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
900 (int)src_rect.size.height == (int)dst_rect.size.height,
903 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
904 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
905 size_t src_pitch0 = src_pitch;
906 size_t dst_pitch0 = dst_pitch;
907 size_t bytes = width0 * 4;
909 if (src == dst && dst_y0 > src_y0) {
910 // Copy upwards if the areas might overlap.
911 src_data0 += src_pitch0 * (height0 - 1);
912 dst_data0 += dst_pitch0 * (height0 - 1);
913 src_pitch0 = -src_pitch0;
914 dst_pitch0 = -dst_pitch0;
917 size_t lines0 = height0;
919 // memcpy is an alias for memmove on OS X.
920 memmove(dst_data0, src_data0, bytes);
921 src_data0 += src_pitch0;
922 dst_data0 += dst_pitch0;
928 int orig_dst_x = orig_dst_rect.origin.x;
929 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
930 orig_dst_rect.origin.y - orig_dst_rect.size.height);
931 int orig_width = orig_dst_rect.size.width;
932 int orig_height = orig_dst_rect.size.height;
934 Assert (orig_dst_x >= 0 &&
935 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
937 orig_dst_y + orig_height <= (int) dst_frame.size.height,
940 if (orig_dst_y < dst_y0) {
941 fill_rect_memset (seek_xy (dst_data, dst_pitch,
942 orig_dst_x, orig_dst_y), dst_pitch,
943 (uint32_t) gc->gcv.background, orig_width,
944 dst_y0 - orig_dst_y);
947 if (orig_dst_y + orig_height > dst_y0 + height0) {
948 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
951 (uint32_t) gc->gcv.background, orig_width,
952 orig_dst_y + orig_height - dst_y0 - height0);
955 if (orig_dst_x < dst_x0) {
956 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
957 dst_pitch, (uint32_t) gc->gcv.background,
958 dst_x0 - orig_dst_x, height0);
961 if (dst_x0 + width0 < orig_dst_x + orig_width) {
962 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
964 dst_pitch, (uint32_t) gc->gcv.background,
965 orig_dst_x + orig_width - dst_x0 - width0,
970 invalidate_drawable_cache (dst);
975 // If we are copying from a Pixmap to a Pixmap or Window, we must first
976 // copy the bits to an intermediary CGImage object, then copy that to the
977 // destination drawable's CGContext.
979 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
980 // case of copying from a Pixmap back to itself, but I don't think that
981 // happens very often anyway.)
983 // First we get a CGImage out of the pixmap CGContext -- it's the whole
984 // pixmap, but it presumably shares the data pointer instead of copying
985 // it. We then cache that CGImage it inside the Pixmap object. Note:
986 // invalidate_drawable_cache() must be called to discard this any time a
987 // modification is made to the pixmap, or we'll end up re-using old bits.
990 src->cgi = CGBitmapContextCreateImage (src->cgc);
993 // if doing a sub-rect, trim it down.
994 if (src_rect.origin.x != src_frame.origin.x ||
995 src_rect.origin.y != src_frame.origin.y ||
996 src_rect.size.width != src_frame.size.width ||
997 src_rect.size.height != src_frame.size.height) {
998 // #### I don't understand why this is needed...
999 src_rect.origin.y = (src_frame.size.height -
1000 src_rect.size.height - src_rect.origin.y);
1001 // This does not copy image data, so it should be fast.
1002 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1006 if (src->type == PIXMAP && src->pixmap.depth == 1)
1009 # ifndef USE_BACKBUFFER
1010 } else { /* (src->type == WINDOW) */
1012 NSRect nsfrom; // NSRect != CGRect on 10.4
1013 nsfrom.origin.x = src_rect.origin.x;
1014 nsfrom.origin.y = src_rect.origin.y;
1015 nsfrom.size.width = src_rect.size.width;
1016 nsfrom.size.height = src_rect.size.height;
1020 // If we are copying from a window to itself, we can use NSCopyBits()
1021 // without first copying the rectangle to an intermediary CGImage.
1022 // This is ~28% faster (but I *expected* it to be twice as fast...)
1023 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1029 // If we are copying from a Window to a Pixmap, we must first copy
1030 // the bits to an intermediary CGImage object, then copy that to the
1031 // Pixmap's CGContext.
1033 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1034 initWithFocusedViewRect:nsfrom];
1035 unsigned char *data = [bm bitmapData];
1036 int bps = [bm bitsPerSample];
1037 int bpp = [bm bitsPerPixel];
1038 int bpl = [bm bytesPerRow];
1041 // create a CGImage from those bits.
1042 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1043 // but that method didn't exist in 10.4.)
1045 CGDataProviderRef prov =
1046 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1048 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1051 /* Use whatever default bit ordering we got from
1052 initWithFocusedViewRect. I would have assumed
1053 that it was (kCGImageAlphaNoneSkipFirst |
1054 kCGBitmapByteOrder32Host), but on Intel,
1059 NULL, /* decode[] */
1060 NO, /* interpolate */
1061 kCGRenderingIntentDefault);
1063 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1064 CGDataProviderRelease (prov);
1067 # endif // !USE_BACKBUFFER
1070 CGContextRef cgc = dst->cgc;
1072 if (mask_p) { // src depth == 1
1074 push_bg_gc (dst, gc, YES);
1076 // fill the destination rectangle with solid background...
1077 CGContextFillRect (cgc, orig_dst_rect);
1079 Assert (cgc, "no CGC with 1-bit XCopyArea");
1081 // then fill in a solid rectangle of the fg color, using the image as an
1082 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1083 set_color (cgc, gc->gcv.foreground, gc->depth,
1084 gc->gcv.alpha_allowed_p, YES);
1085 CGContextClipToMask (cgc, dst_rect, cgi);
1086 CGContextFillRect (cgc, dst_rect);
1090 } else { // src depth > 1
1094 // If either the src or dst rects did not lie within their drawables,
1095 // then we have adjusted both the src and dst rects to account for
1096 // the clipping; that means we need to first clear to the background,
1097 // so that clipped bits end up in the bg color instead of simply not
1101 set_color (cgc, gc->gcv.background, gc->depth,
1102 gc->gcv.alpha_allowed_p, YES);
1103 CGContextFillRect (cgc, orig_dst_rect);
1107 // copy the CGImage onto the destination CGContext
1108 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1109 CGContextDrawImage (cgc, dst_rect, cgi);
1111 // No cgi means src == dst, and both are Windows.
1113 # ifdef USE_BACKBUFFER
1114 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1116 # else // !USE_BACKBUFFER
1118 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1119 nsfrom.origin.y = src_rect.origin.y;
1120 nsfrom.size.width = src_rect.size.width;
1121 nsfrom.size.height = src_rect.size.height;
1123 nsto.x = dst_rect.origin.x;
1124 nsto.y = dst_rect.origin.y;
1125 NSCopyBits (0, nsfrom, nsto);
1126 # endif // !USE_BACKBUFFER
1132 if (free_cgi_p) CGImageRelease (cgi);
1134 if (releaseme) [releaseme release];
1135 invalidate_drawable_cache (dst);
1141 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1142 int src_x, int src_y,
1143 unsigned width, int height,
1144 int dest_x, int dest_y, unsigned long plane)
1146 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1148 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1149 // not to white/black.
1150 return XCopyArea (dpy, src, dest, gc,
1151 src_x, src_y, width, height, dest_x, dest_y);
1156 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1158 // when drawing a zero-length line, obey line-width and cap-style.
1159 if (x1 == x2 && y1 == y2) {
1160 int w = gc->gcv.line_width;
1163 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1164 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1166 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1169 CGRect wr = d->frame;
1171 p.x = wr.origin.x + x1;
1172 p.y = wr.origin.y + wr.size.height - y1;
1174 push_fg_gc (d, gc, NO);
1176 CGContextRef cgc = d->cgc;
1177 set_line_mode (cgc, &gc->gcv);
1178 CGContextBeginPath (cgc);
1179 CGContextMoveToPoint (cgc, p.x, p.y);
1180 p.x = wr.origin.x + x2;
1181 p.y = wr.origin.y + wr.size.height - y2;
1182 CGContextAddLineToPoint (cgc, p.x, p.y);
1183 CGContextStrokePath (cgc);
1185 invalidate_drawable_cache (d);
1190 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1195 CGRect wr = d->frame;
1196 push_fg_gc (d, gc, NO);
1198 CGContextRef cgc = d->cgc;
1200 set_line_mode (cgc, &gc->gcv);
1202 // if the first and last points coincide, use closepath to get
1203 // the proper line-joining.
1204 BOOL closed_p = (points[0].x == points[count-1].x &&
1205 points[0].y == points[count-1].y);
1206 if (closed_p) count--;
1208 p.x = wr.origin.x + points->x;
1209 p.y = wr.origin.y + wr.size.height - points->y;
1211 CGContextBeginPath (cgc);
1212 CGContextMoveToPoint (cgc, p.x, p.y);
1213 for (i = 1; i < count; i++) {
1214 if (mode == CoordModePrevious) {
1218 p.x = wr.origin.x + points->x;
1219 p.y = wr.origin.y + wr.size.height - points->y;
1221 CGContextAddLineToPoint (cgc, p.x, p.y);
1224 if (closed_p) CGContextClosePath (cgc);
1225 CGContextStrokePath (cgc);
1227 invalidate_drawable_cache (d);
1233 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1236 CGRect wr = d->frame;
1238 CGContextRef cgc = d->cgc;
1240 push_fg_gc (d, gc, NO);
1241 set_line_mode (cgc, &gc->gcv);
1242 CGContextBeginPath (cgc);
1243 for (i = 0; i < count; i++) {
1244 CGContextMoveToPoint (cgc,
1245 wr.origin.x + segments->x1,
1246 wr.origin.y + wr.size.height - segments->y1);
1247 CGContextAddLineToPoint (cgc,
1248 wr.origin.x + segments->x2,
1249 wr.origin.y + wr.size.height - segments->y2);
1252 CGContextStrokePath (cgc);
1254 invalidate_drawable_cache (d);
1260 XClearWindow (Display *dpy, Window win)
1262 Assert (win && win->type == WINDOW, "not a window");
1263 CGRect wr = win->frame;
1264 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1268 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1270 Assert (w && w->type == WINDOW, "not a window");
1271 validate_pixel (pixel, 32, NO);
1272 w->window.background = pixel;
1277 draw_rect (Display *dpy, Drawable d, GC gc,
1278 int x, int y, unsigned int width, unsigned int height,
1279 BOOL foreground_p, BOOL fill_p)
1281 CGRect wr = d->frame;
1283 r.origin.x = wr.origin.x + x;
1284 r.origin.y = wr.origin.y + wr.size.height - y - height;
1285 r.size.width = width;
1286 r.size.height = height;
1290 push_fg_gc (d, gc, fill_p);
1292 push_bg_gc (d, gc, fill_p);
1295 CGContextRef cgc = d->cgc;
1297 CGContextFillRect (cgc, r);
1300 set_line_mode (cgc, &gc->gcv);
1301 CGContextStrokeRect (cgc, r);
1306 invalidate_drawable_cache (d);
1311 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1312 unsigned int width, unsigned int height)
1314 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1319 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1320 unsigned int width, unsigned int height)
1322 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1327 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1329 CGRect wr = d->frame;
1331 CGContextRef cgc = d->cgc;
1332 push_fg_gc (d, gc, YES);
1333 for (i = 0; i < n; i++) {
1335 r.origin.x = wr.origin.x + rects->x;
1336 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1337 r.size.width = rects->width;
1338 r.size.height = rects->height;
1339 CGContextFillRect (cgc, r);
1343 invalidate_drawable_cache (d);
1349 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1351 Assert (win && win->type == WINDOW, "not a window");
1352 CGContextRef cgc = win->cgc;
1353 set_color (cgc, win->window.background, 32, NO, YES);
1354 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1360 XFillPolygon (Display *dpy, Drawable d, GC gc,
1361 XPoint *points, int npoints, int shape, int mode)
1363 CGRect wr = d->frame;
1365 push_fg_gc (d, gc, YES);
1366 CGContextRef cgc = d->cgc;
1367 CGContextBeginPath (cgc);
1369 for (i = 0; i < npoints; i++) {
1370 if (i > 0 && mode == CoordModePrevious) {
1374 x = wr.origin.x + points[i].x;
1375 y = wr.origin.y + wr.size.height - points[i].y;
1379 CGContextMoveToPoint (cgc, x, y);
1381 CGContextAddLineToPoint (cgc, x, y);
1383 CGContextClosePath (cgc);
1384 if (gc->gcv.fill_rule == EvenOddRule)
1385 CGContextEOFillPath (cgc);
1387 CGContextFillPath (cgc);
1389 invalidate_drawable_cache (d);
1393 #define radians(DEG) ((DEG) * M_PI / 180.0)
1394 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1397 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1398 unsigned int width, unsigned int height, int angle1, int angle2,
1401 CGRect wr = d->frame;
1403 bound.origin.x = wr.origin.x + x;
1404 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1405 bound.size.width = width;
1406 bound.size.height = height;
1409 ctr.x = bound.origin.x + bound.size.width /2;
1410 ctr.y = bound.origin.y + bound.size.height/2;
1412 float r1 = radians (angle1/64.0);
1413 float r2 = radians (angle2/64.0) + r1;
1414 BOOL clockwise = angle2 < 0;
1415 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1417 push_fg_gc (d, gc, fill_p);
1419 CGContextRef cgc = d->cgc;
1420 CGContextBeginPath (cgc);
1422 CGContextSaveGState(cgc);
1423 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1424 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1426 CGContextMoveToPoint (cgc, 0, 0);
1428 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1429 CGContextRestoreGState (cgc); // restore before stroke, for line width
1432 CGContextClosePath (cgc); // for proper line joining
1435 CGContextFillPath (cgc);
1437 set_line_mode (cgc, &gc->gcv);
1438 CGContextStrokePath (cgc);
1442 invalidate_drawable_cache (d);
1447 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1448 unsigned int width, unsigned int height, int angle1, int angle2)
1450 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1454 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1455 unsigned int width, unsigned int height, int angle1, int angle2)
1457 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1461 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1464 for (i = 0; i < narcs; i++)
1465 draw_arc (dpy, d, gc,
1466 arcs[i].x, arcs[i].y,
1467 arcs[i].width, arcs[i].height,
1468 arcs[i].angle1, arcs[i].angle2,
1474 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1477 for (i = 0; i < narcs; i++)
1478 draw_arc (dpy, d, gc,
1479 arcs[i].x, arcs[i].y,
1480 arcs[i].width, arcs[i].height,
1481 arcs[i].angle1, arcs[i].angle2,
1488 gcv_defaults (XGCValues *gcv, int depth)
1490 memset (gcv, 0, sizeof(*gcv));
1491 gcv->function = GXcopy;
1492 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1493 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1494 gcv->line_width = 1;
1495 gcv->cap_style = CapNotLast;
1496 gcv->join_style = JoinMiter;
1497 gcv->fill_rule = EvenOddRule;
1499 gcv->alpha_allowed_p = NO;
1500 gcv->antialias_p = YES;
1504 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1507 Assert (gc && from, "no gc");
1508 if (!gc || !from) return;
1510 if (mask & GCFunction) gc->gcv.function = from->function;
1511 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1512 if (mask & GCBackground) gc->gcv.background = from->background;
1513 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1514 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1515 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1516 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1517 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1518 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1519 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1521 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1522 if (mask & GCFont) XSetFont (0, gc, from->font);
1524 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1525 gc->gcv.alpha_allowed_p);
1526 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1527 gc->gcv.alpha_allowed_p);
1529 Assert ((! (mask & (GCLineStyle |
1536 GCGraphicsExposures |
1540 "unimplemented gcvalues mask");
1545 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1547 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1548 if (d->type == WINDOW) {
1550 } else { /* (d->type == PIXMAP) */
1551 gc->depth = d->pixmap.depth;
1554 gcv_defaults (&gc->gcv, gc->depth);
1555 set_gcv (gc, xgcv, mask);
1560 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1562 set_gcv (gc, gcv, mask);
1568 XFreeGC (Display *dpy, GC gc)
1571 XUnloadFont (dpy, gc->gcv.font);
1573 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1575 if (gc->gcv.clip_mask) {
1576 XFreePixmap (dpy, gc->gcv.clip_mask);
1577 CGImageRelease (gc->clip_mask);
1585 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1587 Assert (w && w->type == WINDOW, "not a window");
1588 memset (xgwa, 0, sizeof(*xgwa));
1589 xgwa->x = w->frame.origin.x;
1590 xgwa->y = w->frame.origin.y;
1591 xgwa->width = w->frame.size.width;
1592 xgwa->height = w->frame.size.height;
1594 xgwa->screen = dpy->screen;
1595 xgwa->visual = dpy->screen->visual;
1600 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1601 int *x_ret, int *y_ret,
1602 unsigned int *w_ret, unsigned int *h_ret,
1603 unsigned int *bw_ret, unsigned int *d_ret)
1605 *x_ret = d->frame.origin.x;
1606 *y_ret = d->frame.origin.y;
1607 *w_ret = d->frame.size.width;
1608 *h_ret = d->frame.size.height;
1609 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1610 *root_ret = RootWindow (dpy, 0);
1617 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1619 // store 32 bit ARGB in the pixel field.
1620 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1621 color->pixel = (uint32_t)
1623 (((color->red >> 8) & 0xFF) << 16) |
1624 (((color->green >> 8) & 0xFF) << 8) |
1625 (((color->blue >> 8) & 0xFF) ));
1630 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1631 unsigned long *pmret, unsigned int npl,
1632 unsigned long *pxret, unsigned int npx)
1638 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1640 Assert(0, "XStoreColors called");
1645 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1647 Assert(0, "XStoreColor called");
1652 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1653 unsigned long planes)
1659 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1661 unsigned char r=0, g=0, b=0;
1662 if (*spec == '#' && strlen(spec) == 7) {
1663 static unsigned const char hex[] = { // yeah yeah, shoot me.
1664 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,
1665 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,
1666 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,
1667 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,
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 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,
1671 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};
1672 r = (hex[spec[1]] << 4) | hex[spec[2]];
1673 g = (hex[spec[3]] << 4) | hex[spec[4]];
1674 b = (hex[spec[5]] << 4) | hex[spec[6]];
1675 } else if (!strcasecmp(spec,"black")) {
1677 } else if (!strcasecmp(spec,"white")) {
1679 } else if (!strcasecmp(spec,"red")) {
1681 } else if (!strcasecmp(spec,"green")) {
1683 } else if (!strcasecmp(spec,"blue")) {
1685 } else if (!strcasecmp(spec,"cyan")) {
1687 } else if (!strcasecmp(spec,"magenta")) {
1689 } else if (!strcasecmp(spec,"yellow")) {
1695 ret->red = (r << 8) | r;
1696 ret->green = (g << 8) | g;
1697 ret->blue = (b << 8) | b;
1698 ret->flags = DoRed|DoGreen|DoBlue;
1703 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1704 XColor *screen_ret, XColor *exact_ret)
1706 if (! XParseColor (dpy, cmap, name, screen_ret))
1708 *exact_ret = *screen_ret;
1709 return XAllocColor (dpy, cmap, screen_ret);
1713 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1715 validate_pixel (color->pixel, 32, NO);
1716 unsigned char r = ((color->pixel >> 16) & 0xFF);
1717 unsigned char g = ((color->pixel >> 8) & 0xFF);
1718 unsigned char b = ((color->pixel ) & 0xFF);
1719 color->red = (r << 8) | r;
1720 color->green = (g << 8) | g;
1721 color->blue = (b << 8) | b;
1722 color->flags = DoRed|DoGreen|DoBlue;
1727 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1730 for (i = 0; i < n; i++)
1731 XQueryColor (dpy, cmap, &c[i]);
1736 static unsigned long
1737 ximage_getpixel_1 (XImage *ximage, int x, int y)
1739 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1743 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1746 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1748 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1753 static unsigned long
1754 ximage_getpixel_32 (XImage *ximage, int x, int y)
1756 return ((unsigned long)
1757 *((uint32_t *) ximage->data +
1758 (y * (ximage->bytes_per_line >> 2)) +
1763 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1765 *((uint32_t *) ximage->data +
1766 (y * (ximage->bytes_per_line >> 2)) +
1767 x) = (uint32_t) pixel;
1773 XInitImage (XImage *ximage)
1775 if (!ximage->bytes_per_line)
1776 ximage->bytes_per_line = (ximage->depth == 1
1777 ? (ximage->width + 7) / 8
1778 : ximage->width * 4);
1780 if (ximage->depth == 1) {
1781 ximage->f.put_pixel = ximage_putpixel_1;
1782 ximage->f.get_pixel = ximage_getpixel_1;
1783 } else if (ximage->depth == 32 || ximage->depth == 24) {
1784 ximage->f.put_pixel = ximage_putpixel_32;
1785 ximage->f.get_pixel = ximage_getpixel_32;
1787 Assert (0, "unknown depth");
1794 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1795 int format, int offset, char *data,
1796 unsigned int width, unsigned int height,
1797 int bitmap_pad, int bytes_per_line)
1799 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1800 ximage->width = width;
1801 ximage->height = height;
1802 ximage->format = format;
1803 ximage->data = data;
1804 ximage->bitmap_unit = 8;
1805 ximage->byte_order = LSBFirst;
1806 ximage->bitmap_bit_order = ximage->byte_order;
1807 ximage->bitmap_pad = bitmap_pad;
1808 ximage->depth = depth;
1809 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1810 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1811 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1812 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1813 ximage->bytes_per_line = bytes_per_line;
1815 XInitImage (ximage);
1820 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1822 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1823 w, h, from->bitmap_pad, 0);
1824 to->data = (char *) malloc (h * to->bytes_per_line);
1826 if (x >= from->width)
1828 else if (x+w > from->width)
1829 w = from->width - x;
1831 if (y >= from->height)
1833 else if (y+h > from->height)
1834 h = from->height - y;
1837 for (ty = 0; ty < h; ty++)
1838 for (tx = 0; tx < w; tx++)
1839 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1844 XPixmapFormatValues *
1845 XListPixmapFormats (Display *dpy, int *n_ret)
1847 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1849 ret[0].bits_per_pixel = 32;
1850 ret[0].scanline_pad = 8;
1852 ret[1].bits_per_pixel = 1;
1853 ret[1].scanline_pad = 8;
1860 XGetPixel (XImage *ximage, int x, int y)
1862 return ximage->f.get_pixel (ximage, x, y);
1867 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1869 return ximage->f.put_pixel (ximage, x, y, pixel);
1873 XDestroyImage (XImage *ximage)
1875 if (ximage->data) free (ximage->data);
1882 flipbits (unsigned const char *in, unsigned char *out, int length)
1884 static const unsigned char table[256] = {
1885 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1886 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1887 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1888 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1889 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1890 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1891 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1892 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1893 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1894 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1895 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1896 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1897 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1898 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1899 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1900 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1901 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1902 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1903 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1904 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1905 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1906 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1907 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1908 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1909 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1910 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1911 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1912 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1913 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1914 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1915 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1916 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1918 while (length-- > 0)
1919 *out++ = table[*in++];
1924 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1925 int src_x, int src_y, int dest_x, int dest_y,
1926 unsigned int w, unsigned int h)
1928 CGRect wr = d->frame;
1930 Assert (gc, "no GC");
1931 Assert ((w < 65535), "improbably large width");
1932 Assert ((h < 65535), "improbably large height");
1933 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1934 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1935 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1936 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1938 // Clip width and height to the bounds of the Drawable
1940 if (dest_x + w > wr.size.width) {
1941 if (dest_x > wr.size.width)
1943 w = wr.size.width - dest_x;
1945 if (dest_y + h > wr.size.height) {
1946 if (dest_y > wr.size.height)
1948 h = wr.size.height - dest_y;
1950 if (w <= 0 || h <= 0)
1953 // Clip width and height to the bounds of the XImage
1955 if (src_x + w > ximage->width) {
1956 if (src_x > ximage->width)
1958 w = ximage->width - src_x;
1960 if (src_y + h > ximage->height) {
1961 if (src_y > ximage->height)
1963 h = ximage->height - src_y;
1965 if (w <= 0 || h <= 0)
1968 CGContextRef cgc = d->cgc;
1970 if (gc->gcv.function == GXset ||
1971 gc->gcv.function == GXclear) {
1972 // "set" and "clear" are dumb drawing modes that ignore the source
1973 // bits and just draw solid rectangles.
1974 set_color (cgc, (gc->gcv.function == GXset
1975 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1976 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1977 gc->depth, gc->gcv.alpha_allowed_p, YES);
1978 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1982 int bpl = ximage->bytes_per_line;
1983 int bpp = ximage->bits_per_pixel;
1984 int bsize = bpl * h;
1985 char *data = ximage->data;
1988 r.origin.x = wr.origin.x + dest_x;
1989 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1995 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1996 to create a CGImage from a sub-rectagle of the XImage.
1998 data += (src_y * bpl) + (src_x * 4);
1999 CGDataProviderRef prov =
2000 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2002 CGImageRef cgi = CGImageCreate (w, h,
2005 /* Need this for XPMs to have the right
2006 colors, e.g. the logo in "maze". */
2007 (kCGImageAlphaNoneSkipFirst |
2008 kCGBitmapByteOrder32Host),
2010 NULL, /* decode[] */
2011 NO, /* interpolate */
2012 kCGRenderingIntentDefault);
2013 CGDataProviderRelease (prov);
2014 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2015 CGContextDrawImage (cgc, r, cgi);
2016 CGImageRelease (cgi);
2018 } else { // (bpp == 1)
2020 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2022 #### However, the bit order within a byte in a 1bpp XImage is
2023 the wrong way around from what Quartz expects, so first we
2024 have to copy the data to reverse it. Shit! Maybe it
2025 would be worthwhile to go through the hacks and #ifdef
2026 each one that diddles 1bpp XImage->data directly...
2028 Assert ((src_x % 8) == 0,
2029 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2031 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
2032 unsigned char *flipped = (unsigned char *) malloc (bsize);
2034 flipbits ((unsigned char *) data, flipped, bsize);
2036 CGDataProviderRef prov =
2037 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2038 CGImageRef mask = CGImageMaskCreate (w, h,
2041 NULL, /* decode[] */
2042 NO); /* interpolate */
2043 push_fg_gc (d, gc, YES);
2045 CGContextFillRect (cgc, r); // foreground color
2046 CGContextClipToMask (cgc, r, mask);
2047 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
2048 CGContextFillRect (cgc, r); // background color
2052 CGDataProviderRelease (prov);
2053 CGImageRelease (mask);
2056 invalidate_drawable_cache (d);
2063 XGetImage (Display *dpy, Drawable d, int x, int y,
2064 unsigned int width, unsigned int height,
2065 unsigned long plane_mask, int format)
2067 const unsigned char *data = 0;
2068 size_t depth, ibpp, ibpl;
2069 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2070 # ifndef USE_BACKBUFFER
2071 NSBitmapImageRep *bm = 0;
2074 Assert ((width < 65535), "improbably large width");
2075 Assert ((height < 65535), "improbably large height");
2076 Assert ((x < 65535 && x > -65535), "improbably large x");
2077 Assert ((y < 65535 && y > -65535), "improbably large y");
2079 CGContextRef cgc = d->cgc;
2081 #ifndef USE_BACKBUFFER
2082 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2083 if (d->type == PIXMAP)
2086 depth = (d->type == PIXMAP
2089 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2090 src_format = BGRA; // #### Should this be ARGB on PPC?
2091 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2092 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2093 data = CGBitmapContextGetData (cgc);
2094 Assert (data, "CGBitmapContextGetData failed");
2096 # ifndef USE_BACKBUFFER
2097 } else { /* (d->type == WINDOW) */
2099 // get the bits (desired sub-rectangle) out of the NSView
2101 nsfrom.origin.x = x;
2102 // nsfrom.origin.y = y;
2103 nsfrom.origin.y = d->frame.size.height - height - y;
2104 nsfrom.size.width = width;
2105 nsfrom.size.height = height;
2106 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2108 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2109 ibpp = [bm bitsPerPixel];
2110 ibpl = [bm bytesPerRow];
2111 data = [bm bitmapData];
2112 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2113 # endif // !USE_BACKBUFFER
2116 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2117 data += (y * ibpl) + (x * (ibpp/8));
2119 format = (depth == 1 ? XYPixmap : ZPixmap);
2120 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2121 format, 0, 0, width, height, 0, 0);
2122 image->data = (char *) malloc (height * image->bytes_per_line);
2124 int obpl = image->bytes_per_line;
2126 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2127 means that on Intel it is BGRA when viewed by bytes (And BGR
2128 when using 24bpp packing).
2130 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2131 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2132 indicator of this latest kink.
2136 const unsigned char *iline = data;
2137 for (yy = 0; yy < height; yy++) {
2139 const unsigned char *iline2 = iline;
2140 for (xx = 0; xx < width; xx++) {
2142 iline2++; // ignore R or A or A or B
2143 iline2++; // ignore G or B or R or G
2144 unsigned char r = *iline2++; // use B or G or G or R
2145 if (ibpp == 32) iline2++; // ignore A or R or B or A
2147 XPutPixel (image, xx, yy, (r ? 1 : 0));
2152 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2153 const unsigned char *iline = data;
2154 unsigned char *oline = (unsigned char *) image->data;
2155 for (yy = 0; yy < height; yy++) {
2157 const unsigned char *iline2 = iline;
2158 unsigned char *oline2 = oline;
2160 switch (src_format) {
2162 for (xx = 0; xx < width; xx++) {
2163 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2164 unsigned char r = *iline2++;
2165 unsigned char g = *iline2++;
2166 unsigned char b = *iline2++;
2167 uint32_t pixel = ((a << 24) |
2171 *((uint32_t *) oline2) = pixel;
2176 for (xx = 0; xx < width; xx++) {
2177 unsigned char r = *iline2++;
2178 unsigned char g = *iline2++;
2179 unsigned char b = *iline2++;
2180 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2181 uint32_t pixel = ((a << 24) |
2185 *((uint32_t *) oline2) = pixel;
2190 for (xx = 0; xx < width; xx++) {
2191 unsigned char b = *iline2++;
2192 unsigned char g = *iline2++;
2193 unsigned char r = *iline2++;
2194 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2195 uint32_t pixel = ((a << 24) |
2199 *((uint32_t *) oline2) = pixel;
2213 # ifndef USE_BACKBUFFER
2214 if (bm) [bm release];
2222 /* Returns a transformation matrix to do rotation as per the provided
2223 EXIF "Orientation" value.
2225 static CGAffineTransform
2226 exif_rotate (int rot, CGSize rect)
2228 CGAffineTransform trans = CGAffineTransformIdentity;
2230 case 2: // flip horizontal
2231 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2232 trans = CGAffineTransformScale (trans, -1, 1);
2235 case 3: // rotate 180
2236 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2237 trans = CGAffineTransformRotate (trans, M_PI);
2240 case 4: // flip vertical
2241 trans = CGAffineTransformMakeTranslation (0, rect.height);
2242 trans = CGAffineTransformScale (trans, 1, -1);
2245 case 5: // transpose (UL-to-LR axis)
2246 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2247 trans = CGAffineTransformScale (trans, -1, 1);
2248 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2251 case 6: // rotate 90
2252 trans = CGAffineTransformMakeTranslation (0, rect.width);
2253 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2256 case 7: // transverse (UR-to-LL axis)
2257 trans = CGAffineTransformMakeScale (-1, 1);
2258 trans = CGAffineTransformRotate (trans, M_PI / 2);
2261 case 8: // rotate 270
2262 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2263 trans = CGAffineTransformRotate (trans, M_PI / 2);
2275 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2276 Bool nsimg_p, void *img_arg,
2277 XRectangle *geom_ret, int exif_rotation)
2281 CGImageSourceRef cgsrc;
2282 # endif // USE_IPHONE
2285 CGContextRef cgc = d->cgc;
2289 NSImage *nsimg = (NSImage *) img_arg;
2290 imgr = [nsimg size];
2293 // convert the NSImage to a CGImage via the toll-free-bridging
2294 // of NSData and CFData...
2296 NSData *nsdata = [NSBitmapImageRep
2297 TIFFRepresentationOfImageRepsInArray:
2298 [nsimg representations]];
2299 CFDataRef cfdata = (CFDataRef) nsdata;
2300 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2301 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2302 # else // USE_IPHONE
2303 cgi = nsimg.CGImage;
2304 # endif // USE_IPHONE
2307 cgi = (CGImageRef) img_arg;
2308 imgr.width = CGImageGetWidth (cgi);
2309 imgr.height = CGImageGetHeight (cgi);
2312 Bool rot_p = (exif_rotation >= 5);
2315 imgr = NSMakeSize (imgr.height, imgr.width);
2317 CGRect winr = d->frame;
2318 float rw = winr.size.width / imgr.width;
2319 float rh = winr.size.height / imgr.height;
2320 float r = (rw < rh ? rw : rh);
2323 dst.size.width = imgr.width * r;
2324 dst.size.height = imgr.height * r;
2325 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2326 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2328 dst2.origin.x = dst2.origin.y = 0;
2330 dst2.size.width = dst.size.height;
2331 dst2.size.height = dst.size.width;
2333 dst2.size = dst.size;
2336 // Clear the part not covered by the image to background or black.
2338 if (d->type == WINDOW)
2339 XClearWindow (dpy, d);
2341 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2342 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2345 CGAffineTransform trans =
2346 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2348 CGContextSaveGState (cgc);
2349 CGContextConcatCTM (cgc,
2350 CGAffineTransformMakeTranslation (dst.origin.x,
2352 CGContextConcatCTM (cgc, trans);
2353 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2354 CGContextDrawImage (cgc, dst2, cgi);
2355 CGContextRestoreGState (cgc);
2360 CGImageRelease (cgi);
2362 # endif // USE_IPHONE
2365 geom_ret->x = dst.origin.x;
2366 geom_ret->y = dst.origin.y;
2367 geom_ret->width = dst.size.width;
2368 geom_ret->height = dst.size.height;
2371 invalidate_drawable_cache (d);
2377 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2379 unsigned int w, unsigned int h,
2380 unsigned long fg, unsigned int bg,
2383 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2384 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2385 (char *) data, w, h, 0, 0);
2387 gcv.foreground = fg;
2388 gcv.background = bg;
2389 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2390 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2393 XDestroyImage (image);
2398 XCreatePixmap (Display *dpy, Drawable d,
2399 unsigned int width, unsigned int height, unsigned int depth)
2401 char *data = (char *) malloc (width * height * 4);
2402 if (! data) return 0;
2404 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2406 p->frame.size.width = width;
2407 p->frame.size.height = height;
2408 p->pixmap.depth = depth;
2409 p->pixmap.cgc_buffer = data;
2411 /* Quartz doesn't have a 1bpp image type.
2412 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2413 don't support that! So we always use 32bpp, regardless of depth. */
2415 p->cgc = CGBitmapContextCreate (data, width, height,
2416 8, /* bits per component */
2417 width * 4, /* bpl */
2419 // Without this, it returns 0...
2420 (kCGImageAlphaNoneSkipFirst |
2421 kCGBitmapByteOrder32Host)
2423 Assert (p->cgc, "could not create CGBitmapContext");
2429 XFreePixmap (Display *d, Pixmap p)
2431 Assert (p && p->type == PIXMAP, "not a pixmap");
2432 invalidate_drawable_cache (p);
2433 CGContextRelease (p->cgc);
2434 if (p->pixmap.cgc_buffer)
2435 free (p->pixmap.cgc_buffer);
2442 copy_pixmap (Display *dpy, Pixmap p)
2445 Assert (p->type == PIXMAP, "not a pixmap");
2447 int width = p->frame.size.width;
2448 int height = p->frame.size.height;
2449 char *data = (char *) malloc (width * height * 4);
2450 if (! data) return 0;
2452 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2454 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2457 p2->pixmap.cgc_buffer = data;
2458 p2->cgc = CGBitmapContextCreate (data, width, height,
2459 8, /* bits per component */
2460 width * 4, /* bpl */
2462 // Without this, it returns 0...
2463 (kCGImageAlphaNoneSkipFirst |
2464 kCGBitmapByteOrder32Host)
2466 Assert (p2->cgc, "could not create CGBitmapContext");
2472 /* Font metric terminology, as used by X11:
2474 "lbearing" is the distance from the logical origin to the leftmost pixel.
2475 If a character's ink extends to the left of the origin, it is negative.
2477 "rbearing" is the distance from the logical origin to the rightmost pixel.
2479 "descent" is the distance from the logical origin to the bottommost pixel.
2480 For characters with descenders, it is negative.
2482 "ascent" is the distance from the logical origin to the topmost pixel.
2483 It is the number of pixels above the baseline.
2485 "width" is the distance from the logical origin to the position where
2486 the logical origin of the next character should be placed.
2488 If "rbearing" is greater than "width", then this character overlaps the
2489 following character. If smaller, then there is trailing blank space.
2493 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2496 query_font (Font fid)
2498 if (!fid || !fid->nsfont) {
2499 Assert (0, "no NSFont in fid");
2502 if (![fid->nsfont fontName]) {
2503 Assert(0, @"broken NSFont in fid");
2510 XFontStruct *f = &fid->metrics;
2511 XCharStruct *min = &f->min_bounds;
2512 XCharStruct *max = &f->max_bounds;
2514 //#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2515 //#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
2516 #define CEIL(F) ceil(F)
2517 #define FLOOR(F) floor(F)
2520 f->min_char_or_byte2 = first;
2521 f->max_char_or_byte2 = last;
2522 f->default_char = 'M';
2523 f->ascent = CEIL ([fid->nsfont ascender]);
2524 f->descent = -FLOOR ([fid->nsfont descender]);
2526 min->width = 255; // set to smaller values in the loop
2529 min->lbearing = 255;
2530 min->rbearing = 255;
2532 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2536 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2537 [fid->nsfont pointSize],
2539 Assert (ctfont, @"no CTFontRef for UIFont");
2541 for (i = first; i <= last; i++) {
2542 CGSize advancement = CGSizeMake (0, 0);
2543 CGRect bbox = CGRectMake (0, 0, 0, 0);
2545 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2546 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2547 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2548 width of the character and the ascent of the font.
2550 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2551 the CoreText library, but there's no non-CoreText way to turn a
2552 unichar into a CGGlyph.
2554 CGGlyph cgglyph = 0;
2556 if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
2558 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2559 kCTFontDefaultOrientation,
2561 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2562 &cgglyph, &advancement, 1);
2566 // This is normal, since Latin1 does not encode 0-31 or 127-159.
2567 // NSLog (@"no glyph for character %d\n", i);
2571 if (i == 224) { // Latin1 == "agrave", MacRoman == "daggerdouble".
2572 NSString *glyph_name = (NSString *)
2573 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2575 Assert ([glyph_name isEqualToString:@"agrave"], @"wrong encoding");
2577 if (i == 250) { // Latin1 == "uacute", MacRoman == "dotaccent".
2578 NSString *glyph_name = (NSString *)
2579 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2581 Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
2586 // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2587 bbox.origin.x -= 2.0/3.0;
2588 bbox.size.width += 4.0/3.0;
2589 bbox.size.height += 1.0/2.0;
2592 /* Now that we know the advancement and bounding box, we can compute
2593 the lbearing and rbearing.
2595 XCharStruct *cs = &f->per_char[i-first];
2597 // cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2598 cs->ascent = CEIL (bbox.origin.y + bbox.size.height);
2599 cs->descent = CEIL(-bbox.origin.y);
2600 cs->lbearing = FLOOR (bbox.origin.x);
2601 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2602 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width);
2603 cs->width = FLOOR (advancement.width + 0.5);
2605 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2607 // Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2610 max->width = MAX (max->width, cs->width);
2611 max->ascent = MAX (max->ascent, cs->ascent);
2612 max->descent = MAX (max->descent, cs->descent);
2613 max->lbearing = MAX (max->lbearing, cs->lbearing);
2614 max->rbearing = MAX (max->rbearing, cs->rbearing);
2616 min->width = MIN (min->width, cs->width);
2617 min->ascent = MIN (min->ascent, cs->ascent);
2618 min->descent = MIN (min->descent, cs->descent);
2619 min->lbearing = MIN (min->lbearing, cs->lbearing);
2620 min->rbearing = MIN (min->rbearing, cs->rbearing);
2626 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2627 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2628 i, i, cs->width, cs->lbearing, cs->rbearing,
2629 cs->ascent, cs->descent,
2630 bbox.size.width, bbox.size.height,
2631 bbox.origin.x, bbox.origin.y,
2632 advancement.width, advancement.height);
2640 // Since 'Font' includes the metrics, this just makes a copy of that.
2643 XQueryFont (Display *dpy, Font fid)
2646 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2649 // copy XCharStruct array
2650 int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2651 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2652 memcpy (f->per_char, fid->metrics.per_char,
2653 size * sizeof (XCharStruct));
2660 copy_font (Font fid)
2662 // copy 'Font' struct
2663 Font fid2 = (Font) malloc (sizeof(*fid2));
2666 // copy XCharStruct array
2667 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2668 fid2->metrics.per_char = (XCharStruct *)
2669 malloc ((size + 2) * sizeof (XCharStruct));
2670 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2671 size * sizeof (XCharStruct));
2673 // copy the other pointers
2674 fid2->ps_name = strdup (fid->ps_name);
2675 // [fid2->nsfont retain];
2676 fid2->metrics.fid = fid2;
2683 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2686 Assert (size > 0, "zero font size");
2691 // "Monaco" only exists in plain.
2692 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2694 if (bold && ital) name = "Courier-BoldOblique";
2695 else if (bold) name = "Courier-Bold";
2696 else if (ital) name = "Courier-Oblique";
2697 else name = "Courier";
2701 // "Georgia" looks better than "Times".
2703 if (bold && ital) name = "Georgia-BoldItalic";
2704 else if (bold) name = "Georgia-Bold";
2705 else if (ital) name = "Georgia-Italic";
2706 else name = "Georgia";
2710 // "Geneva" only exists in plain.
2711 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2712 // "Verdana" renders smoother than "Helvetica" for some reason.
2714 if (bold && ital) name = "Verdana-BoldItalic";
2715 else if (bold) name = "Verdana-Bold";
2716 else if (ital) name = "Verdana-Italic";
2717 else name = "Verdana";
2720 NSString *nsname = [NSString stringWithCString:name
2721 encoding:NSUTF8StringEncoding];
2722 NSFont *f = [NSFont fontWithName:nsname size:size];
2724 *name_ret = strdup(name);
2729 try_native_font (const char *name, float scale,
2730 char **name_ret, float *size_ret)
2732 if (!name) return 0;
2733 const char *spc = strrchr (name, ' ');
2736 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2739 if (size <= 4) return 0;
2743 char *name2 = strdup (name);
2744 name2[strlen(name2) - strlen(spc)] = 0;
2745 NSString *nsname = [NSString stringWithCString:name2
2746 encoding:NSUTF8StringEncoding];
2747 NSFont *f = [NSFont fontWithName:nsname size:size];
2759 /* Returns a random font in the given size and face.
2762 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2765 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2766 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2767 NSArray *fonts = [[NSFontManager sharedFontManager]
2768 availableFontNamesWithTraits:mask];
2769 if (!fonts) return 0;
2771 int n = [fonts count];
2772 if (n <= 0) return 0;
2775 for (j = 0; j < n; j++) {
2776 int i = random() % n;
2777 NSString *name = [fonts objectAtIndex:i];
2778 NSFont *f = [NSFont fontWithName:name size:size];
2781 /* Don't use this font if it (probably) doesn't include ASCII characters.
2783 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2784 if (! (enc == NSUTF8StringEncoding ||
2785 enc == NSISOLatin1StringEncoding ||
2786 enc == NSNonLossyASCIIStringEncoding ||
2787 enc == NSISOLatin2StringEncoding ||
2788 enc == NSUnicodeStringEncoding ||
2789 enc == NSWindowsCP1250StringEncoding ||
2790 enc == NSWindowsCP1252StringEncoding ||
2791 enc == NSMacOSRomanStringEncoding)) {
2792 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2795 // NSLog(@"using \"%@\": %d", name, enc);
2797 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2801 // None of the fonts support ASCII?
2804 # else // USE_IPHONE
2806 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2807 NSArray *families = [UIFont familyNames];
2808 NSMutableDictionary *famdict = [NSMutableDictionary
2809 dictionaryWithCapacity:100];
2810 NSObject *y = [NSNumber numberWithBool:YES];
2811 for (NSString *name in families) {
2812 // There are many dups in the families array -- uniquify it.
2813 [famdict setValue:y forKey:name];
2816 for (NSString *name in famdict) {
2817 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2820 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2823 BOOL bb = MATCH(@"Bold");
2824 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2826 if (!bold != !bb) continue;
2827 if (!ital != !ii) continue;
2829 /* Check if it can do ASCII. No good way to accomplish this!
2830 These are fonts present in iPhone Simulator as of June 2012
2831 that don't include ASCII.
2833 if (MATCH(@"AppleGothic") || // Korean
2834 MATCH(@"Dingbats") || // Dingbats
2835 MATCH(@"Emoji") || // Emoticons
2836 MATCH(@"Geeza") || // Arabic
2837 MATCH(@"Hebrew") || // Hebrew
2838 MATCH(@"HiraKaku") || // Japanese
2839 MATCH(@"HiraMin") || // Japanese
2840 MATCH(@"Kailasa") || // Tibetan
2841 MATCH(@"Ornaments") || // Dingbats
2842 MATCH(@"STHeiti") // Chinese
2846 [fonts addObject:fn];
2851 if (! [fonts count]) return 0; // Nothing suitable?
2853 int i = random() % [fonts count];
2854 NSString *name = [fonts objectAtIndex:i];
2855 UIFont *ff = [UIFont fontWithName:name size:size];
2856 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2860 # endif // USE_IPHONE
2865 try_xlfd_font (const char *name, float scale,
2866 char **name_ret, float *size_ret)
2877 const char *s = (name ? name : "");
2879 while (*s && (*s == '*' || *s == '-'))
2882 while (*s2 && (*s2 != '*' && *s2 != '-'))
2885 unsigned long L = s2-s;
2888 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2889 else if (CMP ("random")) rand = YES;
2890 else if (CMP ("bold")) bold = YES;
2891 else if (CMP ("i")) ital = YES;
2892 else if (CMP ("o")) ital = YES;
2893 else if (CMP ("courier")) fixed = YES;
2894 else if (CMP ("fixed")) fixed = YES;
2895 else if (CMP ("m")) fixed = YES;
2896 else if (CMP ("times")) serif = YES;
2897 else if (CMP ("6x10")) fixed = YES, size = 8;
2898 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2899 else if (CMP ("9x15")) fixed = YES, size = 12;
2900 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2901 else if (CMP ("vga")) fixed = YES, size = 12;
2902 else if (CMP ("console")) fixed = YES, size = 12;
2903 else if (CMP ("gallant")) fixed = YES, size = 12;
2905 else if (size == 0) {
2907 if (1 == sscanf (s, " %d ", &n))
2914 if (size < 6 || size > 1000)
2920 nsfont = random_font (bold, ital, size, &ps_name);
2923 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2925 // if that didn't work, turn off attibutes until it does
2926 // (e.g., there is no "Monaco-Bold".)
2928 if (!nsfont && serif) {
2930 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2932 if (!nsfont && ital) {
2934 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2936 if (!nsfont && bold) {
2938 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2940 if (!nsfont && fixed) {
2942 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2946 *name_ret = ps_name;
2956 XLoadFont (Display *dpy, const char *name)
2958 Font fid = (Font) calloc (1, sizeof(*fid));
2963 // Scale up fonts on Retina displays.
2964 scale = dpy->main_window->window.view.contentScaleFactor;
2967 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2969 if (!fid->nsfont && name &&
2970 strchr (name, ' ') &&
2971 !strchr (name, '*')) {
2972 // If name contains a space but no stars, it is a native font spec --
2973 // return NULL so that we know it really didn't exist. Else, it is an
2974 // XLFD font, so keep trying.
2975 XUnloadFont (dpy, fid);
2980 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2982 // We should never return NULL for XLFD fonts.
2984 Assert (0, "no font");
2987 CFRetain (fid->nsfont); // needed for garbage collection?
2989 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2998 XLoadQueryFont (Display *dpy, const char *name)
3000 Font fid = XLoadFont (dpy, name);
3002 return XQueryFont (dpy, fid);
3006 XUnloadFont (Display *dpy, Font fid)
3009 free (fid->ps_name);
3010 if (fid->metrics.per_char)
3011 free (fid->metrics.per_char);
3013 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3014 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3015 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3016 // They're probably not very big...
3018 // [fid->nsfont release];
3019 // CFRelease (fid->nsfont);
3026 XFreeFontInfo (char **names, XFontStruct *info, int n)
3030 for (i = 0; i < n; i++)
3031 if (names[i]) free (names[i]);
3035 for (i = 0; i < n; i++)
3036 if (info[i].per_char)
3037 free (info[i].per_char);
3044 XFreeFont (Display *dpy, XFontStruct *f)
3047 XFreeFontInfo (0, f, 1);
3048 XUnloadFont (dpy, fid);
3054 XSetFont (Display *dpy, GC gc, Font fid)
3057 XUnloadFont (dpy, gc->gcv.font);
3058 gc->gcv.font = copy_font (fid);
3059 [gc->gcv.font->nsfont retain];
3060 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3065 XTextExtents (XFontStruct *f, const char *s, int length,
3066 int *dir_ret, int *ascent_ret, int *descent_ret,
3069 memset (cs, 0, sizeof(*cs));
3071 for (i = 0; i < length; i++) {
3072 unsigned char c = (unsigned char) s[i];
3073 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3074 c = f->default_char;
3075 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3079 cs->ascent = MAX (cs->ascent, cc->ascent);
3080 cs->descent = MAX (cs->descent, cc->descent);
3081 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3082 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3083 cs->width += cc->width;
3087 *ascent_ret = f->ascent;
3088 *descent_ret = f->descent;
3093 XTextWidth (XFontStruct *f, const char *s, int length)
3095 int ascent, descent, dir;
3097 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3103 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3104 const char *str, int len, BOOL clear_background_p)
3106 if (clear_background_p) {
3107 int ascent, descent, dir;
3109 XTextExtents (&gc->gcv.font->metrics, str, len,
3110 &dir, &ascent, &descent, &cs);
3111 draw_rect (dpy, d, gc,
3112 x + MIN (0, cs.lbearing),
3113 y - MAX (0, ascent),
3114 MAX (MAX (0, cs.rbearing) -
3115 MIN (0, cs.lbearing),
3117 MAX (0, ascent) + MAX (0, descent),
3121 CGRect wr = d->frame;
3122 CGContextRef cgc = d->cgc;
3124 unsigned long argb = gc->gcv.foreground;
3125 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3126 float a = ((argb >> 24) & 0xFF) / 255.0;
3127 float r = ((argb >> 16) & 0xFF) / 255.0;
3128 float g = ((argb >> 8) & 0xFF) / 255.0;
3129 float b = ((argb ) & 0xFF) / 255.0;
3130 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3132 if (!gc->gcv.font) {
3133 Assert (0, "no font");
3137 NSDictionary *attr =
3138 [NSDictionary dictionaryWithObjectsAndKeys:
3139 gc->gcv.font->nsfont, NSFontAttributeName,
3140 fg, NSForegroundColorAttributeName,
3143 // Don't understand why we have to do both set_color and
3144 // NSForegroundColorAttributeName, but we do.
3146 set_color (cgc, argb, 32, NO, YES);
3148 char *s2 = (char *) malloc (len + 1);
3149 strncpy (s2, str, len);
3151 NSString *nsstr = [NSString stringWithCString:s2
3152 encoding:NSISOLatin1StringEncoding];
3153 NSAttributedString *astr = [[NSAttributedString alloc]
3154 initWithString:nsstr
3156 CTLineRef dl = CTLineCreateWithAttributedString (
3157 (__bridge CFAttributedStringRef) astr);
3158 CGContextSetTextPosition (cgc,
3160 wr.origin.y + wr.size.height - y);
3161 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3162 CTLineDraw (dl, cgc);
3165 invalidate_drawable_cache (d);
3171 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3172 const char *str, int len)
3174 return draw_string (dpy, d, gc, x, y, str, len, NO);
3178 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3179 const char *str, int len)
3181 return draw_string (dpy, d, gc, x, y, str, len, YES);
3186 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3188 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3189 gc->gcv.foreground = fg;
3195 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3197 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3198 gc->gcv.background = bg;
3203 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3205 gc->gcv.alpha_allowed_p = allowed;
3210 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3212 gc->gcv.antialias_p = antialias_p;
3218 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3219 int line_style, int cap_style, int join_style)
3221 gc->gcv.line_width = line_width;
3222 Assert (line_style == LineSolid, "only LineSolid implemented");
3223 // gc->gcv.line_style = line_style;
3224 gc->gcv.cap_style = cap_style;
3225 gc->gcv.join_style = join_style;
3230 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3236 XSetFunction (Display *dpy, GC gc, int which)
3238 gc->gcv.function = which;
3243 XSetSubwindowMode (Display *dpy, GC gc, int which)
3245 gc->gcv.subwindow_mode = which;
3250 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3252 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3254 if (gc->gcv.clip_mask) {
3255 XFreePixmap (dpy, gc->gcv.clip_mask);
3256 CGImageRelease (gc->clip_mask);
3259 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3260 if (gc->gcv.clip_mask)
3262 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3270 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3272 gc->gcv.clip_x_origin = x;
3273 gc->gcv.clip_y_origin = y;
3279 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3280 int *root_x_ret, int *root_y_ret,
3281 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3283 Assert (w && w->type == WINDOW, "not a window");
3286 int x = w->window.last_mouse_x;
3287 int y = w->window.last_mouse_y;
3288 if (root_x_ret) *root_x_ret = x;
3289 if (root_y_ret) *root_y_ret = y;
3290 if (win_x_ret) *win_x_ret = x;
3291 if (win_y_ret) *win_y_ret = y;
3293 # else // !USE_IPHONE
3295 NSWindow *nsw = [w->window.view window];
3297 // get bottom left of window on screen, from bottom left
3298 wpos.x = wpos.y = 0;
3299 wpos = [nsw convertBaseToScreen:wpos];
3302 // get bottom left of view on window, from bottom left
3303 vpos.x = vpos.y = 0;
3304 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3306 // get bottom left of view on screen, from bottom left
3310 // get top left of view on screen, from bottom left
3311 vpos.y += w->frame.size.height;
3313 // get top left of view on screen, from top left
3314 NSArray *screens = [NSScreen screens];
3315 NSScreen *screen = (screens && [screens count] > 0
3316 ? [screens objectAtIndex:0]
3317 : [NSScreen mainScreen]);
3319 double s = w->window.view.contentScaleFactor;
3323 NSRect srect = [screen frame];
3324 vpos.y = (s * srect.size.height) - vpos.y;
3326 // get the mouse position on window, from bottom left
3327 NSEvent *e = [NSApp currentEvent];
3328 NSPoint p = [e locationInWindow];
3330 // get mouse position on screen, from bottom left
3334 // get mouse position on screen, from top left
3335 p.y = srect.size.height - p.y;
3337 if (root_x_ret) *root_x_ret = (int) p.x;
3338 if (root_y_ret) *root_y_ret = (int) p.y;
3339 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3340 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3341 # endif // !USE_IPHONE
3343 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3344 if (root_ret) *root_ret = 0;
3345 if (child_ret) *child_ret = 0;
3350 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3351 int src_x, int src_y,
3352 int *dest_x_ret, int *dest_y_ret,
3355 Assert (w && w->type == WINDOW, "not a window");
3363 # else // !USE_IPHONE
3365 NSWindow *nsw = [w->window.view window];
3367 // get bottom left of window on screen, from bottom left
3368 wpos.x = wpos.y = 0;
3369 wpos = [nsw convertBaseToScreen:wpos];
3372 // get bottom left of view on window, from bottom left
3373 vpos.x = vpos.y = 0;
3374 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3376 // get bottom left of view on screen, from bottom left
3380 // get top left of view on screen, from bottom left
3381 vpos.y += w->frame.size.height;
3383 // get top left of view on screen, from top left
3384 NSArray *screens = [NSScreen screens];
3385 NSScreen *screen = (screens && [screens count] > 0
3386 ? [screens objectAtIndex:0]
3387 : [NSScreen mainScreen]);
3389 double s = w->window.view.contentScaleFactor;
3393 NSRect srect = [screen frame];
3394 vpos.y = (s * srect.size.height) - vpos.y;
3396 // point starts out relative to top left of view
3401 // get point relative to top left of screen
3404 # endif // !USE_IPHONE
3415 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3421 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3424 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3426 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3427 if ((unsigned int) ks <= 255)
3430 // Put control characters in the string. Not meta.
3431 if (e->state & ControlMask) {
3432 if (c >= 'a' && c <= 'z') // Upcase control.
3434 if (c >= '@' && c <= '_') // Shift to control page.
3436 if (c == ' ') // C-SPC is NULL.
3440 if (k_ret) *k_ret = ks;
3441 if (size > 0) buf[0] = c;
3442 if (size > 1) buf[1] = 0;
3443 return (size > 0 ? 1 : 0);
3448 XFlush (Display *dpy)
3450 // Just let the event loop take care of this on its own schedule.
3455 XSync (Display *dpy, Bool flush)
3457 return XFlush (dpy);
3461 // declared in utils/visual.h
3463 has_writable_cells (Screen *s, Visual *v)
3469 visual_depth (Screen *s, Visual *v)
3475 visual_cells (Screen *s, Visual *v)
3481 visual_class (Screen *s, Visual *v)
3487 get_bits_per_pixel (Display *dpy, int depth)
3489 Assert (depth == 32 || depth == 1, "unexpected depth");
3493 // declared in utils/grabclient.h
3495 use_subwindow_mode_p (Screen *screen, Window window)