aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/devdraw
diff options
context:
space:
mode:
authorDavid Jeannot <djeannot24@gmail.com>2012-03-05 15:54:26 -0500
committerRuss Cox <rsc@swtch.com>2012-03-05 15:54:26 -0500
commita31903778164deec9e58a525e2203bb416cafe0e (patch)
tree6ce03e12de1891df8f739ef9d324c0a44992b6b2 /src/cmd/devdraw
parentce26c364331c19034735972bb95f0270b7ebca9e (diff)
downloadplan9port-a31903778164deec9e58a525e2203bb416cafe0e.tar.gz
plan9port-a31903778164deec9e58a525e2203bb416cafe0e.tar.bz2
plan9port-a31903778164deec9e58a525e2203bb416cafe0e.zip
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
Diffstat (limited to 'src/cmd/devdraw')
-rw-r--r--src/cmd/devdraw/cocoa-screen.m300
1 files changed, 226 insertions, 74 deletions
diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m
index 3f2bcb8b..9cc0e134 100644
--- a/src/cmd/devdraw/cocoa-screen.m
+++ b/src/cmd/devdraw/cocoa-screen.m
@@ -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");
+
+ 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;
- /*
- * 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.)
- */
+ 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
- target:[appdelegate class]
- selector:@selector(callflushwin:) userInfo:nil
- repeats:YES];
- first = 0;
+ 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();
}
- sr = [win.content convertRect:dr fromView:nil];
+ [win.content unlockFocus];
+}
- if([win.content lockFocusIfCanDraw]){
+static void
+autoflushwin(int set)
+{
+ static NSTimer *t;
+ if(set){
+ if(t)
+ return;
/*
- * 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.
+ * We need "NSRunLoopCommonModes", otherwise the
+ * timer will not fire during live resizing.
*/
- [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;
+ t = [NSTimer
+ timerWithTimeInterval:0.033
+ target:[appdelegate class]
+ selector:@selector(callflushwin:) userInfo:nil
+ repeats:YES];
+ [[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];