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. * Cocoa's event loop must be in main thread.
*
* Unless otherwise stated, all coordinate systems
* are bottom-left-based.
*/ */
#define Cursor OSXCursor #define Cursor OSXCursor
@ -27,9 +30,11 @@
AUTOFRAMEWORK(Cocoa) AUTOFRAMEWORK(Cocoa)
#define panic sysfatal #define LOG if(0)NSLog
#define panic sysfatal
int usegestures = 0; int usegestures = 0;
int useliveresizing = 0;
int useoldfullscreen = 0; int useoldfullscreen = 0;
int usebigarrow = 0; int usebigarrow = 0;
@ -91,7 +96,8 @@ struct
int isnfs; int isnfs;
NSView *content; NSView *content;
NSBitmapImageRep *img; NSBitmapImageRep *img;
int needflush; int needimg;
int deferflush;
NSCursor *cursor; NSCursor *cursor;
} win; } win;
@ -106,7 +112,8 @@ struct
} in; } in;
static void hidebars(int); static void hidebars(int);
static void drawimg(NSRect); static void flushimg(NSRect);
static void autoflushwin(int);
static void flushwin(void); static void flushwin(void);
static void followzoombutton(NSRect); static void followzoombutton(NSRect);
static void getmousepos(void); static void getmousepos(void);
@ -140,9 +147,15 @@ static NSCursor* makecursor(Cursor*);
getmousepos(); getmousepos();
sendmouse(); sendmouse();
} }
- (void)windowWillStartLiveResize:(id)arg
{
if(useliveresizing == 0)
[win.content setHidden:YES];
}
- (void)windowDidEndLiveResize:(id)arg - (void)windowDidEndLiveResize:(id)arg
{ {
[win.content display]; if(useliveresizing == 0)
[win.content setHidden:NO];
} }
- (void)windowDidChangeScreen:(id)arg - (void)windowDidChangeScreen:(id)arg
{ {
@ -174,6 +187,10 @@ static NSCursor* makecursor(Cursor*);
hidebars(0); hidebars(0);
} }
} }
- (void)windowWillClose:(id)arg
{
autoflushwin(0); /* can crash otherwise */
}
+ (void)callservep9p:(id)arg + (void)callservep9p:(id)arg
{ {
@ -189,7 +206,7 @@ static NSCursor* makecursor(Cursor*);
+ (void)callflushwin:(id)arg{ flushwin();} + (void)callflushwin:(id)arg{ flushwin();}
- (void)calltogglefs:(id)arg{ togglefs();} - (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)callmakewin:(NSValue*)v{ makewin([v pointerValue]);}
+ (void)callsetcursor0:(NSValue*)v{ setcursor0([v pointerValue]);} + (void)callsetcursor0:(NSValue*)v{ setcursor0([v pointerValue]);}
@end @end
@ -208,6 +225,8 @@ attachscreen(char *label, char *winsize)
if(label == nil) if(label == nil)
label = "gnot a label"; label = "gnot a label";
if(strcmp(label, "page") == 0)
useliveresizing = 1;
/* /*
* Create window in main thread, else no cursor * 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 */ 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 @end
double double
@ -302,7 +343,6 @@ makewin(char *s)
win.isofs = 0; win.isofs = 0;
win.content = [contentview new]; win.content = [contentview new];
[WIN setContentView:win.content]; [WIN setContentView:win.content];
[WIN makeKeyAndOrderFront:nil];
} }
static Memimage* static Memimage*
@ -313,6 +353,7 @@ initimg(void)
Rectangle r; Rectangle r;
size = [win.content bounds].size; size = [win.content bounds].size;
LOG(@"initimg %.0f %.0f", size.width, size.height);
r = Rect(0, 0, size.width, size.height); r = Rect(0, 0, size.width, size.height);
i = allocmemimage(r, XBGR32); i = allocmemimage(r, XBGR32);
@ -332,102 +373,204 @@ initimg(void)
colorSpaceName:NSDeviceRGBColorSpace colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:bytesperline(r, 32) bytesPerRow:bytesperline(r, 32)
bitsPerPixel:32]; bitsPerPixel:32];
return i; 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 void
_flushmemscreen(Rectangle r) _flushmemscreen(Rectangle r)
{ {
static int n;
NSRect rect; NSRect rect;
rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); LOG(@"_flushmemscreen");
/* if(n==0){
* Call "lockFocusIfCanDraw" from main thread, else n++;
* we deadlock while synchronizing both threads with return; /* to skip useless white init rect */
* qlock(): main thread must apparently be idle while }else
* we call it. (This is also why Devdraw shows if(n==1){
* occasionally an empty window: I found no [WIN performSelectorOnMainThread:
* satisfactory way to wait for P9P's image.) @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 [appdelegate
performSelectorOnMainThread:@selector(calldrawimg:) performSelectorOnMainThread:@selector(callflushimg:)
withObject:[NSValue valueWithRect:rect] 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 static void
drawimg(NSRect dr) flushimg(NSRect rect)
{ {
static int first = 1; NSRect dr, r;
NSRect sr;
if(first){ if([win.content lockFocusIfCanDraw] == 0)
[NSTimer scheduledTimerWithTimeInterval:0.033 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] target:[appdelegate class]
selector:@selector(callflushwin:) userInfo:nil selector:@selector(callflushwin:) userInfo:nil
repeats:YES]; repeats:YES];
first = 0; [[NSRunLoop currentRunLoop] addTimer:t
} forMode:NSRunLoopCommonModes];
sr = [win.content convertRect:dr fromView:nil]; }else{
[t invalidate];
if([win.content lockFocusIfCanDraw]){ t = nil;
win.deferflush = 0;
/*
* 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;
} }
} }
static void static void
flushwin(void) flushwin(void)
{ {
if(win.needflush){ if(win.deferflush && win.needimg==0){
[WIN flushWindow]; [WIN flushWindow];
win.needflush = 0; win.deferflush = 0;
} }
} }
enum static void
drawimg(NSRect dr, uint op)
{ {
Pixel = 1, NSRect sr;
Barsize = 4*Pixel,
Handlesize = 3*Barsize + 1*Pixel, 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 static void
drawresizehandle(NSRect dr) drawresizehandle(void)
{ {
NSColor *color[Barsize]; NSColor *color[Barsize];
NSPoint a,b; NSPoint a,b;
NSRect r;
NSSize size;
Point c; Point c;
int i,j; int i,j;
size = [win.img size]; c = Pt([win.img size].width, [win.img size].height);
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;
[[WIN graphicsContext] setShouldAntialias:NO]; [[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 getgesture(NSEvent*);
static void getkeyboard(NSEvent*); static void getkeyboard(NSEvent*);
static void getmouse(NSEvent*); static void getmouse(NSEvent*);
@ -462,20 +596,28 @@ static void gettouch(NSEvent*, int);
static void updatecursor(void); static void updatecursor(void);
@implementation contentview @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 - (void)drawRect:(NSRect)r
{ {
static int first = 1; static int first = 1;
if([WIN inLiveResize]) LOG(@"drawrect %.0f %.0f %.0f %.0f",
return; r.origin.x, r.origin.y, r.size.width, r.size.height);
if(first) if(first)
first = 0; first = 0;
else else
resizeimg(); resizeimg();
/* We should wait for P9P's image here. */ if([WIN inLiveResize])
waitimg(100);
else
waitimg(500);
} }
- (BOOL)isFlipped - (BOOL)isFlipped
{ {
@ -489,8 +631,15 @@ static void updatecursor(void);
{ {
[super initWithFrame:r]; [super initWithFrame:r];
[self setAcceptsTouchEvents:YES]; [self setAcceptsTouchEvents:YES];
[self setHidden:YES]; /* to avoid early "drawRect" call */
return self; return self;
} }
- (void)setHidden:(BOOL)set
{
if(!set)
[WIN makeFirstResponder:self]; /* for keyboard focus */
[super setHidden:set];
}
- (void)cursorUpdate:(NSEvent*)e{ updatecursor();} - (void)cursorUpdate:(NSEvent*)e{ updatecursor();}
- (void)mouseMoved:(NSEvent*)e{ getmouse(e);} - (void)mouseMoved:(NSEvent*)e{ getmouse(e);}
@ -881,6 +1030,9 @@ setmouse(Point p)
CGSetLocalEventsSuppressionInterval(0); CGSetLocalEventsSuppressionInterval(0);
first = 0; first = 0;
} }
if([WIN inLiveResize])
return;
in.mpos = NSMakePoint(p.x, p.y); // race condition in.mpos = NSMakePoint(p.x, p.y); // race condition
r = [[WIN screen] frame]; r = [[WIN screen] frame];