X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=jwxyz%2Fjwxyz.m;h=a9ee89c0f44773d7ac9d3e149cb2dee204c0c86c;hb=39809ded547bdbb08207d3e514950425215b4410;hp=bb189b0270e1c55f1e9cd48b1f4a7ce23f18abf2;hpb=aa75c7476aeaa84cf3abc192b376a8b03c325213;p=xscreensaver diff --git a/jwxyz/jwxyz.m b/jwxyz/jwxyz.m index bb189b02..a9ee89c0 100644 --- a/jwxyz/jwxyz.m +++ b/jwxyz/jwxyz.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2017 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -43,20 +43,12 @@ # define NSMakeSize CGSizeMake # define NSBezierPath UIBezierPath # define colorWithDeviceRed colorWithRed - -# define NSFontTraitMask UIFontDescriptorSymbolicTraits -// The values for the flags for NSFontTraitMask and -// UIFontDescriptorSymbolicTraits match up, not that it really matters here. -# define NSBoldFontMask UIFontDescriptorTraitBold -# define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace -# define NSItalicFontMask UIFontDescriptorTraitItalic #else # import #endif #import #import -#import #import "jwxyzI.h" #import "jwxyz-cocoa.h" @@ -65,16 +57,13 @@ #import "utf8wc.h" #import "xft.h" -# undef MAX -# undef MIN -# define MAX(a,b) ((a)>(b)?(a):(b)) -# define MIN(a,b) ((a)<(b)?(a):(b)) - struct jwxyz_Display { + const struct jwxyz_vtbl *vtbl; // Must come first. + Window main_window; - Screen *screen; - int screen_count; + CGBitmapInfo bitmap_info; + Visual visual; struct jwxyz_sources_data *timers_data; # ifndef USE_IPHONE @@ -90,36 +79,12 @@ struct jwxyz_Display { unsigned long window_background; }; -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; -}; - // 24/32bpp -> 32bpp image conversion. // Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24 @@ -201,9 +166,9 @@ convert_row (uint32_t *dest, const void *src, size_t count, 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. + Put together, this means that the assertions in + jwxyz_quartz_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 @@ -316,102 +281,24 @@ union color_bytes }; -uint32_t -jwxyz_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))); -} - - -void -jwxyz_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) +query_color_float (Display *dpy, unsigned long pixel, CGFloat *rgba) { - uint8_t rgba8[4]; - jwxyz_query_color (dpy, pixel, rgba8); - for (unsigned i = 0; i != 4; ++i) - rgba[i] = rgba8[i] * (1.0f / 255.0f); + JWXYZ_QUERY_COLOR (dpy, pixel, (CGFloat)1, rgba); } -/* 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 - +extern const struct jwxyz_vtbl quartz_vtbl; Display * -jwxyz_make_display (Window w) +jwxyz_quartz_make_display (Window w) { CGContextRef cgc = w->cgc; 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 + d->vtbl = &quartz_vtbl; - d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc); - d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF); - d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); + d->bitmap_info = CGBitmapContextGetBitmapInfo (cgc); # if 0 // Tests for the image conversion modes. @@ -437,18 +324,23 @@ jwxyz_make_display (Window w) } # endif - Visual *v = (Visual *) calloc (1, sizeof(Visual)); + Visual *v = &d->visual; v->class = TrueColor; - v->red_mask = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000); - v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000); - v->blue_mask = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000); - CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask; - Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) && + + union color_bytes color; + convert_mode_t mode = + convert_mode_invert (convert_mode_to_rgba (d->bitmap_info)); + for (unsigned i = 0; i != 4; ++i) { + color.pixel = 0; + color.bytes[i] = 0xff; + v->rgba_masks[i] = convert_px (color.pixel, mode); + } + + CGBitmapInfo byte_order = d->bitmap_info & kCGBitmapByteOrderMask; + Assert ( ! (d->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)); @@ -461,27 +353,10 @@ jwxyz_make_display (Window w) } void -jwxyz_free_display (Display *dpy) +jwxyz_quartz_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); free (dpy); } @@ -554,72 +429,23 @@ jwxyz_flush_context (Display *dpy) CGContextFlush(dpy->main_window->cgc); } -jwxyz_sources_data * +static jwxyz_sources_data * display_sources_data (Display *dpy) { return dpy->timers_data; } -Window -XRootWindow (Display *dpy, int screen) -{ - return dpy ? dpy->main_window : 0; -} - -Screen * -XDefaultScreenOfDisplay (Display *dpy) +static Window +root (Display *dpy) { - return dpy ? dpy->screen : 0; + return dpy->main_window; } -Visual * -XDefaultVisualOfScreen (Screen *screen) +static Visual * +visual (Display *dpy) { - return screen ? screen->visual : 0; -} - -Display * -XDisplayOfScreen (Screen *s) -{ - return s ? s->dpy : 0; -} - -int -XDisplayNumberOfScreen (Screen *s) -{ - return 0; -} - -int -XScreenNumberOfScreen (Screen *s) -{ - return s? s->screen_number : 0; -} - -int -jwxyz_ScreenCount (Display *dpy) -{ - return dpy ? dpy->screen_count : 0; -} - -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; + return &dpy->visual; } @@ -634,7 +460,7 @@ set_color (Display *dpy, CGContextRef cgc, unsigned long argb, else CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0); } else { - float rgba[4]; + CGFloat rgba[4]; query_color_float (dpy, argb, rgba); if (fill_p) CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]); @@ -754,9 +580,9 @@ bitmap_context_p (Drawable d) */ #define XDRAWPOINTS_CGDATA -int -XDrawPoints (Display *dpy, Drawable d, GC gc, - XPoint *points, int count, int mode) +static int +DrawPoints (Display *dpy, Drawable d, GC gc, + XPoint *points, int count, int mode) { int i; XRectangle wr = d->frame; @@ -827,7 +653,7 @@ XDrawPoints (Display *dpy, Drawable d, GC gc, dpy->colorspace, /* Host-ordered, since we're using the address of an int as the color data. */ - dpy->screen->bitmap_info, + dpy->bitmap_info, prov, NULL, /* decode[] */ NO, /* interpolate */ @@ -931,41 +757,8 @@ point_for_line (Drawable d, GC gc, int x, int y) } -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, +static int +DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, int mode) { int i; @@ -1004,8 +797,8 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, } -int -XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) +static int +DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) { int i; @@ -1015,10 +808,25 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) 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); + // when drawing a zero-length line, obey line-width and cap-style. + if (segments->x1 == segments->x2 && segments->y1 == segments->y2) { + int w = gc->gcv.line_width; + int x1 = segments->x1 - w/2; + int y1 = segments->y1 - w/2; + if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound) + XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64); + else { + if (!w) + w = 1; // Actually show zero-length lines. + XFillRectangle (dpy, d, gc, x1, y1, w, w); + } + } else { + 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); @@ -1028,33 +836,24 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) } -int -XClearWindow (Display *dpy, Window win) +static int +ClearWindow (Display *dpy, Window win) { Assert (win && win->type == WINDOW, "not a window"); XRectangle wr = win->frame; return XClearArea (dpy, win, 0, 0, wr.width, wr.height, 0); } -unsigned long -jwxyz_window_background (Display *dpy) +static unsigned long * +window_background (Display *dpy) { - return dpy->window_background; + return &dpy->window_background; } -int -XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) -{ - Assert (w && w->type == WINDOW, "not a window"); - jwxyz_validate_pixel (dpy, pixel, 32, NO); - dpy->window_background = pixel; - return 0; -} - -void -jwxyz_fill_rects (Display *dpy, Drawable d, GC gc, - const XRectangle *rectangles, unsigned long nrectangles, - unsigned long pixel) +static void +fill_rects (Display *dpy, Drawable d, GC gc, + const XRectangle *rectangles, unsigned long nrectangles, + unsigned long pixel) { Assert (!gc || gc->depth == jwxyz_drawable_depth (d), "depth mismatch"); @@ -1112,7 +911,7 @@ jwxyz_fill_rects (Display *dpy, Drawable d, GC gc, pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0); size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc); - void *dst = seek_xy (CGBitmapContextGetData (d->cgc), + void *dst = SEEK_XY (CGBitmapContextGetData (d->cgc), dst_bytes_per_row, x, y); Assert(sizeof(wchar_t) == 4, "somebody changed the ABI"); @@ -1139,18 +938,9 @@ jwxyz_fill_rects (Display *dpy, Drawable d, GC gc, } -int -XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) -{ - Assert (win && win->type == WINDOW, "not a window"); - jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background); - return 0; -} - - -int -XFillPolygon (Display *dpy, Drawable d, GC gc, - XPoint *points, int npoints, int shape, int mode) +static int +FillPolygon (Display *dpy, Drawable d, GC gc, + XPoint *points, int npoints, int shape, int mode) { XRectangle wr = d->frame; int i; @@ -1185,8 +975,8 @@ XFillPolygon (Display *dpy, Drawable d, GC gc, #define radians(DEG) ((DEG) * M_PI / 180.0) #define degrees(RAD) ((RAD) * 180.0 / M_PI) -int -jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, +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) { @@ -1236,22 +1026,22 @@ jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, } -XGCValues * -jwxyz_gc_gcv (GC gc) +static XGCValues * +gc_gcv (GC gc) { return &gc->gcv; } -unsigned int -jwxyz_gc_depth (GC gc) +static unsigned int +gc_depth (GC gc) { return gc->depth; } -GC -XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) +static GC +CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) { struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc)); gc->depth = jwxyz_drawable_depth (d); @@ -1262,8 +1052,8 @@ XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) } -int -XFreeGC (Display *dpy, GC gc) +static int +FreeGC (Display *dpy, GC gc) { if (gc->gcv.font) XUnloadFont (dpy, gc->gcv.font); @@ -1321,10 +1111,10 @@ flipbits (unsigned const char *in, unsigned char *out, int length) } -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) +static int +PutImage (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) { XRectangle wr = d->frame; @@ -1394,7 +1184,7 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, CGImageRef cgi = CGImageCreate (w, h, bpp/4, bpp, bpl, dpy->colorspace, - dpy->screen->bitmap_info, + dpy->bitmap_info, prov, NULL, /* decode[] */ NO, /* interpolate */ @@ -1448,10 +1238,11 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, } -XImage * -XGetImage (Display *dpy, Drawable d, int x, int y, - unsigned int width, unsigned int height, - unsigned long plane_mask, int format) +static XImage * +GetSubImage (Display *dpy, Drawable d, int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, int format, + XImage *image, int dest_x, int dest_y) { const unsigned char *data = 0; size_t depth, ibpp, ibpl; @@ -1466,7 +1257,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y, { depth = jwxyz_drawable_depth (d); - mode = convert_mode_to_rgba (dpy->screen->bitmap_info); + mode = convert_mode_to_rgba (dpy->bitmap_info); ibpp = CGBitmapContextGetBitsPerPixel (cgc); ibpl = CGBitmapContextGetBytesPerRow (cgc); data = CGBitmapContextGetData (cgc); @@ -1477,9 +1268,6 @@ XGetImage (Display *dpy, Drawable d, int x, int y, 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; @@ -1504,17 +1292,18 @@ XGetImage (Display *dpy, Drawable d, int x, int y, 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)); + XPutPixel (image, xx + dest_x, yy + dest_y, (r ? 1 : 0)); } iline += ibpl; } } else { const unsigned char *iline = data; - unsigned char *oline = (unsigned char *) image->data; + unsigned char *oline = (unsigned char *) image->data + dest_y * obpl + + dest_x * 4; mode = convert_mode_merge (mode, convert_mode_invert ( - convert_mode_to_rgba (dpy->screen->bitmap_info))); + convert_mode_to_rgba (dpy->bitmap_info))); for (yy = 0; yy < height; yy++) { @@ -1630,6 +1419,16 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, float rh = winr.height / imgr.height; float r = (rw < rh ? rw : rh); + /* If the window is a goofy aspect ratio, take a middle slice of + the image instead. */ + if (winr.width > winr.height * 5 || + winr.width > winr.width * 5) { + r *= (winr.width > winr.height + ? winr.width / (double) winr.height + : winr.height / (double) winr.width); + // NSLog (@"weird aspect: scaling by %.1f\n", r); + } + CGRect dst, dst2; dst.size.width = imgr.width * r; dst.size.height = imgr.height * r; @@ -1707,7 +1506,7 @@ XCreatePixmap (Display *dpy, Drawable d, 8, /* bits per component */ width * 4, /* bpl */ dpy->colorspace, - dpy->screen->bitmap_info); + dpy->bitmap_info); Assert (p->cgc, "could not create CGBitmapContext"); return p; } @@ -1756,827 +1555,17 @@ copy_pixmap (Display *dpy, Pixmap p) } -/* 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++; - } - - [astr release]; - 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 - - // This trick needs iOS 3.1, see "Using SDK-Based Development". - Class has_font_descriptor = [UIFontDescriptor class]; - - for (NSString *fn in family_members) { -# define MATCH(X) \ - ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \ - != NSNotFound) - - NSFontTraitMask font_mask; - if (has_font_descriptor) { - // This only works on iOS 7 and later. - font_mask = [[UIFontDescriptor - fontDescriptorWithFontAttributes: - @{UIFontDescriptorNameAttribute:fn}] - symbolicTraits]; - } else { - font_mask = 0; - if (MATCH(@"Bold")) - font_mask |= NSBoldFontMask; - if (MATCH(@"Italic") || MATCH(@"Oblique")) - font_mask |= NSItalicFontMask; - if (MATCH(@"Courier")) - font_mask |= NSFixedPitchFontMask; - } - - 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 *otoken = token; - char *name2; - char *lasts; - - while ((name2 = strtok_r (token, ",", &lasts))) { - 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 = strdup (name2); - *size_ret = size; - *xa_font = strdup (name); // Maybe this should be an XLFD? - break; - } else { - NSLog(@"No native font: \"%@\" %.0f", nsname, size); -# if 0 - for (NSString *fam in [UIFont familyNames]) { - NSLog(@"Family: %@", fam); - for (NSString *f in [UIFont fontNamesForFamilyName:fam]) { - NSLog(@" Font: %@", f); - } - } -# endif - } - } - - free (otoken); - 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 = @""; - for (NSString *family in sorted_families) { - if ([family compare:prev_family]) - [new_families addObject:family]; - prev_family = 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; -} - - -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 (Display *dpy, const char *name, float scale, - char **name_ret, float *size_ret, char **xa_font) -{ - NSFont *nsfont = 0; - NSString *family_name = nil; - NSFontTraitMask require = 0, - // Default mask is for the built-in X11 font aliases. - mask = NSFixedPitchFontMask | NSBoldFontMask | NSItalicFontMask; - 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 { - - NSFontTraitMask forbid = 0; - - // 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. - - mask = require | forbid; - } -# undef CMP -# undef UNSPEC - - if (!family_name && !rand) - family_name = default_font_family (require); - - if (size < 6 || size > 1000) - size = 12; - - size *= scale; - - 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) { - unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2; - unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d); - *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 { - if (ps_name) free (ps_name); - 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 = dpy->main_window->window.view.hackedContentScaleFactor; - if (scale < 1) // iPad Pro magnifies the backbuffer by 3x, which makes text - scale = 1; // excessively blurry in BSOD. -# 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 (dpy, 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) +jwxyz_unicode_character_name (Display *dpy, Font fid, unsigned long uc) { char *ret = 0; + NSFont *nsfont = (NSFont *) jwxyz_native_font (fid); CTFontRef ctfont = - CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName], - [fid->nsfont pointSize], + CTFontCreateWithName ((CFStringRef) [nsfont fontName], + [nsfont pointSize], NULL); Assert (ctfont, "no CTFontRef for UIFont"); @@ -2594,153 +1583,12 @@ jwxyz_unicode_character_name (Font fid, unsigned long uc) } -// 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) + const char *str, size_t len, int utf8_p) { + NSString *nsstr = nsstring_from (str, len, utf8_p); + if (! nsstr) return 1; XRectangle wr = d->frame; @@ -2748,7 +1596,7 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y, unsigned long argb = gc->gcv.foreground; if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0)); - float rgba[4]; + CGFloat rgba[4]; query_color_float (dpy, argb, rgba); NSColor *fg = [NSColor colorWithDeviceRed:rgba[0] green:rgba[1] @@ -2769,7 +1617,7 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y, NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: - gc->gcv.font->nsfont, NSFontAttributeName, + (NSFont *) jwxyz_native_font (gc->gcv.font), NSFontAttributeName, fg, NSForegroundColorAttributeName, nil]; @@ -2810,69 +1658,8 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y, } -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); - jwxyz_fill_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); - return XDrawString (dpy, d, gc, x, y, str, len); -} - - -int -XSetClipMask (Display *dpy, GC gc, Pixmap m) +static int +SetClipMask (Display *dpy, GC gc, Pixmap m) { Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup"); @@ -2891,12 +1678,40 @@ XSetClipMask (Display *dpy, GC gc, Pixmap m) return 0; } -int -XSetClipOrigin (Display *dpy, GC gc, int x, int y) +static int +SetClipOrigin (Display *dpy, GC gc, int x, int y) { gc->gcv.clip_x_origin = x; gc->gcv.clip_y_origin = y; return 0; } + +const struct jwxyz_vtbl quartz_vtbl = { + root, + visual, + display_sources_data, + + window_background, + draw_arc, + fill_rects, + gc_gcv, + gc_depth, + draw_string, + + jwxyz_quartz_copy_area, + + DrawPoints, + DrawSegments, + CreateGC, + FreeGC, + ClearWindow, + SetClipMask, + SetClipOrigin, + FillPolygon, + DrawLines, + PutImage, + GetSubImage +}; + #endif // JWXYZ_QUARTZ -- entire file