commit 933b98054f40bb224acda134d7bb77a023bcc57f from: Russ Cox date: Mon Jan 13 21:46:14 2020 UTC devdraw: use consistent mac-* prefix on macOS files We were using osx- and cocoa- but it's not even OS X anymore. commit - ce27d7babdf2ee09ff6d1f8d4a166c2208995774 commit + 933b98054f40bb224acda134d7bb77a023bcc57f blob - b5e3c70107ba82cd2020c2d9137d8d7950f3c519 (mode 644) blob + /dev/null --- src/cmd/devdraw/cocoa-screen.h +++ /dev/null @@ -1,24 +0,0 @@ -#define setcursor dsetcursor - -Memimage *attachscreen(char*, char*); -void setmouse(Point); -void setcursor(Cursor*, Cursor2*); -void setlabel(char*); -char* getsnarf(void); -void putsnarf(char*); -void topwin(void); - -void mousetrack(int, int, int, uint); -void keystroke(int); -void kicklabel(char*); - -void servep9p(void); -void zlock(void); -void zunlock(void); - -void resizeimg(void); - -Rectangle mouserect; - -int mouseresized; -void resizewindow(Rectangle); blob - 64b2bf4c581c63789f8cff04c5e4671d3aaef464 (mode 644) blob + /dev/null --- src/cmd/devdraw/cocoa-screen.m +++ /dev/null @@ -1,1249 +0,0 @@ -#define Cursor OSXCursor -#define Point OSXPoint -#define Rect OSXRect - -#import -#import -#import - -#undef Cursor -#undef Point -#undef Rect - -#include -#include -#include -#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)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]; - } -} - - -void -callservep9p(void *v) -{ - USED(v); - - servep9p(); - [NSApp terminate:myApp]; -} - -@implementation AppDelegate - -+ (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]; - - 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; -} - -- (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); - s = nil; - 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"); -} blob - 2211a06f75a0cbb29313c811a4940a86371b5605 (mode 644) blob + /dev/null --- src/cmd/devdraw/cocoa-srv.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Window system protocol server. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cocoa-screen.h" -#include "devdraw.h" - -typedef struct Kbdbuf Kbdbuf; -typedef struct Mousebuf Mousebuf; -typedef struct Fdbuf Fdbuf; -typedef struct Tagbuf Tagbuf; - -struct Kbdbuf -{ - Rune r[256]; - int ri; - int wi; - int stall; -}; - -struct Mousebuf -{ - Mouse m[256]; - Mouse last; - int ri; - int wi; - int stall; -}; - -struct Tagbuf -{ - int t[256]; - int ri; - int wi; -}; - -Kbdbuf kbd; -Mousebuf mouse; -Tagbuf kbdtags; -Tagbuf mousetags; - -void runmsg(Wsysmsg*); -void replymsg(Wsysmsg*); -void matchkbd(void); -void matchmouse(void); - - -QLock lk; -void -zlock(void) -{ - qlock(&lk); -} - -void -zunlock(void) -{ - qunlock(&lk); -} - -int trace = 0; - -void -servep9p(void) -{ - uchar buf[4], *mbuf; - int nmbuf, n, nn; - Wsysmsg m; - - fmtinstall('W', drawfcallfmt); - - mbuf = nil; - nmbuf = 0; - while((n = read(3, 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(3, 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(&m); - } -} - -void -replyerror(Wsysmsg *m) -{ - char err[256]; - - rerrstr(err, sizeof err); - m->type = Rerror; - m->error = err; - replymsg(m); -} - -/* - * Handle a single wsysmsg. - * Might queue for later (kbd, mouse read) - */ -void -runmsg(Wsysmsg *m) -{ - static uchar buf[65536]; - int n; - Memimage *i; - - switch(m->type){ - case Tinit: - memimageinit(); - i = attachscreen(m->label, m->winsize); - _initdisplaymemimage(i); - replymsg(m); - break; - - case Trdmouse: - zlock(); - mousetags.t[mousetags.wi++] = m->tag; - if(mousetags.wi == nelem(mousetags.t)) - mousetags.wi = 0; - if(mousetags.wi == mousetags.ri) - sysfatal("too many queued mouse reads"); - mouse.stall = 0; - matchmouse(); - zunlock(); - break; - - case Trdkbd: - zlock(); - kbdtags.t[kbdtags.wi++] = m->tag; - if(kbdtags.wi == nelem(kbdtags.t)) - kbdtags.wi = 0; - if(kbdtags.wi == kbdtags.ri) - sysfatal("too many queued keyboard reads"); - kbd.stall = 0; - matchkbd(); - zunlock(); - break; - - case Tmoveto: - setmouse(m->mouse.xy); - replymsg(m); - break; - - case Tcursor: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, nil); - replymsg(m); - break; - - case Tcursor2: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, &m->cursor2); - replymsg(m); - break; - - case Tbouncemouse: - // _xbouncemouse(&m->mouse); - replymsg(m); - break; - - case Tlabel: - kicklabel(m->label); - replymsg(m); - break; - - case Trdsnarf: - m->snarf = getsnarf(); - replymsg(m); - free(m->snarf); - break; - - case Twrsnarf: - putsnarf(m->snarf); - replymsg(m); - break; - - case Trddraw: - zlock(); - n = m->count; - if(n > sizeof buf) - n = sizeof buf; - n = _drawmsgread(buf, n); - if(n < 0) - replyerror(m); - else{ - m->count = n; - m->data = buf; - replymsg(m); - } - zunlock(); - break; - - case Twrdraw: - zlock(); - if(_drawmsgwrite(m->data, m->count) < 0) - replyerror(m); - else - replymsg(m); - zunlock(); - break; - - case Ttop: - topwin(); - replymsg(m); - break; - - case Tresize: - resizewindow(m->rect); - replymsg(m); - break; - } -} - -/* - * Reply to m. - */ -QLock replylock; -void -replymsg(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(4, mbuf, n) != n) - sysfatal("write: %r"); - qunlock(&replylock); -} - -/* - * Match queued kbd reads with queued kbd characters. - */ -void -matchkbd(void) -{ - Wsysmsg m; - - if(kbd.stall) - return; - while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){ - m.type = Rrdkbd; - m.tag = kbdtags.t[kbdtags.ri++]; - if(kbdtags.ri == nelem(kbdtags.t)) - kbdtags.ri = 0; - m.rune = kbd.r[kbd.ri++]; - if(kbd.ri == nelem(kbd.r)) - kbd.ri = 0; - replymsg(&m); - } -} - -/* - * Match queued mouse reads with queued mouse events. - */ -void -matchmouse(void) -{ - Wsysmsg m; - - while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){ - m.type = Rrdmouse; - m.tag = mousetags.t[mousetags.ri++]; - if(mousetags.ri == nelem(mousetags.t)) - mousetags.ri = 0; - m.mouse = mouse.m[mouse.ri]; - m.resized = mouseresized; - mouseresized = 0; - /* - if(m.resized) - fprint(2, "sending resize\n"); - */ - mouse.ri++; - if(mouse.ri == nelem(mouse.m)) - mouse.ri = 0; - replymsg(&m); - } -} - -void -mousetrack(int x, int y, int b, uint ms) -{ - Mouse *m; - - if(x < mouserect.min.x) - x = mouserect.min.x; - if(x > mouserect.max.x) - x = mouserect.max.x; - if(y < mouserect.min.y) - y = mouserect.min.y; - if(y > mouserect.max.y) - y = mouserect.max.y; - - zlock(); - // If reader has stopped reading, don't bother. - // If reader is completely caught up, definitely queue. - // Otherwise, queue only button change events. - if(!mouse.stall) - if(mouse.wi == mouse.ri || mouse.last.buttons != b){ - m = &mouse.last; - m->xy.x = x; - m->xy.y = y; - m->buttons = b; - m->msec = ms; - - mouse.m[mouse.wi] = *m; - if(++mouse.wi == nelem(mouse.m)) - mouse.wi = 0; - if(mouse.wi == mouse.ri){ - mouse.stall = 1; - mouse.ri = 0; - mouse.wi = 1; - mouse.m[0] = *m; - } - matchmouse(); - } - zunlock(); -} - -void -kputc(int c) -{ - zlock(); - kbd.r[kbd.wi++] = c; - if(kbd.wi == nelem(kbd.r)) - kbd.wi = 0; - if(kbd.ri == kbd.wi) - kbd.stall = 1; - matchkbd(); - zunlock(); -} - -static int alting; - -void -abortcompose(void) -{ - if(alting) - keystroke(Kalt); -} - -void -keystroke(int c) -{ - static Rune k[10]; - static int nk; - int i; - - if(c == Kalt){ - alting = !alting; - nk = 0; - return; - } - if(c == Kcmd+'r') { - if(forcedpi) - forcedpi = 0; - else if(displaydpi >= 200) - forcedpi = 100; - else - forcedpi = 225; - resizeimg(); - return; - } - if(!alting){ - kputc(c); - return; - } - if(nk >= nelem(k)) // should not happen - nk = 0; - k[nk++] = c; - c = _latin1(k, nk); - if(c > 0){ - alting = 0; - kputc(c); - nk = 0; - return; - } - if(c == -1){ - alting = 0; - for(i=0; i +#import +#import + +#undef Cursor +#undef Point +#undef Rect + +#include +#include +#include +#include +#include +#include +#include +#include "mac-screen.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)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]; + } +} + + +void +callservep9p(void *v) +{ + USED(v); + + servep9p(); + [NSApp terminate:myApp]; +} + +@implementation AppDelegate + ++ (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]; + + 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; +} + +- (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); + s = nil; + 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"); +} blob - /dev/null blob + b0925eee5d210ea3f97663f77ce1380f1f1edd4d (mode 644) --- /dev/null +++ src/cmd/devdraw/mac-srv.c @@ -0,0 +1,427 @@ +/* + * Window system protocol server. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mac-screen.h" +#include "devdraw.h" + +typedef struct Kbdbuf Kbdbuf; +typedef struct Mousebuf Mousebuf; +typedef struct Fdbuf Fdbuf; +typedef struct Tagbuf Tagbuf; + +struct Kbdbuf +{ + Rune r[256]; + int ri; + int wi; + int stall; +}; + +struct Mousebuf +{ + Mouse m[256]; + Mouse last; + int ri; + int wi; + int stall; +}; + +struct Tagbuf +{ + int t[256]; + int ri; + int wi; +}; + +Kbdbuf kbd; +Mousebuf mouse; +Tagbuf kbdtags; +Tagbuf mousetags; + +void runmsg(Wsysmsg*); +void replymsg(Wsysmsg*); +void matchkbd(void); +void matchmouse(void); + + +QLock lk; +void +zlock(void) +{ + qlock(&lk); +} + +void +zunlock(void) +{ + qunlock(&lk); +} + +int trace = 0; + +void +servep9p(void) +{ + uchar buf[4], *mbuf; + int nmbuf, n, nn; + Wsysmsg m; + + fmtinstall('W', drawfcallfmt); + + mbuf = nil; + nmbuf = 0; + while((n = read(3, 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(3, 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(&m); + } +} + +void +replyerror(Wsysmsg *m) +{ + char err[256]; + + rerrstr(err, sizeof err); + m->type = Rerror; + m->error = err; + replymsg(m); +} + +/* + * Handle a single wsysmsg. + * Might queue for later (kbd, mouse read) + */ +void +runmsg(Wsysmsg *m) +{ + static uchar buf[65536]; + int n; + Memimage *i; + + switch(m->type){ + case Tinit: + memimageinit(); + i = attachscreen(m->label, m->winsize); + _initdisplaymemimage(i); + replymsg(m); + break; + + case Trdmouse: + zlock(); + mousetags.t[mousetags.wi++] = m->tag; + if(mousetags.wi == nelem(mousetags.t)) + mousetags.wi = 0; + if(mousetags.wi == mousetags.ri) + sysfatal("too many queued mouse reads"); + mouse.stall = 0; + matchmouse(); + zunlock(); + break; + + case Trdkbd: + zlock(); + kbdtags.t[kbdtags.wi++] = m->tag; + if(kbdtags.wi == nelem(kbdtags.t)) + kbdtags.wi = 0; + if(kbdtags.wi == kbdtags.ri) + sysfatal("too many queued keyboard reads"); + kbd.stall = 0; + matchkbd(); + zunlock(); + break; + + case Tmoveto: + setmouse(m->mouse.xy); + replymsg(m); + break; + + case Tcursor: + if(m->arrowcursor) + setcursor(nil, nil); + else + setcursor(&m->cursor, nil); + replymsg(m); + break; + + case Tcursor2: + if(m->arrowcursor) + setcursor(nil, nil); + else + setcursor(&m->cursor, &m->cursor2); + replymsg(m); + break; + + case Tbouncemouse: + // _xbouncemouse(&m->mouse); + replymsg(m); + break; + + case Tlabel: + kicklabel(m->label); + replymsg(m); + break; + + case Trdsnarf: + m->snarf = getsnarf(); + replymsg(m); + free(m->snarf); + break; + + case Twrsnarf: + putsnarf(m->snarf); + replymsg(m); + break; + + case Trddraw: + zlock(); + n = m->count; + if(n > sizeof buf) + n = sizeof buf; + n = _drawmsgread(buf, n); + if(n < 0) + replyerror(m); + else{ + m->count = n; + m->data = buf; + replymsg(m); + } + zunlock(); + break; + + case Twrdraw: + zlock(); + if(_drawmsgwrite(m->data, m->count) < 0) + replyerror(m); + else + replymsg(m); + zunlock(); + break; + + case Ttop: + topwin(); + replymsg(m); + break; + + case Tresize: + resizewindow(m->rect); + replymsg(m); + break; + } +} + +/* + * Reply to m. + */ +QLock replylock; +void +replymsg(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(4, mbuf, n) != n) + sysfatal("write: %r"); + qunlock(&replylock); +} + +/* + * Match queued kbd reads with queued kbd characters. + */ +void +matchkbd(void) +{ + Wsysmsg m; + + if(kbd.stall) + return; + while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){ + m.type = Rrdkbd; + m.tag = kbdtags.t[kbdtags.ri++]; + if(kbdtags.ri == nelem(kbdtags.t)) + kbdtags.ri = 0; + m.rune = kbd.r[kbd.ri++]; + if(kbd.ri == nelem(kbd.r)) + kbd.ri = 0; + replymsg(&m); + } +} + +/* + * Match queued mouse reads with queued mouse events. + */ +void +matchmouse(void) +{ + Wsysmsg m; + + while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){ + m.type = Rrdmouse; + m.tag = mousetags.t[mousetags.ri++]; + if(mousetags.ri == nelem(mousetags.t)) + mousetags.ri = 0; + m.mouse = mouse.m[mouse.ri]; + m.resized = mouseresized; + mouseresized = 0; + /* + if(m.resized) + fprint(2, "sending resize\n"); + */ + mouse.ri++; + if(mouse.ri == nelem(mouse.m)) + mouse.ri = 0; + replymsg(&m); + } +} + +void +mousetrack(int x, int y, int b, uint ms) +{ + Mouse *m; + + if(x < mouserect.min.x) + x = mouserect.min.x; + if(x > mouserect.max.x) + x = mouserect.max.x; + if(y < mouserect.min.y) + y = mouserect.min.y; + if(y > mouserect.max.y) + y = mouserect.max.y; + + zlock(); + // If reader has stopped reading, don't bother. + // If reader is completely caught up, definitely queue. + // Otherwise, queue only button change events. + if(!mouse.stall) + if(mouse.wi == mouse.ri || mouse.last.buttons != b){ + m = &mouse.last; + m->xy.x = x; + m->xy.y = y; + m->buttons = b; + m->msec = ms; + + mouse.m[mouse.wi] = *m; + if(++mouse.wi == nelem(mouse.m)) + mouse.wi = 0; + if(mouse.wi == mouse.ri){ + mouse.stall = 1; + mouse.ri = 0; + mouse.wi = 1; + mouse.m[0] = *m; + } + matchmouse(); + } + zunlock(); +} + +void +kputc(int c) +{ + zlock(); + kbd.r[kbd.wi++] = c; + if(kbd.wi == nelem(kbd.r)) + kbd.wi = 0; + if(kbd.ri == kbd.wi) + kbd.stall = 1; + matchkbd(); + zunlock(); +} + +static int alting; + +void +abortcompose(void) +{ + if(alting) + keystroke(Kalt); +} + +void +keystroke(int c) +{ + static Rune k[10]; + static int nk; + int i; + + if(c == Kalt){ + alting = !alting; + nk = 0; + return; + } + if(c == Kcmd+'r') { + if(forcedpi) + forcedpi = 0; + else if(displaydpi >= 200) + forcedpi = 100; + else + forcedpi = 225; + resizeimg(); + return; + } + if(!alting){ + kputc(c); + return; + } + if(nk >= nelem(k)) // should not happen + nk = 0; + k[nk++] = c; + c = _latin1(k, nk); + if(c > 0){ + alting = 0; + kputc(c); + nk = 0; + return; + } + if(c == -1){ + alting = 0; + for(i=0; i&2 'OS X 10.12 and older are not supported' exit 1 fi - WSYSTYPE=osx-cocoa + WSYSTYPE=mac elif [ -d "$X11" ]; then WSYSTYPE=x11 else @@ -51,9 +51,9 @@ if [ $WSYSTYPE = x11 ]; then echo 'HFILES=$HFILES $XHFILES' XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'` echo 'WSYSOFILES=$WSYSOFILES '$XO -elif [ $WSYSTYPE = osx-cocoa ]; then +elif [ $WSYSTYPE = mac ]; then echo 'OBJCFLAGS=$OBJCFLAGS -fobjc-arc' - echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen.o cocoa-srv.o' + echo 'WSYSOFILES=$WSYSOFILES mac-draw.o mac-screen.o mac-srv.o' echo 'MACARGV=macargv.o' elif [ $WSYSTYPE = nowsys ]; then echo 'WSYSOFILES=nowsys.o' blob - fdf7acecdae95f76540889079a1eb442e91e5066 (mode 644) blob + /dev/null --- src/cmd/devdraw/osx-draw.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "u.h" -#include "libc.h" -#include "draw.h" -#include "memdraw.h" - -Memimage* -allocmemimage(Rectangle r, u32int chan) -{ - return _allocmemimage(r, chan); -} - -void -freememimage(Memimage *i) -{ - _freememimage(i); -} - -void -memfillcolor(Memimage *i, u32int val) -{ - _memfillcolor(i, val); -} - - -int -cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) -{ - return _cloadmemimage(i, r, data, ndata); -} - -void -memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op) -{ - Memdrawparam *par; - - par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op); - if(par == nil) - return; - _memimagedraw(par); -} - -u32int -pixelbits(Memimage *m, Point p) -{ - return _pixelbits(m, p); -} - -int -loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) -{ - return _loadmemimage(i, r, data, ndata); -} - -int -unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) -{ - return _unloadmemimage(i, r, data, ndata); -} blob - 52328ace09dbe74b8a6d9c0a188e5eee9aeafc4a (mode 644) blob + /dev/null --- src/cmd/devdraw/osx-keycodes.h +++ /dev/null @@ -1,189 +0,0 @@ -/* These are the Macintosh key scancode constants -- from Inside Macintosh */ -#define QZ_ESCAPE 0x35 -#define QZ_F1 0x7A -#define QZ_F2 0x78 -#define QZ_F3 0x63 -#define QZ_F4 0x76 -#define QZ_F5 0x60 -#define QZ_F6 0x61 -#define QZ_F7 0x62 -#define QZ_F8 0x64 -#define QZ_F9 0x65 -#define QZ_F10 0x6D -#define QZ_F11 0x67 -#define QZ_F12 0x6F -#define QZ_PRINT 0x69 -#define QZ_SCROLLOCK 0x6B -#define QZ_PAUSE 0x71 -#define QZ_POWER 0x7F -#define QZ_BACKQUOTE 0x32 -#define QZ_1 0x12 -#define QZ_2 0x13 -#define QZ_3 0x14 -#define QZ_4 0x15 -#define QZ_5 0x17 -#define QZ_6 0x16 -#define QZ_7 0x1A -#define QZ_8 0x1C -#define QZ_9 0x19 -#define QZ_0 0x1D -#define QZ_MINUS 0x1B -#define QZ_EQUALS 0x18 -#define QZ_BACKSPACE 0x33 -#define QZ_INSERT 0x72 -#define QZ_HOME 0x73 -#define QZ_PAGEUP 0x74 -#define QZ_NUMLOCK 0x47 -#define QZ_KP_EQUALS 0x51 -#define QZ_KP_DIVIDE 0x4B -#define QZ_KP_MULTIPLY 0x43 -#define QZ_TAB 0x30 -#define QZ_q 0x0C -#define QZ_w 0x0D -#define QZ_e 0x0E -#define QZ_r 0x0F -#define QZ_t 0x11 -#define QZ_y 0x10 -#define QZ_u 0x20 -#define QZ_i 0x22 -#define QZ_o 0x1F -#define QZ_p 0x23 -#define QZ_LEFTBRACKET 0x21 -#define QZ_RIGHTBRACKET 0x1E -#define QZ_BACKSLASH 0x2A -#define QZ_DELETE 0x75 -#define QZ_END 0x77 -#define QZ_PAGEDOWN 0x79 -#define QZ_KP7 0x59 -#define QZ_KP8 0x5B -#define QZ_KP9 0x5C -#define QZ_KP_MINUS 0x4E -#define QZ_CAPSLOCK 0x39 -#define QZ_a 0x00 -#define QZ_s 0x01 -#define QZ_d 0x02 -#define QZ_f 0x03 -#define QZ_g 0x05 -#define QZ_h 0x04 -#define QZ_j 0x26 -#define QZ_k 0x28 -#define QZ_l 0x25 -#define QZ_SEMICOLON 0x29 -#define QZ_QUOTE 0x27 -#define QZ_RETURN 0x24 -#define QZ_KP4 0x56 -#define QZ_KP5 0x57 -#define QZ_KP6 0x58 -#define QZ_KP_PLUS 0x45 -#define QZ_LSHIFT 0x38 -#define QZ_z 0x06 -#define QZ_x 0x07 -#define QZ_c 0x08 -#define QZ_v 0x09 -#define QZ_b 0x0B -#define QZ_n 0x2D -#define QZ_m 0x2E -#define QZ_COMMA 0x2B -#define QZ_PERIOD 0x2F -#define QZ_SLASH 0x2C -/* These are the same as the left versions - use left by default */ -#if 0 -#define QZ_RSHIFT 0x38 -#endif -#define QZ_UP 0x7E -#define QZ_KP1 0x53 -#define QZ_KP2 0x54 -#define QZ_KP3 0x55 -#define QZ_KP_ENTER 0x4C -#define QZ_LCTRL 0x3B -#define QZ_LALT 0x3A -#define QZ_LMETA 0x37 -#define QZ_SPACE 0x31 -/* These are the same as the left versions - use left by default */ -#if 0 -#define QZ_RMETA 0x37 -#define QZ_RALT 0x3A -#define QZ_RCTRL 0x3B -#endif -#define QZ_LEFT 0x7B -#define QZ_DOWN 0x7D -#define QZ_RIGHT 0x7C -#define QZ_KP0 0x52 -#define QZ_KP_PERIOD 0x41 - -/* Wierd, these keys are on my iBook under MacOS X */ -#define QZ_IBOOK_ENTER 0x34 -#define QZ_IBOOK_LEFT 0x3B -#define QZ_IBOOK_RIGHT 0x3C -#define QZ_IBOOK_DOWN 0x3D -#define QZ_IBOOK_UP 0x3E -#define KEY_ENTER 13 -#define KEY_TAB 9 - -#define KEY_BASE 0x100 - -/* Function keys */ -#define KEY_F (KEY_BASE+64) - -/* Control keys */ -#define KEY_CTRL (KEY_BASE) -#define KEY_BACKSPACE (KEY_CTRL+0) -#define KEY_DELETE (KEY_CTRL+1) -#define KEY_INSERT (KEY_CTRL+2) -#define KEY_HOME (KEY_CTRL+3) -#define KEY_END (KEY_CTRL+4) -#define KEY_PAGE_UP (KEY_CTRL+5) -#define KEY_PAGE_DOWN (KEY_CTRL+6) -#define KEY_ESC (KEY_CTRL+7) - -/* Control keys short name */ -#define KEY_BS KEY_BACKSPACE -#define KEY_DEL KEY_DELETE -#define KEY_INS KEY_INSERT -#define KEY_PGUP KEY_PAGE_UP -#define KEY_PGDOWN KEY_PAGE_DOWN -#define KEY_PGDWN KEY_PAGE_DOWN - -/* Cursor movement */ -#define KEY_CRSR (KEY_BASE+16) -#define KEY_RIGHT (KEY_CRSR+0) -#define KEY_LEFT (KEY_CRSR+1) -#define KEY_DOWN (KEY_CRSR+2) -#define KEY_UP (KEY_CRSR+3) - -/* Multimedia keyboard/remote keys */ -#define KEY_MM_BASE (0x100+384) -#define KEY_POWER (KEY_MM_BASE+0) -#define KEY_MENU (KEY_MM_BASE+1) -#define KEY_PLAY (KEY_MM_BASE+2) -#define KEY_PAUSE (KEY_MM_BASE+3) -#define KEY_PLAYPAUSE (KEY_MM_BASE+4) -#define KEY_STOP (KEY_MM_BASE+5) -#define KEY_FORWARD (KEY_MM_BASE+6) -#define KEY_REWIND (KEY_MM_BASE+7) -#define KEY_NEXT (KEY_MM_BASE+8) -#define KEY_PREV (KEY_MM_BASE+9) -#define KEY_VOLUME_UP (KEY_MM_BASE+10) -#define KEY_VOLUME_DOWN (KEY_MM_BASE+11) -#define KEY_MUTE (KEY_MM_BASE+12) - -/* Keypad keys */ -#define KEY_KEYPAD (KEY_BASE+32) -#define KEY_KP0 (KEY_KEYPAD+0) -#define KEY_KP1 (KEY_KEYPAD+1) -#define KEY_KP2 (KEY_KEYPAD+2) -#define KEY_KP3 (KEY_KEYPAD+3) -#define KEY_KP4 (KEY_KEYPAD+4) -#define KEY_KP5 (KEY_KEYPAD+5) -#define KEY_KP6 (KEY_KEYPAD+6) -#define KEY_KP7 (KEY_KEYPAD+7) -#define KEY_KP8 (KEY_KEYPAD+8) -#define KEY_KP9 (KEY_KEYPAD+9) -#define KEY_KPDEC (KEY_KEYPAD+10) -#define KEY_KPINS (KEY_KEYPAD+11) -#define KEY_KPDEL (KEY_KEYPAD+12) -#define KEY_KPENTER (KEY_KEYPAD+13) - -/* Special keys */ -#define KEY_INTERN (0x1000) -#define KEY_CLOSE_WIN (KEY_INTERN+0) blob - /dev/null blob + b4dadd90a0a2fc4954636a0fbdef626817593792 (mode 644) --- /dev/null +++ src/cmd/fontsrv/mac.c @@ -0,0 +1,362 @@ +#include + +#define Point OSXPoint +#define Rect OSXRect +#define Cursor OSXCursor +#include +#undef Rect +#undef Point +#undef Cursor +#undef offsetof +#undef nil + +#include +#include +#include +#include "a.h" + +extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t); + +// In these fonts, it's too hard to distinguish U+2018 and U+2019, +// so don't map the ASCII quotes there. +// See https://github.com/9fans/plan9port/issues/86 +static char *skipquotemap[] = { + "Courier", + "Osaka", +}; + +int +mapUnicode(char *name, int i) +{ + int j; + + if(0xd800 <= i && i < 0xe000) // surrogate pairs, will crash OS X libraries! + return 0xfffd; + for(j=0; jname); + desc = CTFontDescriptorCreateWithNameAndSize(s, size); + CFRelease(s); + if(desc == nil) + return; + font = CTFontCreateWithFontDescriptor(desc, 0, nil); + CFRelease(desc); + if(font == nil) + return; + + color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); + ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone); + CGColorSpaceRelease(color); + CGContextSetTextPosition(ctxt, 0, 0); + + for(i=0; i r.origin.x) + bbox.origin.x = r.origin.x; + if(bbox.origin.y > r.origin.y) + bbox.origin.y = r.origin.y; + if(bbox.size.width < r.size.width) + bbox.size.width = r.size.width; + if(bbox.size.height < r.size.height) + bbox.size.height = r.size.height; + } + + bbox.size.width -= bbox.origin.x; + bbox.size.height -= bbox.origin.y; + + *height = bbox.size.height + 0.999999; + *ascent = *height - (-bbox.origin.y + 0.999999); + + CGContextRelease(ctxt); + CFRelease(font); +} + +void +load(XFont *f) +{ + int i; + + if(f->loaded) + return; + f->loaded = 1; + + // compute height and ascent for each size on demand + f->loadheight = fontheight; + + // enable all Unicode ranges + for(i=0; irange); i++) { + f->range[i] = 1; + f->nrange++; + } +} + +Memsubfont* +mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias) +{ + CFStringRef s; + CGColorSpaceRef color; + CGContextRef ctxt; + CTFontRef font; + CTFontDescriptorRef desc; + CGRect bbox; + Memimage *m, *mc, *m1; + int x, y, y0; + int i, height, ascent; + Fontchar *fc, *fc0; + Memsubfont *sf; + CGFloat whitef[] = { 1.0, 1.0 }; + CGColorRef white; + + s = c2mac(name); + desc = CTFontDescriptorCreateWithNameAndSize(s, size); + CFRelease(s); + if(desc == nil) + return nil; + font = CTFontCreateWithFontDescriptor(desc, 0, nil); + CFRelease(desc); + if(font == nil) + return nil; + + + bbox = CTFontGetBoundingBox(font); + x = (int)(bbox.size.width*2 + 0.99999999); + + fontheight(f, size, &height, &ascent); + y = height; + y0 = height - ascent; + + m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8); + if(m == nil) + return nil; + mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8); + if(mc == nil){ + freememimage(m); + return nil; + } + memfillcolor(m, DBlack); + memfillcolor(mc, DBlack); + fc = malloc((hi+2 - lo) * sizeof fc[0]); + sf = malloc(sizeof *sf); + if(fc == nil || sf == nil) { + freememimage(m); + freememimage(mc); + free(fc); + free(sf); + return nil; + } + fc0 = fc; + + color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); + ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8, + mc->width*sizeof(u32int), color, kCGImageAlphaNone); + white = CGColorCreate(color, whitef); + CGColorSpaceRelease(color); + if(ctxt == nil) { + freememimage(m); + freememimage(mc); + free(fc); + free(sf); + return nil; + } + + CGContextSetAllowsAntialiasing(ctxt, antialias); + CGContextSetTextPosition(ctxt, 0, 0); // XXX +#if OSX_VERSION >= 101400 + CGContextSetAllowsFontSmoothing(ctxt, false); +#endif + + x = 0; + for(i=lo; i<=hi; i++, fc++) { + char buf[20]; + CFStringRef str; + CFDictionaryRef attrs; + CFAttributedStringRef attrString; + CTLineRef line; + CGRect r; + CGPoint p1; + CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; + CFTypeRef values[] = { font, white }; + + sprint(buf, "%C", (Rune)mapUnicode(name, i)); + str = c2mac(buf); + + // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2 + attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, + (const void**)&values, sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs); + CFRelease(str); + CFRelease(attrs); + + line = CTLineCreateWithAttributedString(attrString); + CGContextSetTextPosition(ctxt, 0, y0); + r = CTLineGetImageBounds(line, ctxt); + memfillcolor(mc, DBlack); + CTLineDraw(line, ctxt); + CFRelease(line); + + fc->x = x; + fc->top = 0; + fc->bottom = Dy(m->r); + +// fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y); + p1 = CGContextGetTextPosition(ctxt); + if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) { + fc->width = 0; + fc->left = 0; + if(i == 0) { + drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0); + x += fc->width; + } + continue; + } + + memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S); + fc->width = p1.x; + fc->left = 0; + x += p1.x; + } + fc->x = x; + + // round up to 32-bit boundary + // so that in-memory data is same + // layout as in-file data. + if(x == 0) + x = 1; + if(y == 0) + y = 1; + if(antialias) + x += -x & 3; + else + x += -x & 31; + m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); + memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); + freememimage(m); + freememimage(mc); + + sf->name = nil; + sf->n = hi+1 - lo; + sf->height = Dy(m1->r); + sf->ascent = Dy(m1->r) - y0; + sf->info = fc0; + sf->bits = m1; + + return sf; +} blob - 8ad99dbc0f05f2e485a97678548e8d3a05128bd5 blob + 218b7c73a6abdd3bb0032e6f59ace486676cc595 --- src/cmd/fontsrv/mkfile +++ src/cmd/fontsrv/mkfile @@ -14,8 +14,6 @@ OFILES=\ <$PLAN9/src/mkone -osx-cocoa.$O: osx.c - showpjw: showpjw.c 9c showpjw.c 9l -o showpjw showpjw.o blob - 0e36b6e851fcde5a8f60b5f422f6d354a3e5e35a (mode 644) blob + /dev/null --- src/cmd/fontsrv/osx-cocoa.c +++ /dev/null @@ -1 +0,0 @@ -#include "osx.c" blob - b4dadd90a0a2fc4954636a0fbdef626817593792 (mode 644) blob + /dev/null --- src/cmd/fontsrv/osx.c +++ /dev/null @@ -1,362 +0,0 @@ -#include - -#define Point OSXPoint -#define Rect OSXRect -#define Cursor OSXCursor -#include -#undef Rect -#undef Point -#undef Cursor -#undef offsetof -#undef nil - -#include -#include -#include -#include "a.h" - -extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t); - -// In these fonts, it's too hard to distinguish U+2018 and U+2019, -// so don't map the ASCII quotes there. -// See https://github.com/9fans/plan9port/issues/86 -static char *skipquotemap[] = { - "Courier", - "Osaka", -}; - -int -mapUnicode(char *name, int i) -{ - int j; - - if(0xd800 <= i && i < 0xe000) // surrogate pairs, will crash OS X libraries! - return 0xfffd; - for(j=0; jname); - desc = CTFontDescriptorCreateWithNameAndSize(s, size); - CFRelease(s); - if(desc == nil) - return; - font = CTFontCreateWithFontDescriptor(desc, 0, nil); - CFRelease(desc); - if(font == nil) - return; - - color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); - ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone); - CGColorSpaceRelease(color); - CGContextSetTextPosition(ctxt, 0, 0); - - for(i=0; i r.origin.x) - bbox.origin.x = r.origin.x; - if(bbox.origin.y > r.origin.y) - bbox.origin.y = r.origin.y; - if(bbox.size.width < r.size.width) - bbox.size.width = r.size.width; - if(bbox.size.height < r.size.height) - bbox.size.height = r.size.height; - } - - bbox.size.width -= bbox.origin.x; - bbox.size.height -= bbox.origin.y; - - *height = bbox.size.height + 0.999999; - *ascent = *height - (-bbox.origin.y + 0.999999); - - CGContextRelease(ctxt); - CFRelease(font); -} - -void -load(XFont *f) -{ - int i; - - if(f->loaded) - return; - f->loaded = 1; - - // compute height and ascent for each size on demand - f->loadheight = fontheight; - - // enable all Unicode ranges - for(i=0; irange); i++) { - f->range[i] = 1; - f->nrange++; - } -} - -Memsubfont* -mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias) -{ - CFStringRef s; - CGColorSpaceRef color; - CGContextRef ctxt; - CTFontRef font; - CTFontDescriptorRef desc; - CGRect bbox; - Memimage *m, *mc, *m1; - int x, y, y0; - int i, height, ascent; - Fontchar *fc, *fc0; - Memsubfont *sf; - CGFloat whitef[] = { 1.0, 1.0 }; - CGColorRef white; - - s = c2mac(name); - desc = CTFontDescriptorCreateWithNameAndSize(s, size); - CFRelease(s); - if(desc == nil) - return nil; - font = CTFontCreateWithFontDescriptor(desc, 0, nil); - CFRelease(desc); - if(font == nil) - return nil; - - - bbox = CTFontGetBoundingBox(font); - x = (int)(bbox.size.width*2 + 0.99999999); - - fontheight(f, size, &height, &ascent); - y = height; - y0 = height - ascent; - - m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8); - if(m == nil) - return nil; - mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8); - if(mc == nil){ - freememimage(m); - return nil; - } - memfillcolor(m, DBlack); - memfillcolor(mc, DBlack); - fc = malloc((hi+2 - lo) * sizeof fc[0]); - sf = malloc(sizeof *sf); - if(fc == nil || sf == nil) { - freememimage(m); - freememimage(mc); - free(fc); - free(sf); - return nil; - } - fc0 = fc; - - color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); - ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8, - mc->width*sizeof(u32int), color, kCGImageAlphaNone); - white = CGColorCreate(color, whitef); - CGColorSpaceRelease(color); - if(ctxt == nil) { - freememimage(m); - freememimage(mc); - free(fc); - free(sf); - return nil; - } - - CGContextSetAllowsAntialiasing(ctxt, antialias); - CGContextSetTextPosition(ctxt, 0, 0); // XXX -#if OSX_VERSION >= 101400 - CGContextSetAllowsFontSmoothing(ctxt, false); -#endif - - x = 0; - for(i=lo; i<=hi; i++, fc++) { - char buf[20]; - CFStringRef str; - CFDictionaryRef attrs; - CFAttributedStringRef attrString; - CTLineRef line; - CGRect r; - CGPoint p1; - CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; - CFTypeRef values[] = { font, white }; - - sprint(buf, "%C", (Rune)mapUnicode(name, i)); - str = c2mac(buf); - - // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2 - attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, - (const void**)&values, sizeof(keys) / sizeof(keys[0]), - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs); - CFRelease(str); - CFRelease(attrs); - - line = CTLineCreateWithAttributedString(attrString); - CGContextSetTextPosition(ctxt, 0, y0); - r = CTLineGetImageBounds(line, ctxt); - memfillcolor(mc, DBlack); - CTLineDraw(line, ctxt); - CFRelease(line); - - fc->x = x; - fc->top = 0; - fc->bottom = Dy(m->r); - -// fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y); - p1 = CGContextGetTextPosition(ctxt); - if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) { - fc->width = 0; - fc->left = 0; - if(i == 0) { - drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0); - x += fc->width; - } - continue; - } - - memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S); - fc->width = p1.x; - fc->left = 0; - x += p1.x; - } - fc->x = x; - - // round up to 32-bit boundary - // so that in-memory data is same - // layout as in-file data. - if(x == 0) - x = 1; - if(y == 0) - y = 1; - if(antialias) - x += -x & 3; - else - x += -x & 31; - m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); - memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); - freememimage(m); - freememimage(mc); - - sf->name = nil; - sf->n = hi+1 - lo; - sf->height = Dy(m1->r); - sf->ascent = Dy(m1->r) - y0; - sf->info = fc0; - sf->bits = m1; - - return sf; -} blob - 66fa9015507eb3edbe00ff8d922a4805a5fd10e3 blob + 66192085e7fef5b3a901b663f898001656a64dda --- src/cmd/snarfer/mkfile +++ src/cmd/snarfer/mkfile @@ -9,4 +9,4 @@ HFILES= <$PLAN9/src/mkone x11-snarfer.$O: snarfer.c -osx-snarfer.$O: snarfer.c +mac-snarfer.$O: snarfer.c blob - /dev/null blob + 613a8cc519b4b465a863c277c0337e3c1f37132f (mode 644) --- /dev/null +++ src/cmd/snarfer/mac-snarfer.c @@ -0,0 +1 @@ +#include "snarfer.c" blob - 613a8cc519b4b465a863c277c0337e3c1f37132f (mode 644) blob + /dev/null --- src/cmd/snarfer/osx-cocoa-snarfer.c +++ /dev/null @@ -1 +0,0 @@ -#include "snarfer.c" blob - 613a8cc519b4b465a863c277c0337e3c1f37132f (mode 644) blob + /dev/null --- src/cmd/snarfer/osx-snarfer.c +++ /dev/null @@ -1 +0,0 @@ -#include "snarfer.c"