X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2Fjwxyz.m;h=7643c4400003513f5a7508fa54c0e727ce3b5089;hp=7fce07d45e48c49e7c44bee83e03bc4a1f79a909;hb=f8cf5ac7b2f53510f80a0eaf286a25298be17bfe;hpb=ec8d2b32b63649e6d32bdfb306eda062769af823 diff --git a/OSX/jwxyz.m b/OSX/jwxyz.m index 7fce07d4..7643c440 100644 --- a/OSX/jwxyz.m +++ b/OSX/jwxyz.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2010 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2012 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 @@ -18,7 +18,27 @@ #import #import -#import + +#ifdef USE_IPHONE +# import +# import +# import +# define NSView UIView +# define NSRect CGRect +# define NSPoint CGPoint +# define NSSize CGSize +# define NSColor UIColor +# define NSImage UIImage +# define NSEvent UIEvent +# define NSFont UIFont +# define NSGlyph CGGlyph +# define NSWindow UIWindow +# define NSMakeSize CGSizeMake +# define NSBezierPath UIBezierPath +#else +# import +#endif + #import "jwxyz.h" #import "jwxyz-timers.h" @@ -44,6 +64,7 @@ struct jwxyz_Drawable { struct { NSView *view; unsigned long background; + int last_mouse_x, last_mouse_y; } window; struct { int depth; @@ -57,9 +78,11 @@ struct jwxyz_Display { Screen *screen; 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 @@ -89,8 +112,9 @@ struct jwxyz_Font { Display * -jwxyz_make_display (void *nsview_arg) +jwxyz_make_display (void *nsview_arg, void *cgc_arg) { + CGContextRef cgc = (CGContextRef) cgc_arg; NSView *view = (NSView *) nsview_arg; if (!view) abort(); @@ -111,21 +135,22 @@ jwxyz_make_display (void *nsview_arg) Window w = (Window) calloc (1, sizeof(*w)); w->type = WINDOW; - // kludge! this needs to be set late, so we do it in XClearWindow! - // w->cgc = [[[view window] graphicsContext] graphicsPort]; - w->cgc = 0; w->window.view = view; CFRetain (w->window.view); // needed for garbage collection? w->window.background = BlackPixel(0,0); d->main_window = w; - [view lockFocus]; - w->cgc = [[[view window] graphicsContext] graphicsPort]; - [view unlockFocus]; - - jwxyz_window_resized (d, 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; } @@ -167,15 +192,21 @@ invalidate_drawable_cache (Drawable d) /* Call this when the View changes size or position. */ void -jwxyz_window_resized (Display *dpy, Window w) +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->type == WINDOW, "not a window"); - NSRect r = [w->window.view frame]; - w->frame.origin.x = r.origin.x; // NSRect -> CGRect - w->frame.origin.y = r.origin.y; - w->frame.size.width = r.size.width; - w->frame.size.height = r.size.height; + 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; @@ -188,6 +219,7 @@ jwxyz_window_resized (Display *dpy, Window w) CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n); Assert (dpy->cgdpy, "unable to find CGDisplay"); } +# endif // USE_IPHONE #if 0 { @@ -210,6 +242,18 @@ jwxyz_window_resized (Display *dpy, Window w) } +#ifdef USE_IPHONE +void +jwxyz_mouse_moved (Display *dpy, Window w, int x, int y) +{ + Assert (w->type == WINDOW, "not a window"); + w->window.last_mouse_x = x; + w->window.last_mouse_y = y; +} +#endif // USE_IPHONE + + + jwxyz_sources_data * display_sources_data (Display *dpy) { @@ -356,7 +400,7 @@ push_gc (Drawable d, GC gc) set_clip_mask (d, gc); } -#define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc) +#define pop_gc(d,gc) CGContextRestoreGState (d->cgc) /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors. @@ -374,7 +418,6 @@ push_color_gc (Drawable d, GC gc, unsigned long color, } CGContextRef cgc = d->cgc; - set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p); CGContextSetShouldAntialias (cgc, antialias_p); } @@ -522,9 +565,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, gc->gcv.function == GXclear) { // "set" and "clear" are dumb drawing modes that ignore the source // bits and just draw solid rectangles. - set_color (dst->cgc, (gc->gcv.function == GXset - ? (gc->depth == 1 ? 1 : WhitePixel(0,0)) - : (gc->depth == 1 ? 0 : BlackPixel(0,0))), + set_color (dst->cgc, + (gc->gcv.function == GXset + ? (gc->depth == 1 ? 1 : WhitePixel(0,0)) + : (gc->depth == 1 ? 0 : BlackPixel(0,0))), gc->depth, gc->gcv.alpha_allowed_p, YES); draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES); return 0; @@ -592,7 +636,12 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, BOOL mask_p = NO; BOOL free_cgi_p = NO; - if (src->type == PIXMAP) { + +#ifndef USE_IPHONE + // Because of the backbuffer, all iPhone Windows work like Pixmaps. + if (src->type == PIXMAP) +# endif + { // 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 @@ -625,9 +674,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, free_cgi_p = YES; } - if (src->pixmap.depth == 1) + if (src->type == PIXMAP && src->pixmap.depth == 1) mask_p = YES; +# ifndef USE_IPHONE } else { /* (src->type == WINDOW) */ NSRect nsfrom; // NSRect != CGRect on 10.4 @@ -684,21 +734,27 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace"); CGDataProviderRelease (prov); } + +# endif // !USE_IPHONE } + CGContextRef cgc = dst->cgc; + if (mask_p) { // src depth == 1 push_bg_gc (dst, gc, YES); // fill the destination rectangle with solid background... - CGContextFillRect (dst->cgc, orig_dst_rect); + CGContextFillRect (cgc, orig_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 (dst->cgc, gc->gcv.foreground, gc->depth, + set_color (cgc, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p, YES); - CGContextClipToMask (dst->cgc, dst_rect, cgi); - CGContextFillRect (dst->cgc, dst_rect); + CGContextClipToMask (cgc, dst_rect, cgi); + CGContextFillRect (cgc, dst_rect); pop_gc (dst, gc); @@ -713,17 +769,21 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, // being copied. // if (clipped) { - set_color (dst->cgc, gc->gcv.background, gc->depth, + set_color (cgc, gc->gcv.background, gc->depth, gc->gcv.alpha_allowed_p, YES); - CGContextFillRect (dst->cgc, orig_dst_rect); + CGContextFillRect (cgc, orig_dst_rect); } if (cgi) { // copy the CGImage onto the destination CGContext //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (dst->cgc, dst_rect, cgi); + CGContextDrawImage (cgc, dst_rect, cgi); } else { // No cgi means src == dst, and both are Windows. + +# ifdef USE_IPHONE + abort(); // No NSCopyBits on iOS, but shouldn't be reached anyway. +# else // !USE_IPHONE NSRect nsfrom; nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4 nsfrom.origin.y = src_rect.origin.y; @@ -733,12 +793,14 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, nsto.x = dst_rect.origin.x; nsto.y = dst_rect.origin.y; NSCopyBits (0, nsfrom, nsto); +# endif // !USE_IPHONE } pop_gc (dst, gc); } if (free_cgi_p) CGImageRelease (cgi); + if (releaseme) [releaseme release]; invalidate_drawable_cache (dst); return 0; @@ -781,13 +843,14 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) push_fg_gc (d, gc, NO); - set_line_mode (d->cgc, &gc->gcv); - CGContextBeginPath (d->cgc); - CGContextMoveToPoint (d->cgc, p.x, p.y); + CGContextRef cgc = d->cgc; + set_line_mode (cgc, &gc->gcv); + CGContextBeginPath (cgc); + CGContextMoveToPoint (cgc, p.x, p.y); p.x = wr.origin.x + x2; p.y = wr.origin.y + wr.size.height - y2; - CGContextAddLineToPoint (d->cgc, p.x, p.y); - CGContextStrokePath (d->cgc); + CGContextAddLineToPoint (cgc, p.x, p.y); + CGContextStrokePath (cgc); pop_gc (d, gc); invalidate_drawable_cache (d); return 0; @@ -801,7 +864,10 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, NSPoint p; CGRect wr = d->frame; push_fg_gc (d, gc, NO); - set_line_mode (d->cgc, &gc->gcv); + + 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. @@ -812,8 +878,8 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, p.x = wr.origin.x + points->x; p.y = wr.origin.y + wr.size.height - points->y; points++; - CGContextBeginPath (d->cgc); - CGContextMoveToPoint (d->cgc, p.x, p.y); + CGContextBeginPath (cgc); + CGContextMoveToPoint (cgc, p.x, p.y); for (i = 1; i < count; i++) { if (mode == CoordModePrevious) { p.x += points->x; @@ -822,11 +888,11 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, p.x = wr.origin.x + points->x; p.y = wr.origin.y + wr.size.height - points->y; } - CGContextAddLineToPoint (d->cgc, p.x, p.y); + CGContextAddLineToPoint (cgc, p.x, p.y); points++; } - if (closed_p) CGContextClosePath (d->cgc); - CGContextStrokePath (d->cgc); + if (closed_p) CGContextClosePath (cgc); + CGContextStrokePath (cgc); pop_gc (d, gc); invalidate_drawable_cache (d); return 0; @@ -839,19 +905,21 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) int i; CGRect wr = d->frame; + CGContextRef cgc = d->cgc; + push_fg_gc (d, gc, NO); - set_line_mode (d->cgc, &gc->gcv); - CGContextBeginPath (d->cgc); + set_line_mode (cgc, &gc->gcv); + CGContextBeginPath (cgc); for (i = 0; i < count; i++) { - CGContextMoveToPoint (d->cgc, + CGContextMoveToPoint (cgc, wr.origin.x + segments->x1, wr.origin.y + wr.size.height - segments->y1); - CGContextAddLineToPoint (d->cgc, + CGContextAddLineToPoint (cgc, wr.origin.x + segments->x2, wr.origin.y + wr.size.height - segments->y2); segments++; } - CGContextStrokePath (d->cgc); + CGContextStrokePath (cgc); pop_gc (d, gc); invalidate_drawable_cache (d); return 0; @@ -894,12 +962,13 @@ draw_rect (Display *dpy, Drawable d, GC gc, push_bg_gc (d, gc, fill_p); } + CGContextRef cgc = d->cgc; if (fill_p) - CGContextFillRect (d->cgc, r); + CGContextFillRect (cgc, r); else { if (gc) - set_line_mode (d->cgc, &gc->gcv); - CGContextStrokeRect (d->cgc, r); + set_line_mode (cgc, &gc->gcv); + CGContextStrokeRect (cgc, r); } if (gc) @@ -929,6 +998,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) { CGRect wr = d->frame; int i; + CGContextRef cgc = d->cgc; push_fg_gc (d, gc, YES); for (i = 0; i < n; i++) { CGRect r; @@ -936,7 +1006,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height; r.size.width = rects->width; r.size.height = rects->height; - CGContextFillRect (d->cgc, r); + CGContextFillRect (cgc, r); rects++; } pop_gc (d, gc); @@ -949,7 +1019,8 @@ int XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) { Assert (win->type == WINDOW, "not a window"); - set_color (win->cgc, win->window.background, 32, NO, YES); + CGContextRef cgc = win->cgc; + set_color (cgc, win->window.background, 32, NO, YES); draw_rect (dpy, win, 0, x, y, w, h, NO, YES); return 0; } @@ -962,7 +1033,8 @@ XFillPolygon (Display *dpy, Drawable d, GC gc, CGRect wr = d->frame; int i; push_fg_gc (d, gc, YES); - CGContextBeginPath (d->cgc); + CGContextRef cgc = d->cgc; + CGContextBeginPath (cgc); float x = 0, y = 0; for (i = 0; i < npoints; i++) { if (i > 0 && mode == CoordModePrevious) { @@ -974,15 +1046,15 @@ XFillPolygon (Display *dpy, Drawable d, GC gc, } if (i == 0) - CGContextMoveToPoint (d->cgc, x, y); + CGContextMoveToPoint (cgc, x, y); else - CGContextAddLineToPoint (d->cgc, x, y); + CGContextAddLineToPoint (cgc, x, y); } - CGContextClosePath (d->cgc); + CGContextClosePath (cgc); if (gc->gcv.fill_rule == EvenOddRule) - CGContextEOFillPath (d->cgc); + CGContextEOFillPath (cgc); else - CGContextFillPath (d->cgc); + CGContextFillPath (cgc); pop_gc (d, gc); invalidate_drawable_cache (d); return 0; @@ -1014,25 +1086,26 @@ draw_arc (Display *dpy, Drawable d, GC gc, int x, int y, push_fg_gc (d, gc, fill_p); - CGContextBeginPath (d->cgc); + CGContextRef cgc = d->cgc; + CGContextBeginPath (cgc); - CGContextSaveGState(d->cgc); - CGContextTranslateCTM (d->cgc, ctr.x, ctr.y); - CGContextScaleCTM (d->cgc, width/2.0, height/2.0); + CGContextSaveGState(cgc); + CGContextTranslateCTM (cgc, ctr.x, ctr.y); + CGContextScaleCTM (cgc, width/2.0, height/2.0); if (fill_p) - CGContextMoveToPoint (d->cgc, 0, 0); + CGContextMoveToPoint (cgc, 0, 0); - CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise); - CGContextRestoreGState (d->cgc); // restore before stroke, for line width + CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise); + CGContextRestoreGState (cgc); // restore before stroke, for line width if (closed_p) - CGContextClosePath (d->cgc); // for proper line joining + CGContextClosePath (cgc); // for proper line joining if (fill_p) { - CGContextFillPath (d->cgc); + CGContextFillPath (cgc); } else { - set_line_mode (d->cgc, &gc->gcv); - CGContextStrokePath (d->cgc); + set_line_mode (cgc, &gc->gcv); + CGContextStrokePath (cgc); } pop_gc (d, gc); @@ -1100,6 +1173,10 @@ gcv_defaults (XGCValues *gcv, int depth) static void set_gcv (GC gc, XGCValues *from, unsigned long mask) { + if (! mask) return; + if (! gc) abort(); + if (! from) abort(); + if (mask & GCFunction) gc->gcv.function = from->function; if (mask & GCForeground) gc->gcv.foreground = from->foreground; if (mask & GCBackground) gc->gcv.background = from->background; @@ -1557,11 +1634,13 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, 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. - set_color (d->cgc, (gc->gcv.function == GXset + set_color (cgc, (gc->gcv.function == GXset ? (gc->depth == 1 ? 1 : WhitePixel(0,0)) : (gc->depth == 1 ? 0 : BlackPixel(0,0))), gc->depth, gc->gcv.alpha_allowed_p, YES); @@ -1602,7 +1681,7 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, kCGRenderingIntentDefault); CGDataProviderRelease (prov); //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (d->cgc, r, cgi); + CGContextDrawImage (cgc, r, cgi); CGImageRelease (cgi); } else { // (bpp == 1) @@ -1632,10 +1711,10 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, NO); /* interpolate */ push_fg_gc (d, gc, YES); - CGContextFillRect (d->cgc, r); // foreground color - CGContextClipToMask (d->cgc, r, mask); - set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES); - CGContextFillRect (d->cgc, r); // background color + CGContextFillRect (cgc, r); // foreground color + CGContextClipToMask (cgc, r, mask); + set_color (cgc, gc->gcv.background, gc->depth, NO, YES); + CGContextFillRect (cgc, r); // background color pop_gc (d, gc); free (flipped); @@ -1656,21 +1735,36 @@ XGetImage (Display *dpy, Drawable d, int x, int y, { const unsigned char *data = 0; int depth, ibpp, ibpl, alpha_first_p; +# ifndef USE_IPHONE 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"); - if (d->type == PIXMAP) { - depth = d->pixmap.depth; - alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst. - ibpp = CGBitmapContextGetBitsPerPixel (d->cgc); - ibpl = CGBitmapContextGetBytesPerRow (d->cgc); - data = CGBitmapContextGetData (d->cgc); + CGContextRef cgc = d->cgc; + +#ifndef USE_IPHONE + // Because of the backbuffer, all iPhone Windows work like Pixmaps. + if (d->type == PIXMAP) +# endif + { + depth = (d->type == PIXMAP + ? d->pixmap.depth + : 32); + // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst. + // If it's an iPhone window, it's the other way around. + alpha_first_p = (d->type == PIXMAP); + ibpp = CGBitmapContextGetBitsPerPixel (cgc); + ibpl = CGBitmapContextGetBytesPerRow (cgc); + data = CGBitmapContextGetData (cgc); Assert (data, "CGBitmapContextGetData failed"); - } else { + +# ifndef USE_IPHONE + } else { /* (d->type == WINDOW) */ + // get the bits (desired sub-rectangle) out of the NSView NSRect nsfrom; nsfrom.origin.x = x; @@ -1684,6 +1778,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y, ibpl = [bm bytesPerRow]; data = [bm bitmapData]; Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed"); +# endif // !USE_IPHONE } // data points at (x,y) with ibpl rowstride. ignore x,y from now on. @@ -1762,12 +1857,15 @@ XGetImage (Display *dpy, Drawable d, int x, int y, } } +# ifndef USE_IPHONE if (bm) [bm release]; +# endif return image; } + /* Returns a transformation matrix to do rotation as per the provided EXIF "Orientation" value. */ @@ -1826,14 +1924,19 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 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... // @@ -1843,6 +1946,9 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 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; @@ -1879,26 +1985,28 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, if (d->type == WINDOW) XClearWindow (dpy, d); else { - set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES); + set_color (cgc, BlackPixel(dpy,0), 32, NO, YES); draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES); } CGAffineTransform trans = exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size); - CGContextSaveGState (d->cgc); - CGContextConcatCTM (d->cgc, + CGContextSaveGState (cgc); + CGContextConcatCTM (cgc, CGAffineTransformMakeTranslation (dst.origin.x, dst.origin.y)); - CGContextConcatCTM (d->cgc, trans); + CGContextConcatCTM (cgc, trans); //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (d->cgc, dst2, cgi); - CGContextRestoreGState (d->cgc); + 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; @@ -1911,6 +2019,7 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, } + Pixmap XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable, const char *data, @@ -2065,7 +2174,9 @@ query_font (Font fid) f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct)); int i; +# ifndef USE_IPHONE NSBezierPath *bpath = [NSBezierPath bezierPath]; +# endif for (i = first; i <= last; i++) { unsigned char str[2]; @@ -2074,6 +2185,10 @@ query_font (Font fid) NSString *nsstr = [NSString stringWithCString:(char *) str encoding:NSISOLatin1StringEncoding]; + NSPoint advancement; + NSRect bbox; + +# ifndef USE_IPHONE /* I can't believe we have to go through this bullshit just to convert a 'char' to an NSGlyph!! @@ -2111,8 +2226,6 @@ query_font (Font fid) [NSString sizeWithAttributes] both return an advancement-sized rectangle, not a rectangle completely enclosing the glyph's ink. */ - NSPoint advancement; - NSRect bbox; advancement.x = advancement.y = 0; [bpath removeAllPoints]; [bpath moveToPoint:advancement]; @@ -2120,6 +2233,37 @@ query_font (Font fid) advancement = [bpath currentPoint]; bbox = [bpath bounds]; +# else // USE_IPHONE + + UIFont *ff = fid->nsfont; + CGSize size = [nsstr sizeWithFont:ff]; + + /* sizeWithFont gives us a character's "width" and "height". + There is no way to get a character's "lbearing", "rbearing", + or "descent". We do have the font's overall "descent", though. + + drawAtPoint (to an offscreen CGContext) returns "width" and the + "ascent" of the font (not of the glyph, I think) so that doesn't + help. + + CGFontGetGlyphBBoxes might help (if it actually returns a bounding + box and not just the ascent/width again, which I don't know) but + we can't use it anyway because there is no way to map a unichar to + a CGGlyph. + + Fuck you sideways, Apple. + */ + + bbox.origin.x = 0; + bbox.origin.y = [ff descender]; + bbox.size.width = size.width; + bbox.size.height = size.height; + + advancement.x = size.width; + advancement.y = 0; + +# endif // USE_IPHONE + /* Now that we know the advancement and bounding box, we can compute the lbearing and rbearing. */ @@ -2160,7 +2304,6 @@ query_font (Font fid) (int) advancement.x, (int) advancement.y); #endif } - } @@ -2253,15 +2396,20 @@ try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size, } static NSFont * -try_native_font (const char *name, char **name_ret, float *size_ret) +try_native_font (const char *name, float scale, + char **name_ret, float *size_ret) { if (!name) return 0; const char *spc = strrchr (name, ' '); if (!spc) return 0; - int size = 0; - if (1 != sscanf (spc, " %d ", &size)) return 0; + int dsize = 0; + if (1 != sscanf (spc, " %d ", &dsize)) return 0; + float size = dsize; + if (size <= 4) return 0; + size *= scale; + char *name2 = strdup (name); name2[strlen(name2) - strlen(spc)] = 0; NSString *nsname = [NSString stringWithCString:name2 @@ -2283,6 +2431,7 @@ try_native_font (const char *name, char **name_ret, float *size_ret) static NSFont * random_font (BOOL bold, BOOL ital, float size, char **name_ret) { +# ifndef USE_IPHONE NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) | (ital ? NSItalicFontMask : NSUnitalicFontMask)); NSArray *fonts = [[NSFontManager sharedFontManager] @@ -2321,11 +2470,70 @@ random_font (BOOL bold, BOOL ital, float size, char **name_ret) // None of the fonts support ASCII? return 0; + +# else // USE_IPHONE + + NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100]; + NSArray *families = [UIFont familyNames]; + NSMutableDictionary *famdict = [NSMutableDictionary + dictionaryWithCapacity:100]; + NSObject *y = [NSNumber numberWithBool:YES]; + for (NSString *name in families) { + // There are many dups in the families array -- uniquify it. + [famdict setValue:y forKey:name]; + } + + for (NSString *name in famdict) { + for (NSString *fn in [UIFont fontNamesForFamilyName:name]) { + +# define MATCH(X) \ + ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \ + != NSNotFound) + + BOOL bb = MATCH(@"Bold"); + BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique"); + + if (!bold != !bb) continue; + if (!ital != !ii) continue; + + /* 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 + ) + continue; + + [fonts addObject:fn]; +# undef MATCH + } + } + + if (! [fonts count]) return 0; // Nothing suitable? + + int i = random() % [fonts count]; + NSString *name = [fonts objectAtIndex:i]; + UIFont *ff = [UIFont fontWithName:name size:size]; + *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]); + + return ff; + +# endif // USE_IPHONE } static NSFont * -try_xlfd_font (const char *name, char **name_ret, float *size_ret) +try_xlfd_font (const char *name, float scale, + char **name_ret, float *size_ret) { NSFont *nsfont = 0; BOOL bold = NO; @@ -2376,6 +2584,8 @@ try_xlfd_font (const char *name, char **name_ret, float *size_ret) if (size < 6 || size > 1000) size = 12; + size *= scale; + if (rand) nsfont = random_font (bold, ital, size, &ps_name); @@ -2417,7 +2627,14 @@ XLoadFont (Display *dpy, const char *name) { Font fid = (Font) calloc (1, sizeof(*fid)); - fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size); + float scale = 1; + +# ifdef USE_IPHONE + // Scale up fonts on Retina displays. + scale = dpy->main_window->window.view.contentScaleFactor; +# endif + + fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size); if (!fid->nsfont && name && strchr (name, ' ') && @@ -2430,11 +2647,10 @@ XLoadFont (Display *dpy, const char *name) } if (! fid->nsfont) - fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size); + fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size); // We should never return NULL for XLFD fonts. if (!fid->nsfont) { - NSLog(@"no NSFont for \"%s\"", name); abort(); } CFRetain (fid->nsfont); // needed for garbage collection? @@ -2553,11 +2769,11 @@ XTextWidth (XFontStruct *f, const char *s, int length) static void -set_font (CGContextRef cgc, GC gc) +set_font (Display *dpy, CGContextRef cgc, GC gc) { Font font = gc->gcv.font; if (! font) { - font = XLoadFont (0, 0); + font = XLoadFont (dpy, 0); gc->gcv.font = font; [gc->gcv.font->nsfont retain]; CFRetain (gc->gcv.font->nsfont); // needed for garbage collection? @@ -2593,13 +2809,14 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y, But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)! */ + CGContextRef cgc = d->cgc; push_fg_gc (d, gc, YES); - set_font (d->cgc, gc); + set_font (dpy, cgc, gc); - CGContextSetTextDrawingMode (d->cgc, kCGTextFill); + CGContextSetTextDrawingMode (cgc, kCGTextFill); if (gc->gcv.antialias_p) - CGContextSetShouldAntialias (d->cgc, YES); - CGContextShowTextAtPoint (d->cgc, + CGContextSetShouldAntialias (cgc, YES); + CGContextShowTextAtPoint (cgc, wr.origin.x + x, wr.origin.y + wr.size.height - y, str, len); @@ -2731,7 +2948,8 @@ XSetClipMask (Display *dpy, GC gc, Pixmap m) gc->gcv.clip_mask = copy_pixmap (dpy, m); if (gc->gcv.clip_mask) - gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc); + gc->clip_mask = + CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc); else gc->clip_mask = 0; @@ -2753,6 +2971,17 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, int *win_x_ret, int *win_y_ret, unsigned int *mask_ret) { Assert (w->type == WINDOW, "not a window"); + +# ifdef USE_IPHONE + int x = w->window.last_mouse_x; + int y = w->window.last_mouse_y; + if (root_x_ret) *root_x_ret = x; + if (root_y_ret) *root_y_ret = y; + if (win_x_ret) *win_x_ret = x; + if (win_y_ret) *win_y_ret = y; + +# else // !USE_IPHONE + NSWindow *nsw = [w->window.view window]; NSPoint wpos; // get bottom left of window on screen, from bottom left @@ -2776,8 +3005,13 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, NSScreen *screen = (screens && [screens count] > 0 ? [screens objectAtIndex:0] : [NSScreen mainScreen]); +#ifdef USE_IPHONE + double s = w->window.view.contentScaleFactor; +#else + int s = 1; +#endif NSRect srect = [screen frame]; - vpos.y = srect.size.height - vpos.y; + vpos.y = (s * srect.size.height) - vpos.y; // get the mouse position on window, from bottom left NSEvent *e = [NSApp currentEvent]; @@ -2794,8 +3028,9 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, 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); +# endif // !USE_IPHONE - if (mask_ret) *mask_ret = 0; // #### + if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers? if (root_ret) *root_ret = 0; if (child_ret) *child_ret = 0; return True; @@ -2808,6 +3043,15 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w, Window *child_ret) { Assert (w->type == WINDOW, "not a window"); + +# ifdef USE_IPHONE + + NSPoint p; + p.x = src_x; + p.y = src_y; + +# else // !USE_IPHONE + NSWindow *nsw = [w->window.view window]; NSPoint wpos; // get bottom left of window on screen, from bottom left @@ -2831,8 +3075,13 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w, NSScreen *screen = (screens && [screens count] > 0 ? [screens objectAtIndex:0] : [NSScreen mainScreen]); +# ifdef USE_IPHONE + double s = w->window.view.contentScaleFactor; +# else + int s = 1; +# endif NSRect srect = [screen frame]; - vpos.y = srect.size.height - vpos.y; + vpos.y = (s * srect.size.height) - vpos.y; // point starts out relative to top left of view NSPoint p; @@ -2842,6 +3091,7 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w, // get point relative to top left of screen p.x += vpos.x; p.y += vpos.y; +# endif // !USE_IPHONE *dest_x_ret = p.x; *dest_y_ret = p.y;