1 #define Cursor OSXCursor
5 #import <Cocoa/Cocoa.h>
6 #import <Metal/Metal.h>
7 #import <QuartzCore/CAMetalLayer.h>
15 #include "cocoa-thread.h"
20 #include "cocoa-screen.h"
21 #include "osx-keycodes.h"
24 #include "glendapng.h"
28 AUTOFRAMEWORK(QuartzCore)
30 #define LOG if(0)NSLog
32 static void setprocname(const char*);
33 static uint keycvt(uint);
34 static uint msec(void);
35 static Memimage* initimg(void);
40 fprint(2, "usage: devdraw (don't run directly)\n");
41 threadexitsall("usage");
44 @interface AppDelegate : NSObject<NSApplicationDelegate,NSWindowDelegate>
45 + (void)callservep9p:(id)arg;
46 + (void)makewin:(NSValue *)v;
47 + (void)callkicklabel:(NSString *)v;
48 + (void)callsetNeedsDisplayInRect:(NSValue *)v;
49 + (void)callsetcursor:(NSValue *)v;
51 @interface DevDrawView : NSView<NSTextInputClient>
53 - (void)getmouse:(NSEvent *)e;
54 - (void)sendmouse:(NSUInteger)b;
55 - (void)resetLastInputRect;
56 - (void)enlargeLastInputRect:(NSRect)r;
58 @interface DrawLayer : CAMetalLayer
61 static AppDelegate *myApp = NULL;
62 static DevDrawView *myContent = NULL;
63 static NSWindow *win = NULL;
64 static NSCursor *currentCursor = NULL;
66 static DrawLayer *layer;
67 static id<MTLDevice> device;
68 static id<MTLCommandQueue> commandQueue;
69 static id<MTLTexture> texture;
71 static Memimage *img = NULL;
76 threadmain(int argc, char **argv)
79 * Move the protocol off stdin/stdout so that
80 * any inadvertent prints don't screw things up.
86 open("/dev/null", OREAD);
87 open("/dev/null", OWRITE);
90 case 'D': /* for good ps -a listings */
92 case 'f': /* fall through for backward compatibility */
103 [NSApplication sharedApplication];
104 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
105 myApp = [AppDelegate new];
106 [NSApp setDelegate:myApp];
111 @implementation AppDelegate
113 + (void)callservep9p:(id)arg
116 [NSApp terminate:self];
119 + (void)makewin:(NSValue *)v
127 const NSWindowStyleMask Winstyle = NSWindowStyleMaskTitled
128 | NSWindowStyleMaskClosable
129 | NSWindowStyleMaskMiniaturizable
130 | NSWindowStyleMaskResizable;
132 sr = [[NSScreen mainScreen] frame];
133 r = [[NSScreen mainScreen] visibleFrame];
135 s = [v pointerValue];
136 LOG(@"makewin(%s)", s);
138 if(parsewinsize(s, &wr, &set) < 0)
141 wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3);
145 r.origin.x = wr.min.x;
146 r.origin.y = sr.size.height-wr.max.y; /* winsize is top-left-based */
147 r.size.width = fmin(Dx(wr), r.size.width);
148 r.size.height = fmin(Dy(wr), r.size.height);
149 r = [NSWindow contentRectForFrameRect:r styleMask:Winstyle];
151 win = [[NSWindow alloc]
152 initWithContentRect:r
154 backing:NSBackingStoreBuffered defer:NO];
155 [win setTitle:@"devdraw"];
159 [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
160 [win setContentMinSize:NSMakeSize(64,64)];
162 [win setRestorable:NO];
163 [win setAcceptsMouseMovedEvents:YES];
164 [win setDelegate:myApp];
166 myContent = [DevDrawView new];
167 [win setContentView:myContent];
168 [myContent setWantsLayer:YES];
169 [myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
172 allDevices = MTLCopyAllDevices();
173 for(id mtlDevice in allDevices) {
174 if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) {
180 device = MTLCreateSystemDefaultDevice();
182 commandQueue = [device newCommandQueue];
184 layer = (DrawLayer *)[myContent layer];
185 layer.device = device;
186 layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
187 layer.framebufferOnly = YES;
190 // We use a default transparent layer on top of the CAMetalLayer.
191 // This seems to make fullscreen applications behave.
193 CALayer *stub = [CALayer layer];
194 stub.frame = CGRectMake(0, 0, 1, 1);
195 [stub setNeedsDisplay];
196 [layer addSublayer:stub];
199 [NSEvent setMouseCoalescingEnabled:NO];
204 + (void)callkicklabel:(NSString *)s
206 LOG(@"callkicklabel(%@)", s);
208 [[NSApp dockTile] setBadgeLabel:s];
212 + (void)callsetNeedsDisplayInRect:(NSValue *)v
215 dispatch_time_t time;
218 LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height);
219 r = [win convertRectFromBacking:r];
220 LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height);
221 [layer setNeedsDisplayInRect:r];
223 time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC);
224 dispatch_after(time, dispatch_get_main_queue(), ^(void){
225 [layer setNeedsDisplayInRect:r];
228 [myContent enlargeLastInputRect:r];
231 typedef struct Cursors Cursors;
237 + (void)callsetcursor:(NSValue *)v
242 NSBitmapImageRep *r, *r2;
245 uchar *plane[5], *plane2[5];
248 cs = [v pointerValue];
256 r = [[NSBitmapImageRep alloc]
257 initWithBitmapDataPlanes:nil
264 colorSpaceName:NSDeviceWhiteColorSpace
267 [r getBitmapDataPlanes:plane];
268 for(b=0; b<nelem(c->set); b++){
269 plane[0][b] = ~c->set[b] & c->clr[b];
270 plane[1][b] = c->set[b] | c->clr[b];
273 r2 = [[NSBitmapImageRep alloc]
274 initWithBitmapDataPlanes:nil
281 colorSpaceName:NSDeviceWhiteColorSpace
284 [r2 getBitmapDataPlanes:plane2];
285 for(b=0; b<nelem(c2->set); b++){
286 plane2[0][b] = ~c2->set[b] & c2->clr[b];
287 plane2[1][b] = c2->set[b] | c2->clr[b];
290 // For checking out the cursor bitmap image
292 static BOOL saveimg = YES;
294 NSData *data = [r representationUsingType: NSBitmapImageFileTypeBMP properties: @{}];
295 [data writeToFile: @"/tmp/r.bmp" atomically: NO];
296 data = [r2 representationUsingType: NSBitmapImageFileTypeBMP properties: @{}];
297 [data writeToFile: @"/tmp/r2.bmp" atomically: NO];
302 i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)];
303 [i addRepresentation:r2];
304 [i addRepresentation:r];
306 p = NSMakePoint(-c->offset.x, -c->offset.y);
307 currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p];
309 [win invalidateCursorRectsForView:myContent];
312 - (void)applicationDidFinishLaunching:(id)arg
318 LOG(@"applicationDidFinishLaunching");
321 [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
322 [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"];
323 [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
325 [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""];
326 [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]];
327 [NSApp setMainMenu:m];
329 d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)];
330 i = [[NSImage alloc] initWithData:d];
331 [NSApp setApplicationIconImage:i];
332 [[NSApp dockTile] display];
335 detachNewThreadSelector:@selector(callservep9p:)
336 toTarget:[self class] withObject:nil];
339 - (NSApplicationPresentationOptions)window:(id)arg
340 willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions {
341 NSApplicationPresentationOptions o;
343 o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar);
344 o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
348 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
352 - (void)windowDidResize:(NSNotification *)notification
354 if(![myContent inLiveResize] && img) {
359 - (void)windowDidBecomeKey:(id)arg
361 [myContent sendmouse:0];
366 @implementation DevDrawView
368 NSMutableString *_tmpText;
369 NSRange _markedRange;
370 NSRange _selectedRange;
371 NSRect _lastInputRect; // The view is flipped, this is not.
373 NSUInteger _tapFingers;
381 [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMaskIndirect];
382 _tmpText = [[NSMutableString alloc] initWithCapacity:2];
383 _markedRange = NSMakeRange(NSNotFound, 0);
384 _selectedRange = NSMakeRange(0, 0);
388 - (CALayer *)makeBackingLayer
390 LOG(@"makeBackingLayer");
391 return [DrawLayer layer];
394 - (BOOL)wantsUpdateLayer
409 - (BOOL)acceptsFirstResponder
414 - (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];}
415 - (void)mouseDown:(NSEvent*)e{ [self getmouse:e];}
416 - (void)mouseDragged:(NSEvent*)e{ [self getmouse:e];}
417 - (void)mouseUp:(NSEvent*)e{ [self getmouse:e];}
418 - (void)otherMouseDown:(NSEvent*)e{ [self getmouse:e];}
419 - (void)otherMouseDragged:(NSEvent*)e{ [self getmouse:e];}
420 - (void)otherMouseUp:(NSEvent*)e{ [self getmouse:e];}
421 - (void)rightMouseDown:(NSEvent*)e{ [self getmouse:e];}
422 - (void)rightMouseDragged:(NSEvent*)e{ [self getmouse:e];}
423 - (void)rightMouseUp:(NSEvent*)e{ [self getmouse:e];}
425 - (void)scrollWheel:(NSEvent*)e
429 s = [e scrollingDeltaY];
436 - (void)keyDown:(NSEvent*)e
438 LOG(@"keyDown to interpret");
440 [self interpretKeyEvents:[NSArray arrayWithObject:e]];
442 [self resetLastInputRect];
445 - (void)flagsChanged:(NSEvent*)e
447 static NSEventModifierFlags omod;
448 NSEventModifierFlags m;
451 LOG(@"flagsChanged");
452 m = [e modifierFlags];
454 b = [NSEvent pressedMouseButtons];
455 b = (b&~6) | (b&4)>>1 | (b&2)<<1;
457 if(m & ~omod & NSEventModifierFlagControl)
459 if(m & ~omod & NSEventModifierFlagOption)
461 if(m & ~omod & NSEventModifierFlagCommand)
464 }else if(m & ~omod & NSEventModifierFlagOption)
470 - (void)magnifyWithEvent:(NSEvent*)e
472 if(fabs([e magnification]) > 0.02)
473 [[self window] toggleFullScreen:nil];
476 - (void)touchesBeganWithEvent:(NSEvent*)e
479 _tapFingers = [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count;
482 - (void)touchesMovedWithEvent:(NSEvent*)e
486 - (void)touchesEndedWithEvent:(NSEvent*)e
489 && [e touchesMatchingPhase:NSTouchPhaseTouching inView:nil].count == 0
490 && msec() - _tapTime < 250){
505 - (void)touchesCancelledWithEvent:(NSEvent*)e
510 - (void)getmouse:(NSEvent *)e
513 NSEventModifierFlags m;
515 b = [NSEvent pressedMouseButtons];
516 b = b&~6 | (b&4)>>1 | (b&2)<<1;
520 m = [e modifierFlags];
521 if(m & NSEventModifierFlagOption){
525 if(m & NSEventModifierFlagCommand)
531 - (void)sendmouse:(NSUInteger)b
535 p = [self.window convertPointToBacking:
536 [self.window mouseLocationOutsideOfEventStream]];
537 p.y = Dy(mouserect) - p.y;
538 // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b);
539 mousetrack(p.x, p.y, b, msec());
540 if(b && _lastInputRect.size.width && _lastInputRect.size.height)
541 [self resetLastInputRect];
544 - (void)resetCursorRects {
545 [super resetCursorRects];
546 [self addCursorRect:self.bounds cursor:currentCursor];
549 - (void)viewDidEndLiveResize
551 [super viewDidEndLiveResize];
556 - (void)viewDidChangeBackingProperties
558 [super viewDidChangeBackingProperties];
563 // conforms to protocol NSTextInputClient
564 - (BOOL)hasMarkedText
566 LOG(@"hasMarkedText");
567 return _markedRange.location != NSNotFound;
569 - (NSRange)markedRange
574 - (NSRange)selectedRange
576 LOG(@"selectedRange");
577 return _selectedRange;
579 - (void)setMarkedText:(id)string
580 selectedRange:(NSRange)sRange
581 replacementRange:(NSRange)rRange
585 LOG(@"setMarkedText: %@ (%ld, %ld) (%ld, %ld)", string,
586 sRange.location, sRange.length,
587 rRange.location, rRange.length);
591 if([string isKindOfClass:[NSAttributedString class]])
592 str = [string string];
596 if(rRange.location == NSNotFound){
597 if(_markedRange.location != NSNotFound){
598 rRange = _markedRange;
600 rRange = _selectedRange;
605 [_tmpText deleteCharactersInRange:rRange];
608 _markedRange = NSMakeRange(rRange.location, str.length);
609 [_tmpText replaceCharactersInRange:rRange withString:str];
611 _selectedRange.location = rRange.location + sRange.location;
612 _selectedRange.length = sRange.length;
616 LOG(@"text length %ld", _tmpText.length);
617 for(i = 0; i <= _tmpText.length; ++i){
618 if(i == _markedRange.location)
620 if(_selectedRange.length){
621 if(i == _selectedRange.location)
623 if(i == NSMaxRange(_selectedRange))
626 if(i == NSMaxRange(_markedRange))
628 if(i < _tmpText.length)
629 keystroke([_tmpText characterAtIndex:i]);
632 l = 1 + _tmpText.length - NSMaxRange(_selectedRange)
633 + (_selectedRange.length > 0);
634 LOG(@"move left %d", l);
635 for(i = 0; i < l; ++i)
639 LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText,
640 _markedRange.location, _markedRange.length,
641 _selectedRange.location, _selectedRange.length);
649 len = [_tmpText length];
650 //for(i = 0; i < len; ++i)
651 // keystroke([_tmpText characterAtIndex:i]);
652 [_tmpText deleteCharactersInRange:NSMakeRange(0, len)];
653 _markedRange = NSMakeRange(NSNotFound, 0);
654 _selectedRange = NSMakeRange(0, 0);
656 - (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText
658 LOG(@"validAttributesForMarkedText");
661 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r
662 actualRange:(NSRangePointer)actualRange
665 NSAttributedString *s;
667 LOG(@"attributedSubstringForProposedRange: (%ld, %ld) (%ld, %ld)",
668 r.location, r.length, actualRange->location, actualRange->length);
669 sr = NSMakeRange(0, [_tmpText length]);
670 sr = NSIntersectionRange(sr, r);
673 LOG(@"use range: %ld, %ld", sr.location, sr.length);
675 s = [[NSAttributedString alloc]
676 initWithString:[_tmpText substringWithRange:sr]];
677 LOG(@" return %@", s);
680 - (void)insertText:(id)s
681 replacementRange:(NSRange)r
686 LOG(@"insertText: %@ replacementRange: %ld, %ld", s, r.location, r.length);
691 for(i = 0; i < len; ++i)
692 keystroke([s characterAtIndex:i]);
693 [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)];
694 _markedRange = NSMakeRange(NSNotFound, 0);
695 _selectedRange = NSMakeRange(0, 0);
697 - (NSUInteger)characterIndexForPoint:(NSPoint)point
699 LOG(@"characterIndexForPoint: %g, %g", point.x, point.y);
702 - (NSRect)firstRectForCharacterRange:(NSRange)r
703 actualRange:(NSRangePointer)actualRange
705 LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)",
706 r.location, r.length, actualRange->location, actualRange->length);
709 return [[self window] convertRectToScreen:_lastInputRect];
711 - (void)doCommandBySelector:(SEL)s
714 NSEventModifierFlags m;
717 LOG(@"doCommandBySelector (%@)", NSStringFromSelector(s));
719 e = [NSApp currentEvent];
720 c = [[e characters] characterAtIndex:0];
722 LOG(@"keyDown: character0: 0x%x -> 0x%x", c, k);
723 m = [e modifierFlags];
725 if(m & NSEventModifierFlagCommand){
726 if((m & NSEventModifierFlagShift) && 'a' <= k && k <= 'z')
735 // Helper for managing input rect approximately
736 - (void)resetLastInputRect
738 LOG(@"resetLastInputRect");
739 _lastInputRect.origin.x = 0.0;
740 _lastInputRect.origin.y = 0.0;
741 _lastInputRect.size.width = 0.0;
742 _lastInputRect.size.height = 0.0;
745 - (void)enlargeLastInputRect:(NSRect)r
747 r.origin.y = [self bounds].size.height - r.origin.y - r.size.height;
748 _lastInputRect = NSUnionRect(_lastInputRect, r);
749 LOG(@"update last input rect (%g, %g, %g, %g)",
750 _lastInputRect.origin.x, _lastInputRect.origin.y,
751 _lastInputRect.size.width, _lastInputRect.size.height);
759 l = 1 + _tmpText.length - NSMaxRange(_selectedRange)
760 + (_selectedRange.length > 0);
761 LOG(@"move right %d", l);
762 for(i = 0; i < l; ++i)
764 l = _tmpText.length+2+2*(_selectedRange.length > 0);
765 LOG(@"backspace %d", l);
766 for(uint i = 0; i < l; ++i)
773 @implementation DrawLayer
777 id<MTLCommandBuffer> cbuf;
778 id<MTLBlitCommandEncoder> blit;
782 cbuf = [commandQueue commandBuffer];
784 LOG(@"display query drawable");
787 id<CAMetalDrawable> drawable;
789 drawable = [layer nextDrawable];
791 LOG(@"display couldn't get drawable");
792 [self setNeedsDisplay];
796 LOG(@"display got drawable");
798 blit = [cbuf blitCommandEncoder];
799 [blit copyFromTexture:texture
802 sourceOrigin:MTLOriginMake(0, 0, 0)
803 sourceSize:MTLSizeMake(texture.width, texture.height, texture.depth)
804 toTexture:drawable.texture
807 destinationOrigin:MTLOriginMake(0, 0, 0)];
810 [cbuf presentDrawable:drawable];
813 [cbuf addCompletedHandler:^(id<MTLCommandBuffer> cmdBuff){
815 NSLog(@"command buffer finished with error: %@",
816 cmdBuff.error.localizedDescription);
818 LOG(@"command buffer finishes present drawable");
822 LOG(@"display commit");
830 return nsec()/1000000;
837 case '\r': return '\n';
838 case 127: return '\b';
839 case NSUpArrowFunctionKey: return Kup;
840 case NSDownArrowFunctionKey: return Kdown;
841 case NSLeftArrowFunctionKey: return Kleft;
842 case NSRightArrowFunctionKey: return Kright;
843 case NSInsertFunctionKey: return Kins;
844 case NSDeleteFunctionKey: return Kdel;
845 case NSHomeFunctionKey: return Khome;
846 case NSEndFunctionKey: return Kend;
847 case NSPageUpFunctionKey: return Kpgup;
848 case NSPageDownFunctionKey: return Kpgdown;
849 case NSF1FunctionKey: return KF|1;
850 case NSF2FunctionKey: return KF|2;
851 case NSF3FunctionKey: return KF|3;
852 case NSF4FunctionKey: return KF|4;
853 case NSF5FunctionKey: return KF|5;
854 case NSF6FunctionKey: return KF|6;
855 case NSF7FunctionKey: return KF|7;
856 case NSF8FunctionKey: return KF|8;
857 case NSF9FunctionKey: return KF|9;
858 case NSF10FunctionKey: return KF|10;
859 case NSF11FunctionKey: return KF|11;
860 case NSF12FunctionKey: return KF|12;
861 case NSBeginFunctionKey:
862 case NSPrintScreenFunctionKey:
863 case NSScrollLockFunctionKey:
864 case NSF13FunctionKey:
865 case NSF14FunctionKey:
866 case NSF15FunctionKey:
867 case NSF16FunctionKey:
868 case NSF17FunctionKey:
869 case NSF18FunctionKey:
870 case NSF19FunctionKey:
871 case NSF20FunctionKey:
872 case NSF21FunctionKey:
873 case NSF22FunctionKey:
874 case NSF23FunctionKey:
875 case NSF24FunctionKey:
876 case NSF25FunctionKey:
877 case NSF26FunctionKey:
878 case NSF27FunctionKey:
879 case NSF28FunctionKey:
880 case NSF29FunctionKey:
881 case NSF30FunctionKey:
882 case NSF31FunctionKey:
883 case NSF32FunctionKey:
884 case NSF33FunctionKey:
885 case NSF34FunctionKey:
886 case NSF35FunctionKey:
887 case NSPauseFunctionKey:
888 case NSSysReqFunctionKey:
889 case NSBreakFunctionKey:
890 case NSResetFunctionKey:
891 case NSStopFunctionKey:
892 case NSMenuFunctionKey:
893 case NSUserFunctionKey:
894 case NSSystemFunctionKey:
895 case NSPrintFunctionKey:
896 case NSClearLineFunctionKey:
897 case NSClearDisplayFunctionKey:
898 case NSInsertLineFunctionKey:
899 case NSDeleteLineFunctionKey:
900 case NSInsertCharFunctionKey:
901 case NSDeleteCharFunctionKey:
902 case NSPrevFunctionKey:
903 case NSNextFunctionKey:
904 case NSSelectFunctionKey:
905 case NSExecuteFunctionKey:
906 case NSUndoFunctionKey:
907 case NSRedoFunctionKey:
908 case NSFindFunctionKey:
909 case NSHelpFunctionKey:
910 case NSModeSwitchFunctionKey: return 0;
911 default: return code;
916 attachscreen(char *label, char *winsize)
918 LOG(@"attachscreen(%s, %s)", label, winsize);
920 performSelectorOnMainThread:@selector(makewin:)
921 withObject:[NSValue valueWithPointer:winsize]
935 MTLTextureDescriptor *textureDesc;
937 size = [myContent convertSizeToBacking:[myContent bounds].size];
938 mouserect = Rect(0, 0, size.width, size.height);
940 LOG(@"initimg %.0f %.0f", size.width, size.height);
942 img = allocmemimage(mouserect, XRGB32);
944 panic("allocmemimage: %r");
946 panic("img->data == nil");
948 textureDesc = [MTLTextureDescriptor
949 texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
953 textureDesc.allowGPUOptimizedContents = YES;
954 textureDesc.usage = MTLTextureUsageShaderRead;
955 textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
956 texture = [device newTextureWithDescriptor:textureDesc];
958 scale = [win backingScaleFactor];
959 [layer setDrawableSize:size];
960 [layer setContentsScale:scale];
962 // NOTE: This is not really the display DPI.
963 // On retina, scale is 2; otherwise it is 1.
964 // This formula gives us 220 for retina, 110 otherwise.
965 // That's not quite right but it's close to correct.
966 // https://en.wikipedia.org/wiki/Retina_display#Models
967 displaydpi = scale * 110;
969 LOG(@"initimg return");
975 _flushmemscreen(Rectangle r)
977 LOG(@"_flushmemscreen(%d,%d,%d,%d)", r.min.x, r.min.y, Dx(r), Dy(r));
978 if(!rectinrect(r, Rect(0, 0, texture.width, texture.height))){
979 LOG(@"Rectangle is out of bounds, return.");
985 replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r))
987 withBytes:byteaddr(img, Pt(r.min.x, r.min.y))
988 bytesPerRow:img->width*sizeof(u32int)];
990 performSelectorOnMainThread:@selector(callsetNeedsDisplayInRect:)
991 withObject:[NSValue valueWithRect:NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r))]
1002 LOG(@"setmouse(%d,%d)", p.x, p.y);
1003 q = [win convertPointFromBacking:NSMakePoint(p.x, p.y)];
1004 LOG(@"(%g, %g) <- fromBacking", q.x, q.y);
1005 q = [myContent convertPoint:q toView:nil];
1006 LOG(@"(%g, %g) <- toWindow", q.x, q.y);
1007 q = [win convertPointToScreen:q];
1008 LOG(@"(%g, %g) <- toScreen", q.x, q.y);
1009 // Quartz has the origin of the "global display
1010 // coordinate space" at the top left of the primary
1011 // screen with y increasing downward, while Cocoa has
1012 // the origin at the bottom left of the primary screen
1013 // with y increasing upward. We flip the coordinate
1014 // with a negative sign and shift upward by the height
1015 // of the primary screen.
1016 q.y = NSScreen.screens[0].frame.size.height - q.y;
1017 LOG(@"(%g, %g) <- setmouse", q.x, q.y);
1018 CGWarpMouseCursorPosition(NSPointToCGPoint(q));
1019 CGAssociateMouseAndMouseCursorPosition(true);
1030 pb = [NSPasteboard generalPasteboard];
1033 s = [pb stringForType:NSPasteboardTypeString];
1037 return strdup((char *)[s UTF8String]);
1050 if(strlen(s) >= SnarfSize)
1054 t = [NSArray arrayWithObject:NSPasteboardTypeString];
1055 pb = [NSPasteboard generalPasteboard];
1056 str = [[NSString alloc] initWithUTF8String:s];
1059 [pb declareTypes:t owner:nil];
1060 [pb setString:str forType:NSPasteboardTypeString];
1066 kicklabel(char *label)
1070 LOG(@"kicklabel(%s)", label);
1075 s = [[NSString alloc] initWithUTF8String:label];
1077 performSelectorOnMainThread:@selector(callkicklabel:)
1084 setcursor(Cursor *c, Cursor2 *c2)
1092 performSelectorOnMainThread:@selector(callsetcursor:)
1093 withObject:[NSValue valueWithPointer:&cs]
1101 performSelectorOnMainThread:
1102 @selector(makeKeyAndOrderFront:)
1106 [NSApp activateIgnoringOtherApps:YES];
1113 _drawreplacescreenimage(initimg());
1117 [myContent sendmouse:0];
1121 resizewindow(Rectangle r)
1123 LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r));
1124 dispatch_async(dispatch_get_main_queue(), ^(void){
1127 s = [myContent convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))];
1128 [win setContentSize:s];
1133 setprocname(const char *s)
1135 CFStringRef process_name;
1137 process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFStringEncodingUTF8, false);
1139 // Adapted from Chrome's mac_util.mm.
1140 // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm
1142 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
1144 // Redistribution and use in source and binary forms, with or without
1145 // modification, are permitted provided that the following conditions are
1148 // * Redistributions of source code must retain the above copyright
1149 // notice, this list of conditions and the following disclaimer.
1150 // * Redistributions in binary form must reproduce the above
1151 // copyright notice, this list of conditions and the following disclaimer
1152 // in the documentation and/or other materials provided with the
1154 // * Neither the name of Google Inc. nor the names of its
1155 // contributors may be used to endorse or promote products derived from
1156 // this software without specific prior written permission.
1158 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1159 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1160 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1161 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1162 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1163 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1164 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1165 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1166 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1167 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1168 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1169 // Warning: here be dragons! This is SPI reverse-engineered from WebKit's
1170 // plugin host, and could break at any time (although realistically it's only
1171 // likely to break in a new major release).
1172 // When 10.7 is available, check that this still works, and update this
1173 // comment for 10.8.
1175 // Private CFType used in these LaunchServices calls.
1176 typedef CFTypeRef PrivateLSASN;
1177 typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
1178 typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
1183 static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
1185 static LSSetApplicationInformationItemType
1186 ls_set_application_information_item_func = NULL;
1187 static CFStringRef ls_display_name_key = NULL;
1189 static bool did_symbol_lookup = false;
1190 if (!did_symbol_lookup) {
1191 did_symbol_lookup = true;
1192 CFBundleRef launch_services_bundle =
1193 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
1194 if (!launch_services_bundle) {
1195 fprint(2, "Failed to look up LaunchServices bundle\n");
1199 ls_get_current_application_asn_func =
1200 (LSGetCurrentApplicationASNType)(
1201 CFBundleGetFunctionPointerForName(
1202 launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
1203 if (!ls_get_current_application_asn_func)
1204 fprint(2, "Could not find _LSGetCurrentApplicationASN\n");
1206 ls_set_application_information_item_func =
1207 (LSSetApplicationInformationItemType)(
1208 CFBundleGetFunctionPointerForName(
1209 launch_services_bundle,
1210 CFSTR("_LSSetApplicationInformationItem")));
1211 if (!ls_set_application_information_item_func)
1212 fprint(2, "Could not find _LSSetApplicationInformationItem\n");
1214 CFStringRef* key_pointer = (CFStringRef*)(
1215 CFBundleGetDataPointerForName(launch_services_bundle,
1216 CFSTR("_kLSDisplayNameKey")));
1217 ls_display_name_key = key_pointer ? *key_pointer : NULL;
1218 if (!ls_display_name_key)
1219 fprint(2, "Could not find _kLSDisplayNameKey\n");
1221 // Internally, this call relies on the Mach ports that are started up by the
1222 // Carbon Process Manager. In debug builds this usually happens due to how
1223 // the logging layers are started up; but in release, it isn't started in as
1224 // much of a defined order. So if the symbols had to be loaded, go ahead
1225 // and force a call to make sure the manager has been initialized and hence
1226 // the ports are opened.
1227 ProcessSerialNumber psn;
1228 GetCurrentProcess(&psn);
1230 if (!ls_get_current_application_asn_func ||
1231 !ls_set_application_information_item_func ||
1232 !ls_display_name_key) {
1236 PrivateLSASN asn = ls_get_current_application_asn_func();
1237 // Constant used by WebKit; what exactly it means is unknown.
1238 const int magic_session_constant = -2;
1240 ls_set_application_information_item_func(magic_session_constant, asn,
1241 ls_display_name_key,
1243 NULL /* optional out param */);
1245 fprint(2, "Call to set process name failed\n");