devdraw: MacBook retina support

Enable with export devdrawretina=1 (everything will be smaller).

R=rsc
CC=plan9port.codebot
http://codereview.appspot.com/6592072
This commit is contained in:
Rob Kroeger 2012-10-16 13:55:44 -04:00 committed by Russ Cox
parent 60a47420a8
commit ef99c9f1ae
2 changed files with 128 additions and 8 deletions

View file

@ -10,6 +10,13 @@ invoked via
.I Devdraw .I Devdraw
serves a custom graphics protocol and is the only program serves a custom graphics protocol and is the only program
that talks directly to X window servers. 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 .SH SOURCE
.B \*9/src/cmd/devdraw .B \*9/src/cmd/devdraw
.SH "SEE ALSO .SH "SEE ALSO

View file

@ -38,6 +38,12 @@ int useliveresizing = 0;
int useoldfullscreen = 0; int useoldfullscreen = 0;
int usebigarrow = 0; int usebigarrow = 0;
/*
* By default, devdraw ignores retina displays. A non-zero evironment variable
* |devdrawretina| will override this.
*/
int devdrawretina = 0;
void void
usage(void) usage(void)
{ {
@ -50,6 +56,8 @@ usage(void)
void void
threadmain(int argc, char **argv) threadmain(int argc, char **argv)
{ {
char *envvar;
/* /*
* Move the protocol off stdin/stdout so that * Move the protocol off stdin/stdout so that
* any inadvertent prints don't screw things up. * any inadvertent prints don't screw things up.
@ -77,6 +85,9 @@ threadmain(int argc, char **argv)
usage(); usage();
}ARGEND }ARGEND
if (envvar = getenv("devdrawretina"))
devdrawretina = atoi(envvar) > 0;
if(OSX_VERSION < 100700) if(OSX_VERSION < 100700)
[NSAutoreleasePool new]; [NSAutoreleasePool new];
@ -99,6 +110,8 @@ struct
int needimg; int needimg;
int deferflush; int deferflush;
NSCursor *cursor; NSCursor *cursor;
CGFloat topointscale;
CGFloat topixelscale;
} win; } win;
struct struct
@ -127,6 +140,12 @@ static void acceptresizing(int);
static NSCursor* makecursor(Cursor*); 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 @implementation appdelegate
- (void)applicationDidFinishLaunching:(id)arg - (void)applicationDidFinishLaunching:(id)arg
{ {
@ -349,10 +368,10 @@ static Memimage*
initimg(void) initimg(void)
{ {
Memimage *i; Memimage *i;
NSSize size; NSSize size, ptsize;
Rectangle r; Rectangle r;
size = [win.content bounds].size; size = winsizepixels();
LOG(@"initimg %.0f %.0f", size.width, size.height); LOG(@"initimg %.0f %.0f", size.width, size.height);
r = Rect(0, 0, size.width, size.height); r = Rect(0, 0, size.width, size.height);
@ -373,6 +392,10 @@ initimg(void)
colorSpaceName:NSDeviceRGBColorSpace colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:bytesperline(r, 32) bytesPerRow:bytesperline(r, 32)
bitsPerPixel:32]; bitsPerPixel:32];
ptsize = winsizepoints();
[win.img setSize: ptsize];
win.topixelscale = size.width / ptsize.width;
win.topointscale = 1.0f / win.topixelscale;
return i; return i;
} }
@ -452,6 +475,9 @@ enum
Handlesize = 3*Barsize + 1*Pixel, Handlesize = 3*Barsize + 1*Pixel,
}; };
/*
* |rect| is in pixel coordinates.
*/
static void static void
flushimg(NSRect rect) flushimg(NSRect rect)
{ {
@ -461,7 +487,7 @@ flushimg(NSRect rect)
return; return;
if(win.needimg){ 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", LOG(@"flushimg reject %.0f %.0f",
rect.size.width, rect.size.height); rect.size.width, rect.size.height);
[win.content unlockFocus]; [win.content unlockFocus];
@ -482,8 +508,12 @@ flushimg(NSRect rect)
* Acme. * Acme.
*/ */
r = [win.content bounds]; r = [win.content bounds];
rect = dilate(scalerect(rect, win.topointscale));
r.size.height -= Cornersize; r.size.height -= Cornersize;
dr = NSIntersectionRect(r, rect); 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); drawimg(dr, NSCompositeCopy);
r.origin.y = r.size.height; r.origin.y = r.size.height;
@ -545,6 +575,9 @@ flushwin(void)
} }
} }
/*
* |dr| is sized in points. What if I make it pixels?
*/
static void static void
drawimg(NSRect dr, uint op) drawimg(NSRect dr, uint op)
{ {
@ -556,7 +589,14 @@ drawimg(NSRect dr, uint op)
return; return;
sr = [win.content convertRect:dr fromView:nil]; 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){ if(OSX_VERSION >= 100800){
i = CGImageCreateWithImageInRect([win.img CGImage], NSRectToCGRect(dr)); i = CGImageCreateWithImageInRect([win.img CGImage], NSRectToCGRect(dr));
c = [[WIN graphicsContext] graphicsPort]; c = [[WIN graphicsContext] graphicsPort];
@ -564,8 +604,9 @@ drawimg(NSRect dr, uint op)
CGContextSaveGState(c); CGContextSaveGState(c);
if(op == NSCompositeSourceIn) if(op == NSCompositeSourceIn)
CGContextSetBlendMode(c, kCGBlendModeSourceIn); 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); CGContextTranslateCTM(c, 0, [win.img size].height);
CGContextScaleCTM(c, 1, -1); CGContextScaleCTM(c, win.topointscale, -win.topointscale);
CGContextDrawImage(c, NSRectToCGRect(sr), i); CGContextDrawImage(c, NSRectToCGRect(sr), i);
CGContextRestoreGState(c); CGContextRestoreGState(c);
@ -871,6 +912,10 @@ getmousepos(void)
p = [WIN mouseLocationOutsideOfEventStream]; p = [WIN mouseLocationOutsideOfEventStream];
q = [win.content convertPoint:p fromView:nil]; 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.x = round(q.x);
in.mpos.y = round(q.y); in.mpos.y = round(q.y);
@ -1023,7 +1068,7 @@ sendmouse(void)
NSSize size; NSSize size;
int b; int b;
size = [win.content bounds].size; size = winsizepixels();
mouserect = Rect(0, 0, size.width, size.height); mouserect = Rect(0, 0, size.width, size.height);
b = in.kbuttons | in.mbuttons | in.mscroll; b = in.kbuttons | in.mbuttons | in.mscroll;
@ -1031,6 +1076,9 @@ sendmouse(void)
in.mscroll = 0; in.mscroll = 0;
} }
/*
* |p| is in pixels.
*/
void void
setmouse(Point p) setmouse(Point p)
{ {
@ -1049,7 +1097,7 @@ setmouse(Point p)
if([WIN inLiveResize]) if([WIN inLiveResize])
return; 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.content convertPoint:in.mpos toView:nil];
q = [WIN convertBaseToScreen:q]; q = [WIN convertBaseToScreen:q];
@ -1060,19 +1108,31 @@ setmouse(Point p)
CGWarpMouseCursorPosition(NSPointToCGPoint(q)); CGWarpMouseCursorPosition(NSPointToCGPoint(q));
} }
/*
* |r| is in points.
*/
static void static void
followzoombutton(NSRect r) followzoombutton(NSRect r)
{ {
NSRect wr; NSRect wr;
Point p; Point p;
NSPoint pt;
wr = [WIN frame]; wr = [WIN frame];
wr.origin.y += wr.size.height; wr.origin.y += wr.size.height;
r.origin.y += r.size.height; r.origin.y += r.size.height;
getmousepos(); getmousepos();
p.x = (r.origin.x - wr.origin.x) + in.mpos.x; pt.x = in.mpos.x;
p.y = -(r.origin.y - wr.origin.y) + in.mpos.y; 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); setmouse(p);
} }
@ -1181,6 +1241,7 @@ makemenu(void)
[m release]; [m release];
} }
// FIXME: Introduce a high-resolution Glenda image.
static void static void
makeicon(void) makeicon(void)
{ {
@ -1285,6 +1346,9 @@ setcursor0(Cursor *c)
[d release]; [d release];
} }
/*
* Cursors will be scaled on retina display.
*/
static NSCursor* static NSCursor*
makecursor(Cursor *c) makecursor(Cursor *c)
{ {
@ -1334,3 +1398,52 @@ topwin(void)
in.willactivate = 1; in.willactivate = 1;
[NSApp activateIgnoringOtherApps:YES]; [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;
}