From db20f89c3286f277945ac4307f789a9980d31bf6 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 8 Jan 2020 19:54:43 -0500 Subject: devdraw: simplify mac file names Now that we only have Metal, we can drop the -metal. Also now that Carbon is gone we can drop the macargv.c, and then the -objc from object file names. --- src/cmd/devdraw/cocoa-screen-metal.m | 1246 ---------------------------------- src/cmd/devdraw/cocoa-screen.m | 1246 ++++++++++++++++++++++++++++++++++ src/cmd/devdraw/macargv.c | 90 --- src/cmd/devdraw/mkfile | 9 +- src/cmd/devdraw/mkwsysrules.sh | 4 +- 5 files changed, 1251 insertions(+), 1344 deletions(-) delete mode 100644 src/cmd/devdraw/cocoa-screen-metal.m create mode 100644 src/cmd/devdraw/cocoa-screen.m delete mode 100644 src/cmd/devdraw/macargv.c (limited to 'src') diff --git a/src/cmd/devdraw/cocoa-screen-metal.m b/src/cmd/devdraw/cocoa-screen-metal.m deleted file mode 100644 index 984ede03..00000000 --- a/src/cmd/devdraw/cocoa-screen-metal.m +++ /dev/null @@ -1,1246 +0,0 @@ -#define Cursor OSXCursor -#define Point OSXPoint -#define Rect OSXRect - -#import -#import -#import - -#undef Cursor -#undef Point -#undef Rect - -#include -#include -#include "cocoa-thread.h" -#include -#include -#include -#include -#include "cocoa-screen.h" -#include "osx-keycodes.h" -#include "devdraw.h" -#include "bigarrow.h" -#include "glendapng.h" - -AUTOFRAMEWORK(Cocoa) -AUTOFRAMEWORK(Metal) -AUTOFRAMEWORK(QuartzCore) - -#define LOG if(0)NSLog - -static void setprocname(const char*); -static uint keycvt(uint); -static uint msec(void); -static Memimage* initimg(void); - -void -usage(void) -{ - fprint(2, "usage: devdraw (don't run directly)\n"); - threadexitsall("usage"); -} - -@interface AppDelegate : NSObject -+ (void)callservep9p:(id)arg; -+ (void)makewin:(NSValue *)v; -+ (void)callkicklabel:(NSString *)v; -+ (void)callsetNeedsDisplayInRect:(NSValue *)v; -+ (void)callsetcursor:(NSValue *)v; -@end -@interface DevDrawView : NSView -- (void)clearInput; -- (void)getmouse:(NSEvent *)e; -- (void)sendmouse:(NSUInteger)b; -- (void)resetLastInputRect; -- (void)enlargeLastInputRect:(NSRect)r; -@end -@interface DrawLayer : CAMetalLayer -@end - -static AppDelegate *myApp = NULL; -static DevDrawView *myContent = NULL; -static NSWindow *win = NULL; -static NSCursor *currentCursor = NULL; - -static DrawLayer *layer; -static id device; -static id commandQueue; -static id texture; - -static Memimage *img = NULL; - -static QLock snarfl; - -void -threadmain(int argc, char **argv) -{ - /* - * Move the protocol off stdin/stdout so that - * any inadvertent prints don't screw things up. - */ - dup(0,3); - dup(1,4); - close(0); - close(1); - open("/dev/null", OREAD); - open("/dev/null", OWRITE); - - ARGBEGIN{ - case 'D': /* for good ps -a listings */ - break; - case 'f': /* fall through for backward compatibility */ - case 'g': - case 'b': - break; - default: - usage(); - }ARGEND - - setprocname(argv0); - - @autoreleasepool{ - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - myApp = [AppDelegate new]; - [NSApp setDelegate:myApp]; - [NSApp run]; - } -} - -@implementation AppDelegate - -+ (void)callservep9p:(id)arg -{ - servep9p(); - [NSApp terminate:self]; -} - -+ (void)makewin:(NSValue *)v -{ - NSRect r, sr; - Rectangle wr; - int set; - char *s; - NSArray *allDevices; - - const NSWindowStyleMask Winstyle = NSWindowStyleMaskTitled - | NSWindowStyleMaskClosable - | NSWindowStyleMaskMiniaturizable - | NSWindowStyleMaskResizable; - - sr = [[NSScreen mainScreen] frame]; - r = [[NSScreen mainScreen] visibleFrame]; - - s = [v pointerValue]; - LOG(@"makewin(%s)", s); - if(s && *s){ - if(parsewinsize(s, &wr, &set) < 0) - sysfatal("%r"); - }else{ - wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3); - set = 0; - } - - r.origin.x = wr.min.x; - r.origin.y = sr.size.height-wr.max.y; /* winsize is top-left-based */ - r.size.width = fmin(Dx(wr), r.size.width); - r.size.height = fmin(Dy(wr), r.size.height); - r = [NSWindow contentRectForFrameRect:r styleMask:Winstyle]; - - win = [[NSWindow alloc] - initWithContentRect:r - styleMask:Winstyle - backing:NSBackingStoreBuffered defer:NO]; - [win setTitle:@"devdraw"]; - - if(!set) - [win center]; - [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - [win setContentMinSize:NSMakeSize(64,64)]; - [win setOpaque:YES]; - [win setRestorable:NO]; - [win setAcceptsMouseMovedEvents:YES]; - [win setDelegate:myApp]; - - myContent = [DevDrawView new]; - [win setContentView:myContent]; - [myContent setWantsLayer:YES]; - [myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; - - device = nil; - allDevices = MTLCopyAllDevices(); - for(id mtlDevice in allDevices) { - if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) { - device = mtlDevice; - break; - } - } - if(!device) - device = MTLCreateSystemDefaultDevice(); - - commandQueue = [device newCommandQueue]; - - layer = (DrawLayer *)[myContent layer]; - layer.device = device; - 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. - { - CALayer *stub = [CALayer layer]; - stub.frame = CGRectMake(0, 0, 1, 1); - [stub setNeedsDisplay]; - [layer addSublayer:stub]; - } - - [NSEvent setMouseCoalescingEnabled:NO]; - - topwin(); -} - -+ (void)callkicklabel:(NSString *)s -{ - LOG(@"callkicklabel(%@)", s); - [win setTitle:s]; - [[NSApp dockTile] setBadgeLabel:s]; -} - - -+ (void)callsetNeedsDisplayInRect:(NSValue *)v -{ - NSRect r; - dispatch_time_t time; - - r = [v rectValue]; - LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - r = [win convertRectFromBacking:r]; - LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - [layer setNeedsDisplayInRect:r]; - - time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); - dispatch_after(time, dispatch_get_main_queue(), ^(void){ - [layer setNeedsDisplayInRect:r]; - }); - - [myContent enlargeLastInputRect:r]; -} - -typedef struct Cursors Cursors; -struct Cursors { - Cursor *c; - Cursor2 *c2; -}; - -+ (void)callsetcursor:(NSValue *)v -{ - Cursors *cs; - Cursor *c; - Cursor2 *c2; - NSBitmapImageRep *r, *r2; - NSImage *i; - NSPoint p; - uchar *plane[5], *plane2[5]; - uint b; - - cs = [v pointerValue]; - c = cs->c; - if(!c) - c = &bigarrow; - c2 = cs->c2; - if(!c2) - c2 = &bigarrow2; - - r = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nil - pixelsWide:16 - pixelsHigh:16 - bitsPerSample:1 - samplesPerPixel:2 - hasAlpha:YES - isPlanar:YES - colorSpaceName:NSDeviceWhiteColorSpace - bytesPerRow:2 - bitsPerPixel:0]; - [r getBitmapDataPlanes:plane]; - for(b=0; bset); b++){ - plane[0][b] = ~c->set[b] & c->clr[b]; - plane[1][b] = c->set[b] | c->clr[b]; - } - - r2 = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nil - pixelsWide:32 - pixelsHigh:32 - bitsPerSample:1 - samplesPerPixel:2 - hasAlpha:YES - isPlanar:YES - colorSpaceName:NSDeviceWhiteColorSpace - bytesPerRow:4 - bitsPerPixel:0]; - [r2 getBitmapDataPlanes:plane2]; - for(b=0; bset); b++){ - plane2[0][b] = ~c2->set[b] & c2->clr[b]; - plane2[1][b] = c2->set[b] | c2->clr[b]; - } - - // For checking out the cursor bitmap image -/* - static BOOL saveimg = YES; - if(saveimg){ - 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; - } -*/ - - i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; - [i addRepresentation:r2]; - [i addRepresentation:r]; - - p = NSMakePoint(-c->offset.x, -c->offset.y); - currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; - - [win invalidateCursorRectsForView:myContent]; -} - -- (void)applicationDidFinishLaunching:(id)arg -{ - NSMenu *m, *sm; - NSData *d; - NSImage *i; - - 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]; - - [NSThread - detachNewThreadSelector:@selector(callservep9p:) - toTarget:[self class] withObject:nil]; -} - -- (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; -} - -- (void)windowDidResize:(NSNotification *)notification -{ - if(![myContent inLiveResize] && img) { - resizeimg(); - } -} - -- (void)windowDidBecomeKey:(id)arg -{ - [myContent sendmouse:0]; -} - -@end - -@implementation DevDrawView -{ - 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 -{ - LOG(@"makeBackingLayer"); - return [DrawLayer layer]; -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -- (BOOL)isOpaque -{ - return YES; -} - -- (BOOL)isFlipped -{ - return YES; -} - -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -- (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];} -- (void)mouseDown:(NSEvent*)e{ [self getmouse:e];} -- (void)mouseDragged:(NSEvent*)e{ [self getmouse:e];} -- (void)mouseUp:(NSEvent*)e{ [self getmouse:e];} -- (void)otherMouseDown:(NSEvent*)e{ [self getmouse:e];} -- (void)otherMouseDragged:(NSEvent*)e{ [self getmouse:e];} -- (void)otherMouseUp:(NSEvent*)e{ [self getmouse:e];} -- (void)rightMouseDown:(NSEvent*)e{ [self getmouse:e];} -- (void)rightMouseDragged:(NSEvent*)e{ [self getmouse:e];} -- (void)rightMouseUp:(NSEvent*)e{ [self getmouse:e];} - -- (void)scrollWheel:(NSEvent*)e -{ - NSInteger s; - - s = [e scrollingDeltaY]; - if(s > 0) - [self sendmouse:8]; - else if (s < 0) - [self sendmouse:16]; -} - -- (void)keyDown:(NSEvent*)e -{ - LOG(@"keyDown to interpret"); - - [self interpretKeyEvents:[NSArray arrayWithObject:e]]; - - [self resetLastInputRect]; -} - -- (void)flagsChanged:(NSEvent*)e -{ - static NSEventModifierFlags omod; - NSEventModifierFlags m; - uint b; - - LOG(@"flagsChanged"); - m = [e modifierFlags]; - - b = [NSEvent pressedMouseButtons]; - b = (b&~6) | (b&4)>>1 | (b&2)<<1; - if(b){ - if(m & ~omod & NSEventModifierFlagControl) - b |= 1; - if(m & ~omod & NSEventModifierFlagOption) - b |= 2; - if(m & ~omod & NSEventModifierFlagCommand) - b |= 4; - [self sendmouse:b]; - }else if(m & ~omod & NSEventModifierFlagOption) - keystroke(Kalt); - - omod = m; -} - -- (void)magnifyWithEvent:(NSEvent*)e -{ - if(fabs([e magnification]) > 0.02) - [[self window] toggleFullScreen:nil]; -} - -- (void)touchesBeganWithEvent:(NSEvent*)e -{ - _tapping = YES; - _tapFingers = [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count; - _tapTime = msec(); -} -- (void)touchesMovedWithEvent:(NSEvent*)e -{ - _tapping = NO; -} -- (void)touchesEndedWithEvent:(NSEvent*)e -{ - if(_tapping - && [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count == 0 - && msec() - _tapTime < 250){ - switch(_tapFingers){ - case 3: - [self sendmouse:2]; - [self sendmouse:0]; - break; - case 4: - [self sendmouse:2]; - [self sendmouse:1]; - [self sendmouse:0]; - break; - } - _tapping = NO; - } -} -- (void)touchesCancelledWithEvent:(NSEvent*)e -{ - _tapping = NO; -} - -- (void)getmouse:(NSEvent *)e -{ - NSUInteger b; - NSEventModifierFlags m; - - b = [NSEvent pressedMouseButtons]; - b = b&~6 | (b&4)>>1 | (b&2)<<1; - b = mouseswap(b); - - if(b == 1){ - m = [e modifierFlags]; - if(m & NSEventModifierFlagOption){ - abortcompose(); - b = 2; - }else - if(m & NSEventModifierFlagCommand) - b = 4; - } - [self sendmouse:b]; -} - -- (void)sendmouse:(NSUInteger)b -{ - NSPoint p; - - p = [self.window convertPointToBacking: - [self.window mouseLocationOutsideOfEventStream]]; - p.y = Dy(mouserect) - p.y; - // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b); - mousetrack(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:currentCursor]; -} - -- (void)viewDidEndLiveResize -{ - [super viewDidEndLiveResize]; - if(img) - resizeimg(); -} - -- (void)viewDidChangeBackingProperties -{ - [super viewDidChangeBackingProperties]; - if(img) - resizeimg(); -} - -// conforms to protocol NSTextInputClient -- (BOOL)hasMarkedText -{ - LOG(@"hasMarkedText"); - return _markedRange.location != NSNotFound; -} -- (NSRange)markedRange -{ - LOG(@"markedRange"); - return _markedRange; -} -- (NSRange)selectedRange -{ - LOG(@"selectedRange"); - return _selectedRange; -} -- (void)setMarkedText:(id)string - selectedRange:(NSRange)sRange - replacementRange:(NSRange)rRange -{ - NSString *str; - - LOG(@"setMarkedText: %@ (%ld, %ld) (%ld, %ld)", string, - sRange.location, sRange.length, - rRange.location, rRange.length); - - [self clearInput]; - - if([string isKindOfClass:[NSAttributedString class]]) - str = [string string]; - else - str = string; - - if(rRange.location == NSNotFound){ - if(_markedRange.location != NSNotFound){ - rRange = _markedRange; - }else{ - rRange = _selectedRange; - } - } - - if(str.length == 0){ - [_tmpText deleteCharactersInRange:rRange]; - [self unmarkText]; - }else{ - _markedRange = NSMakeRange(rRange.location, str.length); - [_tmpText replaceCharactersInRange:rRange withString:str]; - } - _selectedRange.location = rRange.location + sRange.location; - _selectedRange.length = sRange.length; - - if(_tmpText.length){ - uint i; - LOG(@"text length %ld", _tmpText.length); - for(i = 0; i <= _tmpText.length; ++i){ - if(i == _markedRange.location) - keystroke('['); - if(_selectedRange.length){ - if(i == _selectedRange.location) - keystroke('{'); - if(i == NSMaxRange(_selectedRange)) - keystroke('}'); - } - if(i == NSMaxRange(_markedRange)) - keystroke(']'); - if(i < _tmpText.length) - keystroke([_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(Kleft); - } - - LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText, - _markedRange.location, _markedRange.length, - _selectedRange.location, _selectedRange.length); -} -- (void)unmarkText -{ - //NSUInteger i; - NSUInteger len; - - LOG(@"unmarkText"); - len = [_tmpText length]; - //for(i = 0; i < len; ++i) - // keystroke([_tmpText characterAtIndex:i]); - [_tmpText deleteCharactersInRange:NSMakeRange(0, len)]; - _markedRange = NSMakeRange(NSNotFound, 0); - _selectedRange = NSMakeRange(0, 0); -} -- (NSArray *)validAttributesForMarkedText -{ - LOG(@"validAttributesForMarkedText"); - return @[]; -} -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r - actualRange:(NSRangePointer)actualRange -{ - NSRange sr; - NSAttributedString *s; - - LOG(@"attributedSubstringForProposedRange: (%ld, %ld) (%ld, %ld)", - r.location, r.length, actualRange->location, actualRange->length); - sr = NSMakeRange(0, [_tmpText length]); - sr = NSIntersectionRange(sr, r); - if(actualRange) - *actualRange = sr; - LOG(@"use range: %ld, %ld", sr.location, sr.length); - if(sr.length) - s = [[NSAttributedString alloc] - initWithString:[_tmpText substringWithRange:sr]]; - LOG(@" return %@", s); - return s; -} -- (void)insertText:(id)s - replacementRange:(NSRange)r -{ - NSUInteger i; - NSUInteger len; - - LOG(@"insertText: %@ replacementRange: %ld, %ld", s, r.location, r.length); - - [self clearInput]; - - len = [s length]; - for(i = 0; i < len; ++i) - keystroke([s characterAtIndex:i]); - [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)]; - _markedRange = NSMakeRange(NSNotFound, 0); - _selectedRange = NSMakeRange(0, 0); -} -- (NSUInteger)characterIndexForPoint:(NSPoint)point -{ - LOG(@"characterIndexForPoint: %g, %g", point.x, point.y); - return 0; -} -- (NSRect)firstRectForCharacterRange:(NSRange)r - actualRange:(NSRangePointer)actualRange -{ - LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)", - r.location, r.length, actualRange->location, actualRange->length); - if(actualRange) - *actualRange = r; - return [[self window] convertRectToScreen:_lastInputRect]; -} -- (void)doCommandBySelector:(SEL)s -{ - NSEvent *e; - NSEventModifierFlags m; - uint c, k; - - LOG(@"doCommandBySelector (%@)", NSStringFromSelector(s)); - - e = [NSApp currentEvent]; - c = [[e characters] characterAtIndex:0]; - k = keycvt(c); - LOG(@"keyDown: character0: 0x%x -> 0x%x", c, k); - m = [e modifierFlags]; - - if(m & NSEventModifierFlagCommand){ - if((m & NSEventModifierFlagShift) && 'a' <= k && k <= 'z') - k += 'A' - 'a'; - if(' '<=k && k<='~') - k += Kcmd; - } - if(k>0) - keystroke(k); -} - -// Helper for managing input rect approximately -- (void)resetLastInputRect -{ - LOG(@"resetLastInputRect"); - _lastInputRect.origin.x = 0.0; - _lastInputRect.origin.y = 0.0; - _lastInputRect.size.width = 0.0; - _lastInputRect.size.height = 0.0; -} - -- (void)enlargeLastInputRect:(NSRect)r -{ - r.origin.y = [self bounds].size.height - r.origin.y - r.size.height; - _lastInputRect = NSUnionRect(_lastInputRect, r); - LOG(@"update last input rect (%g, %g, %g, %g)", - _lastInputRect.origin.x, _lastInputRect.origin.y, - _lastInputRect.size.width, _lastInputRect.size.height); -} - -- (void)clearInput -{ - if(_tmpText.length){ - uint i; - int l; - l = 1 + _tmpText.length - NSMaxRange(_selectedRange) - + (_selectedRange.length > 0); - LOG(@"move right %d", l); - for(i = 0; i < l; ++i) - keystroke(Kright); - l = _tmpText.length+2+2*(_selectedRange.length > 0); - LOG(@"backspace %d", l); - for(uint i = 0; i < l; ++i) - keystroke(Kbs); - } -} - -@end - -@implementation DrawLayer - -- (void)display -{ - id cbuf; - id blit; - - LOG(@"display"); - - cbuf = [commandQueue commandBuffer]; - - LOG(@"display query drawable"); - -@autoreleasepool{ - id drawable; - - drawable = [layer nextDrawable]; - if(!drawable){ - LOG(@"display couldn't get drawable"); - [self setNeedsDisplay]; - return; - } - - 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 -msec(void) -{ - return nsec()/1000000; -} - -static uint -keycvt(uint code) -{ - switch(code){ - case '\r': return '\n'; - case 127: return '\b'; - case NSUpArrowFunctionKey: return Kup; - case NSDownArrowFunctionKey: return Kdown; - case NSLeftArrowFunctionKey: return Kleft; - case NSRightArrowFunctionKey: return Kright; - case NSInsertFunctionKey: return Kins; - case NSDeleteFunctionKey: return Kdel; - case NSHomeFunctionKey: return Khome; - case NSEndFunctionKey: return Kend; - case NSPageUpFunctionKey: return Kpgup; - case NSPageDownFunctionKey: return Kpgdown; - case NSF1FunctionKey: return KF|1; - case NSF2FunctionKey: return KF|2; - case NSF3FunctionKey: return KF|3; - case NSF4FunctionKey: return KF|4; - case NSF5FunctionKey: return KF|5; - case NSF6FunctionKey: return KF|6; - case NSF7FunctionKey: return KF|7; - case NSF8FunctionKey: return KF|8; - case NSF9FunctionKey: return KF|9; - case NSF10FunctionKey: return KF|10; - case NSF11FunctionKey: return KF|11; - case NSF12FunctionKey: return KF|12; - case NSBeginFunctionKey: - case NSPrintScreenFunctionKey: - case NSScrollLockFunctionKey: - case NSF13FunctionKey: - case NSF14FunctionKey: - case NSF15FunctionKey: - case NSF16FunctionKey: - case NSF17FunctionKey: - case NSF18FunctionKey: - case NSF19FunctionKey: - case NSF20FunctionKey: - case NSF21FunctionKey: - case NSF22FunctionKey: - case NSF23FunctionKey: - case NSF24FunctionKey: - case NSF25FunctionKey: - case NSF26FunctionKey: - case NSF27FunctionKey: - case NSF28FunctionKey: - case NSF29FunctionKey: - case NSF30FunctionKey: - case NSF31FunctionKey: - case NSF32FunctionKey: - case NSF33FunctionKey: - case NSF34FunctionKey: - case NSF35FunctionKey: - case NSPauseFunctionKey: - case NSSysReqFunctionKey: - case NSBreakFunctionKey: - case NSResetFunctionKey: - case NSStopFunctionKey: - case NSMenuFunctionKey: - case NSUserFunctionKey: - case NSSystemFunctionKey: - case NSPrintFunctionKey: - case NSClearLineFunctionKey: - case NSClearDisplayFunctionKey: - case NSInsertLineFunctionKey: - case NSDeleteLineFunctionKey: - case NSInsertCharFunctionKey: - case NSDeleteCharFunctionKey: - case NSPrevFunctionKey: - case NSNextFunctionKey: - case NSSelectFunctionKey: - case NSExecuteFunctionKey: - case NSUndoFunctionKey: - case NSRedoFunctionKey: - case NSFindFunctionKey: - case NSHelpFunctionKey: - case NSModeSwitchFunctionKey: return 0; - default: return code; - } -} - -Memimage* -attachscreen(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); - mouseresized = 0; - return initimg(); -} - -static Memimage* -initimg(void) -{ -@autoreleasepool{ - CGFloat scale; - NSSize size; - MTLTextureDescriptor *textureDesc; - - size = [myContent convertSizeToBacking:[myContent bounds].size]; - mouserect = Rect(0, 0, size.width, size.height); - - LOG(@"initimg %.0f %.0f", size.width, size.height); - - img = allocmemimage(mouserect, XRGB32); - if(img == nil) - panic("allocmemimage: %r"); - if(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 = [win backingScaleFactor]; - [layer setDrawableSize:size]; - [layer 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 - displaydpi = scale * 110; -} - LOG(@"initimg return"); - - return img; -} - -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, 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(img, Pt(r.min.x, r.min.y)) - bytesPerRow: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) -{ - @autoreleasepool{ - NSPoint q; - - LOG(@"setmouse(%d,%d)", p.x, p.y); - q = [win convertPointFromBacking:NSMakePoint(p.x, p.y)]; - LOG(@"(%g, %g) <- fromBacking", q.x, q.y); - q = [myContent convertPoint:q toView:nil]; - LOG(@"(%g, %g) <- toWindow", q.x, q.y); - q = [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); - } -} - -char* -getsnarf(void) -{ - NSPasteboard *pb; - NSString *s; - - @autoreleasepool{ - pb = [NSPasteboard generalPasteboard]; - - qlock(&snarfl); - s = [pb stringForType:NSPasteboardTypeString]; - qunlock(&snarfl); - - if(s) - return strdup((char *)[s UTF8String]); - else - return nil; - } -} - -void -putsnarf(char *s) -{ - NSArray *t; - NSPasteboard *pb; - NSString *str; - - if(strlen(s) >= SnarfSize) - return; - - @autoreleasepool{ - t = [NSArray arrayWithObject:NSPasteboardTypeString]; - pb = [NSPasteboard generalPasteboard]; - str = [[NSString alloc] initWithUTF8String:s]; - - qlock(&snarfl); - [pb declareTypes:t owner:nil]; - [pb setString:str forType:NSPasteboardTypeString]; - qunlock(&snarfl); - } -} - -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) -{ - [win - performSelectorOnMainThread: - @selector(makeKeyAndOrderFront:) - withObject:nil - waitUntilDone:YES]; - - [NSApp activateIgnoringOtherApps:YES]; -} - -void -resizeimg(void) -{ - zlock(); - _drawreplacescreenimage(initimg()); - - mouseresized = 1; - zunlock(); - [myContent sendmouse:0]; -} - -void -resizewindow(Rectangle r) -{ - 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 = [myContent convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; - [win setContentSize:s]; - }); -} - -static void -setprocname(const char *s) -{ - CFStringRef process_name; - - process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFStringEncodingUTF8, false); - - // Adapted from Chrome's mac_util.mm. - // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm - // - // Copyright (c) 2012 The Chromium Authors. All rights reserved. - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions are - // met: - // - // * Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // * Redistributions in binary form must reproduce the above - // copyright notice, this list of conditions and the following disclaimer - // in the documentation and/or other materials provided with the - // distribution. - // * Neither the name of Google Inc. nor the names of its - // contributors may be used to endorse or promote products derived from - // this software without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - // Warning: here be dragons! This is SPI reverse-engineered from WebKit's - // plugin host, and could break at any time (although realistically it's only - // likely to break in a new major release). - // When 10.7 is available, check that this still works, and update this - // comment for 10.8. - - // Private CFType used in these LaunchServices calls. - typedef CFTypeRef PrivateLSASN; - typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); - typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, - CFStringRef, - CFStringRef, - CFDictionaryRef*); - - static LSGetCurrentApplicationASNType ls_get_current_application_asn_func = - NULL; - static LSSetApplicationInformationItemType - ls_set_application_information_item_func = NULL; - static CFStringRef ls_display_name_key = NULL; - - static bool did_symbol_lookup = false; - if (!did_symbol_lookup) { - did_symbol_lookup = true; - CFBundleRef launch_services_bundle = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); - if (!launch_services_bundle) { - fprint(2, "Failed to look up LaunchServices bundle\n"); - return; - } - - ls_get_current_application_asn_func = - (LSGetCurrentApplicationASNType)( - CFBundleGetFunctionPointerForName( - launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); - if (!ls_get_current_application_asn_func) - fprint(2, "Could not find _LSGetCurrentApplicationASN\n"); - - ls_set_application_information_item_func = - (LSSetApplicationInformationItemType)( - CFBundleGetFunctionPointerForName( - launch_services_bundle, - CFSTR("_LSSetApplicationInformationItem"))); - if (!ls_set_application_information_item_func) - fprint(2, "Could not find _LSSetApplicationInformationItem\n"); - - CFStringRef* key_pointer = (CFStringRef*)( - CFBundleGetDataPointerForName(launch_services_bundle, - CFSTR("_kLSDisplayNameKey"))); - ls_display_name_key = key_pointer ? *key_pointer : NULL; - if (!ls_display_name_key) - fprint(2, "Could not find _kLSDisplayNameKey\n"); - - // Internally, this call relies on the Mach ports that are started up by the - // Carbon Process Manager. In debug builds this usually happens due to how - // the logging layers are started up; but in release, it isn't started in as - // much of a defined order. So if the symbols had to be loaded, go ahead - // and force a call to make sure the manager has been initialized and hence - // the ports are opened. - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - } - if (!ls_get_current_application_asn_func || - !ls_set_application_information_item_func || - !ls_display_name_key) { - return; - } - - PrivateLSASN asn = ls_get_current_application_asn_func(); - // Constant used by WebKit; what exactly it means is unknown. - const int magic_session_constant = -2; - OSErr err = - ls_set_application_information_item_func(magic_session_constant, asn, - ls_display_name_key, - process_name, - NULL /* optional out param */); - if(err != noErr) - fprint(2, "Call to set process name failed\n"); -} diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m new file mode 100644 index 00000000..984ede03 --- /dev/null +++ b/src/cmd/devdraw/cocoa-screen.m @@ -0,0 +1,1246 @@ +#define Cursor OSXCursor +#define Point OSXPoint +#define Rect OSXRect + +#import +#import +#import + +#undef Cursor +#undef Point +#undef Rect + +#include +#include +#include "cocoa-thread.h" +#include +#include +#include +#include +#include "cocoa-screen.h" +#include "osx-keycodes.h" +#include "devdraw.h" +#include "bigarrow.h" +#include "glendapng.h" + +AUTOFRAMEWORK(Cocoa) +AUTOFRAMEWORK(Metal) +AUTOFRAMEWORK(QuartzCore) + +#define LOG if(0)NSLog + +static void setprocname(const char*); +static uint keycvt(uint); +static uint msec(void); +static Memimage* initimg(void); + +void +usage(void) +{ + fprint(2, "usage: devdraw (don't run directly)\n"); + threadexitsall("usage"); +} + +@interface AppDelegate : NSObject ++ (void)callservep9p:(id)arg; ++ (void)makewin:(NSValue *)v; ++ (void)callkicklabel:(NSString *)v; ++ (void)callsetNeedsDisplayInRect:(NSValue *)v; ++ (void)callsetcursor:(NSValue *)v; +@end +@interface DevDrawView : NSView +- (void)clearInput; +- (void)getmouse:(NSEvent *)e; +- (void)sendmouse:(NSUInteger)b; +- (void)resetLastInputRect; +- (void)enlargeLastInputRect:(NSRect)r; +@end +@interface DrawLayer : CAMetalLayer +@end + +static AppDelegate *myApp = NULL; +static DevDrawView *myContent = NULL; +static NSWindow *win = NULL; +static NSCursor *currentCursor = NULL; + +static DrawLayer *layer; +static id device; +static id commandQueue; +static id texture; + +static Memimage *img = NULL; + +static QLock snarfl; + +void +threadmain(int argc, char **argv) +{ + /* + * Move the protocol off stdin/stdout so that + * any inadvertent prints don't screw things up. + */ + dup(0,3); + dup(1,4); + close(0); + close(1); + open("/dev/null", OREAD); + open("/dev/null", OWRITE); + + ARGBEGIN{ + case 'D': /* for good ps -a listings */ + break; + case 'f': /* fall through for backward compatibility */ + case 'g': + case 'b': + break; + default: + usage(); + }ARGEND + + setprocname(argv0); + + @autoreleasepool{ + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + myApp = [AppDelegate new]; + [NSApp setDelegate:myApp]; + [NSApp run]; + } +} + +@implementation AppDelegate + ++ (void)callservep9p:(id)arg +{ + servep9p(); + [NSApp terminate:self]; +} + ++ (void)makewin:(NSValue *)v +{ + NSRect r, sr; + Rectangle wr; + int set; + char *s; + NSArray *allDevices; + + const NSWindowStyleMask Winstyle = NSWindowStyleMaskTitled + | NSWindowStyleMaskClosable + | NSWindowStyleMaskMiniaturizable + | NSWindowStyleMaskResizable; + + sr = [[NSScreen mainScreen] frame]; + r = [[NSScreen mainScreen] visibleFrame]; + + s = [v pointerValue]; + LOG(@"makewin(%s)", s); + if(s && *s){ + if(parsewinsize(s, &wr, &set) < 0) + sysfatal("%r"); + }else{ + wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3); + set = 0; + } + + r.origin.x = wr.min.x; + r.origin.y = sr.size.height-wr.max.y; /* winsize is top-left-based */ + r.size.width = fmin(Dx(wr), r.size.width); + r.size.height = fmin(Dy(wr), r.size.height); + r = [NSWindow contentRectForFrameRect:r styleMask:Winstyle]; + + win = [[NSWindow alloc] + initWithContentRect:r + styleMask:Winstyle + backing:NSBackingStoreBuffered defer:NO]; + [win setTitle:@"devdraw"]; + + if(!set) + [win center]; + [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + [win setContentMinSize:NSMakeSize(64,64)]; + [win setOpaque:YES]; + [win setRestorable:NO]; + [win setAcceptsMouseMovedEvents:YES]; + [win setDelegate:myApp]; + + myContent = [DevDrawView new]; + [win setContentView:myContent]; + [myContent setWantsLayer:YES]; + [myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; + + device = nil; + allDevices = MTLCopyAllDevices(); + for(id mtlDevice in allDevices) { + if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) { + device = mtlDevice; + break; + } + } + if(!device) + device = MTLCreateSystemDefaultDevice(); + + commandQueue = [device newCommandQueue]; + + layer = (DrawLayer *)[myContent layer]; + layer.device = device; + 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. + { + CALayer *stub = [CALayer layer]; + stub.frame = CGRectMake(0, 0, 1, 1); + [stub setNeedsDisplay]; + [layer addSublayer:stub]; + } + + [NSEvent setMouseCoalescingEnabled:NO]; + + topwin(); +} + ++ (void)callkicklabel:(NSString *)s +{ + LOG(@"callkicklabel(%@)", s); + [win setTitle:s]; + [[NSApp dockTile] setBadgeLabel:s]; +} + + ++ (void)callsetNeedsDisplayInRect:(NSValue *)v +{ + NSRect r; + dispatch_time_t time; + + r = [v rectValue]; + LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); + r = [win convertRectFromBacking:r]; + LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); + [layer setNeedsDisplayInRect:r]; + + time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); + dispatch_after(time, dispatch_get_main_queue(), ^(void){ + [layer setNeedsDisplayInRect:r]; + }); + + [myContent enlargeLastInputRect:r]; +} + +typedef struct Cursors Cursors; +struct Cursors { + Cursor *c; + Cursor2 *c2; +}; + ++ (void)callsetcursor:(NSValue *)v +{ + Cursors *cs; + Cursor *c; + Cursor2 *c2; + NSBitmapImageRep *r, *r2; + NSImage *i; + NSPoint p; + uchar *plane[5], *plane2[5]; + uint b; + + cs = [v pointerValue]; + c = cs->c; + if(!c) + c = &bigarrow; + c2 = cs->c2; + if(!c2) + c2 = &bigarrow2; + + r = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:nil + pixelsWide:16 + pixelsHigh:16 + bitsPerSample:1 + samplesPerPixel:2 + hasAlpha:YES + isPlanar:YES + colorSpaceName:NSDeviceWhiteColorSpace + bytesPerRow:2 + bitsPerPixel:0]; + [r getBitmapDataPlanes:plane]; + for(b=0; bset); b++){ + plane[0][b] = ~c->set[b] & c->clr[b]; + plane[1][b] = c->set[b] | c->clr[b]; + } + + r2 = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:nil + pixelsWide:32 + pixelsHigh:32 + bitsPerSample:1 + samplesPerPixel:2 + hasAlpha:YES + isPlanar:YES + colorSpaceName:NSDeviceWhiteColorSpace + bytesPerRow:4 + bitsPerPixel:0]; + [r2 getBitmapDataPlanes:plane2]; + for(b=0; bset); b++){ + plane2[0][b] = ~c2->set[b] & c2->clr[b]; + plane2[1][b] = c2->set[b] | c2->clr[b]; + } + + // For checking out the cursor bitmap image +/* + static BOOL saveimg = YES; + if(saveimg){ + 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; + } +*/ + + i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; + [i addRepresentation:r2]; + [i addRepresentation:r]; + + p = NSMakePoint(-c->offset.x, -c->offset.y); + currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; + + [win invalidateCursorRectsForView:myContent]; +} + +- (void)applicationDidFinishLaunching:(id)arg +{ + NSMenu *m, *sm; + NSData *d; + NSImage *i; + + 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]; + + [NSThread + detachNewThreadSelector:@selector(callservep9p:) + toTarget:[self class] withObject:nil]; +} + +- (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; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + if(![myContent inLiveResize] && img) { + resizeimg(); + } +} + +- (void)windowDidBecomeKey:(id)arg +{ + [myContent sendmouse:0]; +} + +@end + +@implementation DevDrawView +{ + 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 +{ + LOG(@"makeBackingLayer"); + return [DrawLayer layer]; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];} +- (void)mouseDown:(NSEvent*)e{ [self getmouse:e];} +- (void)mouseDragged:(NSEvent*)e{ [self getmouse:e];} +- (void)mouseUp:(NSEvent*)e{ [self getmouse:e];} +- (void)otherMouseDown:(NSEvent*)e{ [self getmouse:e];} +- (void)otherMouseDragged:(NSEvent*)e{ [self getmouse:e];} +- (void)otherMouseUp:(NSEvent*)e{ [self getmouse:e];} +- (void)rightMouseDown:(NSEvent*)e{ [self getmouse:e];} +- (void)rightMouseDragged:(NSEvent*)e{ [self getmouse:e];} +- (void)rightMouseUp:(NSEvent*)e{ [self getmouse:e];} + +- (void)scrollWheel:(NSEvent*)e +{ + NSInteger s; + + s = [e scrollingDeltaY]; + if(s > 0) + [self sendmouse:8]; + else if (s < 0) + [self sendmouse:16]; +} + +- (void)keyDown:(NSEvent*)e +{ + LOG(@"keyDown to interpret"); + + [self interpretKeyEvents:[NSArray arrayWithObject:e]]; + + [self resetLastInputRect]; +} + +- (void)flagsChanged:(NSEvent*)e +{ + static NSEventModifierFlags omod; + NSEventModifierFlags m; + uint b; + + LOG(@"flagsChanged"); + m = [e modifierFlags]; + + b = [NSEvent pressedMouseButtons]; + b = (b&~6) | (b&4)>>1 | (b&2)<<1; + if(b){ + if(m & ~omod & NSEventModifierFlagControl) + b |= 1; + if(m & ~omod & NSEventModifierFlagOption) + b |= 2; + if(m & ~omod & NSEventModifierFlagCommand) + b |= 4; + [self sendmouse:b]; + }else if(m & ~omod & NSEventModifierFlagOption) + keystroke(Kalt); + + omod = m; +} + +- (void)magnifyWithEvent:(NSEvent*)e +{ + if(fabs([e magnification]) > 0.02) + [[self window] toggleFullScreen:nil]; +} + +- (void)touchesBeganWithEvent:(NSEvent*)e +{ + _tapping = YES; + _tapFingers = [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count; + _tapTime = msec(); +} +- (void)touchesMovedWithEvent:(NSEvent*)e +{ + _tapping = NO; +} +- (void)touchesEndedWithEvent:(NSEvent*)e +{ + if(_tapping + && [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count == 0 + && msec() - _tapTime < 250){ + switch(_tapFingers){ + case 3: + [self sendmouse:2]; + [self sendmouse:0]; + break; + case 4: + [self sendmouse:2]; + [self sendmouse:1]; + [self sendmouse:0]; + break; + } + _tapping = NO; + } +} +- (void)touchesCancelledWithEvent:(NSEvent*)e +{ + _tapping = NO; +} + +- (void)getmouse:(NSEvent *)e +{ + NSUInteger b; + NSEventModifierFlags m; + + b = [NSEvent pressedMouseButtons]; + b = b&~6 | (b&4)>>1 | (b&2)<<1; + b = mouseswap(b); + + if(b == 1){ + m = [e modifierFlags]; + if(m & NSEventModifierFlagOption){ + abortcompose(); + b = 2; + }else + if(m & NSEventModifierFlagCommand) + b = 4; + } + [self sendmouse:b]; +} + +- (void)sendmouse:(NSUInteger)b +{ + NSPoint p; + + p = [self.window convertPointToBacking: + [self.window mouseLocationOutsideOfEventStream]]; + p.y = Dy(mouserect) - p.y; + // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b); + mousetrack(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:currentCursor]; +} + +- (void)viewDidEndLiveResize +{ + [super viewDidEndLiveResize]; + if(img) + resizeimg(); +} + +- (void)viewDidChangeBackingProperties +{ + [super viewDidChangeBackingProperties]; + if(img) + resizeimg(); +} + +// conforms to protocol NSTextInputClient +- (BOOL)hasMarkedText +{ + LOG(@"hasMarkedText"); + return _markedRange.location != NSNotFound; +} +- (NSRange)markedRange +{ + LOG(@"markedRange"); + return _markedRange; +} +- (NSRange)selectedRange +{ + LOG(@"selectedRange"); + return _selectedRange; +} +- (void)setMarkedText:(id)string + selectedRange:(NSRange)sRange + replacementRange:(NSRange)rRange +{ + NSString *str; + + LOG(@"setMarkedText: %@ (%ld, %ld) (%ld, %ld)", string, + sRange.location, sRange.length, + rRange.location, rRange.length); + + [self clearInput]; + + if([string isKindOfClass:[NSAttributedString class]]) + str = [string string]; + else + str = string; + + if(rRange.location == NSNotFound){ + if(_markedRange.location != NSNotFound){ + rRange = _markedRange; + }else{ + rRange = _selectedRange; + } + } + + if(str.length == 0){ + [_tmpText deleteCharactersInRange:rRange]; + [self unmarkText]; + }else{ + _markedRange = NSMakeRange(rRange.location, str.length); + [_tmpText replaceCharactersInRange:rRange withString:str]; + } + _selectedRange.location = rRange.location + sRange.location; + _selectedRange.length = sRange.length; + + if(_tmpText.length){ + uint i; + LOG(@"text length %ld", _tmpText.length); + for(i = 0; i <= _tmpText.length; ++i){ + if(i == _markedRange.location) + keystroke('['); + if(_selectedRange.length){ + if(i == _selectedRange.location) + keystroke('{'); + if(i == NSMaxRange(_selectedRange)) + keystroke('}'); + } + if(i == NSMaxRange(_markedRange)) + keystroke(']'); + if(i < _tmpText.length) + keystroke([_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(Kleft); + } + + LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText, + _markedRange.location, _markedRange.length, + _selectedRange.location, _selectedRange.length); +} +- (void)unmarkText +{ + //NSUInteger i; + NSUInteger len; + + LOG(@"unmarkText"); + len = [_tmpText length]; + //for(i = 0; i < len; ++i) + // keystroke([_tmpText characterAtIndex:i]); + [_tmpText deleteCharactersInRange:NSMakeRange(0, len)]; + _markedRange = NSMakeRange(NSNotFound, 0); + _selectedRange = NSMakeRange(0, 0); +} +- (NSArray *)validAttributesForMarkedText +{ + LOG(@"validAttributesForMarkedText"); + return @[]; +} +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r + actualRange:(NSRangePointer)actualRange +{ + NSRange sr; + NSAttributedString *s; + + LOG(@"attributedSubstringForProposedRange: (%ld, %ld) (%ld, %ld)", + r.location, r.length, actualRange->location, actualRange->length); + sr = NSMakeRange(0, [_tmpText length]); + sr = NSIntersectionRange(sr, r); + if(actualRange) + *actualRange = sr; + LOG(@"use range: %ld, %ld", sr.location, sr.length); + if(sr.length) + s = [[NSAttributedString alloc] + initWithString:[_tmpText substringWithRange:sr]]; + LOG(@" return %@", s); + return s; +} +- (void)insertText:(id)s + replacementRange:(NSRange)r +{ + NSUInteger i; + NSUInteger len; + + LOG(@"insertText: %@ replacementRange: %ld, %ld", s, r.location, r.length); + + [self clearInput]; + + len = [s length]; + for(i = 0; i < len; ++i) + keystroke([s characterAtIndex:i]); + [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)]; + _markedRange = NSMakeRange(NSNotFound, 0); + _selectedRange = NSMakeRange(0, 0); +} +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + LOG(@"characterIndexForPoint: %g, %g", point.x, point.y); + return 0; +} +- (NSRect)firstRectForCharacterRange:(NSRange)r + actualRange:(NSRangePointer)actualRange +{ + LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)", + r.location, r.length, actualRange->location, actualRange->length); + if(actualRange) + *actualRange = r; + return [[self window] convertRectToScreen:_lastInputRect]; +} +- (void)doCommandBySelector:(SEL)s +{ + NSEvent *e; + NSEventModifierFlags m; + uint c, k; + + LOG(@"doCommandBySelector (%@)", NSStringFromSelector(s)); + + e = [NSApp currentEvent]; + c = [[e characters] characterAtIndex:0]; + k = keycvt(c); + LOG(@"keyDown: character0: 0x%x -> 0x%x", c, k); + m = [e modifierFlags]; + + if(m & NSEventModifierFlagCommand){ + if((m & NSEventModifierFlagShift) && 'a' <= k && k <= 'z') + k += 'A' - 'a'; + if(' '<=k && k<='~') + k += Kcmd; + } + if(k>0) + keystroke(k); +} + +// Helper for managing input rect approximately +- (void)resetLastInputRect +{ + LOG(@"resetLastInputRect"); + _lastInputRect.origin.x = 0.0; + _lastInputRect.origin.y = 0.0; + _lastInputRect.size.width = 0.0; + _lastInputRect.size.height = 0.0; +} + +- (void)enlargeLastInputRect:(NSRect)r +{ + r.origin.y = [self bounds].size.height - r.origin.y - r.size.height; + _lastInputRect = NSUnionRect(_lastInputRect, r); + LOG(@"update last input rect (%g, %g, %g, %g)", + _lastInputRect.origin.x, _lastInputRect.origin.y, + _lastInputRect.size.width, _lastInputRect.size.height); +} + +- (void)clearInput +{ + if(_tmpText.length){ + uint i; + int l; + l = 1 + _tmpText.length - NSMaxRange(_selectedRange) + + (_selectedRange.length > 0); + LOG(@"move right %d", l); + for(i = 0; i < l; ++i) + keystroke(Kright); + l = _tmpText.length+2+2*(_selectedRange.length > 0); + LOG(@"backspace %d", l); + for(uint i = 0; i < l; ++i) + keystroke(Kbs); + } +} + +@end + +@implementation DrawLayer + +- (void)display +{ + id cbuf; + id blit; + + LOG(@"display"); + + cbuf = [commandQueue commandBuffer]; + + LOG(@"display query drawable"); + +@autoreleasepool{ + id drawable; + + drawable = [layer nextDrawable]; + if(!drawable){ + LOG(@"display couldn't get drawable"); + [self setNeedsDisplay]; + return; + } + + 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 +msec(void) +{ + return nsec()/1000000; +} + +static uint +keycvt(uint code) +{ + switch(code){ + case '\r': return '\n'; + case 127: return '\b'; + case NSUpArrowFunctionKey: return Kup; + case NSDownArrowFunctionKey: return Kdown; + case NSLeftArrowFunctionKey: return Kleft; + case NSRightArrowFunctionKey: return Kright; + case NSInsertFunctionKey: return Kins; + case NSDeleteFunctionKey: return Kdel; + case NSHomeFunctionKey: return Khome; + case NSEndFunctionKey: return Kend; + case NSPageUpFunctionKey: return Kpgup; + case NSPageDownFunctionKey: return Kpgdown; + case NSF1FunctionKey: return KF|1; + case NSF2FunctionKey: return KF|2; + case NSF3FunctionKey: return KF|3; + case NSF4FunctionKey: return KF|4; + case NSF5FunctionKey: return KF|5; + case NSF6FunctionKey: return KF|6; + case NSF7FunctionKey: return KF|7; + case NSF8FunctionKey: return KF|8; + case NSF9FunctionKey: return KF|9; + case NSF10FunctionKey: return KF|10; + case NSF11FunctionKey: return KF|11; + case NSF12FunctionKey: return KF|12; + case NSBeginFunctionKey: + case NSPrintScreenFunctionKey: + case NSScrollLockFunctionKey: + case NSF13FunctionKey: + case NSF14FunctionKey: + case NSF15FunctionKey: + case NSF16FunctionKey: + case NSF17FunctionKey: + case NSF18FunctionKey: + case NSF19FunctionKey: + case NSF20FunctionKey: + case NSF21FunctionKey: + case NSF22FunctionKey: + case NSF23FunctionKey: + case NSF24FunctionKey: + case NSF25FunctionKey: + case NSF26FunctionKey: + case NSF27FunctionKey: + case NSF28FunctionKey: + case NSF29FunctionKey: + case NSF30FunctionKey: + case NSF31FunctionKey: + case NSF32FunctionKey: + case NSF33FunctionKey: + case NSF34FunctionKey: + case NSF35FunctionKey: + case NSPauseFunctionKey: + case NSSysReqFunctionKey: + case NSBreakFunctionKey: + case NSResetFunctionKey: + case NSStopFunctionKey: + case NSMenuFunctionKey: + case NSUserFunctionKey: + case NSSystemFunctionKey: + case NSPrintFunctionKey: + case NSClearLineFunctionKey: + case NSClearDisplayFunctionKey: + case NSInsertLineFunctionKey: + case NSDeleteLineFunctionKey: + case NSInsertCharFunctionKey: + case NSDeleteCharFunctionKey: + case NSPrevFunctionKey: + case NSNextFunctionKey: + case NSSelectFunctionKey: + case NSExecuteFunctionKey: + case NSUndoFunctionKey: + case NSRedoFunctionKey: + case NSFindFunctionKey: + case NSHelpFunctionKey: + case NSModeSwitchFunctionKey: return 0; + default: return code; + } +} + +Memimage* +attachscreen(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); + mouseresized = 0; + return initimg(); +} + +static Memimage* +initimg(void) +{ +@autoreleasepool{ + CGFloat scale; + NSSize size; + MTLTextureDescriptor *textureDesc; + + size = [myContent convertSizeToBacking:[myContent bounds].size]; + mouserect = Rect(0, 0, size.width, size.height); + + LOG(@"initimg %.0f %.0f", size.width, size.height); + + img = allocmemimage(mouserect, XRGB32); + if(img == nil) + panic("allocmemimage: %r"); + if(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 = [win backingScaleFactor]; + [layer setDrawableSize:size]; + [layer 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 + displaydpi = scale * 110; +} + LOG(@"initimg return"); + + return img; +} + +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, 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(img, Pt(r.min.x, r.min.y)) + bytesPerRow: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) +{ + @autoreleasepool{ + NSPoint q; + + LOG(@"setmouse(%d,%d)", p.x, p.y); + q = [win convertPointFromBacking:NSMakePoint(p.x, p.y)]; + LOG(@"(%g, %g) <- fromBacking", q.x, q.y); + q = [myContent convertPoint:q toView:nil]; + LOG(@"(%g, %g) <- toWindow", q.x, q.y); + q = [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); + } +} + +char* +getsnarf(void) +{ + NSPasteboard *pb; + NSString *s; + + @autoreleasepool{ + pb = [NSPasteboard generalPasteboard]; + + qlock(&snarfl); + s = [pb stringForType:NSPasteboardTypeString]; + qunlock(&snarfl); + + if(s) + return strdup((char *)[s UTF8String]); + else + return nil; + } +} + +void +putsnarf(char *s) +{ + NSArray *t; + NSPasteboard *pb; + NSString *str; + + if(strlen(s) >= SnarfSize) + return; + + @autoreleasepool{ + t = [NSArray arrayWithObject:NSPasteboardTypeString]; + pb = [NSPasteboard generalPasteboard]; + str = [[NSString alloc] initWithUTF8String:s]; + + qlock(&snarfl); + [pb declareTypes:t owner:nil]; + [pb setString:str forType:NSPasteboardTypeString]; + qunlock(&snarfl); + } +} + +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) +{ + [win + performSelectorOnMainThread: + @selector(makeKeyAndOrderFront:) + withObject:nil + waitUntilDone:YES]; + + [NSApp activateIgnoringOtherApps:YES]; +} + +void +resizeimg(void) +{ + zlock(); + _drawreplacescreenimage(initimg()); + + mouseresized = 1; + zunlock(); + [myContent sendmouse:0]; +} + +void +resizewindow(Rectangle r) +{ + 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 = [myContent convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; + [win setContentSize:s]; + }); +} + +static void +setprocname(const char *s) +{ + CFStringRef process_name; + + process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFStringEncodingUTF8, false); + + // Adapted from Chrome's mac_util.mm. + // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm + // + // Copyright (c) 2012 The Chromium Authors. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following disclaimer + // in the documentation and/or other materials provided with the + // distribution. + // * Neither the name of Google Inc. nor the names of its + // contributors may be used to endorse or promote products derived from + // this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // Warning: here be dragons! This is SPI reverse-engineered from WebKit's + // plugin host, and could break at any time (although realistically it's only + // likely to break in a new major release). + // When 10.7 is available, check that this still works, and update this + // comment for 10.8. + + // Private CFType used in these LaunchServices calls. + typedef CFTypeRef PrivateLSASN; + typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); + typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, + CFStringRef, + CFStringRef, + CFDictionaryRef*); + + static LSGetCurrentApplicationASNType ls_get_current_application_asn_func = + NULL; + static LSSetApplicationInformationItemType + ls_set_application_information_item_func = NULL; + static CFStringRef ls_display_name_key = NULL; + + static bool did_symbol_lookup = false; + if (!did_symbol_lookup) { + did_symbol_lookup = true; + CFBundleRef launch_services_bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); + if (!launch_services_bundle) { + fprint(2, "Failed to look up LaunchServices bundle\n"); + return; + } + + ls_get_current_application_asn_func = + (LSGetCurrentApplicationASNType)( + CFBundleGetFunctionPointerForName( + launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); + if (!ls_get_current_application_asn_func) + fprint(2, "Could not find _LSGetCurrentApplicationASN\n"); + + ls_set_application_information_item_func = + (LSSetApplicationInformationItemType)( + CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSSetApplicationInformationItem"))); + if (!ls_set_application_information_item_func) + fprint(2, "Could not find _LSSetApplicationInformationItem\n"); + + CFStringRef* key_pointer = (CFStringRef*)( + CFBundleGetDataPointerForName(launch_services_bundle, + CFSTR("_kLSDisplayNameKey"))); + ls_display_name_key = key_pointer ? *key_pointer : NULL; + if (!ls_display_name_key) + fprint(2, "Could not find _kLSDisplayNameKey\n"); + + // Internally, this call relies on the Mach ports that are started up by the + // Carbon Process Manager. In debug builds this usually happens due to how + // the logging layers are started up; but in release, it isn't started in as + // much of a defined order. So if the symbols had to be loaded, go ahead + // and force a call to make sure the manager has been initialized and hence + // the ports are opened. + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + } + if (!ls_get_current_application_asn_func || + !ls_set_application_information_item_func || + !ls_display_name_key) { + return; + } + + PrivateLSASN asn = ls_get_current_application_asn_func(); + // Constant used by WebKit; what exactly it means is unknown. + const int magic_session_constant = -2; + OSErr err = + ls_set_application_information_item_func(magic_session_constant, asn, + ls_display_name_key, + process_name, + NULL /* optional out param */); + if(err != noErr) + fprint(2, "Call to set process name failed\n"); +} diff --git a/src/cmd/devdraw/macargv.c b/src/cmd/devdraw/macargv.c deleted file mode 100644 index a5ea1ade..00000000 --- a/src/cmd/devdraw/macargv.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include - -AUTOFRAMEWORK(Carbon) - -static OSErr Handler(const AppleEvent *event, AppleEvent *reply, long handlerRefcon); - -int -main(void) -{ - AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, Handler, 0, false); - RunApplicationEventLoop(); - return 0; -} - -static OSErr -GetFullPathname(FSSpec *fss, char *path, int len) -{ - FSRef fsr; - OSErr err; - - *path = '\0'; - err = FSpMakeFSRef(fss, &fsr); - if (err == fnfErr) { - /* FSSpecs can point to non-existing files, fsrefs can't. */ - FSSpec fss2; - int tocopy; - - err = FSMakeFSSpec(fss->vRefNum, fss->parID, - (unsigned char*)"", &fss2); - if (err) - return err; - err = FSpMakeFSRef(&fss2, &fsr); - if (err) - return err; - err = (OSErr)FSRefMakePath(&fsr, (unsigned char*)path, len-1); - if (err) - return err; - /* This part is not 100% safe: we append the filename part, but - ** I'm not sure that we don't run afoul of the various 8bit - ** encodings here. Will have to look this up at some point... - */ - strcat(path, "/"); - tocopy = fss->name[0]; - if ((strlen(path) + tocopy) >= len) - tocopy = len - strlen(path) - 1; - if (tocopy > 0) - strncat(path, (char*)fss->name+1, tocopy); - } - else { - if (err) - return err; - err = (OSErr)FSRefMakePath(&fsr, (unsigned char*)path, len); - if (err) - return err; - } - return 0; -} - -static void -chk(int err) -{ - if(err != 0) { - printf("err %d\n", err); - exit(1); - } -} - -static OSErr -Handler(const AppleEvent *event, AppleEvent *reply, long handlerRefcon) -{ - AEDesc list; - DescType type; - FSSpec f; - AEKeyword keyword; - Size actual; - long len; - char s[1000]; - - chk(AEGetParamDesc(event, keyDirectObject, typeAEList, &list)); - chk(AECountItems(&list, &len)); - chk(AEGetNthPtr(&list, 1, typeFSS, &keyword, &type, (Ptr*)&f, sizeof(FSSpec), &actual)); - chk(GetFullPathname(&f, s, sizeof s)); - printf("%s\n", s); - fflush(stdout); - - // uncomment to keep handling more open events - exit(0); -} diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile index e60b427f..27613239 100644 --- a/src/cmd/devdraw/mkfile +++ b/src/cmd/devdraw/mkfile @@ -37,13 +37,10 @@ latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd $O.macargv: $MACARGV $LD -o $target $prereq -cocoa-screen-metal-objc.$O: cocoa-screen-metal.m - $CC $CFLAGS $OBJCFLAGS -o $target cocoa-screen-metal.m +%.$O: %.m + $CC $CFLAGS $OBJCFLAGS -o $target $stem.m -%-objc.$O: %.m - $CC $CFLAGS -o $target $stem.m - -CLEANFILES=$O.macargv $O.mklatinkbd latin1.h +CLEANFILES=$O.devdraw $O.macargv $O.mklatinkbd latin1.h install: mklatinkbd.install install:Q: diff --git a/src/cmd/devdraw/mkwsysrules.sh b/src/cmd/devdraw/mkwsysrules.sh index a1dc56d3..8ab67f76 100644 --- a/src/cmd/devdraw/mkwsysrules.sh +++ b/src/cmd/devdraw/mkwsysrules.sh @@ -53,8 +53,8 @@ if [ $WSYSTYPE = x11 ]; then echo 'WSYSOFILES=$WSYSOFILES '$XO elif [ $WSYSTYPE = osx-cocoa ]; then echo 'OBJCFLAGS=$OBJCFLAGS -fobjc-arc' - echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-metal-objc.o cocoa-srv.o cocoa-thread.o' - echo 'MACARGV=macargv-objc.o' + echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen.o cocoa-srv.o cocoa-thread.o' + echo 'MACARGV=macargv.o' elif [ $WSYSTYPE = nowsys ]; then echo 'WSYSOFILES=nowsys.o' fi -- cgit v1.2.3