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"
52 # define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
55 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
59 # define MAX(a,b) ((a)>(b)?(a):(b))
60 # define MIN(a,b) ((a)<(b)?(a):(b))
63 struct jwxyz_Drawable {
64 enum { WINDOW, PIXMAP } type;
71 unsigned long background;
72 int last_mouse_x, last_mouse_y;
76 void *cgc_buffer; // the bits to which CGContextRef renders
81 struct jwxyz_Display {
85 struct jwxyz_sources_data *timers_data;
88 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
89 This can change if the window is dragged to
90 a different screen. */
93 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
94 our images with this to avoid translation
107 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
113 float size; // points
115 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
116 // But we need the metrics on both of them, so they go here.
120 struct jwxyz_XFontSet {
125 /* Instead of calling abort(), throw a real exception, so that
126 XScreenSaverView can catch it and display a dialog.
129 jwxyz_abort (const char *fmt, ...)
137 va_start (args, fmt);
138 vsprintf (s, fmt, args);
141 [[NSException exceptionWithName: NSInternalInconsistencyException
142 reason: [NSString stringWithCString: s
143 encoding:NSUTF8StringEncoding]
146 abort(); // not reached
150 /* We keep a list of all of the Displays that have been created and not
151 yet freed so that they can have sensible display numbers. If three
152 displays are created (0, 1, 2) and then #1 is closed, then the fourth
153 display will be given the now-unused display number 1. (Everything in
154 here assumes a 1:1 Display/Screen mapping.)
156 The size of this array is the most number of live displays at one time.
157 So if it's 20, then we'll blow up if the system has 19 monitors and also
158 has System Preferences open (the small preview window).
160 Note that xlockmore-style savers tend to allocate big structures, so
161 setting this to 1000 will waste a few megabytes. Also some of them assume
162 that the number of screens never changes, so dynamically expanding this
166 static Display *jwxyz_live_displays[20] = { 0, };
171 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
173 CGContextRef cgc = (CGContextRef) cgc_arg;
174 NSView *view = (NSView *) nsview_arg;
175 Assert (view, "no view");
178 Display *d = (Display *) calloc (1, sizeof(*d));
179 d->screen = (Screen *) calloc (1, sizeof(Screen));
183 d->screen->screen_number = 0;
186 // Find the first empty slot in live_displays and plug us in.
187 int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
189 for (i = 0; i < size; i++) {
190 if (! jwxyz_live_displays[i])
193 if (i >= size) abort();
194 jwxyz_live_displays[i] = d;
195 d->screen_count = size;
196 d->screen->screen_number = i;
198 # endif // !USE_IPHONE
200 Visual *v = (Visual *) calloc (1, sizeof(Visual));
201 v->class = TrueColor;
202 v->red_mask = 0x00FF0000;
203 v->green_mask = 0x0000FF00;
204 v->blue_mask = 0x000000FF;
206 d->screen->visual = v;
208 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
211 Window w = (Window) calloc (1, sizeof(*w));
213 w->window.view = view;
214 CFRetain (w->window.view); // needed for garbage collection?
215 w->window.background = BlackPixel(0,0);
222 cgc = [[[view window] graphicsContext] graphicsPort];
228 Assert (cgc, "no CGContext");
233 jwxyz_free_display (Display *dpy)
235 jwxyz_sources_free (dpy->timers_data);
239 // Find us in live_displays and clear that slot.
240 int size = ScreenCount(dpy);
242 for (i = 0; i < size; i++) {
243 if (dpy == jwxyz_live_displays[i]) {
244 jwxyz_live_displays[i] = 0;
248 if (i >= size) abort();
250 # endif // !USE_IPHONE
252 free (dpy->screen->visual);
254 CFRelease (dpy->main_window->window.view);
255 free (dpy->main_window);
261 jwxyz_window_view (Window w)
263 Assert (w && w->type == WINDOW, "not a window");
264 return w->window.view;
268 /* Call this after any modification to the bits on a Pixmap or Window.
269 Most Pixmaps are used frequently as sources and infrequently as
270 destinations, so it pays to cache the data as a CGImage as needed.
273 invalidate_drawable_cache (Drawable d)
276 CGImageRelease (d->cgi);
282 /* Call this when the View changes size or position.
285 jwxyz_window_resized (Display *dpy, Window w,
286 int new_x, int new_y, int new_width, int new_height,
289 CGContextRef cgc = (CGContextRef) cgc_arg;
290 Assert (w && w->type == WINDOW, "not a window");
291 w->frame.origin.x = new_x;
292 w->frame.origin.y = new_y;
293 w->frame.size.width = new_width;
294 w->frame.size.height = new_height;
296 if (cgc) w->cgc = cgc;
297 Assert (w->cgc, "no CGContext");
300 // Figure out which screen the window is currently on.
303 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
309 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
313 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
315 Assert (dpy->cgdpy, "unable to find CGDisplay");
317 # endif // USE_IPHONE
319 # ifndef USE_BACKBUFFER
320 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
321 // then this one's faster.
324 // Figure out this screen's colorspace, and use that for every CGImage.
326 CMProfileRef profile = 0;
327 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
328 Assert (profile, "unable to find colorspace profile");
329 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
330 Assert (dpy->colorspace, "unable to find colorspace");
332 # else // USE_BACKBUFFER
334 // WTF? It's faster if we *do not* use the screen's colorspace!
336 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
337 # endif // USE_BACKBUFFER
339 invalidate_drawable_cache (w);
345 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
347 Assert (w && w->type == WINDOW, "not a window");
348 w->window.last_mouse_x = x;
349 w->window.last_mouse_y = y;
355 jwxyz_flush_context (Display *dpy)
357 // This is only used when USE_BACKBUFFER is off.
358 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
362 display_sources_data (Display *dpy)
364 return dpy->timers_data;
369 XRootWindow (Display *dpy, int screen)
371 return dpy->main_window;
375 XDefaultScreenOfDisplay (Display *dpy)
381 XDefaultVisualOfScreen (Screen *screen)
383 return screen->visual;
387 XDisplayOfScreen (Screen *s)
393 XDisplayNumberOfScreen (Screen *s)
399 XScreenNumberOfScreen (Screen *s)
401 return s->screen_number;
405 jwxyz_ScreenCount (Display *dpy)
407 return dpy->screen_count;
411 XDisplayWidth (Display *dpy, int screen)
413 return (int) dpy->main_window->frame.size.width;
417 XDisplayHeight (Display *dpy, int screen)
419 return (int) dpy->main_window->frame.size.height;
423 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
426 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
427 else if (!alpha_allowed_p)
428 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
429 "bogus color pixel");
434 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
435 BOOL alpha_allowed_p, BOOL fill_p)
437 validate_pixel (argb, depth, alpha_allowed_p);
440 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
442 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
444 float a = ((argb >> 24) & 0xFF) / 255.0;
445 float r = ((argb >> 16) & 0xFF) / 255.0;
446 float g = ((argb >> 8) & 0xFF) / 255.0;
447 float b = ((argb ) & 0xFF) / 255.0;
449 CGContextSetRGBFillColor (cgc, r, g, b, a);
451 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
456 set_line_mode (CGContextRef cgc, XGCValues *gcv)
458 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
459 CGContextSetLineJoin (cgc,
460 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
461 gcv->join_style == JoinRound ? kCGLineJoinRound :
463 CGContextSetLineCap (cgc,
464 gcv->cap_style == CapNotLast ? kCGLineCapButt :
465 gcv->cap_style == CapButt ? kCGLineCapButt :
466 gcv->cap_style == CapRound ? kCGLineCapRound :
471 set_clip_mask (Drawable d, GC gc)
473 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
475 Pixmap p = gc->gcv.clip_mask;
477 Assert (p->type == PIXMAP, "not a pixmap");
479 CGRect wr = d->frame;
481 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
482 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
483 - p->frame.size.height;
484 to.size.width = p->frame.size.width;
485 to.size.height = p->frame.size.height;
487 CGContextClipToMask (d->cgc, to, gc->clip_mask);
491 /* Pushes a GC context; sets BlendMode and ClipMask.
494 push_gc (Drawable d, GC gc)
496 CGContextRef cgc = d->cgc;
497 CGContextSaveGState (cgc);
499 switch (gc->gcv.function) {
502 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
503 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
504 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
505 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
506 default: Assert(0, "unknown gcv function"); break;
509 if (gc->gcv.clip_mask)
510 set_clip_mask (d, gc);
513 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
516 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
519 push_color_gc (Drawable d, GC gc, unsigned long color,
520 BOOL antialias_p, Bool fill_p)
524 int depth = gc->depth;
525 switch (gc->gcv.function) {
526 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
527 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
530 CGContextRef cgc = d->cgc;
531 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
532 CGContextSetShouldAntialias (cgc, antialias_p);
536 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
539 push_fg_gc (Drawable d, GC gc, Bool fill_p)
541 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
544 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
547 push_bg_gc (Drawable d, GC gc, Bool fill_p)
549 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
554 /* You've got to be fucking kidding me!
556 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
557 with repeated calls to CGContextDrawImage than it is to make a single
558 call to CGContextFillRects() with a list of 1x1 rectangles!
560 I still wouldn't call it *fast*, however...
562 #define XDRAWPOINTS_IMAGES
564 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
565 the bitmap data directly is faster. This only works on Pixmaps, though,
566 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
568 #define XDRAWPOINTS_CGDATA
571 XDrawPoints (Display *dpy, Drawable d, GC gc,
572 XPoint *points, int count, int mode)
575 CGRect wr = d->frame;
577 push_fg_gc (d, gc, YES);
579 # ifdef XDRAWPOINTS_CGDATA
581 # ifdef USE_BACKBUFFER
582 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
584 if (d->type == PIXMAP)
587 CGContextRef cgc = d->cgc;
588 void *data = CGBitmapContextGetData (cgc);
589 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
590 size_t w = CGBitmapContextGetWidth (cgc);
591 size_t h = CGBitmapContextGetHeight (cgc);
593 Assert (data, "no bitmap data in Drawable");
595 unsigned long argb = gc->gcv.foreground;
596 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
598 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
600 CGFloat x0 = wr.origin.x;
601 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
603 // It's uglier, but faster, to hoist the conditional out of the loop.
604 if (mode == CoordModePrevious) {
605 CGFloat x = x0, y = y0;
606 for (i = 0; i < count; i++, points++) {
610 if (x >= 0 && x < w && y >= 0 && y < h) {
611 unsigned int *p = (unsigned int *)
612 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
613 *p = (unsigned int) argb;
617 for (i = 0; i < count; i++, points++) {
618 CGFloat x = x0 + points->x;
619 CGFloat y = y0 + points->y;
621 if (x >= 0 && x < w && y >= 0 && y < h) {
622 unsigned int *p = (unsigned int *)
623 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
624 *p = (unsigned int) argb;
629 } else /* d->type == WINDOW */
631 # endif /* XDRAWPOINTS_CGDATA */
634 # ifdef XDRAWPOINTS_IMAGES
636 unsigned int argb = gc->gcv.foreground;
637 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
639 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
641 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
643 CGImageRef cgi = CGImageCreate (1, 1,
646 /* Host-ordered, since we're using the
647 address of an int as the color data. */
648 (kCGImageAlphaNoneSkipFirst |
649 kCGBitmapByteOrder32Host),
652 NO, /* interpolate */
653 kCGRenderingIntentDefault);
654 CGDataProviderRelease (prov);
656 CGContextRef cgc = d->cgc;
658 rect.size.width = rect.size.height = 1;
659 for (i = 0; i < count; i++) {
660 if (i > 0 && mode == CoordModePrevious) {
661 rect.origin.x += points->x;
662 rect.origin.x -= points->y;
664 rect.origin.x = wr.origin.x + points->x;
665 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
668 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
669 CGContextDrawImage (cgc, rect, cgi);
673 CGImageRelease (cgi);
675 # else /* ! XDRAWPOINTS_IMAGES */
677 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
680 for (i = 0; i < count; i++) {
681 r->size.width = r->size.height = 1;
682 if (i > 0 && mode == CoordModePrevious) {
683 r->origin.x = r[-1].origin.x + points->x;
684 r->origin.y = r[-1].origin.x - points->y;
686 r->origin.x = wr.origin.x + points->x;
687 r->origin.y = wr.origin.y + wr.size.height - points->y;
693 CGContextFillRects (d->cgc, rects, count);
696 # endif /* ! XDRAWPOINTS_IMAGES */
700 invalidate_drawable_cache (d);
707 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
712 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
716 static void draw_rect (Display *, Drawable, GC,
717 int x, int y, unsigned int width, unsigned int height,
718 BOOL foreground_p, BOOL fill_p);
721 bitmap_context_p (Drawable d)
723 # ifdef USE_BACKBUFFER
726 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
727 return d->type == PIXMAP;
732 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
733 size_t fill_width, size_t fill_height)
735 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
736 while (fill_height) {
737 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
738 wmemset (dst, fill_data, fill_width);
740 dst = (char *) dst + dst_pitch;
745 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
747 return (char *)dst + dst_pitch * y + x * 4;
751 drawable_depth (Drawable d)
753 return (d->type == WINDOW
754 ? visual_depth (NULL, NULL)
760 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
761 int src_x, int src_y,
762 unsigned int width, unsigned int height,
763 int dst_x, int dst_y)
765 Assert (gc, "no GC");
766 Assert ((width < 65535), "improbably large width");
767 Assert ((height < 65535), "improbably large height");
768 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
769 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
770 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
771 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
773 if (width == 0 || height == 0)
776 if (gc->gcv.function == GXset ||
777 gc->gcv.function == GXclear) {
778 // "set" and "clear" are dumb drawing modes that ignore the source
779 // bits and just draw solid rectangles.
781 (gc->gcv.function == GXset
782 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
783 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
784 gc->depth, gc->gcv.alpha_allowed_p, YES);
785 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
789 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
790 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
791 // bounds of their drawables.
792 BOOL clipped = NO; // Whether we did any clipping of the rects.
794 src_frame = src->frame;
795 dst_frame = dst->frame;
797 // Initialize src_rect...
799 src_rect.origin.x = src_frame.origin.x + src_x;
800 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
802 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
803 src_rect.size.width = width;
804 src_rect.size.height = height;
806 // Initialize dst_rect...
808 dst_rect.origin.x = dst_frame.origin.x + dst_x;
809 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
811 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
812 dst_rect.size.width = width;
813 dst_rect.size.height = height;
815 // Clip rects to frames...
818 # define CLIP(THIS,THAT,VAL,SIZE) do { \
819 float off = THIS##_rect.origin.VAL; \
822 THIS##_rect.size.SIZE += off; \
823 THAT##_rect.size.SIZE += off; \
824 THIS##_rect.origin.VAL -= off; \
825 THAT##_rect.origin.VAL -= off; \
827 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
828 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
831 THIS##_rect.size.SIZE -= off; \
832 THAT##_rect.size.SIZE -= off; \
835 CLIP (dst, src, x, width);
836 CLIP (dst, src, y, height);
838 // Not actually the original dst_rect, just the one before it's clipped to
840 CGRect orig_dst_rect = dst_rect;
842 CLIP (src, dst, x, width);
843 CLIP (src, dst, y, height);
846 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
849 // Sort-of-special case where no pixels can be grabbed from the source,
850 // and the whole destination is filled with the background color.
851 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
853 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
854 (int)src_rect.size.height == (int)dst_rect.size.height,
857 src_rect.size.width = 0;
858 src_rect.size.height = 0;
859 dst_rect.size.width = 0;
860 dst_rect.size.height = 0;
863 NSObject *releaseme = 0;
866 BOOL free_cgi_p = NO;
869 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
870 going on with clipping masks or depths or anything, optimize it by
871 just doing a memcpy instead of going through a CGI.
873 if (bitmap_context_p (src)) {
875 if (bitmap_context_p (dst) &&
876 gc->gcv.function == GXcopy &&
877 !gc->gcv.clip_mask &&
878 drawable_depth (src) == drawable_depth (dst)) {
880 Assert(!(int)src_frame.origin.x &&
881 !(int)src_frame.origin.y &&
882 !(int)dst_frame.origin.x &&
883 !(int)dst_frame.origin.y,
884 "unexpected non-zero origin");
886 char *src_data = CGBitmapContextGetData(src->cgc);
887 char *dst_data = CGBitmapContextGetData(dst->cgc);
888 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
889 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
891 // Int to float and back again. It's not very safe, but it seems to work.
892 int src_x0 = src_rect.origin.x;
893 int dst_x0 = dst_rect.origin.x;
895 // Flip the Y-axis a second time.
896 int src_y0 = (src_frame.origin.y + src_frame.size.height -
897 src_rect.size.height - src_rect.origin.y);
898 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
899 dst_rect.size.height - dst_rect.origin.y);
901 unsigned width0 = (int) src_rect.size.width;
902 unsigned height0 = (int) src_rect.size.height;
904 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
905 (int)src_rect.size.height == (int)dst_rect.size.height,
908 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
909 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
910 size_t src_pitch0 = src_pitch;
911 size_t dst_pitch0 = dst_pitch;
912 size_t bytes = width0 * 4;
914 if (src == dst && dst_y0 > src_y0) {
915 // Copy upwards if the areas might overlap.
916 src_data0 += src_pitch0 * (height0 - 1);
917 dst_data0 += dst_pitch0 * (height0 - 1);
918 src_pitch0 = -src_pitch0;
919 dst_pitch0 = -dst_pitch0;
922 size_t lines0 = height0;
924 // memcpy is an alias for memmove on OS X.
925 memmove(dst_data0, src_data0, bytes);
926 src_data0 += src_pitch0;
927 dst_data0 += dst_pitch0;
933 int orig_dst_x = orig_dst_rect.origin.x;
934 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
935 orig_dst_rect.origin.y - orig_dst_rect.size.height);
936 int orig_width = orig_dst_rect.size.width;
937 int orig_height = orig_dst_rect.size.height;
939 Assert (orig_dst_x >= 0 &&
940 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
942 orig_dst_y + orig_height <= (int) dst_frame.size.height,
945 if (orig_dst_y < dst_y0) {
946 fill_rect_memset (seek_xy (dst_data, dst_pitch,
947 orig_dst_x, orig_dst_y), dst_pitch,
948 (uint32_t) gc->gcv.background, orig_width,
949 dst_y0 - orig_dst_y);
952 if (orig_dst_y + orig_height > dst_y0 + height0) {
953 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
956 (uint32_t) gc->gcv.background, orig_width,
957 orig_dst_y + orig_height - dst_y0 - height0);
960 if (orig_dst_x < dst_x0) {
961 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
962 dst_pitch, (uint32_t) gc->gcv.background,
963 dst_x0 - orig_dst_x, height0);
966 if (dst_x0 + width0 < orig_dst_x + orig_width) {
967 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
969 dst_pitch, (uint32_t) gc->gcv.background,
970 orig_dst_x + orig_width - dst_x0 - width0,
975 invalidate_drawable_cache (dst);
980 // If we are copying from a Pixmap to a Pixmap or Window, we must first
981 // copy the bits to an intermediary CGImage object, then copy that to the
982 // destination drawable's CGContext.
984 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
985 // case of copying from a Pixmap back to itself, but I don't think that
986 // happens very often anyway.)
988 // First we get a CGImage out of the pixmap CGContext -- it's the whole
989 // pixmap, but it presumably shares the data pointer instead of copying
990 // it. We then cache that CGImage it inside the Pixmap object. Note:
991 // invalidate_drawable_cache() must be called to discard this any time a
992 // modification is made to the pixmap, or we'll end up re-using old bits.
995 src->cgi = CGBitmapContextCreateImage (src->cgc);
998 // if doing a sub-rect, trim it down.
999 if (src_rect.origin.x != src_frame.origin.x ||
1000 src_rect.origin.y != src_frame.origin.y ||
1001 src_rect.size.width != src_frame.size.width ||
1002 src_rect.size.height != src_frame.size.height) {
1003 // #### I don't understand why this is needed...
1004 src_rect.origin.y = (src_frame.size.height -
1005 src_rect.size.height - src_rect.origin.y);
1006 // This does not copy image data, so it should be fast.
1007 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1011 if (src->type == PIXMAP && src->pixmap.depth == 1)
1014 # ifndef USE_BACKBUFFER
1015 } else { /* (src->type == WINDOW) */
1017 NSRect nsfrom; // NSRect != CGRect on 10.4
1018 nsfrom.origin.x = src_rect.origin.x;
1019 nsfrom.origin.y = src_rect.origin.y;
1020 nsfrom.size.width = src_rect.size.width;
1021 nsfrom.size.height = src_rect.size.height;
1025 // If we are copying from a window to itself, we can use NSCopyBits()
1026 // without first copying the rectangle to an intermediary CGImage.
1027 // This is ~28% faster (but I *expected* it to be twice as fast...)
1028 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1034 // If we are copying from a Window to a Pixmap, we must first copy
1035 // the bits to an intermediary CGImage object, then copy that to the
1036 // Pixmap's CGContext.
1038 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1039 initWithFocusedViewRect:nsfrom];
1040 unsigned char *data = [bm bitmapData];
1041 int bps = [bm bitsPerSample];
1042 int bpp = [bm bitsPerPixel];
1043 int bpl = [bm bytesPerRow];
1046 // create a CGImage from those bits.
1047 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1048 // but that method didn't exist in 10.4.)
1050 CGDataProviderRef prov =
1051 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1053 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1056 /* Use whatever default bit ordering we got from
1057 initWithFocusedViewRect. I would have assumed
1058 that it was (kCGImageAlphaNoneSkipFirst |
1059 kCGBitmapByteOrder32Host), but on Intel,
1064 NULL, /* decode[] */
1065 NO, /* interpolate */
1066 kCGRenderingIntentDefault);
1068 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1069 CGDataProviderRelease (prov);
1072 # endif // !USE_BACKBUFFER
1075 CGContextRef cgc = dst->cgc;
1077 if (mask_p) { // src depth == 1
1079 push_bg_gc (dst, gc, YES);
1081 // fill the destination rectangle with solid background...
1082 CGContextFillRect (cgc, orig_dst_rect);
1084 Assert (cgc, "no CGC with 1-bit XCopyArea");
1086 // then fill in a solid rectangle of the fg color, using the image as an
1087 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1088 set_color (cgc, gc->gcv.foreground, gc->depth,
1089 gc->gcv.alpha_allowed_p, YES);
1090 CGContextClipToMask (cgc, dst_rect, cgi);
1091 CGContextFillRect (cgc, dst_rect);
1095 } else { // src depth > 1
1099 // If either the src or dst rects did not lie within their drawables,
1100 // then we have adjusted both the src and dst rects to account for
1101 // the clipping; that means we need to first clear to the background,
1102 // so that clipped bits end up in the bg color instead of simply not
1106 set_color (cgc, gc->gcv.background, gc->depth,
1107 gc->gcv.alpha_allowed_p, YES);
1108 CGContextFillRect (cgc, orig_dst_rect);
1112 // copy the CGImage onto the destination CGContext
1113 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1114 CGContextDrawImage (cgc, dst_rect, cgi);
1116 // No cgi means src == dst, and both are Windows.
1118 # ifdef USE_BACKBUFFER
1119 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1121 # else // !USE_BACKBUFFER
1123 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1124 nsfrom.origin.y = src_rect.origin.y;
1125 nsfrom.size.width = src_rect.size.width;
1126 nsfrom.size.height = src_rect.size.height;
1128 nsto.x = dst_rect.origin.x;
1129 nsto.y = dst_rect.origin.y;
1130 NSCopyBits (0, nsfrom, nsto);
1131 # endif // !USE_BACKBUFFER
1137 if (free_cgi_p) CGImageRelease (cgi);
1139 if (releaseme) [releaseme release];
1140 invalidate_drawable_cache (dst);
1146 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1147 int src_x, int src_y,
1148 unsigned width, int height,
1149 int dest_x, int dest_y, unsigned long plane)
1151 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1153 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1154 // not to white/black.
1155 return XCopyArea (dpy, src, dest, gc,
1156 src_x, src_y, width, height, dest_x, dest_y);
1161 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1163 // when drawing a zero-length line, obey line-width and cap-style.
1164 if (x1 == x2 && y1 == y2) {
1165 int w = gc->gcv.line_width;
1168 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1169 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1171 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1174 CGRect wr = d->frame;
1176 p.x = wr.origin.x + x1;
1177 p.y = wr.origin.y + wr.size.height - y1;
1179 push_fg_gc (d, gc, NO);
1181 CGContextRef cgc = d->cgc;
1182 set_line_mode (cgc, &gc->gcv);
1183 CGContextBeginPath (cgc);
1184 CGContextMoveToPoint (cgc, p.x, p.y);
1185 p.x = wr.origin.x + x2;
1186 p.y = wr.origin.y + wr.size.height - y2;
1187 CGContextAddLineToPoint (cgc, p.x, p.y);
1188 CGContextStrokePath (cgc);
1190 invalidate_drawable_cache (d);
1195 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1200 CGRect wr = d->frame;
1201 push_fg_gc (d, gc, NO);
1203 CGContextRef cgc = d->cgc;
1205 set_line_mode (cgc, &gc->gcv);
1207 // if the first and last points coincide, use closepath to get
1208 // the proper line-joining.
1209 BOOL closed_p = (points[0].x == points[count-1].x &&
1210 points[0].y == points[count-1].y);
1211 if (closed_p) count--;
1213 p.x = wr.origin.x + points->x;
1214 p.y = wr.origin.y + wr.size.height - points->y;
1216 CGContextBeginPath (cgc);
1217 CGContextMoveToPoint (cgc, p.x, p.y);
1218 for (i = 1; i < count; i++) {
1219 if (mode == CoordModePrevious) {
1223 p.x = wr.origin.x + points->x;
1224 p.y = wr.origin.y + wr.size.height - points->y;
1226 CGContextAddLineToPoint (cgc, p.x, p.y);
1229 if (closed_p) CGContextClosePath (cgc);
1230 CGContextStrokePath (cgc);
1232 invalidate_drawable_cache (d);
1238 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1241 CGRect wr = d->frame;
1243 CGContextRef cgc = d->cgc;
1245 push_fg_gc (d, gc, NO);
1246 set_line_mode (cgc, &gc->gcv);
1247 CGContextBeginPath (cgc);
1248 for (i = 0; i < count; i++) {
1249 CGContextMoveToPoint (cgc,
1250 wr.origin.x + segments->x1,
1251 wr.origin.y + wr.size.height - segments->y1);
1252 CGContextAddLineToPoint (cgc,
1253 wr.origin.x + segments->x2,
1254 wr.origin.y + wr.size.height - segments->y2);
1257 CGContextStrokePath (cgc);
1259 invalidate_drawable_cache (d);
1265 XClearWindow (Display *dpy, Window win)
1267 Assert (win && win->type == WINDOW, "not a window");
1268 CGRect wr = win->frame;
1269 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1273 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1275 Assert (w && w->type == WINDOW, "not a window");
1276 validate_pixel (pixel, 32, NO);
1277 w->window.background = pixel;
1282 draw_rect (Display *dpy, Drawable d, GC gc,
1283 int x, int y, unsigned int width, unsigned int height,
1284 BOOL foreground_p, BOOL fill_p)
1286 CGRect wr = d->frame;
1288 r.origin.x = wr.origin.x + x;
1289 r.origin.y = wr.origin.y + wr.size.height - y - height;
1290 r.size.width = width;
1291 r.size.height = height;
1295 push_fg_gc (d, gc, fill_p);
1297 push_bg_gc (d, gc, fill_p);
1300 CGContextRef cgc = d->cgc;
1302 CGContextFillRect (cgc, r);
1305 set_line_mode (cgc, &gc->gcv);
1306 CGContextStrokeRect (cgc, r);
1311 invalidate_drawable_cache (d);
1316 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1317 unsigned int width, unsigned int height)
1319 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1324 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1325 unsigned int width, unsigned int height)
1327 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1332 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1334 CGRect wr = d->frame;
1336 CGContextRef cgc = d->cgc;
1337 push_fg_gc (d, gc, YES);
1338 for (i = 0; i < n; i++) {
1340 r.origin.x = wr.origin.x + rects->x;
1341 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1342 r.size.width = rects->width;
1343 r.size.height = rects->height;
1344 CGContextFillRect (cgc, r);
1348 invalidate_drawable_cache (d);
1354 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1356 Assert (win && win->type == WINDOW, "not a window");
1357 CGContextRef cgc = win->cgc;
1358 set_color (cgc, win->window.background, 32, NO, YES);
1359 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1365 XFillPolygon (Display *dpy, Drawable d, GC gc,
1366 XPoint *points, int npoints, int shape, int mode)
1368 CGRect wr = d->frame;
1370 push_fg_gc (d, gc, YES);
1371 CGContextRef cgc = d->cgc;
1372 CGContextBeginPath (cgc);
1374 for (i = 0; i < npoints; i++) {
1375 if (i > 0 && mode == CoordModePrevious) {
1379 x = wr.origin.x + points[i].x;
1380 y = wr.origin.y + wr.size.height - points[i].y;
1384 CGContextMoveToPoint (cgc, x, y);
1386 CGContextAddLineToPoint (cgc, x, y);
1388 CGContextClosePath (cgc);
1389 if (gc->gcv.fill_rule == EvenOddRule)
1390 CGContextEOFillPath (cgc);
1392 CGContextFillPath (cgc);
1394 invalidate_drawable_cache (d);
1398 #define radians(DEG) ((DEG) * M_PI / 180.0)
1399 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1402 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1403 unsigned int width, unsigned int height, int angle1, int angle2,
1406 CGRect wr = d->frame;
1408 bound.origin.x = wr.origin.x + x;
1409 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1410 bound.size.width = width;
1411 bound.size.height = height;
1414 ctr.x = bound.origin.x + bound.size.width /2;
1415 ctr.y = bound.origin.y + bound.size.height/2;
1417 float r1 = radians (angle1/64.0);
1418 float r2 = radians (angle2/64.0) + r1;
1419 BOOL clockwise = angle2 < 0;
1420 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1422 push_fg_gc (d, gc, fill_p);
1424 CGContextRef cgc = d->cgc;
1425 CGContextBeginPath (cgc);
1427 CGContextSaveGState(cgc);
1428 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1429 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1431 CGContextMoveToPoint (cgc, 0, 0);
1433 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1434 CGContextRestoreGState (cgc); // restore before stroke, for line width
1437 CGContextClosePath (cgc); // for proper line joining
1440 CGContextFillPath (cgc);
1442 set_line_mode (cgc, &gc->gcv);
1443 CGContextStrokePath (cgc);
1447 invalidate_drawable_cache (d);
1452 XDrawArc (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, NO);
1459 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1460 unsigned int width, unsigned int height, int angle1, int angle2)
1462 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1466 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1469 for (i = 0; i < narcs; i++)
1470 draw_arc (dpy, d, gc,
1471 arcs[i].x, arcs[i].y,
1472 arcs[i].width, arcs[i].height,
1473 arcs[i].angle1, arcs[i].angle2,
1479 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1482 for (i = 0; i < narcs; i++)
1483 draw_arc (dpy, d, gc,
1484 arcs[i].x, arcs[i].y,
1485 arcs[i].width, arcs[i].height,
1486 arcs[i].angle1, arcs[i].angle2,
1493 gcv_defaults (XGCValues *gcv, int depth)
1495 memset (gcv, 0, sizeof(*gcv));
1496 gcv->function = GXcopy;
1497 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1498 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1499 gcv->line_width = 1;
1500 gcv->cap_style = CapNotLast;
1501 gcv->join_style = JoinMiter;
1502 gcv->fill_rule = EvenOddRule;
1504 gcv->alpha_allowed_p = NO;
1505 gcv->antialias_p = YES;
1509 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1512 Assert (gc && from, "no gc");
1513 if (!gc || !from) return;
1515 if (mask & GCFunction) gc->gcv.function = from->function;
1516 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1517 if (mask & GCBackground) gc->gcv.background = from->background;
1518 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1519 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1520 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1521 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1522 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1523 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1524 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1526 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1527 if (mask & GCFont) XSetFont (0, gc, from->font);
1529 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1530 gc->gcv.alpha_allowed_p);
1531 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1532 gc->gcv.alpha_allowed_p);
1534 Assert ((! (mask & (GCLineStyle |
1541 GCGraphicsExposures |
1545 "unimplemented gcvalues mask");
1550 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1552 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1553 if (d->type == WINDOW) {
1555 } else { /* (d->type == PIXMAP) */
1556 gc->depth = d->pixmap.depth;
1559 gcv_defaults (&gc->gcv, gc->depth);
1560 set_gcv (gc, xgcv, mask);
1565 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1567 set_gcv (gc, gcv, mask);
1573 XFreeGC (Display *dpy, GC gc)
1576 XUnloadFont (dpy, gc->gcv.font);
1578 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1580 if (gc->gcv.clip_mask) {
1581 XFreePixmap (dpy, gc->gcv.clip_mask);
1582 CGImageRelease (gc->clip_mask);
1590 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1592 Assert (w && w->type == WINDOW, "not a window");
1593 memset (xgwa, 0, sizeof(*xgwa));
1594 xgwa->x = w->frame.origin.x;
1595 xgwa->y = w->frame.origin.y;
1596 xgwa->width = w->frame.size.width;
1597 xgwa->height = w->frame.size.height;
1599 xgwa->screen = dpy->screen;
1600 xgwa->visual = dpy->screen->visual;
1605 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1606 int *x_ret, int *y_ret,
1607 unsigned int *w_ret, unsigned int *h_ret,
1608 unsigned int *bw_ret, unsigned int *d_ret)
1610 *x_ret = d->frame.origin.x;
1611 *y_ret = d->frame.origin.y;
1612 *w_ret = d->frame.size.width;
1613 *h_ret = d->frame.size.height;
1614 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1615 *root_ret = RootWindow (dpy, 0);
1622 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1624 // store 32 bit ARGB in the pixel field.
1625 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1626 color->pixel = (uint32_t)
1628 (((color->red >> 8) & 0xFF) << 16) |
1629 (((color->green >> 8) & 0xFF) << 8) |
1630 (((color->blue >> 8) & 0xFF) ));
1635 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1636 unsigned long *pmret, unsigned int npl,
1637 unsigned long *pxret, unsigned int npx)
1643 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1645 Assert(0, "XStoreColors called");
1650 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1652 Assert(0, "XStoreColor called");
1657 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1658 unsigned long planes)
1664 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1666 unsigned char r=0, g=0, b=0;
1667 if (*spec == '#' && strlen(spec) == 7) {
1668 static unsigned const char hex[] = { // yeah yeah, shoot me.
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,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1671 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,
1672 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,
1673 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,
1674 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,
1675 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,
1676 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};
1677 r = (hex[spec[1]] << 4) | hex[spec[2]];
1678 g = (hex[spec[3]] << 4) | hex[spec[4]];
1679 b = (hex[spec[5]] << 4) | hex[spec[6]];
1680 } else if (!strcasecmp(spec,"black")) {
1682 } else if (!strcasecmp(spec,"white")) {
1684 } else if (!strcasecmp(spec,"red")) {
1686 } else if (!strcasecmp(spec,"green")) {
1688 } else if (!strcasecmp(spec,"blue")) {
1690 } else if (!strcasecmp(spec,"cyan")) {
1692 } else if (!strcasecmp(spec,"magenta")) {
1694 } else if (!strcasecmp(spec,"yellow")) {
1700 ret->red = (r << 8) | r;
1701 ret->green = (g << 8) | g;
1702 ret->blue = (b << 8) | b;
1703 ret->flags = DoRed|DoGreen|DoBlue;
1708 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1709 XColor *screen_ret, XColor *exact_ret)
1711 if (! XParseColor (dpy, cmap, name, screen_ret))
1713 *exact_ret = *screen_ret;
1714 return XAllocColor (dpy, cmap, screen_ret);
1718 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1720 validate_pixel (color->pixel, 32, NO);
1721 unsigned char r = ((color->pixel >> 16) & 0xFF);
1722 unsigned char g = ((color->pixel >> 8) & 0xFF);
1723 unsigned char b = ((color->pixel ) & 0xFF);
1724 color->red = (r << 8) | r;
1725 color->green = (g << 8) | g;
1726 color->blue = (b << 8) | b;
1727 color->flags = DoRed|DoGreen|DoBlue;
1732 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1735 for (i = 0; i < n; i++)
1736 XQueryColor (dpy, cmap, &c[i]);
1741 static unsigned long
1742 ximage_getpixel_1 (XImage *ximage, int x, int y)
1744 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1748 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1751 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1753 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1758 static unsigned long
1759 ximage_getpixel_32 (XImage *ximage, int x, int y)
1761 return ((unsigned long)
1762 *((uint32_t *) ximage->data +
1763 (y * (ximage->bytes_per_line >> 2)) +
1768 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1770 *((uint32_t *) ximage->data +
1771 (y * (ximage->bytes_per_line >> 2)) +
1772 x) = (uint32_t) pixel;
1778 XInitImage (XImage *ximage)
1780 if (!ximage->bytes_per_line)
1781 ximage->bytes_per_line = (ximage->depth == 1
1782 ? (ximage->width + 7) / 8
1783 : ximage->width * 4);
1785 if (ximage->depth == 1) {
1786 ximage->f.put_pixel = ximage_putpixel_1;
1787 ximage->f.get_pixel = ximage_getpixel_1;
1788 } else if (ximage->depth == 32 || ximage->depth == 24) {
1789 ximage->f.put_pixel = ximage_putpixel_32;
1790 ximage->f.get_pixel = ximage_getpixel_32;
1792 Assert (0, "unknown depth");
1799 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1800 int format, int offset, char *data,
1801 unsigned int width, unsigned int height,
1802 int bitmap_pad, int bytes_per_line)
1804 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1805 ximage->width = width;
1806 ximage->height = height;
1807 ximage->format = format;
1808 ximage->data = data;
1809 ximage->bitmap_unit = 8;
1810 ximage->byte_order = LSBFirst;
1811 ximage->bitmap_bit_order = ximage->byte_order;
1812 ximage->bitmap_pad = bitmap_pad;
1813 ximage->depth = depth;
1814 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1815 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1816 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1817 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1818 ximage->bytes_per_line = bytes_per_line;
1820 XInitImage (ximage);
1825 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1827 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1828 w, h, from->bitmap_pad, 0);
1829 to->data = (char *) malloc (h * to->bytes_per_line);
1831 if (x >= from->width)
1833 else if (x+w > from->width)
1834 w = from->width - x;
1836 if (y >= from->height)
1838 else if (y+h > from->height)
1839 h = from->height - y;
1842 for (ty = 0; ty < h; ty++)
1843 for (tx = 0; tx < w; tx++)
1844 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1849 XPixmapFormatValues *
1850 XListPixmapFormats (Display *dpy, int *n_ret)
1852 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1854 ret[0].bits_per_pixel = 32;
1855 ret[0].scanline_pad = 8;
1857 ret[1].bits_per_pixel = 1;
1858 ret[1].scanline_pad = 8;
1865 XGetPixel (XImage *ximage, int x, int y)
1867 return ximage->f.get_pixel (ximage, x, y);
1872 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1874 return ximage->f.put_pixel (ximage, x, y, pixel);
1878 XDestroyImage (XImage *ximage)
1880 if (ximage->data) free (ximage->data);
1887 flipbits (unsigned const char *in, unsigned char *out, int length)
1889 static const unsigned char table[256] = {
1890 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1891 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1892 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1893 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1894 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1895 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1896 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1897 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1898 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1899 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1900 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1901 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1902 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1903 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1904 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1905 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1906 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1907 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1908 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1909 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1910 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1911 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1912 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1913 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1914 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1915 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1916 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1917 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1918 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1919 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1920 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1921 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1923 while (length-- > 0)
1924 *out++ = table[*in++];
1929 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1930 int src_x, int src_y, int dest_x, int dest_y,
1931 unsigned int w, unsigned int h)
1933 CGRect wr = d->frame;
1935 Assert (gc, "no GC");
1936 Assert ((w < 65535), "improbably large width");
1937 Assert ((h < 65535), "improbably large height");
1938 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1939 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1940 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1941 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1943 // Clip width and height to the bounds of the Drawable
1945 if (dest_x + w > wr.size.width) {
1946 if (dest_x > wr.size.width)
1948 w = wr.size.width - dest_x;
1950 if (dest_y + h > wr.size.height) {
1951 if (dest_y > wr.size.height)
1953 h = wr.size.height - dest_y;
1955 if (w <= 0 || h <= 0)
1958 // Clip width and height to the bounds of the XImage
1960 if (src_x + w > ximage->width) {
1961 if (src_x > ximage->width)
1963 w = ximage->width - src_x;
1965 if (src_y + h > ximage->height) {
1966 if (src_y > ximage->height)
1968 h = ximage->height - src_y;
1970 if (w <= 0 || h <= 0)
1973 CGContextRef cgc = d->cgc;
1975 if (gc->gcv.function == GXset ||
1976 gc->gcv.function == GXclear) {
1977 // "set" and "clear" are dumb drawing modes that ignore the source
1978 // bits and just draw solid rectangles.
1979 set_color (cgc, (gc->gcv.function == GXset
1980 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1981 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1982 gc->depth, gc->gcv.alpha_allowed_p, YES);
1983 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1987 int bpl = ximage->bytes_per_line;
1988 int bpp = ximage->bits_per_pixel;
1989 int bsize = bpl * h;
1990 char *data = ximage->data;
1993 r.origin.x = wr.origin.x + dest_x;
1994 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
2000 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
2001 to create a CGImage from a sub-rectagle of the XImage.
2003 data += (src_y * bpl) + (src_x * 4);
2004 CGDataProviderRef prov =
2005 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2007 CGImageRef cgi = CGImageCreate (w, h,
2010 /* Need this for XPMs to have the right
2011 colors, e.g. the logo in "maze". */
2012 (kCGImageAlphaNoneSkipFirst |
2013 kCGBitmapByteOrder32Host),
2015 NULL, /* decode[] */
2016 NO, /* interpolate */
2017 kCGRenderingIntentDefault);
2018 CGDataProviderRelease (prov);
2019 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2020 CGContextDrawImage (cgc, r, cgi);
2021 CGImageRelease (cgi);
2023 } else { // (bpp == 1)
2025 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2027 #### However, the bit order within a byte in a 1bpp XImage is
2028 the wrong way around from what Quartz expects, so first we
2029 have to copy the data to reverse it. Shit! Maybe it
2030 would be worthwhile to go through the hacks and #ifdef
2031 each one that diddles 1bpp XImage->data directly...
2033 Assert ((src_x % 8) == 0,
2034 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2036 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
2037 unsigned char *flipped = (unsigned char *) malloc (bsize);
2039 flipbits ((unsigned char *) data, flipped, bsize);
2041 CGDataProviderRef prov =
2042 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2043 CGImageRef mask = CGImageMaskCreate (w, h,
2046 NULL, /* decode[] */
2047 NO); /* interpolate */
2048 push_fg_gc (d, gc, YES);
2050 CGContextFillRect (cgc, r); // foreground color
2051 CGContextClipToMask (cgc, r, mask);
2052 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
2053 CGContextFillRect (cgc, r); // background color
2057 CGDataProviderRelease (prov);
2058 CGImageRelease (mask);
2061 invalidate_drawable_cache (d);
2068 XGetImage (Display *dpy, Drawable d, int x, int y,
2069 unsigned int width, unsigned int height,
2070 unsigned long plane_mask, int format)
2072 const unsigned char *data = 0;
2073 size_t depth, ibpp, ibpl;
2074 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2075 # ifndef USE_BACKBUFFER
2076 NSBitmapImageRep *bm = 0;
2079 Assert ((width < 65535), "improbably large width");
2080 Assert ((height < 65535), "improbably large height");
2081 Assert ((x < 65535 && x > -65535), "improbably large x");
2082 Assert ((y < 65535 && y > -65535), "improbably large y");
2084 CGContextRef cgc = d->cgc;
2086 #ifndef USE_BACKBUFFER
2087 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2088 if (d->type == PIXMAP)
2091 depth = (d->type == PIXMAP
2094 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2095 src_format = BGRA; // #### Should this be ARGB on PPC?
2096 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2097 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2098 data = CGBitmapContextGetData (cgc);
2099 Assert (data, "CGBitmapContextGetData failed");
2101 # ifndef USE_BACKBUFFER
2102 } else { /* (d->type == WINDOW) */
2104 // get the bits (desired sub-rectangle) out of the NSView
2106 nsfrom.origin.x = x;
2107 // nsfrom.origin.y = y;
2108 nsfrom.origin.y = d->frame.size.height - height - y;
2109 nsfrom.size.width = width;
2110 nsfrom.size.height = height;
2111 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2113 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2114 ibpp = [bm bitsPerPixel];
2115 ibpl = [bm bytesPerRow];
2116 data = [bm bitmapData];
2117 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2118 # endif // !USE_BACKBUFFER
2121 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2122 data += (y * ibpl) + (x * (ibpp/8));
2124 format = (depth == 1 ? XYPixmap : ZPixmap);
2125 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2126 format, 0, 0, width, height, 0, 0);
2127 image->data = (char *) malloc (height * image->bytes_per_line);
2129 int obpl = image->bytes_per_line;
2131 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2132 means that on Intel it is BGRA when viewed by bytes (And BGR
2133 when using 24bpp packing).
2135 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2136 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2137 indicator of this latest kink.
2141 const unsigned char *iline = data;
2142 for (yy = 0; yy < height; yy++) {
2144 const unsigned char *iline2 = iline;
2145 for (xx = 0; xx < width; xx++) {
2147 iline2++; // ignore R or A or A or B
2148 iline2++; // ignore G or B or R or G
2149 unsigned char r = *iline2++; // use B or G or G or R
2150 if (ibpp == 32) iline2++; // ignore A or R or B or A
2152 XPutPixel (image, xx, yy, (r ? 1 : 0));
2157 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2158 const unsigned char *iline = data;
2159 unsigned char *oline = (unsigned char *) image->data;
2160 for (yy = 0; yy < height; yy++) {
2162 const unsigned char *iline2 = iline;
2163 unsigned char *oline2 = oline;
2165 switch (src_format) {
2167 for (xx = 0; xx < width; xx++) {
2168 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2169 unsigned char r = *iline2++;
2170 unsigned char g = *iline2++;
2171 unsigned char b = *iline2++;
2172 uint32_t pixel = ((a << 24) |
2176 *((uint32_t *) oline2) = pixel;
2181 for (xx = 0; xx < width; xx++) {
2182 unsigned char r = *iline2++;
2183 unsigned char g = *iline2++;
2184 unsigned char b = *iline2++;
2185 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2186 uint32_t pixel = ((a << 24) |
2190 *((uint32_t *) oline2) = pixel;
2195 for (xx = 0; xx < width; xx++) {
2196 unsigned char b = *iline2++;
2197 unsigned char g = *iline2++;
2198 unsigned char r = *iline2++;
2199 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2200 uint32_t pixel = ((a << 24) |
2204 *((uint32_t *) oline2) = pixel;
2218 # ifndef USE_BACKBUFFER
2219 if (bm) [bm release];
2227 /* Returns a transformation matrix to do rotation as per the provided
2228 EXIF "Orientation" value.
2230 static CGAffineTransform
2231 exif_rotate (int rot, CGSize rect)
2233 CGAffineTransform trans = CGAffineTransformIdentity;
2235 case 2: // flip horizontal
2236 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2237 trans = CGAffineTransformScale (trans, -1, 1);
2240 case 3: // rotate 180
2241 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2242 trans = CGAffineTransformRotate (trans, M_PI);
2245 case 4: // flip vertical
2246 trans = CGAffineTransformMakeTranslation (0, rect.height);
2247 trans = CGAffineTransformScale (trans, 1, -1);
2250 case 5: // transpose (UL-to-LR axis)
2251 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2252 trans = CGAffineTransformScale (trans, -1, 1);
2253 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2256 case 6: // rotate 90
2257 trans = CGAffineTransformMakeTranslation (0, rect.width);
2258 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2261 case 7: // transverse (UR-to-LL axis)
2262 trans = CGAffineTransformMakeScale (-1, 1);
2263 trans = CGAffineTransformRotate (trans, M_PI / 2);
2266 case 8: // rotate 270
2267 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2268 trans = CGAffineTransformRotate (trans, M_PI / 2);
2280 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2281 Bool nsimg_p, void *img_arg,
2282 XRectangle *geom_ret, int exif_rotation)
2286 CGImageSourceRef cgsrc;
2287 # endif // USE_IPHONE
2290 CGContextRef cgc = d->cgc;
2294 NSImage *nsimg = (NSImage *) img_arg;
2295 imgr = [nsimg size];
2298 // convert the NSImage to a CGImage via the toll-free-bridging
2299 // of NSData and CFData...
2301 NSData *nsdata = [NSBitmapImageRep
2302 TIFFRepresentationOfImageRepsInArray:
2303 [nsimg representations]];
2304 CFDataRef cfdata = (CFDataRef) nsdata;
2305 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2306 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2307 # else // USE_IPHONE
2308 cgi = nsimg.CGImage;
2309 # endif // USE_IPHONE
2312 cgi = (CGImageRef) img_arg;
2313 imgr.width = CGImageGetWidth (cgi);
2314 imgr.height = CGImageGetHeight (cgi);
2317 Bool rot_p = (exif_rotation >= 5);
2320 imgr = NSMakeSize (imgr.height, imgr.width);
2322 CGRect winr = d->frame;
2323 float rw = winr.size.width / imgr.width;
2324 float rh = winr.size.height / imgr.height;
2325 float r = (rw < rh ? rw : rh);
2328 dst.size.width = imgr.width * r;
2329 dst.size.height = imgr.height * r;
2330 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2331 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2333 dst2.origin.x = dst2.origin.y = 0;
2335 dst2.size.width = dst.size.height;
2336 dst2.size.height = dst.size.width;
2338 dst2.size = dst.size;
2341 // Clear the part not covered by the image to background or black.
2343 if (d->type == WINDOW)
2344 XClearWindow (dpy, d);
2346 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2347 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2350 CGAffineTransform trans =
2351 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2353 CGContextSaveGState (cgc);
2354 CGContextConcatCTM (cgc,
2355 CGAffineTransformMakeTranslation (dst.origin.x,
2357 CGContextConcatCTM (cgc, trans);
2358 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2359 CGContextDrawImage (cgc, dst2, cgi);
2360 CGContextRestoreGState (cgc);
2365 CGImageRelease (cgi);
2367 # endif // USE_IPHONE
2370 geom_ret->x = dst.origin.x;
2371 geom_ret->y = dst.origin.y;
2372 geom_ret->width = dst.size.width;
2373 geom_ret->height = dst.size.height;
2376 invalidate_drawable_cache (d);
2382 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2384 unsigned int w, unsigned int h,
2385 unsigned long fg, unsigned int bg,
2388 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2389 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2390 (char *) data, w, h, 0, 0);
2392 gcv.foreground = fg;
2393 gcv.background = bg;
2394 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2395 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2398 XDestroyImage (image);
2403 XCreatePixmap (Display *dpy, Drawable d,
2404 unsigned int width, unsigned int height, unsigned int depth)
2406 char *data = (char *) malloc (width * height * 4);
2407 if (! data) return 0;
2409 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2411 p->frame.size.width = width;
2412 p->frame.size.height = height;
2413 p->pixmap.depth = depth;
2414 p->pixmap.cgc_buffer = data;
2416 /* Quartz doesn't have a 1bpp image type.
2417 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2418 don't support that! So we always use 32bpp, regardless of depth. */
2420 p->cgc = CGBitmapContextCreate (data, width, height,
2421 8, /* bits per component */
2422 width * 4, /* bpl */
2424 // Without this, it returns 0...
2425 (kCGImageAlphaNoneSkipFirst |
2426 kCGBitmapByteOrder32Host)
2428 Assert (p->cgc, "could not create CGBitmapContext");
2434 XFreePixmap (Display *d, Pixmap p)
2436 Assert (p && p->type == PIXMAP, "not a pixmap");
2437 invalidate_drawable_cache (p);
2438 CGContextRelease (p->cgc);
2439 if (p->pixmap.cgc_buffer)
2440 free (p->pixmap.cgc_buffer);
2447 copy_pixmap (Display *dpy, Pixmap p)
2450 Assert (p->type == PIXMAP, "not a pixmap");
2452 int width = p->frame.size.width;
2453 int height = p->frame.size.height;
2454 char *data = (char *) malloc (width * height * 4);
2455 if (! data) return 0;
2457 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2459 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2462 p2->pixmap.cgc_buffer = data;
2463 p2->cgc = CGBitmapContextCreate (data, width, height,
2464 8, /* bits per component */
2465 width * 4, /* bpl */
2467 // Without this, it returns 0...
2468 (kCGImageAlphaNoneSkipFirst |
2469 kCGBitmapByteOrder32Host)
2471 Assert (p2->cgc, "could not create CGBitmapContext");
2477 /* Font metric terminology, as used by X11:
2479 "lbearing" is the distance from the logical origin to the leftmost pixel.
2480 If a character's ink extends to the left of the origin, it is negative.
2482 "rbearing" is the distance from the logical origin to the rightmost pixel.
2484 "descent" is the distance from the logical origin to the bottommost pixel.
2485 For characters with descenders, it is positive. For superscripts, it
2488 "ascent" is the distance from the logical origin to the topmost pixel.
2489 It is the number of pixels above the baseline.
2491 "width" is the distance from the logical origin to the position where
2492 the logical origin of the next character should be placed.
2494 If "rbearing" is greater than "width", then this character overlaps the
2495 following character. If smaller, then there is trailing blank space.
2500 glyph_metrics (CTFontRef ctfont, CGGlyph cgglyph, XCharStruct *cs)
2502 CGRect bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2503 kCTFontDefaultOrientation,
2506 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2507 &cgglyph, &advancement, 1);
2510 // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2511 bbox.origin.x -= 2.0/3.0;
2512 bbox.size.width += 4.0/3.0;
2513 bbox.size.height += 1.0/2.0;
2516 //#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2517 //#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
2518 #define CEIL(F) ceil(F)
2519 #define FLOOR(F) floor(F)
2521 /* Now that we know the advancement and bounding box, we can compute
2522 the lbearing and rbearing.
2524 //cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2525 cs->ascent = CEIL (bbox.origin.y + bbox.size.height);
2526 cs->descent = CEIL(-bbox.origin.y);
2527 cs->lbearing = FLOOR (bbox.origin.x);
2528 //cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2529 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width);
2530 cs->width = FLOOR (advancement.width + 0.5);
2532 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2534 // Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2539 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2542 query_font (Font fid)
2544 if (!fid || !fid->nsfont) {
2545 Assert (0, "no NSFont in fid");
2548 if (![fid->nsfont fontName]) {
2549 Assert(0, @"broken NSFont in fid");
2556 XFontStruct *f = &fid->metrics;
2557 XCharStruct *min = &f->min_bounds;
2558 XCharStruct *max = &f->max_bounds;
2561 f->min_char_or_byte2 = first;
2562 f->max_char_or_byte2 = last;
2563 f->default_char = 'M';
2564 f->ascent = CEIL ([fid->nsfont ascender]);
2565 f->descent = -FLOOR ([fid->nsfont descender]);
2567 min->width = 255; // set to smaller values in the loop
2570 min->lbearing = 255;
2571 min->rbearing = 255;
2573 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2577 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2578 [fid->nsfont pointSize],
2580 Assert (ctfont, @"no CTFontRef for UIFont");
2582 for (i = first; i <= last; i++) {
2583 XCharStruct *cs = &f->per_char[i-first];
2585 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2586 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2587 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2588 width of the character and the ascent of the font.
2590 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2591 the CoreText library, but there's no non-CoreText way to turn a
2592 unichar into a CGGlyph.
2594 CGGlyph cgglyph = 0;
2596 if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
2597 glyph_metrics (ctfont, cgglyph, cs);
2599 // This is normal, since Latin1 does not encode 0-31 or 127-159.
2600 memset (cs, 0, sizeof(*cs));
2603 if (i == 224) { // Latin1 == "agrave", MacRoman == "daggerdouble".
2604 NSString *glyph_name = (NSString *)
2605 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2607 Assert ([glyph_name isEqualToString:@"agrave"], @"wrong encoding");
2609 if (i == 250) { // Latin1 == "uacute", MacRoman == "dotaccent".
2610 NSString *glyph_name = (NSString *)
2611 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2613 Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
2617 max->width = MAX (max->width, cs->width);
2618 max->ascent = MAX (max->ascent, cs->ascent);
2619 max->descent = MAX (max->descent, cs->descent);
2620 max->lbearing = MAX (max->lbearing, cs->lbearing);
2621 max->rbearing = MAX (max->rbearing, cs->rbearing);
2623 min->width = MIN (min->width, cs->width);
2624 min->ascent = MIN (min->ascent, cs->ascent);
2625 min->descent = MIN (min->descent, cs->descent);
2626 min->lbearing = MIN (min->lbearing, cs->lbearing);
2627 min->rbearing = MIN (min->rbearing, cs->rbearing);
2633 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2634 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2635 i, i, cs->width, cs->lbearing, cs->rbearing,
2636 cs->ascent, cs->descent,
2637 bbox.size.width, bbox.size.height,
2638 bbox.origin.x, bbox.origin.y,
2639 advancement.width, advancement.height);
2647 // Since 'Font' includes the metrics, this just makes a copy of that.
2650 XQueryFont (Display *dpy, Font fid)
2653 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2656 // copy XCharStruct array
2657 int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2658 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2659 memcpy (f->per_char, fid->metrics.per_char,
2660 size * sizeof (XCharStruct));
2667 copy_font (Font fid)
2669 // copy 'Font' struct
2670 Font fid2 = (Font) malloc (sizeof(*fid2));
2673 // copy XCharStruct array
2674 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2675 fid2->metrics.per_char = (XCharStruct *)
2676 malloc ((size + 2) * sizeof (XCharStruct));
2677 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2678 size * sizeof (XCharStruct));
2680 // copy the other pointers
2681 fid2->ps_name = strdup (fid->ps_name);
2682 // [fid2->nsfont retain];
2683 fid2->metrics.fid = fid2;
2690 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2693 Assert (size > 0, "zero font size");
2698 // "Monaco" only exists in plain.
2699 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2701 if (bold && ital) name = "Courier-BoldOblique";
2702 else if (bold) name = "Courier-Bold";
2703 else if (ital) name = "Courier-Oblique";
2704 else name = "Courier";
2708 // "Georgia" looks better than "Times".
2710 if (bold && ital) name = "Georgia-BoldItalic";
2711 else if (bold) name = "Georgia-Bold";
2712 else if (ital) name = "Georgia-Italic";
2713 else name = "Georgia";
2717 // "Geneva" only exists in plain.
2718 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2719 // "Verdana" renders smoother than "Helvetica" for some reason.
2721 if (bold && ital) name = "Verdana-BoldItalic";
2722 else if (bold) name = "Verdana-Bold";
2723 else if (ital) name = "Verdana-Italic";
2724 else name = "Verdana";
2727 NSString *nsname = [NSString stringWithCString:name
2728 encoding:NSUTF8StringEncoding];
2729 NSFont *f = [NSFont fontWithName:nsname size:size];
2731 *name_ret = strdup(name);
2736 try_native_font (const char *name, float scale,
2737 char **name_ret, float *size_ret)
2739 if (!name) return 0;
2740 const char *spc = strrchr (name, ' ');
2743 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2746 if (size <= 4) return 0;
2750 char *name2 = strdup (name);
2751 name2[strlen(name2) - strlen(spc)] = 0;
2752 NSString *nsname = [NSString stringWithCString:name2
2753 encoding:NSUTF8StringEncoding];
2754 NSFont *f = [NSFont fontWithName:nsname size:size];
2766 /* Returns a random font in the given size and face.
2769 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2772 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2773 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2774 NSArray *fonts = [[NSFontManager sharedFontManager]
2775 availableFontNamesWithTraits:mask];
2776 if (!fonts) return 0;
2778 int n = [fonts count];
2779 if (n <= 0) return 0;
2782 for (j = 0; j < n; j++) {
2783 int i = random() % n;
2784 NSString *name = [fonts objectAtIndex:i];
2785 NSFont *f = [NSFont fontWithName:name size:size];
2788 /* Don't use this font if it (probably) doesn't include ASCII characters.
2790 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2791 if (! (enc == NSUTF8StringEncoding ||
2792 enc == NSISOLatin1StringEncoding ||
2793 enc == NSNonLossyASCIIStringEncoding ||
2794 enc == NSISOLatin2StringEncoding ||
2795 enc == NSUnicodeStringEncoding ||
2796 enc == NSWindowsCP1250StringEncoding ||
2797 enc == NSWindowsCP1252StringEncoding ||
2798 enc == NSMacOSRomanStringEncoding)) {
2799 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2802 // NSLog(@"using \"%@\": %d", name, enc);
2804 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2808 // None of the fonts support ASCII?
2811 # else // USE_IPHONE
2813 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2814 NSArray *families = [UIFont familyNames];
2815 NSMutableDictionary *famdict = [NSMutableDictionary
2816 dictionaryWithCapacity:100];
2817 NSObject *y = [NSNumber numberWithBool:YES];
2818 for (NSString *name in families) {
2819 // There are many dups in the families array -- uniquify it.
2820 [famdict setValue:y forKey:name];
2823 for (NSString *name in famdict) {
2824 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2827 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2830 BOOL bb = MATCH(@"Bold");
2831 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2833 if (!bold != !bb) continue;
2834 if (!ital != !ii) continue;
2836 /* Check if it can do ASCII. No good way to accomplish this!
2837 These are fonts present in iPhone Simulator as of June 2012
2838 that don't include ASCII.
2840 if (MATCH(@"AppleGothic") || // Korean
2841 MATCH(@"Dingbats") || // Dingbats
2842 MATCH(@"Emoji") || // Emoticons
2843 MATCH(@"Geeza") || // Arabic
2844 MATCH(@"Hebrew") || // Hebrew
2845 MATCH(@"HiraKaku") || // Japanese
2846 MATCH(@"HiraMin") || // Japanese
2847 MATCH(@"Kailasa") || // Tibetan
2848 MATCH(@"Ornaments") || // Dingbats
2849 MATCH(@"STHeiti") // Chinese
2853 [fonts addObject:fn];
2858 if (! [fonts count]) return 0; // Nothing suitable?
2860 int i = random() % [fonts count];
2861 NSString *name = [fonts objectAtIndex:i];
2862 UIFont *ff = [UIFont fontWithName:name size:size];
2863 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2867 # endif // USE_IPHONE
2872 try_xlfd_font (const char *name, float scale,
2873 char **name_ret, float *size_ret)
2884 const char *s = (name ? name : "");
2886 while (*s && (*s == '*' || *s == '-'))
2889 while (*s2 && (*s2 != '*' && *s2 != '-'))
2892 unsigned long L = s2-s;
2895 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2896 else if (CMP ("random")) rand = YES;
2897 else if (CMP ("bold")) bold = YES;
2898 else if (CMP ("i")) ital = YES;
2899 else if (CMP ("o")) ital = YES;
2900 else if (CMP ("courier")) fixed = YES;
2901 else if (CMP ("fixed")) fixed = YES;
2902 else if (CMP ("m")) fixed = YES;
2903 else if (CMP ("times")) serif = YES;
2904 else if (CMP ("6x10")) fixed = YES, size = 8;
2905 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2906 else if (CMP ("9x15")) fixed = YES, size = 12;
2907 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2908 else if (CMP ("vga")) fixed = YES, size = 12;
2909 else if (CMP ("console")) fixed = YES, size = 12;
2910 else if (CMP ("gallant")) fixed = YES, size = 12;
2912 else if (size == 0) {
2914 if (1 == sscanf (s, " %d ", &n))
2921 if (size < 6 || size > 1000)
2927 nsfont = random_font (bold, ital, size, &ps_name);
2930 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2932 // if that didn't work, turn off attibutes until it does
2933 // (e.g., there is no "Monaco-Bold".)
2935 if (!nsfont && serif) {
2937 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2939 if (!nsfont && ital) {
2941 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2943 if (!nsfont && bold) {
2945 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2947 if (!nsfont && fixed) {
2949 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2953 *name_ret = ps_name;
2963 XLoadFont (Display *dpy, const char *name)
2965 Font fid = (Font) calloc (1, sizeof(*fid));
2970 /* Since iOS screens are physically smaller than desktop screens, scale up
2971 the fonts to make them more readable.
2973 Note that X11 apps on iOS also have the backbuffer sized in points
2974 instead of pixels, resulting in an effective X11 screen size of 768x1024
2975 or so, even if the display has significantly higher resolution. That is
2976 unrelated to this hack, which is really about DPI.
2981 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2983 if (!fid->nsfont && name &&
2984 strchr (name, ' ') &&
2985 !strchr (name, '*')) {
2986 // If name contains a space but no stars, it is a native font spec --
2987 // return NULL so that we know it really didn't exist. Else, it is an
2988 // XLFD font, so keep trying.
2989 XUnloadFont (dpy, fid);
2994 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2996 // We should never return NULL for XLFD fonts.
2998 Assert (0, "no font");
3001 CFRetain (fid->nsfont); // needed for garbage collection?
3003 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
3012 XLoadQueryFont (Display *dpy, const char *name)
3014 Font fid = XLoadFont (dpy, name);
3016 return XQueryFont (dpy, fid);
3020 XUnloadFont (Display *dpy, Font fid)
3023 free (fid->ps_name);
3024 if (fid->metrics.per_char)
3025 free (fid->metrics.per_char);
3027 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3028 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3029 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3030 // They're probably not very big...
3032 // [fid->nsfont release];
3033 // CFRelease (fid->nsfont);
3040 XFreeFontInfo (char **names, XFontStruct *info, int n)
3044 for (i = 0; i < n; i++)
3045 if (names[i]) free (names[i]);
3049 for (i = 0; i < n; i++)
3050 if (info[i].per_char)
3051 free (info[i].per_char);
3058 XFreeFont (Display *dpy, XFontStruct *f)
3061 XFreeFontInfo (0, f, 1);
3062 XUnloadFont (dpy, fid);
3068 XSetFont (Display *dpy, GC gc, Font fid)
3071 XUnloadFont (dpy, gc->gcv.font);
3072 gc->gcv.font = copy_font (fid);
3073 [gc->gcv.font->nsfont retain];
3074 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3080 XCreateFontSet (Display *dpy, char *name,
3081 char ***missing_charset_list_return,
3082 int *missing_charset_count_return,
3083 char **def_string_return)
3085 char *name2 = strdup (name);
3086 char *s = strchr (name, ",");
3089 Font f = XLoadFont (dpy, name2);
3092 set = (XFontSet) calloc (1, sizeof(*set));
3096 if (missing_charset_list_return) *missing_charset_list_return = 0;
3097 if (missing_charset_count_return) *missing_charset_count_return = 0;
3098 if (def_string_return) *def_string_return = 0;
3104 XFreeFontSet (Display *dpy, XFontSet set)
3106 XUnloadFont (dpy, set->font);
3112 XFreeStringList (char **list)
3116 for (i = 0; list[i]; i++)
3123 XTextExtents (XFontStruct *f, const char *s, int length,
3124 int *dir_ret, int *ascent_ret, int *descent_ret,
3127 memset (cs, 0, sizeof(*cs));
3128 for (int i = 0; i < length; i++) {
3129 unsigned char c = (unsigned char) s[i];
3130 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3131 c = f->default_char;
3132 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3136 cs->ascent = MAX (cs->ascent, cc->ascent);
3137 cs->descent = MAX (cs->descent, cc->descent);
3138 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3139 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3140 cs->width += cc->width;
3144 *ascent_ret = f->ascent;
3145 *descent_ret = f->descent;
3150 XTextWidth (XFontStruct *f, const char *s, int length)
3152 int ascent, descent, dir;
3154 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3160 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
3161 int *dir_ret, int *ascent_ret, int *descent_ret,
3166 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
3167 [fid->nsfont pointSize],
3169 Assert (ctfont, @"no CTFontRef for UIFont");
3172 char *utf8 = XChar2b_to_utf8 (s, &utf8_len);
3173 NSString *nsstr = [NSString stringWithCString:utf8
3174 encoding:NSUTF8StringEncoding];
3175 NSUInteger L = [nsstr length];
3177 for (int i = 0; i < L; i++) {
3178 unichar c = [nsstr characterAtIndex:i];
3180 CGGlyph cgglyph = 0;
3182 if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
3183 glyph_metrics (ctfont, cgglyph, &cc);
3185 // This is normal, since Latin1 does not encode 0-31 or 127-159.
3186 memset (&cc, 0, sizeof(cc));
3191 cs->ascent = MAX (cs->ascent, cc.ascent);
3192 cs->descent = MAX (cs->descent, cc.descent);
3193 cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
3194 cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
3195 cs->width += cc.width;
3199 *ascent_ret = f->ascent;
3200 *descent_ret = f->descent;
3210 Xutf8TextExtents (XFontSet set, const char *str, int num_bytes,
3211 XRectangle *overall_ink_return,
3212 XRectangle *overall_logical_return)
3214 Font fid = set->font;
3216 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
3217 [fid->nsfont pointSize],
3219 Assert (ctfont, @"no CTFontRef for UIFont");
3221 NSString *nsstr = [NSString stringWithCString:str
3222 encoding:NSUTF8StringEncoding];
3223 NSUInteger L = [nsstr length];
3225 XRectangle ink = { 0, };
3226 XRectangle logical = { 0, };
3228 logical.height = fid->metrics.ascent;
3230 for (int i = 0; i < L; i++) {
3231 unichar c = [nsstr characterAtIndex:i];
3233 CGGlyph cgglyph = 0;
3235 if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
3236 glyph_metrics (ctfont, cgglyph, &cs);
3238 // This is normal, since Latin1 does not encode 0-31 or 127-159.
3239 memset (&cs, 0, sizeof(cs));
3241 logical.width += cs.width;
3243 ink.height = MAX(ink.height, cs.ascent);
3244 ink.y = MIN(ink.y, cs.descent);
3247 ink.x = cs.lbearing;
3250 ink.width += cs.rbearing;
3252 ink.width += cs.width;
3258 if (overall_ink_return)
3259 *overall_ink_return = ink;
3260 if (overall_logical_return)
3261 *overall_logical_return = logical;
3267 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3270 if (! nsstr) return 1;
3272 CGRect wr = d->frame;
3273 CGContextRef cgc = d->cgc;
3275 unsigned long argb = gc->gcv.foreground;
3276 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3277 float a = ((argb >> 24) & 0xFF) / 255.0;
3278 float r = ((argb >> 16) & 0xFF) / 255.0;
3279 float g = ((argb >> 8) & 0xFF) / 255.0;
3280 float b = ((argb ) & 0xFF) / 255.0;
3281 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3283 if (!gc->gcv.font) {
3284 Assert (0, "no font");
3288 NSDictionary *attr =
3289 [NSDictionary dictionaryWithObjectsAndKeys:
3290 gc->gcv.font->nsfont, NSFontAttributeName,
3291 fg, NSForegroundColorAttributeName,
3294 // Don't understand why we have to do both set_color and
3295 // NSForegroundColorAttributeName, but we do.
3297 set_color (cgc, argb, 32, NO, YES);
3299 NSAttributedString *astr = [[NSAttributedString alloc]
3300 initWithString:nsstr
3302 CTLineRef dl = CTLineCreateWithAttributedString (
3303 (__bridge CFAttributedStringRef) astr);
3304 CGContextSetTextPosition (cgc,
3306 wr.origin.y + wr.size.height - y);
3307 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3308 CTLineDraw (dl, cgc);
3311 invalidate_drawable_cache (d);
3317 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3318 const char *str, int len)
3320 char *s2 = (char *) malloc (len + 1);
3321 strncpy (s2, str, len);
3323 NSString *nsstr = [NSString stringWithCString:s2
3324 encoding:NSISOLatin1StringEncoding];
3325 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3332 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
3333 const XChar2b *str, int len)
3335 char *s2 = XChar2b_to_utf8 (str, 0);
3336 NSString *nsstr = [NSString stringWithCString:s2
3337 encoding:NSUTF8StringEncoding];
3339 /* If the C string has invalid UTF8 bytes in it, the result is
3340 "undefined", which turns out to mean "return a null string"
3341 instead of just omitting the bogus characters. Greaaat.
3342 So try it again as Latin1, I guess. */
3343 nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
3344 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3351 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3352 int x, int y, const char *str, int len)
3354 char *s2 = (char *) malloc (len + 1);
3355 strncpy (s2, str, len);
3357 NSString *nsstr = [NSString stringWithCString:s2
3358 encoding:NSUTF8StringEncoding];
3360 nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
3361 draw_string (dpy, d, gc, x, y, nsstr);
3367 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3368 const char *str, int len)
3370 int ascent, descent, dir;
3372 XTextExtents (&gc->gcv.font->metrics, str, len,
3373 &dir, &ascent, &descent, &cs);
3374 draw_rect (dpy, d, gc,
3375 x + MIN (0, cs.lbearing),
3376 y - MAX (0, ascent),
3377 MAX (MAX (0, cs.rbearing) -
3378 MIN (0, cs.lbearing),
3380 MAX (0, ascent) + MAX (0, descent),
3382 return XDrawString (dpy, d, gc, x, y, str, len);
3387 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3389 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3390 gc->gcv.foreground = fg;
3396 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3398 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3399 gc->gcv.background = bg;
3404 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3406 gc->gcv.alpha_allowed_p = allowed;
3411 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3413 gc->gcv.antialias_p = antialias_p;
3419 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3420 int line_style, int cap_style, int join_style)
3422 gc->gcv.line_width = line_width;
3423 Assert (line_style == LineSolid, "only LineSolid implemented");
3424 // gc->gcv.line_style = line_style;
3425 gc->gcv.cap_style = cap_style;
3426 gc->gcv.join_style = join_style;
3431 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3437 XSetFunction (Display *dpy, GC gc, int which)
3439 gc->gcv.function = which;
3444 XSetSubwindowMode (Display *dpy, GC gc, int which)
3446 gc->gcv.subwindow_mode = which;
3451 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3453 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3455 if (gc->gcv.clip_mask) {
3456 XFreePixmap (dpy, gc->gcv.clip_mask);
3457 CGImageRelease (gc->clip_mask);
3460 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3461 if (gc->gcv.clip_mask)
3463 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3471 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3473 gc->gcv.clip_x_origin = x;
3474 gc->gcv.clip_y_origin = y;
3480 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3481 int *root_x_ret, int *root_y_ret,
3482 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3484 Assert (w && w->type == WINDOW, "not a window");
3487 int x = w->window.last_mouse_x;
3488 int y = w->window.last_mouse_y;
3489 if (root_x_ret) *root_x_ret = x;
3490 if (root_y_ret) *root_y_ret = y;
3491 if (win_x_ret) *win_x_ret = x;
3492 if (win_y_ret) *win_y_ret = y;
3494 # else // !USE_IPHONE
3496 NSWindow *nsw = [w->window.view window];
3498 // get bottom left of window on screen, from bottom left
3499 wpos.x = wpos.y = 0;
3500 wpos = [nsw convertBaseToScreen:wpos];
3503 // get bottom left of view on window, from bottom left
3504 vpos.x = vpos.y = 0;
3505 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3507 // get bottom left of view on screen, from bottom left
3511 // get top left of view on screen, from bottom left
3512 vpos.y += w->frame.size.height;
3514 // get top left of view on screen, from top left
3515 NSArray *screens = [NSScreen screens];
3516 NSScreen *screen = (screens && [screens count] > 0
3517 ? [screens objectAtIndex:0]
3518 : [NSScreen mainScreen]);
3519 NSRect srect = [screen frame];
3520 vpos.y = srect.size.height - vpos.y;
3522 // get the mouse position on window, from bottom left
3523 NSEvent *e = [NSApp currentEvent];
3524 NSPoint p = [e locationInWindow];
3526 // get mouse position on screen, from bottom left
3530 // get mouse position on screen, from top left
3531 p.y = srect.size.height - p.y;
3533 if (root_x_ret) *root_x_ret = (int) p.x;
3534 if (root_y_ret) *root_y_ret = (int) p.y;
3535 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3536 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3537 # endif // !USE_IPHONE
3539 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3540 if (root_ret) *root_ret = 0;
3541 if (child_ret) *child_ret = 0;
3546 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3547 int src_x, int src_y,
3548 int *dest_x_ret, int *dest_y_ret,
3551 Assert (w && w->type == WINDOW, "not a window");
3559 # else // !USE_IPHONE
3561 NSWindow *nsw = [w->window.view window];
3563 // get bottom left of window on screen, from bottom left
3564 wpos.x = wpos.y = 0;
3565 wpos = [nsw convertBaseToScreen:wpos];
3568 // get bottom left of view on window, from bottom left
3569 vpos.x = vpos.y = 0;
3570 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3572 // get bottom left of view on screen, from bottom left
3576 // get top left of view on screen, from bottom left
3577 vpos.y += w->frame.size.height;
3579 // get top left of view on screen, from top left
3580 NSArray *screens = [NSScreen screens];
3581 NSScreen *screen = (screens && [screens count] > 0
3582 ? [screens objectAtIndex:0]
3583 : [NSScreen mainScreen]);
3584 NSRect srect = [screen frame];
3585 vpos.y = srect.size.height - vpos.y;
3587 // point starts out relative to top left of view
3592 // get point relative to top left of screen
3595 # endif // !USE_IPHONE
3606 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3612 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3615 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3617 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3618 if ((unsigned int) ks <= 255)
3621 // Put control characters in the string. Not meta.
3622 if (e->state & ControlMask) {
3623 if (c >= 'a' && c <= 'z') // Upcase control.
3625 if (c >= '@' && c <= '_') // Shift to control page.
3627 if (c == ' ') // C-SPC is NULL.
3631 if (k_ret) *k_ret = ks;
3632 if (size > 0) buf[0] = c;
3633 if (size > 1) buf[1] = 0;
3634 return (size > 0 ? 1 : 0);
3639 XFlush (Display *dpy)
3641 // Just let the event loop take care of this on its own schedule.
3646 XSync (Display *dpy, Bool flush)
3648 return XFlush (dpy);
3652 // declared in utils/visual.h
3654 has_writable_cells (Screen *s, Visual *v)
3660 visual_depth (Screen *s, Visual *v)
3666 visual_cells (Screen *s, Visual *v)
3672 visual_class (Screen *s, Visual *v)
3678 get_bits_per_pixel (Display *dpy, int depth)
3680 Assert (depth == 32 || depth == 1, "unexpected depth");
3685 screen_number (Screen *screen)
3687 Display *dpy = DisplayOfScreen (screen);
3689 for (i = 0; i < ScreenCount (dpy); i++)
3690 if (ScreenOfDisplay (dpy, i) == screen)
3696 // declared in utils/grabclient.h
3698 use_subwindow_mode_p (Screen *screen, Window window)