tdevdraw: MacBook retina support - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit ef99c9f1ae9a620d997493558d5029d1d89f4a30
 (DIR) parent 60a47420a8887d7fcda37efe4b6baf730d9cc936
 (HTM) Author: Rob Kroeger <robkroeger@gmail.com>
       Date:   Tue, 16 Oct 2012 13:55:44 -0400
       
       devdraw: MacBook retina support
       
       Enable with export devdrawretina=1 (everything will be smaller).
       
       R=rsc
       CC=plan9port.codebot
       http://codereview.appspot.com/6592072
       
       Diffstat:
         M man/man1/devdraw.1                  |       7 +++++++
         M src/cmd/devdraw/cocoa-screen.m      |     129 +++++++++++++++++++++++++++++--
       
       2 files changed, 128 insertions(+), 8 deletions(-)
       ---
 (DIR) diff --git a/man/man1/devdraw.1 b/man/man1/devdraw.1
       t@@ -10,6 +10,13 @@ invoked via
        .I Devdraw
        serves a custom graphics protocol and is the only program
        that talks directly to X window servers.
       +On Macintosh, setting
       +.BI devdrawretina
       +to
       +.BI 1
       +will cause
       +.I devdraw
       +to use all available physical pixels on a retina display.
        .SH SOURCE
        .B \*9/src/cmd/devdraw
        .SH "SEE ALSO
 (DIR) diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m
       t@@ -38,6 +38,12 @@ int useliveresizing = 0;
        int useoldfullscreen = 0;
        int usebigarrow = 0;
        
       +/*
       + * By default, devdraw ignores retina displays. A non-zero evironment variable
       + * |devdrawretina| will override this.
       + */
       +int devdrawretina = 0;
       +
        void
        usage(void)
        {
       t@@ -50,6 +56,8 @@ usage(void)
        void
        threadmain(int argc, char **argv)
        {
       +        char *envvar;
       +
                /*
                 * Move the protocol off stdin/stdout so that
                 * any inadvertent prints don't screw things up.
       t@@ -77,6 +85,9 @@ threadmain(int argc, char **argv)
                        usage();
                }ARGEND
        
       +        if (envvar = getenv("devdrawretina"))
       +                devdrawretina = atoi(envvar) > 0;
       +
                if(OSX_VERSION < 100700)
                        [NSAutoreleasePool new];
        
       t@@ -99,6 +110,8 @@ struct
                int                        needimg;
                int                        deferflush;
                NSCursor                *cursor;
       +        CGFloat                topointscale;
       +        CGFloat                topixelscale;
        } win;
        
        struct
       t@@ -127,6 +140,12 @@ static void acceptresizing(int);
        
        static NSCursor* makecursor(Cursor*);
        
       +static NSSize winsizepixels();
       +static NSSize winsizepoints();
       +static NSRect scalerect(NSRect, CGFloat);
       +static NSPoint scalepoint(NSPoint, CGFloat);
       +static NSRect dilate(NSRect);
       +
        @implementation appdelegate
        - (void)applicationDidFinishLaunching:(id)arg
        {
       t@@ -349,10 +368,10 @@ static Memimage*
        initimg(void)
        {
                Memimage *i;
       -        NSSize size;
       +        NSSize size, ptsize;
                Rectangle r;
        
       -        size = [win.content bounds].size;
       +        size = winsizepixels();
                LOG(@"initimg %.0f %.0f", size.width, size.height);
        
                r = Rect(0, 0, size.width, size.height);
       t@@ -373,6 +392,10 @@ initimg(void)
                        colorSpaceName:NSDeviceRGBColorSpace
                        bytesPerRow:bytesperline(r, 32)
                        bitsPerPixel:32];
       +        ptsize = winsizepoints();
       +        [win.img setSize: ptsize];
       +        win.topixelscale = size.width / ptsize.width;
       +        win.topointscale = 1.0f / win.topixelscale;
                return i;
        }
        
       t@@ -452,6 +475,9 @@ enum
                Handlesize = 3*Barsize + 1*Pixel,
        };
        
       +/*
       + * |rect| is in pixel coordinates.
       + */
        static void
        flushimg(NSRect rect)
        {
       t@@ -461,7 +487,7 @@ flushimg(NSRect rect)
                        return;
        
                if(win.needimg){
       -                if(!NSEqualSizes(rect.size, [win.img size])){
       +                if(!NSEqualSizes(scalerect(rect, win.topointscale).size, [win.img size])){
                                LOG(@"flushimg reject %.0f %.0f",
                                        rect.size.width, rect.size.height);
                                [win.content unlockFocus];
       t@@ -482,8 +508,12 @@ flushimg(NSRect rect)
                 * Acme.
                 */
                r = [win.content bounds];
       +        rect = dilate(scalerect(rect, win.topointscale));
                r.size.height -= Cornersize;
                dr = NSIntersectionRect(r, rect);
       +        LOG(@"r %.0f %.0f", r.origin.x, r.origin.y, rect.size.width, rect.size.height);
       +        LOG(@"rect in points %f %f %.0f %.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
       +        LOG(@"dr in points %f %f %.0f %.0f", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
                drawimg(dr, NSCompositeCopy);
        
                r.origin.y = r.size.height;
       t@@ -545,6 +575,9 @@ flushwin(void)
                }
        }
        
       +/*
       + * |dr| is sized in points. What if I make it pixels?
       + */
        static void
        drawimg(NSRect dr, uint op)
        {
       t@@ -556,7 +589,14 @@ drawimg(NSRect dr, uint op)
                        return;
        
                sr =  [win.content convertRect:dr fromView:nil];
       +        LOG(@"before dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
       +        LOG(@"before sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height);
       +
       +        dr = scalerect(dr, win.topixelscale);
       +        sr = scalerect(sr, win.topixelscale);
        
       +        LOG(@"dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
       +        LOG(@"sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height);
                if(OSX_VERSION >= 100800){
                        i = CGImageCreateWithImageInRect([win.img CGImage], NSRectToCGRect(dr));
                        c = [[WIN graphicsContext] graphicsPort];
       t@@ -564,8 +604,9 @@ drawimg(NSRect dr, uint op)
                        CGContextSaveGState(c);
                        if(op == NSCompositeSourceIn)
                                CGContextSetBlendMode(c, kCGBlendModeSourceIn);
       +                        LOG(@"wim.img size %f %f\n", [win.img size].width, [win.img size].height);
                        CGContextTranslateCTM(c, 0, [win.img size].height);
       -                CGContextScaleCTM(c, 1, -1);
       +                CGContextScaleCTM(c, win.topointscale, -win.topointscale);
                        CGContextDrawImage(c, NSRectToCGRect(sr), i);
                        CGContextRestoreGState(c);
        
       t@@ -871,6 +912,10 @@ getmousepos(void)
        
                p = [WIN mouseLocationOutsideOfEventStream];
                q = [win.content convertPoint:p fromView:nil];
       +
       +        /* q is in point coordinates. in.mpos is in pixels. */
       +        q = scalepoint(q, win.topixelscale);
       +
                in.mpos.x = round(q.x);
                in.mpos.y = round(q.y);
        
       t@@ -1023,7 +1068,7 @@ sendmouse(void)
                NSSize size;
                int b;
        
       -        size = [win.content bounds].size;
       +        size = winsizepixels();
                mouserect = Rect(0, 0, size.width, size.height);
        
                b = in.kbuttons | in.mbuttons | in.mscroll;
       t@@ -1031,6 +1076,9 @@ sendmouse(void)
                in.mscroll = 0;
        }
        
       +/*
       + * |p| is in pixels.
       + */
        void
        setmouse(Point p)
        {
       t@@ -1049,7 +1097,7 @@ setmouse(Point p)
                if([WIN inLiveResize])
                        return;
        
       -        in.mpos = NSMakePoint(p.x, p.y);        // race condition
       +        in.mpos = scalepoint(NSMakePoint(p.x, p.y), win.topointscale);        // race condition
        
                q = [win.content convertPoint:in.mpos toView:nil];
                q = [WIN convertBaseToScreen:q];
       t@@ -1060,19 +1108,31 @@ setmouse(Point p)
                CGWarpMouseCursorPosition(NSPointToCGPoint(q));
        }
        
       +/*
       + *  |r| is in points.
       + */
        static void
        followzoombutton(NSRect r)
        {
                NSRect wr;
                Point p;
       +        NSPoint pt;
        
                wr = [WIN frame];
                wr.origin.y += wr.size.height;
                r.origin.y += r.size.height;
        
                getmousepos();
       -        p.x = (r.origin.x - wr.origin.x) + in.mpos.x;
       -        p.y = -(r.origin.y - wr.origin.y) + in.mpos.y;
       +        pt.x = in.mpos.x;
       +        pt.y = in.mpos.y;
       +        pt = scalepoint(pt, win.topointscale);
       +        pt.x = (r.origin.x - wr.origin.x) + pt.x;
       +        pt.y = -(r.origin.y - wr.origin.y) + pt.y;
       +        pt = scalepoint(pt, win.topixelscale);
       +
       +        p.x = pt.x;
       +        p.y = pt.y;
       +
                setmouse(p);
        }
        
       t@@ -1181,6 +1241,7 @@ makemenu(void)
                [m release];
        }
        
       +// FIXME: Introduce a high-resolution Glenda image.
        static void
        makeicon(void)
        {
       t@@ -1285,6 +1346,9 @@ setcursor0(Cursor *c)
                        [d release];
        }
        
       +/*
       + * Cursors will be scaled on retina display.
       + */
        static NSCursor*
        makecursor(Cursor *c)
        {
       t@@ -1334,3 +1398,52 @@ topwin(void)
                in.willactivate = 1;
                [NSApp activateIgnoringOtherApps:YES];
        }
       +
       +static NSSize
       +winsizepoints()
       +{
       +    return [win.content bounds].size;
       +}
       +
       +static NSSize
       +winsizepixels()
       +{
       +        if (OSX_VERSION >= 100700 && devdrawretina)
       +                return [win.content convertSizeToBacking: winsizepoints()];
       +        else
       +                return winsizepoints();
       +}
       +
       +static NSRect
       +scalerect(NSRect r, CGFloat scale)
       +{
       +        r.origin.x *= scale;
       +        r.origin.y *= scale;
       +        r.size.width *= scale;
       +         r.size.height *= scale;
       +         return r;
       +}
       +
       +/*
       + * Expands rectangle |r|'s bounds to more inclusive integer bounds to
       + * eliminate 1 pixel gaps.
       + */
       +static NSRect
       +dilate(NSRect r)
       +{
       +        if(win.topixelscale > 1.0f){
       +                r.origin.x = floorf(r.origin.x);
       +                r.origin.y = floorf(r.origin.y);
       +                r.size.width = ceilf(r.size.width + 0.5);
       +                r.size.height = ceilf(r.size.height + 0.5);
       +        }
       +        return r;
       +}
       +
       +static NSPoint
       +scalepoint(NSPoint pt, CGFloat scale)
       +{
       +    pt.x *= scale;
       +    pt.y *= scale;
       +    return pt;
       +}