-/* xscreensaver, Copyright (c) 1991-2015 Jamie Zawinski <jwz@jwz.org>
- *
- * 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 <stdlib.h>
-#import <stdint.h>
-#import <wchar.h>
-
-#ifdef USE_IPHONE
-# import <UIKit/UIKit.h>
-# import <UIKit/UIScreen.h>
-# import <QuartzCore/QuartzCore.h>
-# 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 <Cocoa/Cocoa.h>
-#endif
-
-#import <CoreText/CTFont.h>
-#import <CoreText/CTLine.h>
-#import <CoreText/CTRun.h>
-
-#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 <krevis@snoize.com> 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;
-}