From b1a086dee9bf5846b31323ba2c438f8853a9c87f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 9 Jan 2020 21:47:14 -0500 Subject: devdraw: refactor, clean up mac screen Also turn mac-srv.c into a generic srv.c, so we can remove the duplication with x11-srv.c. --- src/cmd/devdraw/devdraw.c | 8 +- src/cmd/devdraw/devdraw.h | 22 +- src/cmd/devdraw/mac-screen.h | 19 - src/cmd/devdraw/mac-screen.m | 827 ++++++++++++++++++++--------------------- src/cmd/devdraw/mac-srv.c | 378 ------------------- src/cmd/devdraw/mkfile | 3 +- src/cmd/devdraw/mkwsysrules.sh | 4 +- src/cmd/devdraw/srv.c | 402 ++++++++++++++++++++ 8 files changed, 825 insertions(+), 838 deletions(-) delete mode 100644 src/cmd/devdraw/mac-screen.h delete mode 100644 src/cmd/devdraw/mac-srv.c create mode 100644 src/cmd/devdraw/srv.c (limited to 'src/cmd/devdraw') diff --git a/src/cmd/devdraw/devdraw.c b/src/cmd/devdraw/devdraw.c index e83f6f07..77a3f44a 100644 --- a/src/cmd/devdraw/devdraw.c +++ b/src/cmd/devdraw/devdraw.c @@ -14,8 +14,6 @@ #include #include "devdraw.h" -extern void _flushmemscreen(Rectangle); - static Draw sdraw; Client *client0; static int drawuninstall(Client*, int); @@ -32,6 +30,8 @@ _initdisplaymemimage(Client *c, Memimage *m) c->op = SoverD; } +// _drawreplacescreen replaces c's screen image with m. +// It is called by the host driver on the main host thread. void _drawreplacescreenimage(Client *c, Memimage *m) { @@ -141,7 +141,7 @@ addflush(Client *c, Rectangle r) } /* emit current state */ if(c->flushrect.min.x < c->flushrect.max.x) - _flushmemscreen(c->flushrect); + rpc_flushmemscreen(c, c->flushrect); c->flushrect = r; c->waste = 0; } @@ -178,7 +178,7 @@ void drawflush(Client *c) { if(c->flushrect.min.x < c->flushrect.max.x) - _flushmemscreen(c->flushrect); + rpc_flushmemscreen(c, c->flushrect); c->flushrect = Rect(10000, 10000, -10000, -10000); } diff --git a/src/cmd/devdraw/devdraw.h b/src/cmd/devdraw/devdraw.h index fe9532b3..30586228 100644 --- a/src/cmd/devdraw/devdraw.h +++ b/src/cmd/devdraw/devdraw.h @@ -28,6 +28,8 @@ struct Kbdbuf int wi; int stall; int alting; + Rune k[10]; + int nk; }; struct Mousebuf @@ -75,7 +77,7 @@ struct Client int rfd; int wfd; - void* view; + const void* view; QLock inputlk; Kbdbuf kbd; @@ -163,6 +165,22 @@ void _drawreplacescreenimage(Client*, Memimage*); int _latin1(Rune*, int); int parsewinsize(char*, Rectangle*, int*); int mouseswap(int); -void abortcompose(Client*); + +void gfx_abortcompose(Client*); +void gfx_keystroke(Client*, int); +void gfx_mousetrack(Client*, int, int, int, uint); + +void rpc_setmouse(Client*, Point); +void rpc_setcursor(Client*, Cursor*, Cursor2*); +void rpc_setlabel(Client*, char*); +void rpc_resizeimg(Client*); +void rpc_resizewindow(Client*, Rectangle); +void rpc_topwin(Client*); +char* rpc_getsnarf(void); +void rpc_putsnarf(char*); +Memimage *rpc_attachscreen(Client*, char*, char*); +void rpc_flushmemscreen(Client*, Rectangle); extern Client *client0; + +void servep9p(Client*); diff --git a/src/cmd/devdraw/mac-screen.h b/src/cmd/devdraw/mac-screen.h deleted file mode 100644 index 7bc0920d..00000000 --- a/src/cmd/devdraw/mac-screen.h +++ /dev/null @@ -1,19 +0,0 @@ -#define setcursor dsetcursor - -Memimage *attachscreen(Client*, char*, char*); -void setmouse(Point); -void setcursor(Cursor*, Cursor2*); -void setlabel(char*); -char* getsnarf(void); -void putsnarf(char*); -void topwin(void); - -void mousetrack(Client*, int, int, int, uint); -void keystroke(Client*, int); -void kicklabel(char*); - -void servep9p(Client*); - -void resizeimg(Client*); - -void resizewindow(Rectangle); diff --git a/src/cmd/devdraw/mac-screen.m b/src/cmd/devdraw/mac-screen.m index 8ce8a0cd..d756d3d7 100644 --- a/src/cmd/devdraw/mac-screen.m +++ b/src/cmd/devdraw/mac-screen.m @@ -21,7 +21,6 @@ #include #include #include "devdraw.h" -#include "mac-screen.h" #include "bigarrow.h" #include "glendapng.h" @@ -34,7 +33,6 @@ AUTOFRAMEWORK(QuartzCore) static void setprocname(const char*); static uint keycvt(uint); static uint msec(void); -static Memimage* initimg(Client*); void usage(void) @@ -43,34 +41,14 @@ usage(void) threadexitsall("usage"); } -@interface DrawLayer : CAMetalLayer -@end -@interface AppDelegate : NSObject -+ (void)makewin:(NSValue *)v; -+ (void)callkicklabel:(NSString *)v; -+ (void)callsetNeedsDisplayInRect:(NSValue *)v; -+ (void)callsetcursor:(NSValue *)v; -@end - -@interface DevDrawView : NSView -@property (nonatomic, assign) Client *client; -@property (nonatomic, assign) DrawLayer *dlayer; -@property (nonatomic, assign) NSWindow *win; -@property (nonatomic, assign) NSCursor *currentCursor; -@property (nonatomic, assign) Memimage *img; +@class DrawView; +@class DrawLayer; -- (void)clearInput; -- (void)getmouse:(NSEvent *)e; -- (void)sendmouse:(NSUInteger)b; -- (void)resetLastInputRect; -- (void)enlargeLastInputRect:(NSRect)r; +@interface AppDelegate : NSObject @end static AppDelegate *myApp = NULL; -static id device; -static id commandQueue; -static id texture; static QLock snarfl; @@ -100,11 +78,13 @@ threadmain(int argc, char **argv) }ARGEND client0 = mallocz(sizeof(Client), 1); - client0->displaydpi = 100; if(client0 == nil){ fprint(2, "initdraw: allocating client0: out of memory"); abort(); } + client0->displaydpi = 100; + client0->rfd = 3; + client0->wfd = 4; setprocname(argv0); @@ -117,22 +97,168 @@ threadmain(int argc, char **argv) } } - void callservep9p(void *v) { USED(v); - client0->rfd = 3; - client0->wfd = 4; servep9p(client0); [NSApp terminate:myApp]; } @implementation AppDelegate +- (void)applicationDidFinishLaunching:(id)arg +{ + NSMenu *m, *sm; + NSData *d; + NSImage *i; -+ (void)makewin:(NSValue *)v + LOG(@"applicationDidFinishLaunching"); + + sm = [NSMenu new]; + [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; + [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"]; + [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; + m = [NSMenu new]; + [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; + [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; + [NSApp setMainMenu:m]; + + d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)]; + i = [[NSImage alloc] initWithData:d]; + [NSApp setApplicationIconImage:i]; + [[NSApp dockTile] display]; + + proccreate(callservep9p, nil, 0); +} + +- (NSApplicationPresentationOptions)window:(id)arg + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { + NSApplicationPresentationOptions o; + o = proposedOptions; + o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); + o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + return o; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return YES; +} +@end + +@interface DrawLayer : CAMetalLayer +@property (nonatomic, retain) id cmd; +@property (nonatomic, retain) id texture; +@end + +@implementation DrawLayer +- (void)display +{ + LOG(@"display"); + LOG(@"display query drawable"); + + @autoreleasepool{ + id drawable = [self nextDrawable]; + if(!drawable){ + LOG(@"display couldn't get drawable"); + [self setNeedsDisplay]; + return; + } + + LOG(@"display got drawable"); + + id cbuf = [self.cmd commandBuffer]; + id blit = [cbuf blitCommandEncoder]; + [blit copyFromTexture:self.texture + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(self.texture.width, self.texture.height, self.texture.depth) + toTexture:drawable.texture + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; + [blit endEncoding]; + + [cbuf presentDrawable:drawable]; + drawable = nil; + [cbuf addCompletedHandler:^(id cmdBuff){ + if(cmdBuff.error){ + NSLog(@"command buffer finished with error: %@", + cmdBuff.error.localizedDescription); + }else + LOG(@"command buffer finishes present drawable"); + }]; + [cbuf commit]; + } + LOG(@"display commit"); +} +@end + +@interface DrawView : NSView +@property (nonatomic, assign) Client *client; +@property (nonatomic, retain) DrawLayer *dlayer; +@property (nonatomic, retain) NSWindow *win; +@property (nonatomic, retain) NSCursor *currentCursor; +@property (nonatomic, assign) Memimage *img; + +- (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label; +- (void)topwin; +- (void)setlabel:(char*)label; +- (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2; +- (void)setmouse:(Point)p; +- (void)clearInput; +- (void)getmouse:(NSEvent*)e; +- (void)sendmouse:(NSUInteger)b; +- (void)resetLastInputRect; +- (void)enlargeLastInputRect:(NSRect)r; +@end + +@implementation DrawView +{ + NSMutableString *_tmpText; + NSRange _markedRange; + NSRange _selectedRange; + NSRect _lastInputRect; // The view is flipped, this is not. + BOOL _tapping; + NSUInteger _tapFingers; + NSUInteger _tapTime; +} + +- (id)init { + LOG(@"View init"); + self = [super init]; + [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMaskIndirect]; + _tmpText = [[NSMutableString alloc] initWithCapacity:2]; + _markedRange = NSMakeRange(NSNotFound, 0); + _selectedRange = NSMakeRange(0, 0); + return self; +} + +- (CALayer*)makeBackingLayer { return [DrawLayer layer]; } +- (BOOL)wantsUpdateLayer { return YES; } +- (BOOL)isOpaque { return YES; } +- (BOOL)isFlipped { return YES; } +- (BOOL)acceptsFirstResponder { return YES; } + +// rpc_attachscreen allocates a new screen window with the given label and size +// and attaches it to client c (by setting c->view). +Memimage* +rpc_attachscreen(Client *c, char *label, char *winsize) +{ + LOG(@"attachscreen(%s, %s)", label, winsize); + + dispatch_sync(dispatch_get_main_queue(), ^(void) { + @autoreleasepool { + DrawView *view = [[DrawView new] attach:c winsize:winsize label:label]; + [view initimg]; + } + }); + return ((__bridge DrawView*)c->view).img; +} + +- (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label { NSRect r, sr; Rectangle wr; int set; @@ -144,10 +270,10 @@ callservep9p(void *v) | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + s = winsize; sr = [[NSScreen mainScreen] frame]; r = [[NSScreen mainScreen] visibleFrame]; - s = [v pointerValue]; LOG(@"makewin(%s)", s); if(s && *s){ if(parsewinsize(s, &wr, &set) < 0) @@ -177,17 +303,16 @@ callservep9p(void *v) [win setRestorable:NO]; [win setAcceptsMouseMovedEvents:YES]; - DevDrawView *view = [DevDrawView new]; - client0->view = view; - view.client = client0; - view.win = win; - view.currentCursor = nil; - [win setContentView:view]; - [win setDelegate:view]; - [view setWantsLayer:YES]; - [view setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; + client->view = CFBridgingRetain(self); + self.client = client; + self.win = win; + self.currentCursor = nil; + [win setContentView:self]; + [win setDelegate:self]; + [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; - device = nil; + id device = nil; allDevices = MTLCopyAllDevices(); for(id mtlDevice in allDevices) { if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) { @@ -198,18 +323,19 @@ callservep9p(void *v) if(!device) device = MTLCreateSystemDefaultDevice(); - commandQueue = [device newCommandQueue]; - - DrawLayer *layer = (DrawLayer *)[view layer]; - view.dlayer = layer; + DrawLayer *layer = (DrawLayer*)[self layer]; + self.dlayer = layer; layer.device = device; + layer.cmd = [device newCommandQueue]; layer.pixelFormat = MTLPixelFormatBGRA8Unorm; layer.framebufferOnly = YES; layer.opaque = YES; // We use a default transparent layer on top of the CAMetalLayer. // This seems to make fullscreen applications behave. - { + // Specifically, without this code if you enter full screen with Cmd-F, + // the screen goes black until the first mouse click. + if(1) { CALayer *stub = [CALayer layer]; stub.frame = CGRectMake(0, 0, 1, 1); [stub setNeedsDisplay]; @@ -218,64 +344,76 @@ callservep9p(void *v) [NSEvent setMouseCoalescingEnabled:NO]; - topwin(); + [self topwin]; + [self setlabel:label]; + [self setcursor:nil cursor2:nil]; + + return self; } -+ (void)callkicklabel:(NSString *)s +// rpc_topwin moves the window to the top of the desktop. +// Called from an RPC thread with no client lock held. +void +rpc_topwin(Client *c) { - DevDrawView *view = client0->view; - - LOG(@"callkicklabel(%@)", s); - [view.win setTitle:s]; - [[NSApp dockTile] setBadgeLabel:s]; + DrawView *view = (__bridge DrawView*)c->view; + dispatch_sync(dispatch_get_main_queue(), ^(void) { + [view topwin]; + }); } +- (void)topwin { + [self.win makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; +} -+ (void)callsetNeedsDisplayInRect:(NSValue *)v +// rpc_setlabel updates the client window's label. +// If label == nil, the call is a no-op. +// Called from an RPC thread with no client lock held. +void +rpc_setlabel(Client *client, char *label) { - NSRect r; - dispatch_time_t time; - DevDrawView *view = client0->view; - - r = [v rectValue]; - LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - r = [view.win convertRectFromBacking:r]; - LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - [view.dlayer setNeedsDisplayInRect:r]; - - time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); - dispatch_after(time, dispatch_get_main_queue(), ^(void){ - [view.dlayer setNeedsDisplayInRect:r]; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view setlabel:label]; }); - - [view enlargeLastInputRect:r]; } -typedef struct Cursors Cursors; -struct Cursors { - Cursor *c; - Cursor2 *c2; -}; +- (void)setlabel:(char*)label { + LOG(@"setlabel(%s)", label); + if(label == nil) + return; + + @autoreleasepool{ + NSString *s = [[NSString alloc] initWithUTF8String:label]; + [self.win setTitle:s]; + [[NSApp dockTile] setBadgeLabel:s]; // TODO: Not with multiple windows + } +} -+ (void)callsetcursor:(NSValue *)v +// rpc_setcursor updates the client window's cursor image. +// Either c and c2 are both non-nil, or they are both nil to use the default arrow. +// Called from an RPC thread with no client lock held. +void +rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2) { - Cursors *cs; - Cursor *c; - Cursor2 *c2; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view setcursor:c cursor2:c2]; + }); +} + +- (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2 { + if(!c) { + c = &bigarrow; + c2 = &bigarrow2; + } + NSBitmapImageRep *r, *r2; NSImage *i; NSPoint p; uchar *plane[5], *plane2[5]; uint b; - DevDrawView *view = client0->view; - - cs = [v pointerValue]; - c = cs->c; - if(!c) - c = &bigarrow; - c2 = cs->c2; - if(!c2) - c2 = &bigarrow2; r = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil @@ -311,125 +449,164 @@ struct Cursors { plane2[1][b] = c2->set[b] | c2->clr[b]; } - // For checking out the cursor bitmap image -/* - static BOOL saveimg = YES; - if(saveimg){ + static BOOL debug = NO; + if(debug){ NSData *data = [r representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; [data writeToFile: @"/tmp/r.bmp" atomically: NO]; data = [r2 representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; [data writeToFile: @"/tmp/r2.bmp" atomically: NO]; - saveimg = NO; + debug = NO; } -*/ i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; [i addRepresentation:r2]; [i addRepresentation:r]; p = NSMakePoint(-c->offset.x, -c->offset.y); - view.currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; - - [view.win invalidateCursorRectsForView:view]; + self.currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; + [self.win invalidateCursorRectsForView:self]; } -- (void)applicationDidFinishLaunching:(id)arg -{ - NSMenu *m, *sm; - NSData *d; - NSImage *i; +- (void)initimg { +@autoreleasepool{ + CGFloat scale; + NSSize size; + MTLTextureDescriptor *textureDesc; - LOG(@"applicationDidFinishLaunching"); + size = [self convertSizeToBacking:[self bounds].size]; + self.client->mouserect = Rect(0, 0, size.width, size.height); - sm = [NSMenu new]; - [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; - [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"]; - [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; - m = [NSMenu new]; - [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; - [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; - [NSApp setMainMenu:m]; + LOG(@"initimg %.0f %.0f", size.width, size.height); - d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)]; - i = [[NSImage alloc] initWithData:d]; - [NSApp setApplicationIconImage:i]; - [[NSApp dockTile] display]; + self.img = allocmemimage(self.client->mouserect, XRGB32); + if(self.img == nil) + panic("allocmemimage: %r"); + if(self.img->data == nil) + panic("img->data == nil"); - proccreate(callservep9p, nil, 0); -} + textureDesc = [MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:size.width + height:size.height + mipmapped:NO]; + textureDesc.allowGPUOptimizedContents = YES; + textureDesc.usage = MTLTextureUsageShaderRead; + textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; + self.dlayer.texture = [self.dlayer.device newTextureWithDescriptor:textureDesc]; -- (NSApplicationPresentationOptions)window:(id)arg - willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { - NSApplicationPresentationOptions o; - o = proposedOptions; - o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); - o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; - return o; -} + scale = [self.win backingScaleFactor]; + [self.dlayer setDrawableSize:size]; + [self.dlayer setContentsScale:scale]; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { - return YES; + // NOTE: This is not really the display DPI. + // On retina, scale is 2; otherwise it is 1. + // This formula gives us 220 for retina, 110 otherwise. + // That's not quite right but it's close to correct. + // https://en.wikipedia.org/wiki/Retina_display#Models + self.client->displaydpi = scale * 110; } -@end - -@implementation DevDrawView -{ - NSMutableString *_tmpText; - NSRange _markedRange; - NSRange _selectedRange; - NSRect _lastInputRect; // The view is flipped, this is not. - BOOL _tapping; - NSUInteger _tapFingers; - NSUInteger _tapTime; + LOG(@"initimg return"); } -- (id)init +// rpc_flushmemscreen flushes changes to view.img's rectangle r +// to the on-screen window, making them visible. +// Called from an RPC thread with no client lock held. +void +rpc_flushmemscreen(Client *client, Rectangle r) { - LOG(@"View init"); - self = [super init]; - [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMaskIndirect]; - _tmpText = [[NSMutableString alloc] initWithCapacity:2]; - _markedRange = NSMakeRange(NSNotFound, 0); - _selectedRange = NSMakeRange(0, 0); - return self; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_async(dispatch_get_main_queue(), ^(void){ + [view flushmemscreen:r]; + }); } -- (void)windowDidResize:(NSNotification *)notification -{ - if(![self inLiveResize] && self.img) { - resizeimg(self.client); +- (void)flushmemscreen:(Rectangle)r { + LOG(@"flushmemscreen(%d,%d,%d,%d)", r.min.x, r.min.y, Dx(r), Dy(r)); + if(!rectinrect(r, Rect(0, 0, self.dlayer.texture.width, self.dlayer.texture.height))){ + LOG(@"Rectangle is out of bounds, return."); + return; + } + + @autoreleasepool{ + [self.dlayer.texture + replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r)) + mipmapLevel:0 + withBytes:byteaddr(self.img, Pt(r.min.x, r.min.y)) + bytesPerRow:self.img->width*sizeof(u32int)]; + + NSRect nr = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); + dispatch_time_t time; + + LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", nr.origin.x, nr.origin.y, nr.size.width, nr.size.height); + nr = [self.win convertRectFromBacking:nr]; + LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", nr.origin.x, nr.origin.y, nr.size.width, nr.size.height); + [self.dlayer setNeedsDisplayInRect:nr]; + + time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); + dispatch_after(time, dispatch_get_main_queue(), ^(void){ + [self.dlayer setNeedsDisplayInRect:nr]; + }); + + [self enlargeLastInputRect:nr]; } } -- (void)windowDidBecomeKey:(id)arg +// rpc_resizeimg forces the client window to discard its current window and make a new one. +// It is called when the user types Cmd-R to toggle whether retina mode is forced. +// Called from an RPC thread with no client lock held. +void +rpc_resizeimg(Client *c) { - [self sendmouse:0]; + DrawView *view = (__bridge DrawView*)c->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view resizeimg]; + }); } -- (CALayer *)makeBackingLayer -{ - LOG(@"makeBackingLayer"); - return [DrawLayer layer]; +- (void)resizeimg { + [self initimg]; + _drawreplacescreenimage(self.client, self.img); + [self sendmouse:0]; } -- (BOOL)wantsUpdateLayer +- (void)windowDidResize:(NSNotification *)notification { + if(![self inLiveResize] && self.img) { + [self resizeimg]; + } +} +- (void)viewDidEndLiveResize { - return YES; + [super viewDidEndLiveResize]; + if(self.img) + [self resizeimg]; } -- (BOOL)isOpaque +- (void)viewDidChangeBackingProperties { - return YES; + [super viewDidChangeBackingProperties]; + if(self.img) + [self resizeimg]; } -- (BOOL)isFlipped +// rpc_resizewindow asks for the client window to be resized to size r. +// Called from an RPC thread with no client lock held. +void +rpc_resizewindow(Client *c, Rectangle r) { - return YES; + DrawView *view = (__bridge DrawView*)c->view; + + LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r)); + dispatch_async(dispatch_get_main_queue(), ^(void){ + NSSize s; + + s = [view convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; + [view.win setContentSize:s]; + }); } -- (BOOL)acceptsFirstResponder -{ - return YES; + +- (void)windowDidBecomeKey:(id)arg { + [self sendmouse:0]; } - (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];} @@ -483,7 +660,7 @@ struct Cursors { b |= 4; [self sendmouse:b]; }else if(m & ~omod & NSEventModifierFlagOption) - keystroke(self.client, Kalt); + gfx_keystroke(self.client, Kalt); omod = m; } @@ -540,7 +717,7 @@ struct Cursors { if(b == 1){ m = [e modifierFlags]; if(m & NSEventModifierFlagOption){ - abortcompose(self.client); + gfx_abortcompose(self.client); b = 2; }else if(m & NSEventModifierFlagCommand) @@ -557,28 +734,51 @@ struct Cursors { [self.window mouseLocationOutsideOfEventStream]]; p.y = Dy(self.client->mouserect) - p.y; // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b); - mousetrack(self.client, p.x, p.y, b, msec()); + gfx_mousetrack(self.client, p.x, p.y, b, msec()); if(b && _lastInputRect.size.width && _lastInputRect.size.height) [self resetLastInputRect]; } -- (void)resetCursorRects { - [super resetCursorRects]; - [self addCursorRect:self.bounds cursor:self.currentCursor]; +// rpc_setmouse moves the mouse cursor. +// Called from an RPC thread with no client lock held. +void +rpc_setmouse(Client *c, Point p) +{ + DrawView *view = (__bridge DrawView*)c->view; + dispatch_async(dispatch_get_main_queue(), ^(void){ + [view setmouse:p]; + }); } -- (void)viewDidEndLiveResize -{ - [super viewDidEndLiveResize]; - if(self.img) - resizeimg(self.client); +-(void)setmouse:(Point)p { + @autoreleasepool{ + NSPoint q; + + LOG(@"setmouse(%d,%d)", p.x, p.y); + q = [self.win convertPointFromBacking:NSMakePoint(p.x, p.y)]; + LOG(@"(%g, %g) <- fromBacking", q.x, q.y); + q = [self convertPoint:q toView:nil]; + LOG(@"(%g, %g) <- toWindow", q.x, q.y); + q = [self.win convertPointToScreen:q]; + LOG(@"(%g, %g) <- toScreen", q.x, q.y); + // Quartz has the origin of the "global display + // coordinate space" at the top left of the primary + // screen with y increasing downward, while Cocoa has + // the origin at the bottom left of the primary screen + // with y increasing upward. We flip the coordinate + // with a negative sign and shift upward by the height + // of the primary screen. + q.y = NSScreen.screens[0].frame.size.height - q.y; + LOG(@"(%g, %g) <- setmouse", q.x, q.y); + CGWarpMouseCursorPosition(NSPointToCGPoint(q)); + CGAssociateMouseAndMouseCursorPosition(true); + } } -- (void)viewDidChangeBackingProperties -{ - [super viewDidChangeBackingProperties]; - if(self.img) - resizeimg(self.client); + +- (void)resetCursorRects { + [super resetCursorRects]; + [self addCursorRect:self.bounds cursor:self.currentCursor]; } // conforms to protocol NSTextInputClient @@ -637,24 +837,24 @@ struct Cursors { LOG(@"text length %ld", _tmpText.length); for(i = 0; i <= _tmpText.length; ++i){ if(i == _markedRange.location) - keystroke(self.client, '['); + gfx_keystroke(self.client, '['); if(_selectedRange.length){ if(i == _selectedRange.location) - keystroke(self.client, '{'); + gfx_keystroke(self.client, '{'); if(i == NSMaxRange(_selectedRange)) - keystroke(self.client, '}'); + gfx_keystroke(self.client, '}'); } if(i == NSMaxRange(_markedRange)) - keystroke(self.client, ']'); + gfx_keystroke(self.client, ']'); if(i < _tmpText.length) - keystroke(self.client, [_tmpText characterAtIndex:i]); + gfx_keystroke(self.client, [_tmpText characterAtIndex:i]); } int l; l = 1 + _tmpText.length - NSMaxRange(_selectedRange) + (_selectedRange.length > 0); LOG(@"move left %d", l); for(i = 0; i < l; ++i) - keystroke(self.client, Kleft); + gfx_keystroke(self.client, Kleft); } LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText, @@ -669,7 +869,7 @@ struct Cursors { LOG(@"unmarkText"); len = [_tmpText length]; //for(i = 0; i < len; ++i) - // keystroke(self.client, [_tmpText characterAtIndex:i]); + // gfx_keystroke(self.client, [_tmpText characterAtIndex:i]); [_tmpText deleteCharactersInRange:NSMakeRange(0, len)]; _markedRange = NSMakeRange(NSNotFound, 0); _selectedRange = NSMakeRange(0, 0); @@ -711,7 +911,7 @@ struct Cursors { len = [s length]; for(i = 0; i < len; ++i) - keystroke(self.client, [s characterAtIndex:i]); + gfx_keystroke(self.client, [s characterAtIndex:i]); [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)]; _markedRange = NSMakeRange(NSNotFound, 0); _selectedRange = NSMakeRange(0, 0); @@ -751,7 +951,7 @@ struct Cursors { k += Kcmd; } if(k>0) - keystroke(self.client, k); + gfx_keystroke(self.client, k); } // Helper for managing input rect approximately @@ -782,69 +982,13 @@ struct Cursors { + (_selectedRange.length > 0); LOG(@"move right %d", l); for(i = 0; i < l; ++i) - keystroke(self.client, Kright); + gfx_keystroke(self.client, Kright); l = _tmpText.length+2+2*(_selectedRange.length > 0); LOG(@"backspace %d", l); for(uint i = 0; i < l; ++i) - keystroke(self.client, Kbs); - } -} - -@end - -@implementation DrawLayer - -- (void)display -{ - id cbuf; - id blit; - - LOG(@"display"); - - cbuf = [commandQueue commandBuffer]; - - LOG(@"display query drawable"); - -@autoreleasepool{ - id drawable; - DevDrawView *view = client0->view; - - drawable = [view.dlayer nextDrawable]; - if(!drawable){ - LOG(@"display couldn't get drawable"); - [self setNeedsDisplay]; - return; + gfx_keystroke(self.client, Kbs); } - - LOG(@"display got drawable"); - - blit = [cbuf blitCommandEncoder]; - [blit copyFromTexture:texture - sourceSlice:0 - sourceLevel:0 - sourceOrigin:MTLOriginMake(0, 0, 0) - sourceSize:MTLSizeMake(texture.width, texture.height, texture.depth) - toTexture:drawable.texture - destinationSlice:0 - destinationLevel:0 - destinationOrigin:MTLOriginMake(0, 0, 0)]; - [blit endEncoding]; - - [cbuf presentDrawable:drawable]; - drawable = nil; } - [cbuf addCompletedHandler:^(id cmdBuff){ - if(cmdBuff.error){ - NSLog(@"command buffer finished with error: %@", - cmdBuff.error.localizedDescription); - }else - LOG(@"command buffer finishes present drawable"); - }]; - [cbuf commit]; - - LOG(@"display commit"); -} - @end static uint @@ -935,122 +1079,9 @@ keycvt(uint code) } } -Memimage* -attachscreen(Client *c, char *label, char *winsize) -{ - LOG(@"attachscreen(%s, %s)", label, winsize); - [AppDelegate - performSelectorOnMainThread:@selector(makewin:) - withObject:[NSValue valueWithPointer:winsize] - waitUntilDone:YES]; - kicklabel(label); - setcursor(nil, nil); - c->mouse.resized = 0; - return initimg(c); -} - -static Memimage* -initimg(Client *c) -{ - DevDrawView *view = c->view; - -@autoreleasepool{ - CGFloat scale; - NSSize size; - MTLTextureDescriptor *textureDesc; - - size = [view convertSizeToBacking:[view bounds].size]; - c->mouserect = Rect(0, 0, size.width, size.height); - - LOG(@"initimg %.0f %.0f", size.width, size.height); - - view.img = allocmemimage(c->mouserect, XRGB32); - if(view.img == nil) - panic("allocmemimage: %r"); - if(view.img->data == nil) - panic("img->data == nil"); - - textureDesc = [MTLTextureDescriptor - texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm - width:size.width - height:size.height - mipmapped:NO]; - textureDesc.allowGPUOptimizedContents = YES; - textureDesc.usage = MTLTextureUsageShaderRead; - textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; - texture = [device newTextureWithDescriptor:textureDesc]; - - scale = [view.win backingScaleFactor]; - [view.dlayer setDrawableSize:size]; - [view.dlayer setContentsScale:scale]; - - // NOTE: This is not really the display DPI. - // On retina, scale is 2; otherwise it is 1. - // This formula gives us 220 for retina, 110 otherwise. - // That's not quite right but it's close to correct. - // https://en.wikipedia.org/wiki/Retina_display#Models - c->displaydpi = scale * 110; -} - LOG(@"initimg return"); - - return view.img; -} - -void -_flushmemscreen(Rectangle r) -{ - DevDrawView *view = client0->view; - - LOG(@"_flushmemscreen(%d,%d,%d,%d)", r.min.x, r.min.y, Dx(r), Dy(r)); - if(!rectinrect(r, Rect(0, 0, texture.width, texture.height))){ - LOG(@"Rectangle is out of bounds, return."); - return; - } - - @autoreleasepool{ - [texture - replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r)) - mipmapLevel:0 - withBytes:byteaddr(view.img, Pt(r.min.x, r.min.y)) - bytesPerRow:view.img->width*sizeof(u32int)]; - [AppDelegate - performSelectorOnMainThread:@selector(callsetNeedsDisplayInRect:) - withObject:[NSValue valueWithRect:NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r))] - waitUntilDone:NO]; - } -} - -void -setmouse(Point p) -{ - DevDrawView *view = client0->view; - - @autoreleasepool{ - NSPoint q; - - LOG(@"setmouse(%d,%d)", p.x, p.y); - q = [view.win convertPointFromBacking:NSMakePoint(p.x, p.y)]; - LOG(@"(%g, %g) <- fromBacking", q.x, q.y); - q = [view convertPoint:q toView:nil]; - LOG(@"(%g, %g) <- toWindow", q.x, q.y); - q = [view.win convertPointToScreen:q]; - LOG(@"(%g, %g) <- toScreen", q.x, q.y); - // Quartz has the origin of the "global display - // coordinate space" at the top left of the primary - // screen with y increasing downward, while Cocoa has - // the origin at the bottom left of the primary screen - // with y increasing upward. We flip the coordinate - // with a negative sign and shift upward by the height - // of the primary screen. - q.y = NSScreen.screens[0].frame.size.height - q.y; - LOG(@"(%g, %g) <- setmouse", q.x, q.y); - CGWarpMouseCursorPosition(NSPointToCGPoint(q)); - CGAssociateMouseAndMouseCursorPosition(true); - } -} - +// TODO char* -getsnarf(void) +rpc_getsnarf(void) { NSPasteboard *pb; NSString *s; @@ -1069,8 +1100,9 @@ getsnarf(void) } } +// TODO void -putsnarf(char *s) +rpc_putsnarf(char *s) { NSArray *t; NSPasteboard *pb; @@ -1091,75 +1123,6 @@ putsnarf(char *s) } } -void -kicklabel(char *label) -{ - NSString *s; - - LOG(@"kicklabel(%s)", label); - if(label == nil) - return; - - @autoreleasepool{ - s = [[NSString alloc] initWithUTF8String:label]; - [AppDelegate - performSelectorOnMainThread:@selector(callkicklabel:) - withObject:s - waitUntilDone:NO]; - } -} - -void -setcursor(Cursor *c, Cursor2 *c2) -{ - Cursors cs; - - cs.c = c; - cs.c2 = c2; - - [AppDelegate - performSelectorOnMainThread:@selector(callsetcursor:) - withObject:[NSValue valueWithPointer:&cs] - waitUntilDone:YES]; -} - -void -topwin(void) -{ - DevDrawView *view = client0->view; - - [view.win - performSelectorOnMainThread: - @selector(makeKeyAndOrderFront:) - withObject:nil - waitUntilDone:YES]; - - [NSApp activateIgnoringOtherApps:YES]; -} - -void -resizeimg(Client *c) -{ - DevDrawView *view = c->view; - - _drawreplacescreenimage(c, initimg(c)); - [view sendmouse:0]; -} - -void -resizewindow(Rectangle r) -{ - DevDrawView *view = client0->view; - - LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r)); - dispatch_async(dispatch_get_main_queue(), ^(void){ - NSSize s; - - s = [view convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; - [view.win setContentSize:s]; - }); -} - static void setprocname(const char *s) { diff --git a/src/cmd/devdraw/mac-srv.c b/src/cmd/devdraw/mac-srv.c deleted file mode 100644 index 6727ef63..00000000 --- a/src/cmd/devdraw/mac-srv.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Window system protocol server. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "devdraw.h" -#include "mac-screen.h" - -void runmsg(Client*, Wsysmsg*); -void replymsg(Client*, Wsysmsg*); -void matchkbd(Client*); -void matchmouse(Client*); - -int trace = 0; - -void -servep9p(Client *c) -{ - uchar buf[4], *mbuf; - int nmbuf, n, nn; - Wsysmsg m; - - fmtinstall('W', drawfcallfmt); - - mbuf = nil; - nmbuf = 0; - while((n = read(c->rfd, buf, 4)) == 4){ - GET(buf, n); - if(n > nmbuf){ - free(mbuf); - mbuf = malloc(4+n); - if(mbuf == nil) - sysfatal("malloc: %r"); - nmbuf = n; - } - memmove(mbuf, buf, 4); - nn = readn(c->rfd, mbuf+4, n-4); - if(nn != n-4) - sysfatal("eof during message"); - - /* pick off messages one by one */ - if(convM2W(mbuf, nn+4, &m) <= 0) - sysfatal("cannot convert message"); - if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); - runmsg(c, &m); - } -} - -void -replyerror(Client *c, Wsysmsg *m) -{ - char err[256]; - - rerrstr(err, sizeof err); - m->type = Rerror; - m->error = err; - replymsg(c, m); -} - -/* - * Handle a single wsysmsg. - * Might queue for later (kbd, mouse read) - */ -void -runmsg(Client *c, Wsysmsg *m) -{ - static uchar buf[65536]; - int n; - Memimage *i; - - switch(m->type){ - case Tinit: - memimageinit(); - i = attachscreen(c, m->label, m->winsize); - _initdisplaymemimage(c, i); - replymsg(c, m); - break; - - case Trdmouse: - qlock(&c->inputlk); - c->mousetags.t[c->mousetags.wi++] = m->tag; - if(c->mousetags.wi == nelem(c->mousetags.t)) - c->mousetags.wi = 0; - if(c->mousetags.wi == c->mousetags.ri) - sysfatal("too many queued mouse reads"); - c->mouse.stall = 0; - matchmouse(c); - qunlock(&c->inputlk); - break; - - case Trdkbd: - qlock(&c->inputlk); - c->kbdtags.t[c->kbdtags.wi++] = m->tag; - if(c->kbdtags.wi == nelem(c->kbdtags.t)) - c->kbdtags.wi = 0; - if(c->kbdtags.wi == c->kbdtags.ri) - sysfatal("too many queued keyboard reads"); - c->kbd.stall = 0; - matchkbd(c); - qunlock(&c->inputlk); - break; - - case Tmoveto: - setmouse(m->mouse.xy); - replymsg(c, m); - break; - - case Tcursor: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, nil); - replymsg(c, m); - break; - - case Tcursor2: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, &m->cursor2); - replymsg(c, m); - break; - - case Tbouncemouse: - // _xbouncemouse(&m->mouse); - replymsg(c, m); - break; - - case Tlabel: - kicklabel(m->label); - replymsg(c, m); - break; - - case Trdsnarf: - m->snarf = getsnarf(); - replymsg(c, m); - free(m->snarf); - break; - - case Twrsnarf: - putsnarf(m->snarf); - replymsg(c, m); - break; - - case Trddraw: - qlock(&c->inputlk); - n = m->count; - if(n > sizeof buf) - n = sizeof buf; - n = _drawmsgread(c, buf, n); - if(n < 0) - replyerror(c, m); - else{ - m->count = n; - m->data = buf; - replymsg(c, m); - } - qunlock(&c->inputlk); - break; - - case Twrdraw: - qlock(&c->inputlk); - if(_drawmsgwrite(c, m->data, m->count) < 0) - replyerror(c, m); - else - replymsg(c, m); - qunlock(&c->inputlk); - break; - - case Ttop: - topwin(); - replymsg(c, m); - break; - - case Tresize: - resizewindow(m->rect); - replymsg(c, m); - break; - } -} - -/* - * Reply to m. - */ -QLock replylock; -void -replymsg(Client *c, Wsysmsg *m) -{ - int n; - static uchar *mbuf; - static int nmbuf; - - /* T -> R msg */ - if(m->type%2 == 0) - m->type++; - - if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m); - /* copy to output buffer */ - n = sizeW2M(m); - - qlock(&replylock); - if(n > nmbuf){ - free(mbuf); - mbuf = malloc(n); - if(mbuf == nil) - sysfatal("out of memory"); - nmbuf = n; - } - convW2M(m, mbuf, n); - if(write(c->wfd, mbuf, n) != n) - sysfatal("write: %r"); - qunlock(&replylock); -} - -/* - * Match queued kbd reads with queued kbd characters. - */ -void -matchkbd(Client *c) -{ - Wsysmsg m; - - if(c->kbd.stall) - return; - while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){ - m.type = Rrdkbd; - m.tag = c->kbdtags.t[c->kbdtags.ri++]; - if(c->kbdtags.ri == nelem(c->kbdtags.t)) - c->kbdtags.ri = 0; - m.rune = c->kbd.r[c->kbd.ri++]; - if(c->kbd.ri == nelem(c->kbd.r)) - c->kbd.ri = 0; - replymsg(c, &m); - } -} - -/* - * Match queued mouse reads with queued mouse events. - */ -void -matchmouse(Client *c) -{ - Wsysmsg m; - - while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){ - m.type = Rrdmouse; - m.tag = c->mousetags.t[c->mousetags.ri++]; - if(c->mousetags.ri == nelem(c->mousetags.t)) - c->mousetags.ri = 0; - m.mouse = c->mouse.m[c->mouse.ri]; - m.resized = c->mouse.resized; - c->mouse.resized = 0; - /* - if(m.resized) - fprint(2, "sending resize\n"); - */ - c->mouse.ri++; - if(c->mouse.ri == nelem(c->mouse.m)) - c->mouse.ri = 0; - replymsg(c, &m); - } -} - -void -mousetrack(Client *c, int x, int y, int b, uint ms) -{ - Mouse *m; - - if(x < c->mouserect.min.x) - x = c->mouserect.min.x; - if(x > c->mouserect.max.x) - x = c->mouserect.max.x; - if(y < c->mouserect.min.y) - y = c->mouserect.min.y; - if(y > c->mouserect.max.y) - y = c->mouserect.max.y; - - qlock(&c->inputlk); - // If reader has stopped reading, don't bother. - // If reader is completely caught up, definitely queue. - // Otherwise, queue only button change events. - if(!c->mouse.stall) - if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){ - m = &c->mouse.last; - m->xy.x = x; - m->xy.y = y; - m->buttons = b; - m->msec = ms; - - c->mouse.m[c->mouse.wi] = *m; - if(++c->mouse.wi == nelem(c->mouse.m)) - c->mouse.wi = 0; - if(c->mouse.wi == c->mouse.ri){ - c->mouse.stall = 1; - c->mouse.ri = 0; - c->mouse.wi = 1; - c->mouse.m[0] = *m; - } - matchmouse(c); - } - qunlock(&c->inputlk); -} - -void -kputc(Client *c, int ch) -{ - qlock(&c->inputlk); - c->kbd.r[c->kbd.wi++] = ch; - if(c->kbd.wi == nelem(c->kbd.r)) - c->kbd.wi = 0; - if(c->kbd.ri == c->kbd.wi) - c->kbd.stall = 1; - matchkbd(c); - qunlock(&c->inputlk); -} - -void -abortcompose(Client *c) -{ - if(c->kbd.alting) - keystroke(c, Kalt); -} - -void -keystroke(Client *c, int ch) -{ - static Rune k[10]; - static int nk; - int i; - - if(ch == Kalt){ - c->kbd.alting = !c->kbd.alting; - nk = 0; - return; - } - if(ch == Kcmd+'r') { - if(c->forcedpi) - c->forcedpi = 0; - else if(c->displaydpi >= 200) - c->forcedpi = 100; - else - c->forcedpi = 225; - resizeimg(c); - return; - } - if(!c->kbd.alting){ - kputc(c, ch); - return; - } - if(nk >= nelem(k)) // should not happen - nk = 0; - k[nk++] = ch; - ch = _latin1(k, nk); - if(ch > 0){ - c->kbd.alting = 0; - kputc(c, ch); - nk = 0; - return; - } - if(ch == -1){ - c->kbd.alting = 0; - for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devdraw.h" + +static void runmsg(Client*, Wsysmsg*); +static void replymsg(Client*, Wsysmsg*); +static void matchkbd(Client*); +static void matchmouse(Client*); + +int trace = 0; + +void +servep9p(Client *c) +{ + uchar buf[4], *mbuf; + int nmbuf, n, nn; + Wsysmsg m; + + fmtinstall('W', drawfcallfmt); + + mbuf = nil; + nmbuf = 0; + while((n = read(c->rfd, buf, 4)) == 4){ + GET(buf, n); + if(n > nmbuf){ + free(mbuf); + mbuf = malloc(4+n); + if(mbuf == nil) + sysfatal("malloc: %r"); + nmbuf = n; + } + memmove(mbuf, buf, 4); + nn = readn(c->rfd, mbuf+4, n-4); + if(nn != n-4) + sysfatal("eof during message"); + + /* pick off messages one by one */ + if(convM2W(mbuf, nn+4, &m) <= 0) + sysfatal("cannot convert message"); + if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); + runmsg(c, &m); + } +} + +static void +replyerror(Client *c, Wsysmsg *m) +{ + char err[256]; + + rerrstr(err, sizeof err); + m->type = Rerror; + m->error = err; + replymsg(c, m); +} + +/* + * Handle a single wsysmsg. + * Might queue for later (kbd, mouse read) + */ +static void +runmsg(Client *c, Wsysmsg *m) +{ + static uchar buf[65536]; + int n; + Memimage *i; + + switch(m->type){ + case Tinit: + memimageinit(); + i = rpc_attachscreen(c, m->label, m->winsize); + _initdisplaymemimage(c, i); + replymsg(c, m); + break; + + case Trdmouse: + qlock(&c->inputlk); + c->mousetags.t[c->mousetags.wi++] = m->tag; + if(c->mousetags.wi == nelem(c->mousetags.t)) + c->mousetags.wi = 0; + if(c->mousetags.wi == c->mousetags.ri) + sysfatal("too many queued mouse reads"); + c->mouse.stall = 0; + matchmouse(c); + qunlock(&c->inputlk); + break; + + case Trdkbd: + qlock(&c->inputlk); + c->kbdtags.t[c->kbdtags.wi++] = m->tag; + if(c->kbdtags.wi == nelem(c->kbdtags.t)) + c->kbdtags.wi = 0; + if(c->kbdtags.wi == c->kbdtags.ri) + sysfatal("too many queued keyboard reads"); + c->kbd.stall = 0; + matchkbd(c); + qunlock(&c->inputlk); + break; + + case Tmoveto: + rpc_setmouse(c, m->mouse.xy); + replymsg(c, m); + break; + + case Tcursor: + if(m->arrowcursor) + rpc_setcursor(c, nil, nil); + else { + scalecursor(&m->cursor2, &m->cursor); + rpc_setcursor(c, &m->cursor, &m->cursor2); + } + replymsg(c, m); + break; + + case Tcursor2: + if(m->arrowcursor) + rpc_setcursor(c, nil, nil); + else + rpc_setcursor(c, &m->cursor, &m->cursor2); + replymsg(c, m); + break; + + case Tbouncemouse: + // _xbouncemouse(&m->mouse); + replymsg(c, m); + break; + + case Tlabel: + rpc_setlabel(c, m->label); + replymsg(c, m); + break; + + case Trdsnarf: + m->snarf = rpc_getsnarf(); + replymsg(c, m); + free(m->snarf); + break; + + case Twrsnarf: + putsnarf(m->snarf); + replymsg(c, m); + break; + + case Trddraw: + qlock(&c->inputlk); + n = m->count; + if(n > sizeof buf) + n = sizeof buf; + n = _drawmsgread(c, buf, n); + if(n < 0) + replyerror(c, m); + else{ + m->count = n; + m->data = buf; + replymsg(c, m); + } + qunlock(&c->inputlk); + break; + + case Twrdraw: + qlock(&c->inputlk); + if(_drawmsgwrite(c, m->data, m->count) < 0) + replyerror(c, m); + else + replymsg(c, m); + qunlock(&c->inputlk); + break; + + case Ttop: + rpc_topwin(c); + replymsg(c, m); + break; + + case Tresize: + rpc_resizewindow(c, m->rect); + replymsg(c, m); + break; + } +} + +/* + * Reply to m. + */ +QLock replylock; +static void +replymsg(Client *c, Wsysmsg *m) +{ + int n; + static uchar *mbuf; + static int nmbuf; + + /* T -> R msg */ + if(m->type%2 == 0) + m->type++; + + if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m); + /* copy to output buffer */ + n = sizeW2M(m); + + qlock(&replylock); + if(n > nmbuf){ + free(mbuf); + mbuf = malloc(n); + if(mbuf == nil) + sysfatal("out of memory"); + nmbuf = n; + } + convW2M(m, mbuf, n); + if(write(c->wfd, mbuf, n) != n) + sysfatal("write: %r"); + qunlock(&replylock); +} + +/* + * Match queued kbd reads with queued kbd characters. + */ +static void +matchkbd(Client *c) +{ + Wsysmsg m; + + if(c->kbd.stall) + return; + while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){ + m.type = Rrdkbd; + m.tag = c->kbdtags.t[c->kbdtags.ri++]; + if(c->kbdtags.ri == nelem(c->kbdtags.t)) + c->kbdtags.ri = 0; + m.rune = c->kbd.r[c->kbd.ri++]; + if(c->kbd.ri == nelem(c->kbd.r)) + c->kbd.ri = 0; + replymsg(c, &m); + } +} + +// matchmouse matches queued mouse reads with queued mouse events. +// It must be called with c->inputlk held. +static void +matchmouse(Client *c) +{ + Wsysmsg m; + + if(canqlock(&c->inputlk)) { + fprint(2, "misuse of matchmouse\n"); + abort(); + } + + while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){ + m.type = Rrdmouse; + m.tag = c->mousetags.t[c->mousetags.ri++]; + if(c->mousetags.ri == nelem(c->mousetags.t)) + c->mousetags.ri = 0; + m.mouse = c->mouse.m[c->mouse.ri]; + m.resized = c->mouse.resized; + c->mouse.resized = 0; + /* + if(m.resized) + fprint(2, "sending resize\n"); + */ + c->mouse.ri++; + if(c->mouse.ri == nelem(c->mouse.m)) + c->mouse.ri = 0; + replymsg(c, &m); + } +} + +void +gfx_mousetrack(Client *c, int x, int y, int b, uint ms) +{ + Mouse *m; + + qlock(&c->inputlk); + if(x < c->mouserect.min.x) + x = c->mouserect.min.x; + if(x > c->mouserect.max.x) + x = c->mouserect.max.x; + if(y < c->mouserect.min.y) + y = c->mouserect.min.y; + if(y > c->mouserect.max.y) + y = c->mouserect.max.y; + + // If reader has stopped reading, don't bother. + // If reader is completely caught up, definitely queue. + // Otherwise, queue only button change events. + if(!c->mouse.stall) + if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){ + m = &c->mouse.last; + m->xy.x = x; + m->xy.y = y; + m->buttons = b; + m->msec = ms; + + c->mouse.m[c->mouse.wi] = *m; + if(++c->mouse.wi == nelem(c->mouse.m)) + c->mouse.wi = 0; + if(c->mouse.wi == c->mouse.ri){ + c->mouse.stall = 1; + c->mouse.ri = 0; + c->mouse.wi = 1; + c->mouse.m[0] = *m; + } + matchmouse(c); + } + qunlock(&c->inputlk); +} + +// kputc adds ch to the keyboard buffer. +// It must be called with c->inputlk held. +static void +kputc(Client *c, int ch) +{ + if(canqlock(&c->inputlk)) { + fprint(2, "misuse of kputc\n"); + abort(); + } + + c->kbd.r[c->kbd.wi++] = ch; + if(c->kbd.wi == nelem(c->kbd.r)) + c->kbd.wi = 0; + if(c->kbd.ri == c->kbd.wi) + c->kbd.stall = 1; + matchkbd(c); +} + +// gfx_abortcompose stops any pending compose sequence, +// because a mouse button has been clicked. +// It is called from the graphics thread with no locks held. +void +gfx_abortcompose(Client *c) +{ + qlock(&c->inputlk); + if(c->kbd.alting) { + c->kbd.alting = 0; + c->kbd.nk = 0; + } + qunlock(&c->inputlk); +} + +// gfx_keystroke records a single-rune keystroke. +// It is called from the graphics thread with no locks held. +void +gfx_keystroke(Client *c, int ch) +{ + int i; + + qlock(&c->inputlk); + if(ch == Kalt){ + c->kbd.alting = !c->kbd.alting; + c->kbd.nk = 0; + qunlock(&c->inputlk); + return; + } + if(ch == Kcmd+'r') { + if(c->forcedpi) + c->forcedpi = 0; + else if(c->displaydpi >= 200) + c->forcedpi = 100; + else + c->forcedpi = 225; + qunlock(&c->inputlk); + rpc_resizeimg(c); + return; + } + if(!c->kbd.alting){ + kputc(c, ch); + qunlock(&c->inputlk); + return; + } + if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen + c->kbd.nk = 0; + c->kbd.k[c->kbd.nk++] = ch; + ch = _latin1(c->kbd.k, c->kbd.nk); + if(ch > 0){ + c->kbd.alting = 0; + kputc(c, ch); + c->kbd.nk = 0; + qunlock(&c->inputlk); + return; + } + if(ch == -1){ + c->kbd.alting = 0; + for(i=0; ikbd.nk; i++) + kputc(c, c->kbd.k[i]); + c->kbd.nk = 0; + qunlock(&c->inputlk); + return; + } + // need more input + qunlock(&c->inputlk); + return; +} -- cgit v1.2.3