Devdraw now waits for the image: this means that

there is no more blank image when toggling
fullscreen, when unminimizing, or at startup; this
also means that we can live resize, but we only
enable live resizing with Page, because it seems
useless for other apps, and Acme and Sam bug with
it. The tradeoff is that bottom corners are
sometimes automatically rounded. There is a way to
prevent the rounding here:
http://parmanoir.com/Custom_NSThemeFrame
but this would obfuscate the code. Instead, we
make sure that the corners are always rounded.

Closing the window while its content is updated
causes an exception, without this patch. This
seems to happen regularly with stats(1).

This patch avoids a possible deadlock at startup,
which I never experienced. If I recollect right,
there is little chance that this happens on a
multi-core CPU.

Minimizing now activates next app in line, and
Devdraw now stops drawing while minimized.

R=rsc
CC=plan9port.codebot
http://codereview.appspot.com/5499043
This commit is contained in:
David Jeannot 2012-03-05 15:54:26 -05:00 committed by Russ Cox
parent ce26c36433
commit a319037781

View file

@ -1,5 +1,8 @@
/*
* Cocoa's event loop must be in main thread.
*
* Unless otherwise stated, all coordinate systems
* are bottom-left-based.
*/
#define Cursor OSXCursor
@ -27,9 +30,11 @@
AUTOFRAMEWORK(Cocoa)
#define panic sysfatal
#define LOG if(0)NSLog
#define panic sysfatal
int usegestures = 0;
int useliveresizing = 0;
int useoldfullscreen = 0;
int usebigarrow = 0;
@ -91,7 +96,8 @@ struct
int isnfs;
NSView *content;
NSBitmapImageRep *img;
int needflush;
int needimg;
int deferflush;
NSCursor *cursor;
} win;
@ -106,7 +112,8 @@ struct
} in;
static void hidebars(int);
static void drawimg(NSRect);
static void flushimg(NSRect);
static void autoflushwin(int);
static void flushwin(void);
static void followzoombutton(NSRect);
static void getmousepos(void);
@ -140,9 +147,15 @@ static NSCursor* makecursor(Cursor*);
getmousepos();
sendmouse();
}
- (void)windowWillStartLiveResize:(id)arg
{
if(useliveresizing == 0)
[win.content setHidden:YES];
}
- (void)windowDidEndLiveResize:(id)arg
{
[win.content display];
if(useliveresizing == 0)
[win.content setHidden:NO];
}
- (void)windowDidChangeScreen:(id)arg
{
@ -174,6 +187,10 @@ static NSCursor* makecursor(Cursor*);
hidebars(0);
}
}
- (void)windowWillClose:(id)arg
{
autoflushwin(0); /* can crash otherwise */
}
+ (void)callservep9p:(id)arg
{
@ -189,7 +206,7 @@ static NSCursor* makecursor(Cursor*);
+ (void)callflushwin:(id)arg{ flushwin();}
- (void)calltogglefs:(id)arg{ togglefs();}
+ (void)calldrawimg:(NSValue*)v{ drawimg([v rectValue]);}
+ (void)callflushimg:(NSValue*)v{ flushimg([v rectValue]);}
+ (void)callmakewin:(NSValue*)v{ makewin([v pointerValue]);}
+ (void)callsetcursor0:(NSValue*)v{ setcursor0([v pointerValue]);}
@end
@ -208,6 +225,8 @@ attachscreen(char *label, char *winsize)
if(label == nil)
label = "gnot a label";
if(strcmp(label, "page") == 0)
useliveresizing = 1;
/*
* Create window in main thread, else no cursor
@ -235,6 +254,28 @@ attachscreen(char *label, char *winsize)
{
return YES; /* else no keyboard for old fullscreen */
}
- (void)makeKeyAndOrderFront:(id)arg
{
LOG(@"makeKeyAndOrderFront");
autoflushwin(1);
[win.content setHidden:NO];
[super makeKeyAndOrderFront:arg];
}
- (void)miniaturize:(id)arg
{
[super miniaturize:arg];
[NSApp hide:nil];
[win.content setHidden:YES];
autoflushwin(0);
}
- (void)deminiaturize:(id)arg
{
autoflushwin(1);
[win.content setHidden:NO];
[super deminiaturize:arg];
}
@end
double
@ -302,7 +343,6 @@ makewin(char *s)
win.isofs = 0;
win.content = [contentview new];
[WIN setContentView:win.content];
[WIN makeKeyAndOrderFront:nil];
}
static Memimage*
@ -313,6 +353,7 @@ initimg(void)
Rectangle r;
size = [win.content bounds].size;
LOG(@"initimg %.0f %.0f", size.width, size.height);
r = Rect(0, 0, size.width, size.height);
i = allocmemimage(r, XBGR32);
@ -332,102 +373,204 @@ initimg(void)
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:bytesperline(r, 32)
bitsPerPixel:32];
return i;
}
static void
resizeimg()
{
[win.img release];
_drawreplacescreenimage(initimg());
mouseresized = 1;
sendmouse();
}
static void
waitimg(int msec)
{
NSDate *limit;
int n;
win.needimg = 1;
win.deferflush = 0;
n = 0;
limit = [NSDate dateWithTimeIntervalSinceNow:msec/1000.0];
do{
[[NSRunLoop currentRunLoop]
runMode:@"waiting image"
beforeDate:limit];
n++;
}while(win.needimg && [(NSDate*)[NSDate date] compare:limit]<0);
win.deferflush = win.needimg;
LOG(@"waitimg %s (%d loop)", win.needimg?"defer":"ok", n);
}
void
_flushmemscreen(Rectangle r)
{
static int n;
NSRect rect;
rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
LOG(@"_flushmemscreen");
/*
* Call "lockFocusIfCanDraw" from main thread, else
* we deadlock while synchronizing both threads with
* qlock(): main thread must apparently be idle while
* we call it. (This is also why Devdraw shows
* occasionally an empty window: I found no
* satisfactory way to wait for P9P's image.)
*/
if(n==0){
n++;
return; /* to skip useless white init rect */
}else
if(n==1){
[WIN performSelectorOnMainThread:
@selector(makeKeyAndOrderFront:)
withObject:nil
waitUntilDone:NO];
n++;
}else
if([win.content canDraw] == 0)
return;
rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
[appdelegate
performSelectorOnMainThread:@selector(calldrawimg:)
performSelectorOnMainThread:@selector(callflushimg:)
withObject:[NSValue valueWithRect:rect]
waitUntilDone:YES];
waitUntilDone:YES
modes:[NSArray arrayWithObjects:
NSRunLoopCommonModes,
@"waiting image", nil]];
}
static void drawresizehandle(NSRect);
static void drawimg(NSRect, uint);
static void drawresizehandle(void);
enum
{
Pixel = 1,
Barsize = 4*Pixel,
Cornersize = 3*Pixel,
Handlesize = 3*Barsize + 1*Pixel,
};
static void
drawimg(NSRect dr)
flushimg(NSRect rect)
{
static int first = 1;
NSRect sr;
NSRect dr, r;
if(first){
[NSTimer scheduledTimerWithTimeInterval:0.033
if([win.content lockFocusIfCanDraw] == 0)
return;
if(win.needimg){
if(!NSEqualSizes(rect.size, [win.img size])){
LOG(@"flushimg reject %.0f %.0f",
rect.size.width, rect.size.height);
[win.content unlockFocus];
return;
}
win.needimg = 0;
}else
win.deferflush = 1;
LOG(@"flushimg ok %.0f %.0f", rect.size.width, rect.size.height);
/*
* Unless we are inside "drawRect", we have to round
* the corners ourselves, if this is the custom.
* "NSCompositeSourceIn" can do that, but we don't
* apply it to the whole rectangle, because this
* slows down trackpad scrolling considerably in
* Acme.
*/
r = [win.content bounds];
r.size.height -= Cornersize;
dr = NSIntersectionRect(r, rect);
drawimg(dr, NSCompositeCopy);
r.origin.y = r.size.height;
r.size = NSMakeSize(Cornersize, Cornersize);
dr = NSIntersectionRect(r, rect);
drawimg(dr, NSCompositeSourceIn);
r.origin.x = [win.img size].width - Cornersize;
dr = NSIntersectionRect(r, rect);
drawimg(dr, NSCompositeSourceIn);
r.size.width = r.origin.x - Cornersize;
r.origin.x -= r.size.width;
dr = NSIntersectionRect(r, rect);
drawimg(dr, NSCompositeCopy);
if(OSX_VERSION<100700 && win.isofs==0){
r.origin.x = [win.img size].width - Handlesize;
r.origin.y = [win.img size].height - Handlesize;
r.size = NSMakeSize(Handlesize, Handlesize);
if(NSIntersectsRect(r, rect))
drawresizehandle();
}
[win.content unlockFocus];
}
static void
autoflushwin(int set)
{
static NSTimer *t;
if(set){
if(t)
return;
/*
* We need "NSRunLoopCommonModes", otherwise the
* timer will not fire during live resizing.
*/
t = [NSTimer
timerWithTimeInterval:0.033
target:[appdelegate class]
selector:@selector(callflushwin:) userInfo:nil
repeats:YES];
first = 0;
}
sr = [win.content convertRect:dr fromView:nil];
if([win.content lockFocusIfCanDraw]){
/*
* To round the window's bottom corners, we can use
* "NSCompositeSourceIn", but this slows down
* trackpad scrolling considerably in Acme. Else we
* can use "bezierPathWithRoundedRect" with "addClip",
* but it's still too slow for wide Acme windows.
*/
[win.img drawInRect:dr fromRect:sr
// operation:NSCompositeSourceIn fraction:1
operation:NSCompositeCopy fraction:1
respectFlipped:YES hints:nil];
if(OSX_VERSION<100700 && win.isofs==0)
drawresizehandle(dr);
[win.content unlockFocus];
win.needflush = 1;
[[NSRunLoop currentRunLoop] addTimer:t
forMode:NSRunLoopCommonModes];
}else{
[t invalidate];
t = nil;
win.deferflush = 0;
}
}
static void
flushwin(void)
{
if(win.needflush){
if(win.deferflush && win.needimg==0){
[WIN flushWindow];
win.needflush = 0;
win.deferflush = 0;
}
}
enum
static void
drawimg(NSRect dr, uint op)
{
Pixel = 1,
Barsize = 4*Pixel,
Handlesize = 3*Barsize + 1*Pixel,
};
NSRect sr;
if(NSIsEmptyRect(dr))
return;
sr = [win.content convertRect:dr fromView:nil];
[win.img drawInRect:dr fromRect:sr
operation:op fraction:1
respectFlipped:YES hints:nil];
// NSFrameRect(dr);
}
static void
drawresizehandle(NSRect dr)
drawresizehandle(void)
{
NSColor *color[Barsize];
NSPoint a,b;
NSRect r;
NSSize size;
Point c;
int i,j;
size = [win.img size];
c = Pt(size.width, size.height);
r = NSMakeRect(0, 0, Handlesize, Handlesize);
r.origin = NSMakePoint(c.x-Handlesize, c.y-Handlesize);
if(NSIntersectsRect(r,dr) == 0)
return;
c = Pt([win.img size].width, [win.img size].height);
[[WIN graphicsContext] setShouldAntialias:NO];
@ -446,15 +589,6 @@ drawresizehandle(NSRect dr)
}
}
static void
resizeimg()
{
[win.img release];
_drawreplacescreenimage(initimg());
mouseresized = 1;
sendmouse();
}
static void getgesture(NSEvent*);
static void getkeyboard(NSEvent*);
static void getmouse(NSEvent*);
@ -462,20 +596,28 @@ static void gettouch(NSEvent*, int);
static void updatecursor(void);
@implementation contentview
/*
* "drawRect" is called each time Cocoa needs an
* image, and each time we call "display". It is
* preceded by background painting, and followed by
* "flushWindow".
*/
- (void)drawRect:(NSRect)r
{
static int first = 1;
if([WIN inLiveResize])
return;
LOG(@"drawrect %.0f %.0f %.0f %.0f",
r.origin.x, r.origin.y, r.size.width, r.size.height);
if(first)
first = 0;
else
resizeimg();
/* We should wait for P9P's image here. */
if([WIN inLiveResize])
waitimg(100);
else
waitimg(500);
}
- (BOOL)isFlipped
{
@ -489,8 +631,15 @@ static void updatecursor(void);
{
[super initWithFrame:r];
[self setAcceptsTouchEvents:YES];
[self setHidden:YES]; /* to avoid early "drawRect" call */
return self;
}
- (void)setHidden:(BOOL)set
{
if(!set)
[WIN makeFirstResponder:self]; /* for keyboard focus */
[super setHidden:set];
}
- (void)cursorUpdate:(NSEvent*)e{ updatecursor();}
- (void)mouseMoved:(NSEvent*)e{ getmouse(e);}
@ -881,6 +1030,9 @@ setmouse(Point p)
CGSetLocalEventsSuppressionInterval(0);
first = 0;
}
if([WIN inLiveResize])
return;
in.mpos = NSMakePoint(p.x, p.y); // race condition
r = [[WIN screen] frame];