tdevdraw: MacBook retina support - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit ef99c9f1ae9a620d997493558d5029d1d89f4a30
parent 60a47420a8887d7fcda37efe4b6baf730d9cc936
Author: Rob Kroeger 
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(-)
---
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
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;
+}