X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=OSX%2Fjwxyz.m;fp=OSX%2Fjwxyz.m;h=0000000000000000000000000000000000000000;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hp=8d181279e84b990311f0d48a2d4b7c9df95db311;hpb=88cfe534a698a0562e81345957a50714af1453bc;p=xscreensaver diff --git a/OSX/jwxyz.m b/OSX/jwxyz.m deleted file mode 100644 index 8d181279..00000000 --- a/OSX/jwxyz.m +++ /dev/null @@ -1,4203 +0,0 @@ -/* xscreensaver, Copyright (c) 1991-2015 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -/* JWXYZ Is Not Xlib. - - But it's a bunch of function definitions that bear some resemblance to - Xlib and that do Cocoa-ish things that bear some resemblance to the - things that Xlib might have done. - */ - -#import -#import -#import - -#ifdef USE_IPHONE -# import -# import -# import -# define NSView UIView -# define NSRect CGRect -# define NSPoint CGPoint -# define NSSize CGSize -# define NSColor UIColor -# define NSImage UIImage -# define NSEvent UIEvent -# define NSFont UIFont -# define NSGlyph CGGlyph -# define NSWindow UIWindow -# define NSMakeSize CGSizeMake -# define NSBezierPath UIBezierPath -# define colorWithDeviceRed colorWithRed - -# define NSFontTraitMask UIFontDescriptorSymbolicTraits -// The values for the flags for NSFontTraitMask and -// UIFontDescriptorSymbolicTraits match up, not that it really matters here. -# define NSBoldFontMask UIFontDescriptorTraitBold -# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace -# define NSItalicFontMask UIFontDescriptorTraitItalic -#else -# import -#endif - -#import -#import -#import - -#import "jwxyz.h" -#import "jwxyz-timers.h" -#import "yarandom.h" -#import "utf8wc.h" -#import "xft.h" - -# define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */ - -#undef Assert -#define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0) - -# undef MAX -# undef MIN -# define MAX(a,b) ((a)>(b)?(a):(b)) -# define MIN(a,b) ((a)<(b)?(a):(b)) - - -struct jwxyz_Drawable { - enum { WINDOW, PIXMAP } type; - CGContextRef cgc; - CGImageRef cgi; - CGRect frame; - union { - struct { - NSView *view; - unsigned long background; - int last_mouse_x, last_mouse_y; - } window; - struct { - int depth; - void *cgc_buffer; // the bits to which CGContextRef renders - } pixmap; - }; -}; - -struct jwxyz_Display { - Window main_window; - Screen *screen; - int screen_count; - struct jwxyz_sources_data *timers_data; - -# ifndef USE_IPHONE - CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window. - This can change if the window is dragged to - a different screen. */ -# endif - - CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of - our images with this to avoid translation - when rendering. */ -}; - -struct jwxyz_Screen { - Display *dpy; - CGBitmapInfo bitmap_info; - unsigned long black, white; - Visual *visual; - int screen_number; -}; - -struct jwxyz_GC { - XGCValues gcv; - unsigned int depth; - CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask -}; - -struct jwxyz_Font { - Display *dpy; - char *ps_name; - NSFont *nsfont; - float size; // points - char *xa_font; - - // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics. - // But we need the metrics on both of them, so they go here. - XFontStruct metrics; -}; - -struct jwxyz_XFontSet { - XFontStruct *font; -}; - - -/* Instead of calling abort(), throw a real exception, so that - XScreenSaverView can catch it and display a dialog. - */ -void -jwxyz_abort (const char *fmt, ...) -{ - char s[10240]; - if (!fmt || !*fmt) - strcpy (s, "abort"); - else - { - va_list args; - va_start (args, fmt); - vsprintf (s, fmt, args); - va_end (args); - } - [[NSException exceptionWithName: NSInternalInconsistencyException - reason: [NSString stringWithCString: s - encoding:NSUTF8StringEncoding] - userInfo: nil] - raise]; - abort(); // not reached -} - -// 24/32bpp -> 32bpp image conversion. -// Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24 -// bits and an optional byte order swap. - -// This type encodes such a conversion. -typedef unsigned convert_mode_t; - -// It's rotate, then swap. -// A rotation here shifts bytes forward in memory. On x86/ARM, that's a left -// rotate, and on PowerPC, a rightward rotation. -static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3; -static const convert_mode_t CONVERT_MODE_SWAP = 0x4; - - -// Converts an array of pixels ('src') from one format to another, placing the -// result in 'dest', according to the pixel conversion mode 'mode'. -static void -convert_row (uint32_t *dest, const void *src, size_t count, - convert_mode_t mode, size_t src_bpp) -{ - Assert (src_bpp == 24 || src_bpp == 32, "weird bpp"); - - // This works OK iff src == dest or src and dest do not overlap. - - if (!mode) { - if (src != dest) - memcpy (dest, src, count * 4); - return; - } - - // This is correct, but not fast. - convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8; - convert_mode_t flip = mode & CONVERT_MODE_SWAP; - - src_bpp /= 8; - - uint32_t *dest_end = dest + count; - while (dest != dest_end) { - uint32_t x; - - if (src_bpp == 4) - x = *(const uint32_t *)src; - else { // src_bpp == 3 - const uint8_t *src8 = (const uint8_t *)src; - // __LITTLE/BIG_ENDIAN__ are defined by the compiler. -# if defined __LITTLE_ENDIAN__ - x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000; -# elif defined __BIG_ENDIAN__ - x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff; -# else -# error "Can't determine system endianness." -# endif - } - - src = (const uint8_t *)src + src_bpp; - - /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation, - for 32-bit integers, is: - - (x << rot) | (x >> (32 - rot)) - - This works nearly everywhere. Compilers on x86 wil generally recognize - the idiom and convert it to a ROL instruction. But there's a problem - here: according to the C specification, bit shifts greater than or equal - to the length of the integer are undefined. And if rot = 0: - 1. (x << 0) | (x >> (32 - 0)) - 2. (x << 0) | (x >> 32) - 3. (x << 0) | (Undefined!) - - Still, when the compiler converts this to a ROL on x86, everything works - as intended. But, there are two additional problems when Clang does - compile-time constant expression evaluation with the (x >> 32) - expression: - 1. Instead of evaluating it to something reasonable (either 0, like a - human would intuitively expect, or x, like x86 would with SHR), Clang - seems to pull a value out of nowhere, like -1, or some other random - number. - 2. Clang's warning for this, -Wshift-count-overflow, only works when the - shift count is a literal constant, as opposed to an arbitrary - expression that is optimized down to a constant. - Put together, this means that the assertions in jwxyz_make_display with - convert_px break with the above naive rotation, but only for a release - build. - - http://blog.regehr.org/archives/1063 - http://llvm.org/bugs/show_bug.cgi?id=17332 - As described in those links, there is a solution here: Masking the - undefined shift with '& 31' as below makes the experesion well-defined - again. And LLVM is set to pick up on this safe version of the idiom and - use a rotation instruction on architectures (like x86) that support it, - just like it does with the unsafe version. - - Too bad LLVM doesn't want to pick up on that particular optimization - here. Oh well. At least this code usually isn't critical w.r.t. - performance. - */ - -# if defined __LITTLE_ENDIAN__ - x = (x << rot) | (x >> ((32 - rot) & 31)); -# elif defined __BIG_ENDIAN__ - x = (x >> rot) | (x << ((32 - rot) & 31)); -# endif - - if (flip) - x = __builtin_bswap32(x); // LLVM/GCC built-in function. - - *dest = x; - ++dest; - } -} - - -// Converts a single pixel. -static uint32_t -convert_px (uint32_t px, convert_mode_t mode) -{ - convert_row (&px, &px, 1, mode, 32); - return px; -} - - -// This returns the inverse conversion mode, such that: -// pixel -// == convert_px(convert_px(pixel, mode), convert_mode_invert(mode)) -// == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode) -static convert_mode_t -convert_mode_invert (convert_mode_t mode) -{ - // swap(0); rot(n) == rot(n); swap(0) - // swap(1); rot(n) == rot(-n); swap(1) - return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode; -} - - -// This combines two conversions into one, such that: -// convert_px(convert_px(pixel, mode0), mode1) -// == convert_px(pixel, convert_mode_merge(mode0, mode1)) -static convert_mode_t -convert_mode_merge (convert_mode_t m0, convert_mode_t m1) -{ - // rot(r0); swap(s0); rot(r1); swap(s1) - // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1) - // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1) - return - ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) | - ((m0 ^ m1) & CONVERT_MODE_SWAP); -} - - -// This returns a conversion mode that converts an arbitrary 32-bit format -// specified by bitmap_info to RGBA. -static convert_mode_t -convert_mode_to_rgba (CGBitmapInfo bitmap_info) -{ - // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little - // i.e. BGRA - // red = 0x00FF0000; - // green = 0x0000FF00; - // blue = 0x000000FF; - - // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big - - CGImageAlphaInfo alpha_info = - (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask); - - Assert (! (bitmap_info & kCGBitmapFloatComponents), - "kCGBitmapFloatComponents unsupported"); - Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported"); - - convert_mode_t rot = alpha_info == kCGImageAlphaFirst || - alpha_info == kCGImageAlphaPremultipliedFirst || - alpha_info == kCGImageAlphaNoneSkipFirst ? - 3 : 0; - - CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask; - - Assert (byte_order == kCGBitmapByteOrder32Little || - byte_order == kCGBitmapByteOrder32Big, - "byte order not supported"); - - convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ? - CONVERT_MODE_SWAP : 0; - if (swap) - rot = CONVERT_MODE_ROTATE_MASK & -rot; - return swap | rot; -} - - -union color_bytes -{ - uint32_t pixel; - uint8_t bytes[4]; -}; - - -static uint32_t -alloc_color (Display *dpy, uint16_t r, uint16_t g, uint16_t b, uint16_t a) -{ - union color_bytes color; - - /* Instead of (int)(c / 256.0), another possibility is - (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only - uint8_t integer_math(uint16_t c) { - unsigned c0 = c + 128; - return (c0 - (c0 >> 8)) >> 8; - } - */ - - color.bytes[0] = r >> 8; - color.bytes[1] = g >> 8; - color.bytes[2] = b >> 8; - color.bytes[3] = a >> 8; - - return - convert_px (color.pixel, - convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info))); -} - - -static void -query_color (Display *dpy, unsigned long pixel, uint8_t *rgba) -{ - union color_bytes color; - color.pixel = convert_px ((uint32_t)pixel, - convert_mode_to_rgba (dpy->screen->bitmap_info)); - for (unsigned i = 0; i != 4; ++i) - rgba[i] = color.bytes[i]; -} - - -static void -query_color_float (Display *dpy, unsigned long pixel, float *rgba) -{ - uint8_t rgba8[4]; - query_color (dpy, pixel, rgba8); - for (unsigned i = 0; i != 4; ++i) - rgba[i] = rgba8[i] * (1.0f / 255.0f); -} - - -/* We keep a list of all of the Displays that have been created and not - yet freed so that they can have sensible display numbers. If three - displays are created (0, 1, 2) and then #1 is closed, then the fourth - display will be given the now-unused display number 1. (Everything in - here assumes a 1:1 Display/Screen mapping.) - - The size of this array is the most number of live displays at one time. - So if it's 20, then we'll blow up if the system has 19 monitors and also - has System Preferences open (the small preview window). - - Note that xlockmore-style savers tend to allocate big structures, so - setting this to 1000 will waste a few megabytes. Also some of them assume - that the number of screens never changes, so dynamically expanding this - array won't work. - */ -# ifndef USE_IPHONE -static Display *jwxyz_live_displays[20] = { 0, }; -# endif - - -Display * -jwxyz_make_display (void *nsview_arg, void *cgc_arg) -{ - CGContextRef cgc = (CGContextRef) cgc_arg; - NSView *view = (NSView *) nsview_arg; - Assert (view, "no view"); - if (!view) return 0; - - Display *d = (Display *) calloc (1, sizeof(*d)); - d->screen = (Screen *) calloc (1, sizeof(Screen)); - d->screen->dpy = d; - - d->screen_count = 1; - d->screen->screen_number = 0; -# ifndef USE_IPHONE - { - // Find the first empty slot in live_displays and plug us in. - int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays); - int i; - for (i = 0; i < size; i++) { - if (! jwxyz_live_displays[i]) - break; - } - if (i >= size) abort(); - jwxyz_live_displays[i] = d; - d->screen_count = size; - d->screen->screen_number = i; - } -# endif // !USE_IPHONE - -# ifdef USE_BACKBUFFER - d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc); -# else - d->screen->bitmap_info = (kCGImageAlphaNoneSkipFirst | - kCGBitmapByteOrder32Little); -# endif - d->screen->black = alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF); - d->screen->white = alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); - -# if 0 - // Tests for the image conversion modes. - { - const uint32_t key = 0x04030201; -# ifdef __LITTLE_ENDIAN__ - assert (convert_px (key, 0) == key); - assert (convert_px (key, 1) == 0x03020104); - assert (convert_px (key, 3) == 0x01040302); - assert (convert_px (key, 4) == 0x01020304); - assert (convert_px (key, 5) == 0x04010203); -# endif - for (unsigned i = 0; i != 8; ++i) { - assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key); - assert (convert_mode_invert(convert_mode_invert(i)) == i); - } - - for (unsigned i = 0; i != 8; ++i) { - for (unsigned j = 0; j != 8; ++j) - assert (convert_px(convert_px(key, i), j) == - convert_px(key, convert_mode_merge(i, j))); - } - } -# endif - - Visual *v = (Visual *) calloc (1, sizeof(Visual)); - v->class = TrueColor; - v->red_mask = alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000); - v->green_mask = alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000); - v->blue_mask = alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000); - CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask; - Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) && - (byte_order == kCGBitmapByteOrder32Little || - byte_order == kCGBitmapByteOrder32Big), - "invalid bits per channel"); - v->bits_per_rgb = 8; - d->screen->visual = v; - - d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d)); - - - Window w = (Window) calloc (1, sizeof(*w)); - w->type = WINDOW; - w->window.view = view; - CFRetain (w->window.view); // needed for garbage collection? - w->window.background = BlackPixel(d,0); - - d->main_window = w; - -# ifndef USE_IPHONE - if (! cgc) { - [view lockFocus]; - cgc = [[[view window] graphicsContext] graphicsPort]; - [view unlockFocus]; - w->cgc = cgc; - } -# endif - - Assert (cgc, "no CGContext"); - return d; -} - -void -jwxyz_free_display (Display *dpy) -{ - jwxyz_sources_free (dpy->timers_data); - -# ifndef USE_IPHONE - { - // Find us in live_displays and clear that slot. - int size = ScreenCount(dpy); - int i; - for (i = 0; i < size; i++) { - if (dpy == jwxyz_live_displays[i]) { - jwxyz_live_displays[i] = 0; - break; - } - } - if (i >= size) abort(); - } -# endif // !USE_IPHONE - - free (dpy->screen->visual); - free (dpy->screen); - CFRelease (dpy->main_window->window.view); - free (dpy->main_window); - free (dpy); -} - - -void * -jwxyz_window_view (Window w) -{ - Assert (w && w->type == WINDOW, "not a window"); - return w->window.view; -} - - -/* Call this after any modification to the bits on a Pixmap or Window. - Most Pixmaps are used frequently as sources and infrequently as - destinations, so it pays to cache the data as a CGImage as needed. - */ -static void -invalidate_drawable_cache (Drawable d) -{ - if (d && d->cgi) { - CGImageRelease (d->cgi); - d->cgi = 0; - } -} - - -/* Call this when the View changes size or position. - */ -void -jwxyz_window_resized (Display *dpy, Window w, - int new_x, int new_y, int new_width, int new_height, - void *cgc_arg) -{ - CGContextRef cgc = (CGContextRef) cgc_arg; - Assert (w && w->type == WINDOW, "not a window"); - w->frame.origin.x = new_x; - w->frame.origin.y = new_y; - w->frame.size.width = new_width; - w->frame.size.height = new_height; - - if (cgc) w->cgc = cgc; - Assert (w->cgc, "no CGContext"); - -# ifndef USE_IPHONE - // Figure out which screen the window is currently on. - { - int wx, wy; - XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL); - CGPoint p; - p.x = wx; - p.y = wy; - CGDisplayCount n; - dpy->cgdpy = 0; - CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n); - // Auuugh! - if (! dpy->cgdpy) { - p.x = p.y = 0; - CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n); - } - Assert (dpy->cgdpy, "unable to find CGDisplay"); - } -# endif // USE_IPHONE - -# ifndef USE_BACKBUFFER - // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off, - // then this one's faster. - - { - // Figure out this screen's colorspace, and use that for every CGImage. - // - CMProfileRef profile = 0; - CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile); - Assert (profile, "unable to find colorspace profile"); - dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile); - Assert (dpy->colorspace, "unable to find colorspace"); - } -# else // USE_BACKBUFFER - - // WTF? It's faster if we *do not* use the screen's colorspace! - // - dpy->colorspace = CGColorSpaceCreateDeviceRGB(); -# endif // USE_BACKBUFFER - - invalidate_drawable_cache (w); -} - - -#ifdef USE_IPHONE -void -jwxyz_mouse_moved (Display *dpy, Window w, int x, int y) -{ - Assert (w && w->type == WINDOW, "not a window"); - w->window.last_mouse_x = x; - w->window.last_mouse_y = y; -} -#endif // USE_IPHONE - - -void -jwxyz_flush_context (Display *dpy) -{ - // This is only used when USE_BACKBUFFER is off. - // CGContextSynchronize is another possibility. - CGContextFlush(dpy->main_window->cgc); -} - -jwxyz_sources_data * -display_sources_data (Display *dpy) -{ - return dpy->timers_data; -} - - -Window -XRootWindow (Display *dpy, int screen) -{ - return dpy->main_window; -} - -Screen * -XDefaultScreenOfDisplay (Display *dpy) -{ - return dpy->screen; -} - -Visual * -XDefaultVisualOfScreen (Screen *screen) -{ - return screen->visual; -} - -Display * -XDisplayOfScreen (Screen *s) -{ - return s->dpy; -} - -int -XDisplayNumberOfScreen (Screen *s) -{ - return 0; -} - -int -XScreenNumberOfScreen (Screen *s) -{ - return s->screen_number; -} - -int -jwxyz_ScreenCount (Display *dpy) -{ - return dpy->screen_count; -} - -int -XDisplayWidth (Display *dpy, int screen) -{ - return (int) dpy->main_window->frame.size.width; -} - -int -XDisplayHeight (Display *dpy, int screen) -{ - return (int) dpy->main_window->frame.size.height; -} - -unsigned long -XBlackPixelOfScreen(Screen *screen) -{ - return screen->black; -} - -unsigned long -XWhitePixelOfScreen(Screen *screen) -{ - return screen->white; -} - -unsigned long -XCellsOfScreen(Screen *screen) -{ - Visual *v = screen->visual; - return v->red_mask | v->green_mask | v->blue_mask; -} - -static void -validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth, - BOOL alpha_allowed_p) -{ - if (depth == 1) - Assert ((pixel == 0 || pixel == 1), "bogus mono pixel"); - else if (!alpha_allowed_p) - Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)), - "bogus color pixel"); -} - - -static void -set_color (Display *dpy, CGContextRef cgc, unsigned long argb, - unsigned int depth, BOOL alpha_allowed_p, BOOL fill_p) -{ - validate_pixel (dpy, argb, depth, alpha_allowed_p); - if (depth == 1) { - if (fill_p) - CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0); - else - CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0); - } else { - float rgba[4]; - query_color_float (dpy, argb, rgba); - if (fill_p) - CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]); - else - CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]); - } -} - -static void -set_line_mode (CGContextRef cgc, XGCValues *gcv) -{ - CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1); - CGContextSetLineJoin (cgc, - gcv->join_style == JoinMiter ? kCGLineJoinMiter : - gcv->join_style == JoinRound ? kCGLineJoinRound : - kCGLineJoinBevel); - CGContextSetLineCap (cgc, - gcv->cap_style == CapNotLast ? kCGLineCapButt : - gcv->cap_style == CapButt ? kCGLineCapButt : - gcv->cap_style == CapRound ? kCGLineCapRound : - kCGLineCapSquare); -} - -static void -set_clip_mask (Drawable d, GC gc) -{ - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); - - Pixmap p = gc->gcv.clip_mask; - if (!p) return; - Assert (p->type == PIXMAP, "not a pixmap"); - - CGRect wr = d->frame; - CGRect to; - to.origin.x = wr.origin.x + gc->gcv.clip_x_origin; - to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin - - p->frame.size.height; - to.size.width = p->frame.size.width; - to.size.height = p->frame.size.height; - - CGContextClipToMask (d->cgc, to, gc->clip_mask); -} - - -/* Pushes a GC context; sets BlendMode and ClipMask. - */ -static void -push_gc (Drawable d, GC gc) -{ - CGContextRef cgc = d->cgc; - CGContextSaveGState (cgc); - - switch (gc->gcv.function) { - case GXset: - case GXclear: - case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break; - case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break; - case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break; - case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break; - default: Assert(0, "unknown gcv function"); break; - } - - if (gc->gcv.clip_mask) - set_clip_mask (d, gc); -} - -#define pop_gc(d,gc) CGContextRestoreGState (d->cgc) - - -/* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors. - */ -static void -push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color, - BOOL antialias_p, Bool fill_p) -{ - push_gc (d, gc); - - int depth = gc->depth; - switch (gc->gcv.function) { - case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break; - case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break; - } - - CGContextRef cgc = d->cgc; - set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p); - CGContextSetShouldAntialias (cgc, antialias_p); -} - - -/* Pushes a GC context; sets Fill and Stroke colors to the foreground color. - */ -static void -push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p) -{ - push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p); -} - -/* Pushes a GC context; sets Fill and Stroke colors to the background color. - */ -static void -push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p) -{ - push_color_gc (dpy, d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p); -} - -static Bool -bitmap_context_p (Drawable d) -{ -# ifdef USE_BACKBUFFER - return True; -# else - // Because of the backbuffer, all iPhone Windows work like Pixmaps. - return d->type == PIXMAP; -# endif -} - - - -/* You've got to be fucking kidding me! - - It is *way* faster to draw points by creating and drawing a 1x1 CGImage - with repeated calls to CGContextDrawImage than it is to make a single - call to CGContextFillRects() with a list of 1x1 rectangles! - - I still wouldn't call it *fast*, however... - */ -#define XDRAWPOINTS_IMAGES - -/* Update, 2012: Kurt Revis points out that diddling - the bitmap data directly is faster. This only works on Pixmaps, though, - not Windows. (Fortunately, on iOS, the Window is really a Pixmap.) - */ -#define XDRAWPOINTS_CGDATA - -int -XDrawPoints (Display *dpy, Drawable d, GC gc, - XPoint *points, int count, int mode) -{ - int i; - CGRect wr = d->frame; - -# ifdef XDRAWPOINTS_CGDATA - - if (bitmap_context_p (d)) - { - CGContextRef cgc = d->cgc; - void *data = CGBitmapContextGetData (cgc); - size_t bpr = CGBitmapContextGetBytesPerRow (cgc); - size_t w = CGBitmapContextGetWidth (cgc); - size_t h = CGBitmapContextGetHeight (cgc); - - Assert (data, "no bitmap data in Drawable"); - - unsigned long argb = gc->gcv.foreground; - validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p); - if (gc->depth == 1) - argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0)); - - CGFloat x0 = wr.origin.x; - CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped. - - // It's uglier, but faster, to hoist the conditional out of the loop. - if (mode == CoordModePrevious) { - CGFloat x = x0, y = y0; - for (i = 0; i < count; i++, points++) { - x += points->x; - y += points->y; - - if (x >= 0 && x < w && y >= 0 && y < h) { - unsigned int *p = (unsigned int *) - ((char *) data + (size_t) y * bpr + (size_t) x * 4); - *p = (unsigned int) argb; - } - } - } else { - for (i = 0; i < count; i++, points++) { - CGFloat x = x0 + points->x; - CGFloat y = y0 + points->y; - - if (x >= 0 && x < w && y >= 0 && y < h) { - unsigned int *p = (unsigned int *) - ((char *) data + (size_t) y * bpr + (size_t) x * 4); - *p = (unsigned int) argb; - } - } - } - - } else /* d->type == WINDOW */ - -# endif /* XDRAWPOINTS_CGDATA */ - { - push_fg_gc (dpy, d, gc, YES); - -# ifdef XDRAWPOINTS_IMAGES - - unsigned int argb = gc->gcv.foreground; - validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p); - if (gc->depth == 1) - argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0)); - - CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, - NULL); - CGImageRef cgi = CGImageCreate (1, 1, - 8, 32, 4, - dpy->colorspace, - /* Host-ordered, since we're using the - address of an int as the color data. */ - dpy->screen->bitmap_info, - prov, - NULL, /* decode[] */ - NO, /* interpolate */ - kCGRenderingIntentDefault); - CGDataProviderRelease (prov); - - CGContextRef cgc = d->cgc; - CGRect rect; - rect.size.width = rect.size.height = 1; - for (i = 0; i < count; i++) { - if (i > 0 && mode == CoordModePrevious) { - rect.origin.x += points->x; - rect.origin.x -= points->y; - } else { - rect.origin.x = wr.origin.x + points->x; - rect.origin.y = wr.origin.y + wr.size.height - points->y - 1; - } - - //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace"); - CGContextDrawImage (cgc, rect, cgi); - points++; - } - - CGImageRelease (cgi); - -# else /* ! XDRAWPOINTS_IMAGES */ - - CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect)); - CGRect *r = rects; - - for (i = 0; i < count; i++) { - r->size.width = r->size.height = 1; - if (i > 0 && mode == CoordModePrevious) { - r->origin.x = r[-1].origin.x + points->x; - r->origin.y = r[-1].origin.x - points->y; - } else { - r->origin.x = wr.origin.x + points->x; - r->origin.y = wr.origin.y + wr.size.height - points->y; - } - points++; - r++; - } - - CGContextFillRects (d->cgc, rects, count); - free (rects); - -# endif /* ! XDRAWPOINTS_IMAGES */ - - pop_gc (d, gc); - } - - invalidate_drawable_cache (d); - - return 0; -} - - -int -XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y) -{ - XPoint p; - p.x = x; - p.y = y; - return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin); -} - - -static void draw_rects (Display *dpy, Drawable d, GC gc, - const XRectangle *rectangles, unsigned nrectangles, - unsigned long pixel, BOOL fill_p); - -static void draw_rect (Display *, Drawable, GC, - int x, int y, unsigned int width, unsigned int height, - unsigned long pixel, BOOL fill_p); - -static void * -seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y) -{ - return (char *)dst + dst_pitch * y + x * 4; -} - -static unsigned int -drawable_depth (Drawable d) -{ - return (d->type == WINDOW - ? visual_depth (NULL, NULL) - : d->pixmap.depth); -} - - -int -XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, - int src_x, int src_y, - unsigned int width, unsigned int height, - int dst_x, int dst_y) -{ - Assert (gc, "no GC"); - Assert ((width < 65535), "improbably large width"); - Assert ((height < 65535), "improbably large height"); - Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x"); - Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y"); - Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x"); - Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y"); - - if (width == 0 || height == 0) - return 0; - - if (gc->gcv.function == GXset || - gc->gcv.function == GXclear) { - // "set" and "clear" are dumb drawing modes that ignore the source - // bits and just draw solid rectangles. - draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, - (gc->gcv.function == GXset - ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0)) - : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES); - return 0; - } - - CGRect src_frame, dst_frame; // Sizes and origins of the two drawables - CGRect src_rect, dst_rect; // The two rects to draw, clipped to the - // bounds of their drawables. - BOOL clipped = NO; // Whether we did any clipping of the rects. - - src_frame = src->frame; - dst_frame = dst->frame; - - // Initialize src_rect... - // - src_rect.origin.x = src_frame.origin.x + src_x; - src_rect.origin.y = src_frame.origin.y + src_frame.size.height - - height - src_y; - if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts"); - src_rect.size.width = width; - src_rect.size.height = height; - - // Initialize dst_rect... - // - dst_rect.origin.x = dst_frame.origin.x + dst_x; - dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height - - height - dst_y; - if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts"); - dst_rect.size.width = width; - dst_rect.size.height = height; - - // Clip rects to frames... - // - -# define CLIP(THIS,THAT,VAL,SIZE) do { \ - float off = THIS##_rect.origin.VAL; \ - if (off < 0) { \ - clipped = YES; \ - THIS##_rect.size.SIZE += off; \ - THAT##_rect.size.SIZE += off; \ - THIS##_rect.origin.VAL -= off; \ - THAT##_rect.origin.VAL -= off; \ - } \ - off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \ - (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \ - if (off > 0) { \ - clipped = YES; \ - THIS##_rect.size.SIZE -= off; \ - THAT##_rect.size.SIZE -= off; \ - }} while(0) - - CLIP (dst, src, x, width); - CLIP (dst, src, y, height); - - // Not actually the original dst_rect, just the one before it's clipped to - // the src_frame. - CGRect orig_dst_rect = dst_rect; - - CLIP (src, dst, x, width); - CLIP (src, dst, y, height); -# undef CLIP - - if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0) - return 0; - - // Sort-of-special case where no pixels can be grabbed from the source, - // and the whole destination is filled with the background color. - if (src_rect.size.width < 0 || src_rect.size.height < 0) { - - Assert((int)src_rect.size.width == (int)dst_rect.size.width || - (int)src_rect.size.height == (int)dst_rect.size.height, - "size mismatch"); - - src_rect.size.width = 0; - src_rect.size.height = 0; - dst_rect.size.width = 0; - dst_rect.size.height = 0; - } - - BOOL mask_p = src->type == PIXMAP && src->pixmap.depth == 1; - - - /* If we're copying from a bitmap to a bitmap, and there's nothing funny - going on with clipping masks or depths or anything, optimize it by - just doing a memcpy instead of going through a CGI. - */ - if (bitmap_context_p (src) && - bitmap_context_p (dst) && - gc->gcv.function == GXcopy && - !gc->gcv.clip_mask && - drawable_depth (src) == drawable_depth (dst)) { - - Assert(!(int)src_frame.origin.x && - !(int)src_frame.origin.y && - !(int)dst_frame.origin.x && - !(int)dst_frame.origin.y, - "unexpected non-zero origin"); - - char *src_data = CGBitmapContextGetData(src->cgc); - char *dst_data = CGBitmapContextGetData(dst->cgc); - size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc); - size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc); - - // Int to float and back again. It's not very safe, but it seems to work. - int src_x0 = src_rect.origin.x; - int dst_x0 = dst_rect.origin.x; - - // Flip the Y-axis a second time. - int src_y0 = (src_frame.origin.y + src_frame.size.height - - src_rect.size.height - src_rect.origin.y); - int dst_y0 = (dst_frame.origin.y + dst_frame.size.height - - dst_rect.size.height - dst_rect.origin.y); - - unsigned width0 = (int) src_rect.size.width; - unsigned height0 = (int) src_rect.size.height; - - Assert((int)src_rect.size.width == (int)dst_rect.size.width || - (int)src_rect.size.height == (int)dst_rect.size.height, - "size mismatch"); - { - char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0); - char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0); - size_t src_pitch0 = src_pitch; - size_t dst_pitch0 = dst_pitch; - size_t bytes = width0 * 4; - - if (src == dst && dst_y0 > src_y0) { - // Copy upwards if the areas might overlap. - src_data0 += src_pitch0 * (height0 - 1); - dst_data0 += dst_pitch0 * (height0 - 1); - src_pitch0 = -src_pitch0; - dst_pitch0 = -dst_pitch0; - } - - size_t lines0 = height0; - while (lines0) { - // memcpy is an alias for memmove on OS X. - memmove(dst_data0, src_data0, bytes); - src_data0 += src_pitch0; - dst_data0 += dst_pitch0; - --lines0; - } - } -# ifndef USE_BACKBUFFER - } else if (src->type == WINDOW && src == dst && !mask_p) { - - // If we are copying from a window to itself, we can use NSCopyBits() - // without first copying the rectangle to an intermediary CGImage. - // This is ~28% faster (but I *expected* it to be twice as fast...) - // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack) - // - - push_gc (dst, gc); - - NSRect nsfrom; - nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4 - nsfrom.origin.y = src_rect.origin.y; - nsfrom.size.width = src_rect.size.width; - nsfrom.size.height = src_rect.size.height; - NSPoint nsto; - nsto.x = dst_rect.origin.x; - nsto.y = dst_rect.origin.y; - NSCopyBits (0, nsfrom, nsto); - - pop_gc (dst, gc); - -# endif - } else { - - NSObject *releaseme = 0; - CGImageRef cgi; - BOOL free_cgi_p = NO; - - if (bitmap_context_p (src)) { - - // If we are copying from a Pixmap to a Pixmap or Window, we must first - // copy the bits to an intermediary CGImage object, then copy that to the - // destination drawable's CGContext. - // - // (It doesn't seem to be possible to use NSCopyBits() to optimize the - // case of copying from a Pixmap back to itself, but I don't think that - // happens very often anyway.) - // - // First we get a CGImage out of the pixmap CGContext -- it's the whole - // pixmap, but it presumably shares the data pointer instead of copying - // it. We then cache that CGImage it inside the Pixmap object. Note: - // invalidate_drawable_cache() must be called to discard this any time a - // modification is made to the pixmap, or we'll end up re-using old bits. - // - if (!src->cgi) - src->cgi = CGBitmapContextCreateImage (src->cgc); - cgi = src->cgi; - - // if doing a sub-rect, trim it down. - if (src_rect.origin.x != src_frame.origin.x || - src_rect.origin.y != src_frame.origin.y || - src_rect.size.width != src_frame.size.width || - src_rect.size.height != src_frame.size.height) { - // #### I don't understand why this is needed... - src_rect.origin.y = (src_frame.size.height - - src_rect.size.height - src_rect.origin.y); - // This does not copy image data, so it should be fast. - cgi = CGImageCreateWithImageInRect (cgi, src_rect); - free_cgi_p = YES; - } - -# ifndef USE_BACKBUFFER - } else { /* (src->type == WINDOW) */ - - NSRect nsfrom; // NSRect != CGRect on 10.4 - nsfrom.origin.x = src_rect.origin.x; - nsfrom.origin.y = src_rect.origin.y; - nsfrom.size.width = src_rect.size.width; - nsfrom.size.height = src_rect.size.height; - - // If we are copying from a Window to a Pixmap, we must first copy - // the bits to an intermediary CGImage object, then copy that to the - // Pixmap's CGContext. - // - NSBitmapImageRep *bm = [[NSBitmapImageRep alloc] - initWithFocusedViewRect:nsfrom]; - unsigned char *data = [bm bitmapData]; - int bps = [bm bitsPerSample]; - int bpp = [bm bitsPerPixel]; - int bpl = [bm bytesPerRow]; - releaseme = bm; - - // create a CGImage from those bits. - // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased) - // but that method didn't exist in 10.4.) - - CGDataProviderRef prov = - CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height, - NULL); - cgi = CGImageCreate (src_rect.size.width, src_rect.size.height, - bps, bpp, bpl, - dpy->colorspace, - /* Use whatever default bit ordering we got from - initWithFocusedViewRect. I would have assumed - that it was (kCGImageAlphaNoneSkipFirst | - kCGBitmapByteOrder32Host), but on Intel, - it's not! - */ - 0, - prov, - NULL, /* decode[] */ - NO, /* interpolate */ - kCGRenderingIntentDefault); - free_cgi_p = YES; - //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace"); - CGDataProviderRelease (prov); - -# endif // !USE_BACKBUFFER - } - - CGContextRef cgc = dst->cgc; - - if (mask_p) { // src depth == 1 - - push_bg_gc (dpy, dst, gc, YES); - - // fill the destination rectangle with solid background... - CGContextFillRect (cgc, dst_rect); - - Assert (cgc, "no CGC with 1-bit XCopyArea"); - - // then fill in a solid rectangle of the fg color, using the image as an - // alpha mask. (the image has only values of BlackPixel or WhitePixel.) - set_color (dpy, cgc, gc->gcv.foreground, gc->depth, - gc->gcv.alpha_allowed_p, YES); - CGContextClipToMask (cgc, dst_rect, cgi); - CGContextFillRect (cgc, dst_rect); - - pop_gc (dst, gc); - - } else { // src depth > 1 - - push_gc (dst, gc); - - // copy the CGImage onto the destination CGContext - //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (cgc, dst_rect, cgi); - - pop_gc (dst, gc); - } - - if (free_cgi_p) CGImageRelease (cgi); - - if (releaseme) [releaseme release]; - } - - // If either the src or dst rects did not lie within their drawables, then - // we have adjusted both the src and dst rects to account for the clipping; - // that means we need to clear to the background, so that clipped bits end - // up in the bg color instead of simply not being copied. - // - // This has to happen after the copy, because if it happens before, the - // cleared area will get grabbed if it overlaps with the source rectangle. - // - if (clipped && dst->type == WINDOW) { - // Int to float and back again. It's not very safe, but it seems to work. - int dst_x0 = dst_rect.origin.x; - - // Flip the Y-axis a second time. - int dst_y0 = (dst_frame.origin.y + dst_frame.size.height - - dst_rect.size.height - dst_rect.origin.y); - - unsigned width0 = (int) src_rect.size.width; - unsigned height0 = (int) src_rect.size.height; - - int orig_dst_x = orig_dst_rect.origin.x; - int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height - - orig_dst_rect.origin.y - orig_dst_rect.size.height); - int orig_width = orig_dst_rect.size.width; - int orig_height = orig_dst_rect.size.height; - - Assert (orig_dst_x >= 0 && - orig_dst_x + orig_width <= (int) dst_frame.size.width && - orig_dst_y >= 0 && - orig_dst_y + orig_height <= (int) dst_frame.size.height, - "wrong dimensions"); - - XRectangle rects[4]; - XRectangle *rects_end = rects; - - if (orig_dst_y < dst_y0) { - rects_end->x = orig_dst_x; - rects_end->y = orig_dst_y; - rects_end->width = orig_width; - rects_end->height = dst_y0 - orig_dst_y; - ++rects_end; - } - - if (orig_dst_y + orig_height > dst_y0 + height0) { - rects_end->x = orig_dst_x; - rects_end->y = dst_y0 + height0; - rects_end->width = orig_width; - rects_end->height = orig_dst_y + orig_height - dst_y0 - height0; - ++rects_end; - } - - if (orig_dst_x < dst_x0) { - rects_end->x = orig_dst_x; - rects_end->y = dst_y0; - rects_end->width = dst_x0 - orig_dst_x; - rects_end->height = height0; - ++rects_end; - } - - if (dst_x0 + width0 < orig_dst_x + orig_width) { - rects_end->x = dst_x0 + width0; - rects_end->y = dst_y0; - rects_end->width = orig_dst_x + orig_width - dst_x0 - width0; - rects_end->height = height0; - ++rects_end; - } - - unsigned long old_function = gc->gcv.function; - gc->gcv.function = GXcopy; - draw_rects (dpy, dst, gc, rects, rects_end - rects, - dst->window.background, - YES); - gc->gcv.function = old_function; - } - - invalidate_drawable_cache (dst); - return 0; -} - - -int -XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc, - int src_x, int src_y, - unsigned width, int height, - int dest_x, int dest_y, unsigned long plane) -{ - Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!"); - - // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg, - // not to white/black. - return XCopyArea (dpy, src, dest, gc, - src_x, src_y, width, height, dest_x, dest_y); -} - - -static CGPoint -map_point (Drawable d, int x, int y) -{ - const CGRect *wr = &d->frame; - CGPoint p; - p.x = wr->origin.x + x; - p.y = wr->origin.y + wr->size.height - y; - return p; -} - - -static void -adjust_point_for_line (GC gc, CGPoint *p) -{ - // Here's the authoritative discussion on how X draws lines: - // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width - if (gc->gcv.line_width <= 1) { - /* Thin lines are "drawn using an unspecified, device-dependent - algorithm", but seriously though, Bresenham's algorithm. Bresenham's - algorithm runs to and from pixel centers. - - There's a few screenhacks (Maze, at the very least) that set line_width - to 1 when it probably should be set to 0, so it's line_width <= 1 - instead of < 1. - */ - p->x += 0.5; - p->y -= 0.5; - } else { - /* Thick lines OTOH run from the upper-left corners of pixels. This means - that a horizontal thick line of width 1 straddles two scan lines. - Aliasing requires one of these scan lines be chosen; the following - nudges the point so that the right choice is made. */ - p->y -= 1e-3; - } -} - - -static CGPoint -point_for_line (Drawable d, GC gc, int x, int y) -{ - CGPoint result = map_point (d, x, y); - adjust_point_for_line (gc, &result); - return result; -} - - -int -XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) -{ - // when drawing a zero-length line, obey line-width and cap-style. - if (x1 == x2 && y1 == y2) { - int w = gc->gcv.line_width; - x1 -= w/2; - y1 -= w/2; - if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound) - return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64); - else { - if (!w) - w = 1; // Actually show zero-length lines. - return XFillRectangle (dpy, d, gc, x1, y1, w, w); - } - } - - CGPoint p = point_for_line (d, gc, x1, y1); - - push_fg_gc (dpy, d, gc, NO); - - CGContextRef cgc = d->cgc; - set_line_mode (cgc, &gc->gcv); - CGContextBeginPath (cgc); - CGContextMoveToPoint (cgc, p.x, p.y); - p = point_for_line(d, gc, x2, y2); - CGContextAddLineToPoint (cgc, p.x, p.y); - CGContextStrokePath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); - return 0; -} - -int -XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, - int mode) -{ - int i; - CGPoint p; - push_fg_gc (dpy, d, gc, NO); - - CGContextRef cgc = d->cgc; - - set_line_mode (cgc, &gc->gcv); - - // if the first and last points coincide, use closepath to get - // the proper line-joining. - BOOL closed_p = (points[0].x == points[count-1].x && - points[0].y == points[count-1].y); - if (closed_p) count--; - - p = point_for_line(d, gc, points->x, points->y); - points++; - CGContextBeginPath (cgc); - CGContextMoveToPoint (cgc, p.x, p.y); - for (i = 1; i < count; i++) { - if (mode == CoordModePrevious) { - p.x += points->x; - p.y -= points->y; - } else { - p = point_for_line(d, gc, points->x, points->y); - } - CGContextAddLineToPoint (cgc, p.x, p.y); - points++; - } - if (closed_p) CGContextClosePath (cgc); - CGContextStrokePath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); - return 0; -} - - -int -XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) -{ - int i; - - CGContextRef cgc = d->cgc; - - push_fg_gc (dpy, d, gc, NO); - set_line_mode (cgc, &gc->gcv); - CGContextBeginPath (cgc); - for (i = 0; i < count; i++) { - CGPoint p = point_for_line (d, gc, segments->x1, segments->y1); - CGContextMoveToPoint (cgc, p.x, p.y); - p = point_for_line (d, gc, segments->x2, segments->y2); - CGContextAddLineToPoint (cgc, p.x, p.y); - segments++; - } - CGContextStrokePath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); - return 0; -} - - -int -XClearWindow (Display *dpy, Window win) -{ - Assert (win && win->type == WINDOW, "not a window"); - CGRect wr = win->frame; - return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0); -} - -int -XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) -{ - Assert (w && w->type == WINDOW, "not a window"); - validate_pixel (dpy, pixel, 32, NO); - w->window.background = pixel; - return 0; -} - -static void -draw_rects (Display *dpy, Drawable d, GC gc, - const XRectangle *rectangles, unsigned nrectangles, - unsigned long pixel, BOOL fill_p) -{ - Assert (!gc || gc->depth == drawable_depth (d), "depth mismatch"); - - CGContextRef cgc = d->cgc; - - Bool fast_fill_p = - fill_p && - bitmap_context_p (d) && - (!gc || (gc->gcv.function == GXcopy && - !gc->gcv.alpha_allowed_p && - !gc->gcv.clip_mask)); - - if (!fast_fill_p) { - if (gc) { - push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, fill_p); - if (!fill_p) - set_line_mode (cgc, &gc->gcv); - } else { - set_color (dpy, d->cgc, pixel, drawable_depth (d), NO, fill_p); - } - } - - for (unsigned i = 0; i != nrectangles; ++i) { - - int x = rectangles[i].x; - int y = rectangles[i].y; - int width = rectangles[i].width; - int height = rectangles[i].height; - - if (fast_fill_p) { - int - dw = CGBitmapContextGetWidth (cgc), - dh = CGBitmapContextGetHeight (cgc); - - if (x >= dw || y >= dh) - continue; - - if (x < 0) { - width += x; - x = 0; - } - - if (y < 0) { - height += y; - y = 0; - } - - if (width <= 0 || height <= 0) - continue; - - int max_width = dw - x; - if (width > max_width) - width = max_width; - int max_height = dh - y; - if (height > max_height) - height = max_height; - - if (drawable_depth (d) == 1) - pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0); - - size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc); - void *dst = seek_xy (CGBitmapContextGetData (d->cgc), - dst_bytes_per_row, x, y); - - Assert(sizeof(wchar_t) == 4, "somebody changed the ABI"); - while (height) { - // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday. - wmemset (dst, pixel, width); - --height; - dst = (char *) dst + dst_bytes_per_row; - } - - } else { - CGRect r; - r.origin = map_point (d, x, y); - r.origin.y -= height; - r.size.width = width; - r.size.height = height; - if (fill_p) - CGContextFillRect (cgc, r); - else { - adjust_point_for_line (gc, &r.origin); - CGContextStrokeRect (cgc, r); - } - } - } - - if (!fast_fill_p && gc) - pop_gc (d, gc); - invalidate_drawable_cache (d); -} - -static void -draw_rect (Display *dpy, Drawable d, GC gc, - int x, int y, unsigned int width, unsigned int height, - unsigned long pixel, BOOL fill_p) -{ - XRectangle r = {x, y, width, height}; - draw_rects (dpy, d, gc, &r, 1, pixel, fill_p); -} - -int -XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, - unsigned int width, unsigned int height) -{ - draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, YES); - return 0; -} - -int -XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, - unsigned int width, unsigned int height) -{ - draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, NO); - return 0; -} - -int -XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) -{ - draw_rects (dpy, d, gc, rects, n, gc->gcv.foreground, YES); - return 0; -} - - -int -XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) -{ - Assert (win && win->type == WINDOW, "not a window"); - draw_rect (dpy, win, 0, x, y, w, h, win->window.background, YES); - return 0; -} - - -int -XFillPolygon (Display *dpy, Drawable d, GC gc, - XPoint *points, int npoints, int shape, int mode) -{ - CGRect wr = d->frame; - int i; - push_fg_gc (dpy, d, gc, YES); - CGContextRef cgc = d->cgc; - CGContextBeginPath (cgc); - float x = 0, y = 0; - for (i = 0; i < npoints; i++) { - if (i > 0 && mode == CoordModePrevious) { - x += points[i].x; - y -= points[i].y; - } else { - x = wr.origin.x + points[i].x; - y = wr.origin.y + wr.size.height - points[i].y; - } - - if (i == 0) - CGContextMoveToPoint (cgc, x, y); - else - CGContextAddLineToPoint (cgc, x, y); - } - CGContextClosePath (cgc); - if (gc->gcv.fill_rule == EvenOddRule) - CGContextEOFillPath (cgc); - else - CGContextFillPath (cgc); - pop_gc (d, gc); - invalidate_drawable_cache (d); - return 0; -} - -#define radians(DEG) ((DEG) * M_PI / 180.0) -#define degrees(RAD) ((RAD) * 180.0 / M_PI) - -static int -draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, - unsigned int width, unsigned int height, int angle1, int angle2, - BOOL fill_p) -{ - CGRect wr = d->frame; - CGRect bound; - bound.origin.x = wr.origin.x + x; - bound.origin.y = wr.origin.y + wr.size.height - y - height; - bound.size.width = width; - bound.size.height = height; - - CGPoint ctr; - ctr.x = bound.origin.x + bound.size.width /2; - ctr.y = bound.origin.y + bound.size.height/2; - - float r1 = radians (angle1/64.0); - float r2 = radians (angle2/64.0) + r1; - BOOL clockwise = angle2 < 0; - BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64); - - push_fg_gc (dpy, d, gc, fill_p); - - CGContextRef cgc = d->cgc; - CGContextBeginPath (cgc); - - CGContextSaveGState(cgc); - CGContextTranslateCTM (cgc, ctr.x, ctr.y); - CGContextScaleCTM (cgc, width/2.0, height/2.0); - if (fill_p) - CGContextMoveToPoint (cgc, 0, 0); - - CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise); - CGContextRestoreGState (cgc); // restore before stroke, for line width - - if (closed_p) - CGContextClosePath (cgc); // for proper line joining - - if (fill_p) { - CGContextFillPath (cgc); - } else { - set_line_mode (cgc, &gc->gcv); - CGContextStrokePath (cgc); - } - - pop_gc (d, gc); - invalidate_drawable_cache (d); - return 0; -} - -int -XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y, - unsigned int width, unsigned int height, int angle1, int angle2) -{ - return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO); -} - -int -XFillArc (Display *dpy, Drawable d, GC gc, int x, int y, - unsigned int width, unsigned int height, int angle1, int angle2) -{ - return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES); -} - -int -XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) -{ - int i; - for (i = 0; i < narcs; i++) - draw_arc (dpy, d, gc, - arcs[i].x, arcs[i].y, - arcs[i].width, arcs[i].height, - arcs[i].angle1, arcs[i].angle2, - NO); - return 0; -} - -int -XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) -{ - int i; - for (i = 0; i < narcs; i++) - draw_arc (dpy, d, gc, - arcs[i].x, arcs[i].y, - arcs[i].width, arcs[i].height, - arcs[i].angle1, arcs[i].angle2, - YES); - return 0; -} - - -static void -gcv_defaults (Display *dpy, XGCValues *gcv, int depth) -{ - memset (gcv, 0, sizeof(*gcv)); - gcv->function = GXcopy; - gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0)); - gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0)); - gcv->line_width = 1; - gcv->cap_style = CapNotLast; - gcv->join_style = JoinMiter; - gcv->fill_rule = EvenOddRule; - - gcv->alpha_allowed_p = NO; - gcv->antialias_p = YES; -} - -static void -set_gcv (Display *dpy, GC gc, XGCValues *from, unsigned long mask) -{ - if (! mask) return; - Assert (gc && from, "no gc"); - if (!gc || !from) return; - - if (mask & GCFunction) gc->gcv.function = from->function; - if (mask & GCForeground) gc->gcv.foreground = from->foreground; - if (mask & GCBackground) gc->gcv.background = from->background; - if (mask & GCLineWidth) gc->gcv.line_width = from->line_width; - if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style; - if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style; - if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule; - if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin; - if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin; - if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode; - - if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask); - if (mask & GCFont) XSetFont (0, gc, from->font); - - if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth, - gc->gcv.alpha_allowed_p); - if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth, - gc->gcv.alpha_allowed_p); - - Assert ((! (mask & (GCLineStyle | - GCPlaneMask | - GCFillStyle | - GCTile | - GCStipple | - GCTileStipXOrigin | - GCTileStipYOrigin | - GCGraphicsExposures | - GCDashOffset | - GCDashList | - GCArcMode))), - "unimplemented gcvalues mask"); -} - - -GC -XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) -{ - struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc)); - gc->depth = drawable_depth (d); - - gcv_defaults (dpy, &gc->gcv, gc->depth); - set_gcv (dpy, gc, xgcv, mask); - return gc; -} - -int -XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv) -{ - set_gcv (dpy, gc, gcv, mask); - return 0; -} - - -int -XFreeGC (Display *dpy, GC gc) -{ - if (gc->gcv.font) - XUnloadFont (dpy, gc->gcv.font); - - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); - - if (gc->gcv.clip_mask) { - XFreePixmap (dpy, gc->gcv.clip_mask); - CGImageRelease (gc->clip_mask); - } - free (gc); - return 0; -} - - -Status -XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa) -{ - Assert (w && w->type == WINDOW, "not a window"); - memset (xgwa, 0, sizeof(*xgwa)); - xgwa->x = w->frame.origin.x; - xgwa->y = w->frame.origin.y; - xgwa->width = w->frame.size.width; - xgwa->height = w->frame.size.height; - xgwa->depth = 32; - xgwa->screen = dpy->screen; - xgwa->visual = dpy->screen->visual; - return 0; -} - -Status -XGetGeometry (Display *dpy, Drawable d, Window *root_ret, - int *x_ret, int *y_ret, - unsigned int *w_ret, unsigned int *h_ret, - unsigned int *bw_ret, unsigned int *d_ret) -{ - *x_ret = d->frame.origin.x; - *y_ret = d->frame.origin.y; - *w_ret = d->frame.size.width; - *h_ret = d->frame.size.height; - *d_ret = drawable_depth (d); - *root_ret = RootWindow (dpy, 0); - *bw_ret = 0; - return True; -} - - -Status -XAllocColor (Display *dpy, Colormap cmap, XColor *color) -{ - color->pixel = alloc_color (dpy, - color->red, color->green, color->blue, 0xFFFF); - return 1; -} - -Status -XAllocColorCells (Display *dpy, Colormap cmap, Bool contig, - unsigned long *pmret, unsigned int npl, - unsigned long *pxret, unsigned int npx) -{ - return 0; -} - -int -XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n) -{ - Assert(0, "XStoreColors called"); - return 0; -} - -int -XStoreColor (Display *dpy, Colormap cmap, XColor *c) -{ - Assert(0, "XStoreColor called"); - return 0; -} - -int -XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels, - unsigned long planes) -{ - return 0; -} - -Status -XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret) -{ - unsigned char r=0, g=0, b=0; - if (*spec == '#' && strlen(spec) == 7) { - static unsigned const char hex[] = { // yeah yeah, shoot me. - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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}; - r = (hex[spec[1]] << 4) | hex[spec[2]]; - g = (hex[spec[3]] << 4) | hex[spec[4]]; - b = (hex[spec[5]] << 4) | hex[spec[6]]; - } else if (!strcasecmp(spec,"black")) { -// r = g = b = 0; - } else if (!strcasecmp(spec,"white")) { - r = g = b = 255; - } else if (!strcasecmp(spec,"red")) { - r = 255; - } else if (!strcasecmp(spec,"green")) { - g = 255; - } else if (!strcasecmp(spec,"blue")) { - b = 255; - } else if (!strcasecmp(spec,"cyan")) { - g = b = 255; - } else if (!strcasecmp(spec,"magenta")) { - r = b = 255; - } else if (!strcasecmp(spec,"yellow")) { - r = g = 255; - } else { - return 0; - } - - ret->red = (r << 8) | r; - ret->green = (g << 8) | g; - ret->blue = (b << 8) | b; - ret->flags = DoRed|DoGreen|DoBlue; - return 1; -} - -Status -XAllocNamedColor (Display *dpy, Colormap cmap, char *name, - XColor *screen_ret, XColor *exact_ret) -{ - if (! XParseColor (dpy, cmap, name, screen_ret)) - return False; - *exact_ret = *screen_ret; - return XAllocColor (dpy, cmap, screen_ret); -} - -int -XQueryColor (Display *dpy, Colormap cmap, XColor *color) -{ - validate_pixel (dpy, color->pixel, 32, NO); - uint8_t rgba[4]; - query_color(dpy, color->pixel, rgba); - color->red = (rgba[0] << 8) | rgba[0]; - color->green = (rgba[1] << 8) | rgba[1]; - color->blue = (rgba[2] << 8) | rgba[2]; - color->flags = DoRed|DoGreen|DoBlue; - return 0; -} - -int -XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n) -{ - int i; - for (i = 0; i < n; i++) - XQueryColor (dpy, cmap, &c[i]); - return 0; -} - - -static unsigned long -ximage_getpixel_1 (XImage *ximage, int x, int y) -{ - return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1); -} - -static int -ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel) -{ - if (pixel) - ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7)); - else - ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7)); - - return 0; -} - -static unsigned long -ximage_getpixel_32 (XImage *ximage, int x, int y) -{ - return ((unsigned long) - *((uint32_t *) ximage->data + - (y * (ximage->bytes_per_line >> 2)) + - x)); -} - -static int -ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel) -{ - *((uint32_t *) ximage->data + - (y * (ximage->bytes_per_line >> 2)) + - x) = (uint32_t) pixel; - return 0; -} - - -Status -XInitImage (XImage *ximage) -{ - if (!ximage->bytes_per_line) - ximage->bytes_per_line = (ximage->depth == 1 - ? (ximage->width + 7) / 8 - : ximage->width * 4); - - if (ximage->depth == 1) { - ximage->f.put_pixel = ximage_putpixel_1; - ximage->f.get_pixel = ximage_getpixel_1; - } else if (ximage->depth == 32 || ximage->depth == 24) { - ximage->f.put_pixel = ximage_putpixel_32; - ximage->f.get_pixel = ximage_getpixel_32; - } else { - Assert (0, "unknown depth"); - } - return 1; -} - - -XImage * -XCreateImage (Display *dpy, Visual *visual, unsigned int depth, - int format, int offset, char *data, - unsigned int width, unsigned int height, - int bitmap_pad, int bytes_per_line) -{ - XImage *ximage = (XImage *) calloc (1, sizeof(*ximage)); - ximage->width = width; - ximage->height = height; - ximage->format = format; - ximage->data = data; - ximage->bitmap_unit = 8; - ximage->byte_order = LSBFirst; - ximage->bitmap_bit_order = ximage->byte_order; - ximage->bitmap_pad = bitmap_pad; - ximage->depth = depth; - ximage->red_mask = (depth == 1 ? 0 : dpy->screen->visual->red_mask); - ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask); - ximage->blue_mask = (depth == 1 ? 0 : dpy->screen->visual->blue_mask); - ximage->bits_per_pixel = (depth == 1 ? 1 : 32); - ximage->bytes_per_line = bytes_per_line; - - XInitImage (ximage); - return ximage; -} - -XImage * -XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h) -{ - XImage *to = (XImage *) malloc (sizeof(*to)); - memcpy (to, from, sizeof(*from)); - to->width = w; - to->height = h; - to->bytes_per_line = 0; - XInitImage (to); - - to->data = (char *) malloc (h * to->bytes_per_line); - - if (x >= from->width) - w = 0; - else if (x+w > from->width) - w = from->width - x; - - if (y >= from->height) - h = 0; - else if (y+h > from->height) - h = from->height - y; - - int tx, ty; - for (ty = 0; ty < h; ty++) - for (tx = 0; tx < w; tx++) - XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty)); - return to; -} - - -XPixmapFormatValues * -XListPixmapFormats (Display *dpy, int *n_ret) -{ - XPixmapFormatValues *ret = calloc (2, sizeof(*ret)); - ret[0].depth = 32; - ret[0].bits_per_pixel = 32; - ret[0].scanline_pad = 8; - ret[1].depth = 1; - ret[1].bits_per_pixel = 1; - ret[1].scanline_pad = 8; - *n_ret = 2; - return ret; -} - - -unsigned long -XGetPixel (XImage *ximage, int x, int y) -{ - return ximage->f.get_pixel (ximage, x, y); -} - - -int -XPutPixel (XImage *ximage, int x, int y, unsigned long pixel) -{ - return ximage->f.put_pixel (ximage, x, y, pixel); -} - -int -XDestroyImage (XImage *ximage) -{ - if (ximage->data) free (ximage->data); - free (ximage); - return 0; -} - - -static void -flipbits (unsigned const char *in, unsigned char *out, int length) -{ - static const unsigned char table[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF - }; - while (length-- > 0) - *out++ = table[*in++]; -} - - -int -XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, - int src_x, int src_y, int dest_x, int dest_y, - unsigned int w, unsigned int h) -{ - CGRect wr = d->frame; - - Assert (gc, "no GC"); - Assert ((w < 65535), "improbably large width"); - Assert ((h < 65535), "improbably large height"); - Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x"); - Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y"); - Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x"); - Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y"); - - // Clip width and height to the bounds of the Drawable - // - if (dest_x + w > wr.size.width) { - if (dest_x > wr.size.width) - return 0; - w = wr.size.width - dest_x; - } - if (dest_y + h > wr.size.height) { - if (dest_y > wr.size.height) - return 0; - h = wr.size.height - dest_y; - } - if (w <= 0 || h <= 0) - return 0; - - // Clip width and height to the bounds of the XImage - // - if (src_x + w > ximage->width) { - if (src_x > ximage->width) - return 0; - w = ximage->width - src_x; - } - if (src_y + h > ximage->height) { - if (src_y > ximage->height) - return 0; - h = ximage->height - src_y; - } - if (w <= 0 || h <= 0) - return 0; - - CGContextRef cgc = d->cgc; - - if (gc->gcv.function == GXset || - gc->gcv.function == GXclear) { - // "set" and "clear" are dumb drawing modes that ignore the source - // bits and just draw solid rectangles. - draw_rect (dpy, d, 0, dest_x, dest_y, w, h, - (gc->gcv.function == GXset - ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0)) - : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES); - return 0; - } - - int bpl = ximage->bytes_per_line; - int bpp = ximage->bits_per_pixel; - int bsize = bpl * h; - char *data = ximage->data; - - CGRect r; - r.origin.x = wr.origin.x + dest_x; - r.origin.y = wr.origin.y + wr.size.height - dest_y - h; - r.size.width = w; - r.size.height = h; - - if (bpp == 32) { - - /* Take advantage of the fact that it's ok for (bpl != w * bpp) - to create a CGImage from a sub-rectagle of the XImage. - */ - data += (src_y * bpl) + (src_x * 4); - CGDataProviderRef prov = - CGDataProviderCreateWithData (NULL, data, bsize, NULL); - - CGImageRef cgi = CGImageCreate (w, h, - bpp/4, bpp, bpl, - dpy->colorspace, - dpy->screen->bitmap_info, - prov, - NULL, /* decode[] */ - NO, /* interpolate */ - kCGRenderingIntentDefault); - CGDataProviderRelease (prov); - //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (cgc, r, cgi); - CGImageRelease (cgi); - - } else { // (bpp == 1) - - /* To draw a 1bpp image, we use it as a mask and fill two rectangles. - - #### However, the bit order within a byte in a 1bpp XImage is - the wrong way around from what Quartz expects, so first we - have to copy the data to reverse it. Shit! Maybe it - would be worthwhile to go through the hacks and #ifdef - each one that diddles 1bpp XImage->data directly... - */ - Assert ((src_x % 8) == 0, - "XPutImage with non-byte-aligned 1bpp X offset not implemented"); - - data += (src_y * bpl) + (src_x / 8); // move to x,y within the data - unsigned char *flipped = (unsigned char *) malloc (bsize); - - flipbits ((unsigned char *) data, flipped, bsize); - - CGDataProviderRef prov = - CGDataProviderCreateWithData (NULL, flipped, bsize, NULL); - CGImageRef mask = CGImageMaskCreate (w, h, - 1, bpp, bpl, - prov, - NULL, /* decode[] */ - NO); /* interpolate */ - push_fg_gc (dpy, d, gc, YES); - - CGContextFillRect (cgc, r); // foreground color - CGContextClipToMask (cgc, r, mask); - set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES); - CGContextFillRect (cgc, r); // background color - pop_gc (d, gc); - - free (flipped); - CGDataProviderRelease (prov); - CGImageRelease (mask); - } - - invalidate_drawable_cache (d); - - return 0; -} - - -XImage * -XGetImage (Display *dpy, Drawable d, int x, int y, - unsigned int width, unsigned int height, - unsigned long plane_mask, int format) -{ - const unsigned char *data = 0; - size_t depth, ibpp, ibpl; - convert_mode_t mode; -# ifndef USE_BACKBUFFER - NSBitmapImageRep *bm = 0; -# endif - - Assert ((width < 65535), "improbably large width"); - Assert ((height < 65535), "improbably large height"); - Assert ((x < 65535 && x > -65535), "improbably large x"); - Assert ((y < 65535 && y > -65535), "improbably large y"); - - CGContextRef cgc = d->cgc; - -#ifndef USE_BACKBUFFER - // Because of the backbuffer, all iPhone Windows work like Pixmaps. - if (d->type == PIXMAP) -# endif - { - depth = drawable_depth (d); - mode = convert_mode_to_rgba (dpy->screen->bitmap_info); - ibpp = CGBitmapContextGetBitsPerPixel (cgc); - ibpl = CGBitmapContextGetBytesPerRow (cgc); - data = CGBitmapContextGetData (cgc); - Assert (data, "CGBitmapContextGetData failed"); - -# ifndef USE_BACKBUFFER - } else { /* (d->type == WINDOW) */ - - // get the bits (desired sub-rectangle) out of the NSView - NSRect nsfrom; - nsfrom.origin.x = x; -// nsfrom.origin.y = y; - nsfrom.origin.y = d->frame.size.height - height - y; - nsfrom.size.width = width; - nsfrom.size.height = height; - bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom]; - depth = 32; - mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0; - ibpp = [bm bitsPerPixel]; - ibpl = [bm bytesPerRow]; - data = [bm bitmapData]; - Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed"); -# endif // !USE_BACKBUFFER - } - - // data points at (x,y) with ibpl rowstride. ignore x,y from now on. - data += (y * ibpl) + (x * (ibpp/8)); - - format = (depth == 1 ? XYPixmap : ZPixmap); - XImage *image = XCreateImage (dpy, 0, (unsigned int) depth, - format, 0, 0, width, height, 0, 0); - image->data = (char *) malloc (height * image->bytes_per_line); - - int obpl = image->bytes_per_line; - - /* both PPC and Intel use word-ordered ARGB frame buffers, which - means that on Intel it is BGRA when viewed by bytes (And BGR - when using 24bpp packing). - - BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB. - The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the - indicator of this latest kink. - */ - int xx, yy; - if (depth == 1) { - const unsigned char *iline = data; - for (yy = 0; yy < height; yy++) { - - const unsigned char *iline2 = iline; - for (xx = 0; xx < width; xx++) { - - iline2++; // ignore R or A or A or B - iline2++; // ignore G or B or R or G - unsigned char r = *iline2++; // use B or G or G or R - if (ibpp == 32) iline2++; // ignore A or R or B or A - - XPutPixel (image, xx, yy, (r ? 1 : 0)); - } - iline += ibpl; - } - } else { - const unsigned char *iline = data; - unsigned char *oline = (unsigned char *) image->data; - - mode = convert_mode_merge (mode, - convert_mode_invert ( - convert_mode_to_rgba (dpy->screen->bitmap_info))); - - for (yy = 0; yy < height; yy++) { - - convert_row ((uint32_t *)oline, iline, width, mode, ibpp); - - oline += obpl; - iline += ibpl; - } - } - -# ifndef USE_BACKBUFFER - if (bm) [bm release]; -# endif - - return image; -} - - - -/* Returns a transformation matrix to do rotation as per the provided - EXIF "Orientation" value. - */ -static CGAffineTransform -exif_rotate (int rot, CGSize rect) -{ - CGAffineTransform trans = CGAffineTransformIdentity; - switch (rot) { - case 2: // flip horizontal - trans = CGAffineTransformMakeTranslation (rect.width, 0); - trans = CGAffineTransformScale (trans, -1, 1); - break; - - case 3: // rotate 180 - trans = CGAffineTransformMakeTranslation (rect.width, rect.height); - trans = CGAffineTransformRotate (trans, M_PI); - break; - - case 4: // flip vertical - trans = CGAffineTransformMakeTranslation (0, rect.height); - trans = CGAffineTransformScale (trans, 1, -1); - break; - - case 5: // transpose (UL-to-LR axis) - trans = CGAffineTransformMakeTranslation (rect.height, rect.width); - trans = CGAffineTransformScale (trans, -1, 1); - trans = CGAffineTransformRotate (trans, 3 * M_PI / 2); - break; - - case 6: // rotate 90 - trans = CGAffineTransformMakeTranslation (0, rect.width); - trans = CGAffineTransformRotate (trans, 3 * M_PI / 2); - break; - - case 7: // transverse (UR-to-LL axis) - trans = CGAffineTransformMakeScale (-1, 1); - trans = CGAffineTransformRotate (trans, M_PI / 2); - break; - - case 8: // rotate 270 - trans = CGAffineTransformMakeTranslation (rect.height, 0); - trans = CGAffineTransformRotate (trans, M_PI / 2); - break; - - default: - break; - } - - return trans; -} - - -void -jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, - Bool nsimg_p, void *img_arg, - XRectangle *geom_ret, int exif_rotation) -{ - CGImageRef cgi; -# ifndef USE_IPHONE - CGImageSourceRef cgsrc; -# endif // USE_IPHONE - NSSize imgr; - - CGContextRef cgc = d->cgc; - - if (nsimg_p) { - - NSImage *nsimg = (NSImage *) img_arg; - imgr = [nsimg size]; - -# ifndef USE_IPHONE - // convert the NSImage to a CGImage via the toll-free-bridging - // of NSData and CFData... - // - NSData *nsdata = [NSBitmapImageRep - TIFFRepresentationOfImageRepsInArray: - [nsimg representations]]; - CFDataRef cfdata = (CFDataRef) nsdata; - cgsrc = CGImageSourceCreateWithData (cfdata, NULL); - cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL); -# else // USE_IPHONE - cgi = nsimg.CGImage; -# endif // USE_IPHONE - - } else { - cgi = (CGImageRef) img_arg; - imgr.width = CGImageGetWidth (cgi); - imgr.height = CGImageGetHeight (cgi); - } - - Bool rot_p = (exif_rotation >= 5); - - if (rot_p) - imgr = NSMakeSize (imgr.height, imgr.width); - - CGRect winr = d->frame; - float rw = winr.size.width / imgr.width; - float rh = winr.size.height / imgr.height; - float r = (rw < rh ? rw : rh); - - CGRect dst, dst2; - dst.size.width = imgr.width * r; - dst.size.height = imgr.height * r; - dst.origin.x = (winr.size.width - dst.size.width) / 2; - dst.origin.y = (winr.size.height - dst.size.height) / 2; - - dst2.origin.x = dst2.origin.y = 0; - if (rot_p) { - dst2.size.width = dst.size.height; - dst2.size.height = dst.size.width; - } else { - dst2.size = dst.size; - } - - // Clear the part not covered by the image to background or black. - // - if (d->type == WINDOW) - XClearWindow (dpy, d); - else { - draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, - drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0), YES); - } - - CGAffineTransform trans = - exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size); - - CGContextSaveGState (cgc); - CGContextConcatCTM (cgc, - CGAffineTransformMakeTranslation (dst.origin.x, - dst.origin.y)); - CGContextConcatCTM (cgc, trans); - //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (cgc, dst2, cgi); - CGContextRestoreGState (cgc); - -# ifndef USE_IPHONE - if (nsimg_p) { - CFRelease (cgsrc); - CGImageRelease (cgi); - } -# endif // USE_IPHONE - - if (geom_ret) { - geom_ret->x = dst.origin.x; - geom_ret->y = dst.origin.y; - geom_ret->width = dst.size.width; - geom_ret->height = dst.size.height; - } - - invalidate_drawable_cache (d); -} - - - -Pixmap -XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable, - const char *data, - unsigned int w, unsigned int h, - unsigned long fg, unsigned int bg, - unsigned int depth) -{ - Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth); - XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0, - (char *) data, w, h, 0, 0); - XGCValues gcv; - gcv.foreground = fg; - gcv.background = bg; - GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv); - XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h); - XFreeGC (dpy, gc); - image->data = 0; - XDestroyImage (image); - return p; -} - -Pixmap -XCreatePixmap (Display *dpy, Drawable d, - unsigned int width, unsigned int height, unsigned int depth) -{ - char *data = (char *) malloc (width * height * 4); - if (! data) return 0; - - Pixmap p = (Pixmap) calloc (1, sizeof(*p)); - p->type = PIXMAP; - p->frame.size.width = width; - p->frame.size.height = height; - p->pixmap.depth = depth; - p->pixmap.cgc_buffer = data; - - /* Quartz doesn't have a 1bpp image type. - Used to use 8bpp gray images instead of 1bpp, but some Mac video cards - don't support that! So we always use 32bpp, regardless of depth. */ - - p->cgc = CGBitmapContextCreate (data, width, height, - 8, /* bits per component */ - width * 4, /* bpl */ - dpy->colorspace, - dpy->screen->bitmap_info); - Assert (p->cgc, "could not create CGBitmapContext"); - return p; -} - - -int -XFreePixmap (Display *d, Pixmap p) -{ - Assert (p && p->type == PIXMAP, "not a pixmap"); - invalidate_drawable_cache (p); - CGContextRelease (p->cgc); - if (p->pixmap.cgc_buffer) - free (p->pixmap.cgc_buffer); - free (p); - return 0; -} - - -static Pixmap -copy_pixmap (Display *dpy, Pixmap p) -{ - if (!p) return 0; - Assert (p->type == PIXMAP, "not a pixmap"); - - Pixmap p2 = 0; - - Window root; - int x, y; - unsigned int width, height, border_width, depth; - if (XGetGeometry (dpy, p, &root, - &x, &y, &width, &height, &border_width, &depth)) { - XGCValues gcv; - gcv.function = GXcopy; - GC gc = XCreateGC (dpy, p, GCFunction, &gcv); - if (gc) { - p2 = XCreatePixmap (dpy, p, width, height, depth); - if (p2) - XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0); - XFreeGC (dpy, gc); - } - } - - Assert (p2, "could not copy pixmap"); - - return p2; -} - - -char * -XGetAtomName (Display *dpy, Atom atom) -{ - if (atom == XA_FONT) - return strdup ("FONT"); - - // Note that atoms (that aren't predefined) are just char *. - return strdup ((char *) atom); -} - - -/* Font metric terminology, as used by X11: - - "lbearing" is the distance from the logical origin to the leftmost pixel. - If a character's ink extends to the left of the origin, it is negative. - - "rbearing" is the distance from the logical origin to the rightmost pixel. - - "descent" is the distance from the logical origin to the bottommost pixel. - For characters with descenders, it is positive. For superscripts, it - is negative. - - "ascent" is the distance from the logical origin to the topmost pixel. - It is the number of pixels above the baseline. - - "width" is the distance from the logical origin to the position where - the logical origin of the next character should be placed. - - If "rbearing" is greater than "width", then this character overlaps the - following character. If smaller, then there is trailing blank space. - */ -static void -utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs) -{ - // Returns the metrics of the multi-character, single-line UTF8 string. - - NSFont *nsfont = fid->nsfont; - Drawable d = XRootWindow (fid->dpy, 0); - - CGContextRef cgc = d->cgc; - NSDictionary *attr = - [NSDictionary dictionaryWithObjectsAndKeys: - nsfont, NSFontAttributeName, - nil]; - NSAttributedString *astr = [[NSAttributedString alloc] - initWithString:nsstr - attributes:attr]; - CTLineRef ctline = CTLineCreateWithAttributedString ( - (__bridge CFAttributedStringRef) astr); - CGContextSetTextPosition (cgc, 0, 0); - CGContextSetShouldAntialias (cgc, True); // #### Guess? - - memset (cs, 0, sizeof(*cs)); - - // "CTRun represents set of consecutive glyphs sharing the same - // attributes and direction". - // - // We also get multiple runs any time font subsitution happens: - // E.g., if the current font is Verdana-Bold, a ← character - // in the NSString will actually be rendered in LucidaGrande-Bold. - // - int count = 0; - for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) { - CTRunRef run = (CTRunRef) runid; - CFRange r = { 0, }; - CGRect bbox = CTRunGetImageBounds (run, cgc, r); - CGFloat ascent, descent, leading; - CGFloat advancement = - CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading); - -# ifndef USE_IPHONE - // Only necessary for when LCD smoothing is enabled, which iOS doesn't do. - bbox.origin.x -= 2.0/3.0; - bbox.size.width += 4.0/3.0; - bbox.size.height += 1.0/2.0; -# endif - - // Create the metrics for this run: - XCharStruct cc; - cc.ascent = ceil (bbox.origin.y + bbox.size.height); - cc.descent = ceil (-bbox.origin.y); - cc.lbearing = floor (bbox.origin.x); - cc.rbearing = ceil (bbox.origin.x + bbox.size.width); - cc.width = floor (advancement + 0.5); - - // Add those metrics into the cumulative metrics: - if (count == 0) - *cs = cc; - else - { - cs->ascent = MAX (cs->ascent, cc.ascent); - cs->descent = MAX (cs->descent, cc.descent); - cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing); - cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing); - cs->width = MAX (cs->width, cs->width + cc.width); - } - - // Why no y? What about vertical text? - // XCharStruct doesn't encapsulate that but XGlyphInfo does. - - count++; - } - - CFRelease (ctline); -} - - - -// This is XQueryFont, but for the XFontStruct embedded in 'Font' -// -static void -query_font (Font fid) -{ - if (!fid || !fid->nsfont) { - Assert (0, "no NSFont in fid"); - return; - } - if (![fid->nsfont fontName]) { - Assert(0, @"broken NSFont in fid"); - return; - } - - int first = 32; - int last = 255; - - XFontStruct *f = &fid->metrics; - XCharStruct *min = &f->min_bounds; - XCharStruct *max = &f->max_bounds; - - f->fid = fid; - f->min_char_or_byte2 = first; - f->max_char_or_byte2 = last; - f->default_char = 'M'; - f->ascent = ceil ([fid->nsfont ascender]); - f->descent = -floor ([fid->nsfont descender]); - - min->width = 32767; // set to smaller values in the loop - min->ascent = 32767; - min->descent = 32767; - min->lbearing = 32767; - min->rbearing = 32767; - - f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct)); - - for (int i = first; i <= last; i++) { - XCharStruct *cs = &f->per_char[i-first]; - - char s2[2]; - s2[0] = i; - s2[1] = 0; - NSString *nsstr = [NSString stringWithCString:s2 - encoding:NSISOLatin1StringEncoding]; - utf8_metrics (fid, nsstr, cs); - - max->width = MAX (max->width, cs->width); - max->ascent = MAX (max->ascent, cs->ascent); - max->descent = MAX (max->descent, cs->descent); - max->lbearing = MAX (max->lbearing, cs->lbearing); - max->rbearing = MAX (max->rbearing, cs->rbearing); - - min->width = MIN (min->width, cs->width); - min->ascent = MIN (min->ascent, cs->ascent); - min->descent = MIN (min->descent, cs->descent); - min->lbearing = MIN (min->lbearing, cs->lbearing); - min->rbearing = MIN (min->rbearing, cs->rbearing); - -# if 0 - fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d " - " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n", - i, i, cs->width, cs->lbearing, cs->rbearing, - cs->ascent, cs->descent, - bbox.size.width, bbox.size.height, - bbox.origin.x, bbox.origin.y, - advancement.width, advancement.height); -# endif // 0 - } -} - - -// Since 'Font' includes the metrics, this just makes a copy of that. -// -XFontStruct * -XQueryFont (Display *dpy, Font fid) -{ - // copy XFontStruct - XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f)); - *f = fid->metrics; - - // build XFontProps - f->n_properties = 1; - f->properties = malloc (sizeof(*f->properties) * f->n_properties); - f->properties[0].name = XA_FONT; - Assert (sizeof (f->properties[0].card32) >= sizeof (char *), - "atoms probably needs a real implementation"); - // If XInternAtom is ever implemented, use it here. - f->properties[0].card32 = (char *)fid->xa_font; - - // copy XCharStruct array - int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1; - f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct)); - memcpy (f->per_char, fid->metrics.per_char, - size * sizeof (XCharStruct)); - - return f; -} - - -static Font -copy_font (Font fid) -{ - // copy 'Font' struct - Font fid2 = (Font) malloc (sizeof(*fid2)); - *fid2 = *fid; - - // copy XCharStruct array - int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2; - fid2->metrics.per_char = (XCharStruct *) - malloc ((size + 2) * sizeof (XCharStruct)); - memcpy (fid2->metrics.per_char, fid->metrics.per_char, - size * sizeof (XCharStruct)); - - // copy the other pointers - fid2->ps_name = strdup (fid->ps_name); - fid2->xa_font = strdup (fid->xa_font); -// [fid2->nsfont retain]; - fid2->metrics.fid = fid2; - - return fid2; -} - - -static NSArray * -font_family_members (NSString *family_name) -{ -# ifndef USE_IPHONE - return [[NSFontManager sharedFontManager] - availableMembersOfFontFamily:family_name]; -# else - return [UIFont fontNamesForFamilyName:family_name]; -# endif -} - - -static NSString * -default_font_family (NSFontTraitMask require) -{ - return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana"; -} - - -static NSFont * -try_font (NSFontTraitMask traits, NSFontTraitMask mask, - NSString *family_name, float size, - char **name_ret) -{ - Assert (size > 0, "zero font size"); - - NSArray *family_members = font_family_members (family_name); - if (!family_members.count) - family_members = font_family_members (default_font_family (traits)); - -# ifndef USE_IPHONE - for (unsigned k = 0; k != family_members.count; ++k) { - - NSArray *member = [family_members objectAtIndex:k]; - NSFontTraitMask font_mask = - [(NSNumber *)[member objectAtIndex:3] unsignedIntValue]; - - if ((font_mask & mask) == traits) { - - NSString *name = [member objectAtIndex:0]; - NSFont *f = [NSFont fontWithName:name size:size]; - if (!f) - break; - - /* Don't use this font if it (probably) doesn't include ASCII characters. - */ - NSStringEncoding enc = [f mostCompatibleStringEncoding]; - if (! (enc == NSUTF8StringEncoding || - enc == NSISOLatin1StringEncoding || - enc == NSNonLossyASCIIStringEncoding || - enc == NSISOLatin2StringEncoding || - enc == NSUnicodeStringEncoding || - enc == NSWindowsCP1250StringEncoding || - enc == NSWindowsCP1252StringEncoding || - enc == NSMacOSRomanStringEncoding)) { - // NSLog(@"skipping \"%@\": encoding = %d", name, enc); - break; - } - // NSLog(@"using \"%@\": %d", name, enc); - - // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]); - *name_ret = strdup (name.UTF8String); - return f; - } - } -# else // USE_IPHONE - - for (NSString *fn in family_members) { -# define MATCH(X) \ - ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \ - != NSNotFound) - - // The magic invocation for getting font names is - // [[UIFontDescriptor - // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}] - // symbolicTraits] - // ...but this only works on iOS 7 and later. - NSFontTraitMask font_mask = 0; - if (MATCH(@"Bold")) - font_mask |= NSBoldFontMask; - if (MATCH(@"Italic") || MATCH(@"Oblique")) - font_mask |= NSItalicFontMask; - - if ((font_mask & mask) == traits) { - - /* Check if it can do ASCII. No good way to accomplish this! - These are fonts present in iPhone Simulator as of June 2012 - that don't include ASCII. - */ - if (MATCH(@"AppleGothic") || // Korean - MATCH(@"Dingbats") || // Dingbats - MATCH(@"Emoji") || // Emoticons - MATCH(@"Geeza") || // Arabic - MATCH(@"Hebrew") || // Hebrew - MATCH(@"HiraKaku") || // Japanese - MATCH(@"HiraMin") || // Japanese - MATCH(@"Kailasa") || // Tibetan - MATCH(@"Ornaments") || // Dingbats - MATCH(@"STHeiti") // Chinese - ) - break; - - *name_ret = strdup (fn.UTF8String); - return [UIFont fontWithName:fn size:size]; - } -# undef MATCH - } - -# endif - - return NULL; -} - - -/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead - of XLFD strings; also they can be comma-separated strings with multiple - font names. First one that exists wins. - */ -static NSFont * -try_native_font (const char *name, float scale, - char **name_ret, float *size_ret, char **xa_font) -{ - if (!name) return 0; - const char *spc = strrchr (name, ' '); - if (!spc) return 0; - - NSFont *f = 0; - char *token = strdup (name); - char *name2; - - while ((name2 = strtok (token, ","))) { - token = 0; - - while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') - name2++; - - spc = strrchr (name2, ' '); - if (!spc) continue; - - int dsize = 0; - if (1 != sscanf (spc, " %d ", &dsize)) - continue; - float size = dsize; - - if (size <= 4) continue; - - size *= scale; - - name2[strlen(name2) - strlen(spc)] = 0; - - NSString *nsname = [NSString stringWithCString:name2 - encoding:NSUTF8StringEncoding]; - f = [NSFont fontWithName:nsname size:size]; - if (f) { - *name_ret = name2; - *size_ret = size; - *xa_font = strdup (name); // Maybe this should be an XLFD? - break; - } else { - NSLog(@"No native font: \"%@\" %.0f", nsname, size); - } - } - - free (token); - return f; -} - - -/* Returns a random font in the given size and face. - */ -static NSFont * -random_font (NSFontTraitMask traits, NSFontTraitMask mask, - float size, NSString **family_ret, char **name_ret) -{ - -# ifndef USE_IPHONE - // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits - // returns an empty list, at least on a system with default fonts only. - NSArray *families = [[NSFontManager sharedFontManager] - availableFontFamilies]; - if (!families) return 0; -# else - NSArray *families = [UIFont familyNames]; - - // There are many dups in the families array -- uniquify it. - { - NSArray *sorted_families = - [families sortedArrayUsingSelector:@selector(compare:)]; - NSMutableArray *new_families = - [NSMutableArray arrayWithCapacity:sorted_families.count]; - - NSString *prev_family = nil; - for (NSString *family in sorted_families) { - if ([family compare:prev_family]) - [new_families addObject:family]; - } - - families = new_families; - } -# endif // USE_IPHONE - - long n = [families count]; - if (n <= 0) return 0; - - int j; - for (j = 0; j < n; j++) { - int i = random() % n; - NSString *family_name = [families objectAtIndex:i]; - - NSFont *result = try_font (traits, mask, family_name, size, name_ret); - if (result) { - [*family_ret release]; - *family_ret = family_name; - [*family_ret retain]; - return result; - } - } - - // None of the fonts support ASCII? - return 0; -} - - -// Fonts need this. XDisplayHeightMM and friends should probably be consistent -// with this as well if they're ever implemented. -static const unsigned dpi = 75; - - -static const char * -xlfd_field_end (const char *s) -{ - const char *s2 = strchr(s, '-'); - if (!s2) - s2 = s + strlen(s); - return s2; -} - - -static size_t -xlfd_next (const char **s, const char **s2) -{ - if (!**s2) { - *s = *s2; - } else { - Assert (**s2 == '-', "xlfd parse error"); - *s = *s2 + 1; - *s2 = xlfd_field_end (*s); - } - - return *s2 - *s; -} - - -static NSFont * -try_xlfd_font (const char *name, float scale, - char **name_ret, float *size_ret, char **xa_font) -{ - NSFont *nsfont = 0; - NSString *family_name = nil; - NSFontTraitMask require = 0, forbid = 0; - BOOL rand = NO; - float size = 0; - char *ps_name = 0; - - const char *s = (name ? name : ""); - - size_t L = strlen (s); -# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L)) -# define UNSPEC (L == 0 || L == 1 && *s == '*') - if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask; - else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask; - else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask; - else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask; - else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask; - else { - - // Incorrect fields are ignored. - - if (*s == '-') - ++s; - const char *s2 = xlfd_field_end(s); - - // Foundry (ignore) - - L = xlfd_next (&s, &s2); // Family name - // This used to substitute Georgia for Times. Now it doesn't. - if (CMP ("random")) { - rand = YES; - } else if (CMP ("fixed")) { - require |= NSFixedPitchFontMask; - family_name = @"Courier"; - } else if (!UNSPEC) { - family_name = [[[NSString alloc] initWithBytes:s - length:L - encoding:NSUTF8StringEncoding] - autorelease]; - } - - L = xlfd_next (&s, &s2); // Weight name - if (CMP ("bold") || CMP ("demibold")) - require |= NSBoldFontMask; - else if (CMP ("medium") || CMP ("regular")) - forbid |= NSBoldFontMask; - - L = xlfd_next (&s, &s2); // Slant - if (CMP ("i") || CMP ("o")) - require |= NSItalicFontMask; - else if (CMP ("r")) - forbid |= NSItalicFontMask; - - xlfd_next (&s, &s2); // Set width name (ignore) - xlfd_next (&s, &s2); // Add style name (ignore) - - xlfd_next (&s, &s2); // Pixel size (ignore) - - xlfd_next (&s, &s2); // Point size - char *s3; - uintmax_t n = strtoumax(s, &s3, 10); - if (s2 == s3) - size = n / 10.0; - - xlfd_next (&s, &s2); // Resolution X (ignore) - xlfd_next (&s, &s2); // Resolution Y (ignore) - - xlfd_next (&s, &s2); // Spacing - if (CMP ("p")) - forbid |= NSFixedPitchFontMask; - else if (CMP ("m") || CMP ("c")) - require |= NSFixedPitchFontMask; - - // Don't care about average_width or charset registry. - } -# undef CMP -# undef UNSPEC - - if (!family_name && !rand) - family_name = default_font_family (require); - - if (size < 6 || size > 1000) - size = 12; - - size *= scale; - - NSFontTraitMask mask = require | forbid; - - if (rand) { - nsfont = random_font (require, mask, size, &family_name, &ps_name); - [family_name autorelease]; - } - - if (!nsfont) - nsfont = try_font (require, mask, family_name, size, &ps_name); - - // if that didn't work, turn off attibutes until it does - // (e.g., there is no "Monaco-Bold".) - // - if (!nsfont && (mask & NSItalicFontMask)) { - require &= ~NSItalicFontMask; - mask &= ~NSItalicFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - if (!nsfont && (mask & NSBoldFontMask)) { - require &= ~NSBoldFontMask; - mask &= ~NSBoldFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - if (!nsfont && (mask & NSFixedPitchFontMask)) { - require &= ~NSFixedPitchFontMask; - mask &= ~NSFixedPitchFontMask; - nsfont = try_font (require, mask, family_name, size, &ps_name); - } - - if (nsfont) { - *name_ret = ps_name; - *size_ret = size; - float actual_size = size / scale; - asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1", - family_name.UTF8String, - (require & NSBoldFontMask) ? "bold" : "medium", - (require & NSItalicFontMask) ? 'o' : 'r', - (unsigned)(dpi * actual_size / 72.27 + 0.5), - (unsigned)(actual_size * 10 + 0.5), dpi, dpi, - (require & NSFixedPitchFontMask) ? 'm' : 'p'); - return nsfont; - } else { - return 0; - } -} - - -Font -XLoadFont (Display *dpy, const char *name) -{ - Font fid = (Font) calloc (1, sizeof(*fid)); - - float scale = 1; - -# ifdef USE_IPHONE - /* Since iOS screens are physically smaller than desktop screens, scale up - the fonts to make them more readable. - - Note that X11 apps on iOS also have the backbuffer sized in points - instead of pixels, resulting in an effective X11 screen size of 768x1024 - or so, even if the display has significantly higher resolution. That is - unrelated to this hack, which is really about DPI. - */ - scale = 2; -# endif - - fid->dpy = dpy; - fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size, - &fid->xa_font); - - if (!fid->nsfont && name && - strchr (name, ' ') && - !strchr (name, '*')) { - // If name contains a space but no stars, it is a native font spec -- - // return NULL so that we know it really didn't exist. Else, it is an - // XLFD font, so keep trying. - XUnloadFont (dpy, fid); - return 0; - } - - if (! fid->nsfont) - fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size, - &fid->xa_font); - - // We should never return NULL for XLFD fonts. - if (!fid->nsfont) { - Assert (0, "no font"); - return 0; - } - CFRetain (fid->nsfont); // needed for garbage collection? - - //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size); - - query_font (fid); - - return fid; -} - - -XFontStruct * -XLoadQueryFont (Display *dpy, const char *name) -{ - Font fid = XLoadFont (dpy, name); - if (!fid) return 0; - return XQueryFont (dpy, fid); -} - -int -XUnloadFont (Display *dpy, Font fid) -{ - if (fid->ps_name) - free (fid->ps_name); - if (fid->metrics.per_char) - free (fid->metrics.per_char); - - // #### DAMMIT! I can't tell what's going wrong here, but I keep getting - // crashes in [NSFont ascender] <- query_font, and it seems to go away - // if I never release the nsfont. So, fuck it, we'll just leak fonts. - // They're probably not very big... - // - // [fid->nsfont release]; - // CFRelease (fid->nsfont); - - free (fid); - return 0; -} - -int -XFreeFontInfo (char **names, XFontStruct *info, int n) -{ - int i; - if (names) { - for (i = 0; i < n; i++) - if (names[i]) free (names[i]); - free (names); - } - if (info) { - for (i = 0; i < n; i++) - if (info[i].per_char) { - free (info[i].per_char); - free (info[i].properties); - } - free (info); - } - return 0; -} - -int -XFreeFont (Display *dpy, XFontStruct *f) -{ - Font fid = f->fid; - XFreeFontInfo (0, f, 1); - XUnloadFont (dpy, fid); - return 0; -} - - -int -XSetFont (Display *dpy, GC gc, Font fid) -{ - if (gc->gcv.font) - XUnloadFont (dpy, gc->gcv.font); - gc->gcv.font = copy_font (fid); - [gc->gcv.font->nsfont retain]; - CFRetain (gc->gcv.font->nsfont); // needed for garbage collection? - return 0; -} - - -XFontSet -XCreateFontSet (Display *dpy, char *name, - char ***missing_charset_list_return, - int *missing_charset_count_return, - char **def_string_return) -{ - char *name2 = strdup (name); - char *s = strchr (name, ","); - if (s) *s = 0; - XFontSet set = 0; - XFontStruct *f = XLoadQueryFont (dpy, name2); - if (f) - { - set = (XFontSet) calloc (1, sizeof(*set)); - set->font = f; - } - free (name2); - if (missing_charset_list_return) *missing_charset_list_return = 0; - if (missing_charset_count_return) *missing_charset_count_return = 0; - if (def_string_return) *def_string_return = 0; - return set; -} - - -void -XFreeFontSet (Display *dpy, XFontSet set) -{ - XFreeFont (dpy, set->font); - free (set); -} - - -const char * -jwxyz_nativeFontName (Font f, float *size) -{ - if (size) *size = f->size; - return f->ps_name; -} - - -void -XFreeStringList (char **list) -{ - int i; - if (!list) return; - for (i = 0; list[i]; i++) - XFree (list[i]); - XFree (list); -} - - -// Returns the verbose Unicode name of this character, like "agrave" or -// "daggerdouble". Used by fontglide debugMetrics. -// -char * -jwxyz_unicode_character_name (Font fid, unsigned long uc) -{ - char *ret = 0; - CTFontRef ctfont = - CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName], - [fid->nsfont pointSize], - NULL); - Assert (ctfont, @"no CTFontRef for UIFont"); - - CGGlyph cgglyph; - if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) { - NSString *name = (NSString *) - CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0), - cgglyph); - ret = (name ? strdup ([name UTF8String]) : 0); - } - - CFRelease (ctfont); - return ret; -} - - -// Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored. -// We have to do this because stringWithCString returns NULL if there are -// any invalid characters at all. -// -static NSString * -sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP) -{ - int out_len = in_len * 4; // length of string might increase - char *s2 = (char *) malloc (out_len); - char *out = s2; - const char *in_end = in + in_len; - const char *out_end = out + out_len; - Bool latin1_p = True; - - while (in < in_end) - { - unsigned long uc; - long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc); - long L2 = utf8_encode (uc, out, out_end - out); - in += L1; - out += L2; - if (uc > 255) latin1_p = False; - } - *out = 0; - NSString *nsstr = - [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding]; - free (s2); - if (latin1_pP) *latin1_pP = latin1_p; - return (nsstr ? nsstr : @""); -} - - -int -XTextExtents (XFontStruct *f, const char *s, int length, - int *dir_ret, int *ascent_ret, int *descent_ret, - XCharStruct *cs) -{ - // Unfortunately, adding XCharStructs together to get the extents for a - // string doesn't work: Cocoa uses non-integral character advancements, but - // XCharStruct.width is an integer. Plus that doesn't take into account - // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in - // Zapfino. - - NSString *nsstr = [[[NSString alloc] initWithBytes:s - length:length - encoding:NSISOLatin1StringEncoding] - autorelease]; - utf8_metrics (f->fid, nsstr, cs); - *dir_ret = 0; - *ascent_ret = f->ascent; - *descent_ret = f->descent; - return 0; -} - -int -XTextWidth (XFontStruct *f, const char *s, int length) -{ - int ascent, descent, dir; - XCharStruct cs; - XTextExtents (f, s, length, &dir, &ascent, &descent, &cs); - return cs.width; -} - - -int -XTextExtents16 (XFontStruct *f, const XChar2b *s, int length, - int *dir_ret, int *ascent_ret, int *descent_ret, - XCharStruct *cs) -{ - Bool latin1_p = True; - int i, utf8_len = 0; - char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized - - for (i = 0; i < length; i++) - if (s[i].byte1 > 0) { - latin1_p = False; - break; - } - - { - NSString *nsstr = [NSString stringWithCString:utf8 - encoding:NSUTF8StringEncoding]; - utf8_metrics (f->fid, nsstr, cs); - } - - *dir_ret = 0; - *ascent_ret = f->ascent; - *descent_ret = f->descent; - free (utf8); - return 0; -} - - -/* "Returns the distance in pixels in the primary draw direction from - the drawing origin to the origin of the next character to be drawn." - - "overall_ink_return is set to the bbox of the string's character ink." - - "The overall_ink_return for a nondescending, horizontally drawn Latin - character is conventionally entirely above the baseline; that is, - overall_ink_return.height <= -overall_ink_return.y." - - [So this means that y is the top of the ink, and height grows down: - For above-the-baseline characters, y is negative.] - - "The overall_ink_return for a nonkerned character is entirely at, and to - the right of, the origin; that is, overall_ink_return.x >= 0." - - [So this means that x is the left of the ink, and width grows right. - For left-of-the-origin characters, x is negative.] - - "A character consisting of a single pixel at the origin would set - overall_ink_return fields y = 0, x = 0, width = 1, and height = 1." - */ -int -Xutf8TextExtents (XFontSet set, const char *str, int len, - XRectangle *overall_ink_return, - XRectangle *overall_logical_return) -{ - Bool latin1_p; - NSString *nsstr = sanitize_utf8 (str, len, &latin1_p); - XCharStruct cs; - - utf8_metrics (set->font->fid, nsstr, &cs); - - /* "The overall_logical_return is the bounding box that provides minimum - spacing to other graphical features for the string. Other graphical - features, for example, a border surrounding the text, should not - intersect this rectangle." - - So I think that means they're the same? Or maybe "ink" is the bounding - box, and "logical" is the advancement? But then why is the return value - the advancement? - */ - if (overall_ink_return) - XCharStruct_to_XmbRectangle (cs, *overall_ink_return); - if (overall_logical_return) - XCharStruct_to_XmbRectangle (cs, *overall_logical_return); - - return cs.width; -} - - -static int -draw_string (Display *dpy, Drawable d, GC gc, int x, int y, - NSString *nsstr) -{ - if (! nsstr) return 1; - - CGRect wr = d->frame; - CGContextRef cgc = d->cgc; - - unsigned long argb = gc->gcv.foreground; - if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0)); - float rgba[4]; - query_color_float (dpy, argb, rgba); - NSColor *fg = [NSColor colorWithDeviceRed:rgba[0] - green:rgba[1] - blue:rgba[2] - alpha:rgba[3]]; - - if (!gc->gcv.font) { - Assert (0, "no font"); - return 1; - } - - /* This crashes on iOS 5.1 because NSForegroundColorAttributeName, - NSFontAttributeName, and NSAttributedString are only present on iOS 6 - and later. We could resurrect the Quartz code from v5.29 and do a - runtime conditional on that, but that would be a pain in the ass. - Probably time to just make iOS 6 a requirement. - */ - - NSDictionary *attr = - [NSDictionary dictionaryWithObjectsAndKeys: - gc->gcv.font->nsfont, NSFontAttributeName, - fg, NSForegroundColorAttributeName, - nil]; - - // Don't understand why we have to do both set_color and - // NSForegroundColorAttributeName, but we do. - // - set_color (dpy, cgc, argb, 32, NO, YES); - - NSAttributedString *astr = [[NSAttributedString alloc] - initWithString:nsstr - attributes:attr]; - CTLineRef dl = CTLineCreateWithAttributedString ( - (__bridge CFAttributedStringRef) astr); - - // Not sure why this is necessary, but xoff is positive when the first - // character on the line has a negative lbearing. Without this, the - // string is rendered with the first ink at 0 instead of at lbearing. - // I have not seen xoff be negative, so I'm not sure if that can happen. - // - // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by - // a letter. - // - CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL); - Assert (xoff >= 0, "unexpected CTLineOffset"); - x -= xoff; - - CGContextSetTextPosition (cgc, - wr.origin.x + x, - wr.origin.y + wr.size.height - y); - CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p); - - CTLineDraw (dl, cgc); - CFRelease (dl); - - invalidate_drawable_cache (d); - return 0; -} - - -int -XDrawString (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, int len) -{ - char *s2 = (char *) malloc (len + 1); - strncpy (s2, str, len); - s2[len] = 0; - NSString *nsstr = [NSString stringWithCString:s2 - encoding:NSISOLatin1StringEncoding]; - int ret = draw_string (dpy, d, gc, x, y, nsstr); - free (s2); - return ret; -} - - -int -XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y, - const XChar2b *str, int len) -{ - char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized - NSString *nsstr = - [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding]; - int ret = draw_string (dpy, d, gc, x, y, nsstr); - free (s2); - return ret; -} - - -void -Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc, - int x, int y, const char *str, int len) -{ - char *s2 = (char *) malloc (len + 1); - strncpy (s2, str, len); - s2[len] = 0; - NSString *nsstr = sanitize_utf8 (str, len, 0); - draw_string (dpy, d, gc, x, y, nsstr); - free (s2); -} - - -int -XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, - const char *str, int len) -{ - int ascent, descent, dir; - XCharStruct cs; - XTextExtents (&gc->gcv.font->metrics, str, len, - &dir, &ascent, &descent, &cs); - draw_rect (dpy, d, gc, - x + MIN (0, cs.lbearing), - y - MAX (0, ascent), - MAX (MAX (0, cs.rbearing) - - MIN (0, cs.lbearing), - cs.width), - MAX (0, ascent) + MAX (0, descent), - gc->gcv.background, YES); - return XDrawString (dpy, d, gc, x, y, str, len); -} - - -int -XSetForeground (Display *dpy, GC gc, unsigned long fg) -{ - validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p); - gc->gcv.foreground = fg; - return 0; -} - - -int -XSetBackground (Display *dpy, GC gc, unsigned long bg) -{ - validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p); - gc->gcv.background = bg; - return 0; -} - -int -jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed) -{ - gc->gcv.alpha_allowed_p = allowed; - return 0; -} - -int -jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p) -{ - gc->gcv.antialias_p = antialias_p; - return 0; -} - - -int -XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width, - int line_style, int cap_style, int join_style) -{ - gc->gcv.line_width = line_width; - Assert (line_style == LineSolid, "only LineSolid implemented"); -// gc->gcv.line_style = line_style; - gc->gcv.cap_style = cap_style; - gc->gcv.join_style = join_style; - return 0; -} - -int -XSetGraphicsExposures (Display *dpy, GC gc, Bool which) -{ - return 0; -} - -int -XSetFunction (Display *dpy, GC gc, int which) -{ - gc->gcv.function = which; - return 0; -} - -int -XSetSubwindowMode (Display *dpy, GC gc, int which) -{ - gc->gcv.subwindow_mode = which; - return 0; -} - -int -XSetClipMask (Display *dpy, GC gc, Pixmap m) -{ - Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); - - if (gc->gcv.clip_mask) { - XFreePixmap (dpy, gc->gcv.clip_mask); - CGImageRelease (gc->clip_mask); - } - - gc->gcv.clip_mask = copy_pixmap (dpy, m); - if (gc->gcv.clip_mask) - gc->clip_mask = - CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc); - else - gc->clip_mask = 0; - - return 0; -} - -int -XSetClipOrigin (Display *dpy, GC gc, int x, int y) -{ - gc->gcv.clip_x_origin = x; - gc->gcv.clip_y_origin = y; - return 0; -} - - -static void -get_pos (Window w, NSPoint *vpos, NSPoint *p) -{ -# ifdef USE_IPHONE - - vpos->x = 0; - vpos->y = 0; - - if (p) { - p->x = w->window.last_mouse_x; - p->y = w->window.last_mouse_y; - } - -# else // !USE_IPHONE - - NSWindow *nsw = [w->window.view window]; - NSPoint wpos; - // get bottom left of window on screen, from bottom left - wpos.x = wpos.y = 0; - wpos = [nsw convertBaseToScreen:wpos]; - - // get bottom left of view on window, from bottom left - vpos->x = vpos->y = 0; - *vpos = [w->window.view convertPoint:*vpos toView:[nsw contentView]]; - - // get bottom left of view on screen, from bottom left - vpos->x += wpos.x; - vpos->y += wpos.y; - - // get top left of view on screen, from bottom left - vpos->y += w->frame.size.height; - - // get top left of view on screen, from top left - NSArray *screens = [NSScreen screens]; - NSScreen *screen = (screens && [screens count] > 0 - ? [screens objectAtIndex:0] - : [NSScreen mainScreen]); - NSRect srect = [screen frame]; - vpos->y = srect.size.height - vpos->y; - - if (p) { - // get the mouse position on window, from bottom left - NSEvent *e = [NSApp currentEvent]; - *p = [e locationInWindow]; - - // get mouse position on screen, from bottom left - p->x += wpos.x; - p->y += wpos.y; - - // get mouse position on screen, from top left - p->y = srect.size.height - p->y; - } - -# endif // !USE_IPHONE -} - -Bool -XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, - int *root_x_ret, int *root_y_ret, - int *win_x_ret, int *win_y_ret, unsigned int *mask_ret) -{ - Assert (w && w->type == WINDOW, "not a window"); - - NSPoint vpos, p; - get_pos (w, &vpos, &p); - - if (root_x_ret) *root_x_ret = (int) p.x; - if (root_y_ret) *root_y_ret = (int) p.y; - if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x); - if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y); - if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers? - if (root_ret) *root_ret = 0; - if (child_ret) *child_ret = 0; - return True; -} - -Bool -XTranslateCoordinates (Display *dpy, Window w, Window dest_w, - int src_x, int src_y, - int *dest_x_ret, int *dest_y_ret, - Window *child_ret) -{ - Assert (w && w->type == WINDOW, "not a window"); - - NSPoint vpos, p; - get_pos (w, &vpos, NULL); - - // point starts out relative to top left of view - p.x = src_x; - p.y = src_y; - - // get point relative to top left of screen - p.x += vpos.x; - p.y += vpos.y; - - *dest_x_ret = p.x; - *dest_y_ret = p.y; - if (child_ret) - *child_ret = w; - return True; -} - - -KeySym -XKeycodeToKeysym (Display *dpy, KeyCode code, int index) -{ - return code; -} - -int -XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret, - XComposeStatus *xc) -{ - KeySym ks = XKeycodeToKeysym (0, e->keycode, 0); - char c = 0; - // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string. - if ((unsigned int) ks <= 255) - c = (char) ks; - - // Put control characters in the string. Not meta. - if (e->state & ControlMask) { - if (c >= 'a' && c <= 'z') // Upcase control. - c -= 'a'-'A'; - if (c >= '@' && c <= '_') // Shift to control page. - c -= '@'; - if (c == ' ') // C-SPC is NULL. - c = 0; - } - - if (k_ret) *k_ret = ks; - if (size > 0) buf[0] = c; - if (size > 1) buf[1] = 0; - return (size > 0 ? 1 : 0); -} - - -int -XFlush (Display *dpy) -{ - // Just let the event loop take care of this on its own schedule. - return 0; -} - -int -XSync (Display *dpy, Bool flush) -{ - return XFlush (dpy); -} - - -// declared in utils/visual.h -int -has_writable_cells (Screen *s, Visual *v) -{ - return 0; -} - -int -visual_depth (Screen *s, Visual *v) -{ - return 32; -} - -int -visual_cells (Screen *s, Visual *v) -{ - return (int)(v->red_mask | v->green_mask | v->blue_mask); -} - -int -visual_class (Screen *s, Visual *v) -{ - return TrueColor; -} - -int -get_bits_per_pixel (Display *dpy, int depth) -{ - Assert (depth == 32 || depth == 1, "unexpected depth"); - return depth; -} - -int -screen_number (Screen *screen) -{ - Display *dpy = DisplayOfScreen (screen); - int i; - for (i = 0; i < ScreenCount (dpy); i++) - if (ScreenOfDisplay (dpy, i) == screen) - return i; - abort (); - return 0; -} - -// declared in utils/grabclient.h -Bool -use_subwindow_mode_p (Screen *screen, Window window) -{ - return False; -}