2018-10-21 20:59:21 -05:00
|
|
|
#define Cursor OSXCursor
|
|
|
|
|
#define Point OSXPoint
|
|
|
|
|
#define Rect OSXRect
|
|
|
|
|
|
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
|
#import <Metal/Metal.h>
|
|
|
|
|
#import <QuartzCore/CAMetalLayer.h>
|
|
|
|
|
|
|
|
|
|
#undef Cursor
|
|
|
|
|
#undef Point
|
|
|
|
|
#undef Rect
|
|
|
|
|
|
|
|
|
|
#include <u.h>
|
|
|
|
|
#include <libc.h>
|
|
|
|
|
#include "cocoa-thread.h"
|
|
|
|
|
#include <draw.h>
|
|
|
|
|
#include <memdraw.h>
|
|
|
|
|
#include <keyboard.h>
|
|
|
|
|
#include <cursor.h>
|
|
|
|
|
#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<NSApplicationDelegate,NSWindowDelegate>
|
|
|
|
|
+ (void)callservep9p:(id)arg;
|
|
|
|
|
+ (void)makewin:(NSValue *)v;
|
|
|
|
|
+ (void)callkicklabel:(NSString *)v;
|
|
|
|
|
+ (void)callsetNeedsDisplayInRect:(NSValue *)v;
|
|
|
|
|
+ (void)callsetcursor:(NSValue *)v;
|
|
|
|
|
@end
|
|
|
|
|
@interface DevDrawView : NSView<NSTextInputClient>
|
|
|
|
|
- (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 MTLRenderPassDescriptor *renderPass;
|
|
|
|
|
static id<MTLDevice> device;
|
|
|
|
|
static id<MTLRenderPipelineState> pipelineState;
|
|
|
|
|
static id<MTLCommandQueue> commandQueue;
|
|
|
|
|
static id<MTLTexture> texture;
|
|
|
|
|
|
|
|
|
|
static Memimage *img = NULL;
|
|
|
|
|
|
|
|
|
|
static QLock snarfl;
|
|
|
|
|
|
|
|
|
|
static NSString *const metal =
|
|
|
|
|
@"#include<metal_stdlib>\n"
|
|
|
|
|
"using namespace metal;\n"
|
|
|
|
|
"typedef struct {\n"
|
|
|
|
|
" float4 rCoord [[position]];\n"
|
|
|
|
|
" float2 tCoord;\n"
|
|
|
|
|
"} VertexOut;\n"
|
|
|
|
|
"vertex VertexOut\n"
|
|
|
|
|
"renderVertex(unsigned int vid [[ vertex_id ]])\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" const VertexOut fixedV[] = {\n"
|
|
|
|
|
" {{ -1.0f, -1.0f, 0.0f, 1.0f }, { 0.0f, 1.0f }},\n"
|
|
|
|
|
" {{ 1.0f, -1.0f, 0.0f, 1.0f }, { 1.0f, 1.0f }},\n"
|
|
|
|
|
" {{ -1.0f, 1.0f, 0.0f, 1.0f }, { 0.0f, 0.0f }},\n"
|
|
|
|
|
" {{ 1.0f, 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f }},\n"
|
|
|
|
|
" };\n"
|
|
|
|
|
" return fixedV[vid];\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"fragment half4\n"
|
|
|
|
|
"renderFragment(VertexOut in [[ stage_in ]],\n"
|
|
|
|
|
" texture2d<half> texture [[ texture(0) ]]) {\n"
|
|
|
|
|
" constexpr sampler s;\n"
|
|
|
|
|
" return texture.sample(s, in.tCoord);\n"
|
|
|
|
|
"}";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
threadmain(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Move the protocol off stdin/stdout so that
|
|
|
|
|
* any inadvertent prints don't screw things up.
|
|
|
|
|
*/
|
|
|
|
|
dup(0,3);
|
|
|
|
|
dup(1,4);
|
|
|
|
|
close(0);
|
|
|
|
|
close(1);
|
|
|
|
|
open("/dev/null", OREAD);
|
|
|
|
|
open("/dev/null", OWRITE);
|
|
|
|
|
|
|
|
|
|
ARGBEGIN{
|
|
|
|
|
case 'D': /* for good ps -a listings */
|
|
|
|
|
break;
|
|
|
|
|
case 'f': /* fall through for backward compatibility */
|
|
|
|
|
case 'g':
|
|
|
|
|
case 'b':
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
usage();
|
|
|
|
|
}ARGEND
|
|
|
|
|
|
|
|
|
|
setprocname(argv0);
|
|
|
|
|
|
|
|
|
|
@autoreleasepool{
|
|
|
|
|
[NSApplication sharedApplication];
|
|
|
|
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
|
|
|
myApp = [AppDelegate new];
|
|
|
|
|
[NSApp setDelegate:myApp];
|
|
|
|
|
[NSApp run];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@implementation AppDelegate
|
|
|
|
|
|
|
|
|
|
+ (void)callservep9p:(id)arg
|
|
|
|
|
{
|
|
|
|
|
servep9p();
|
|
|
|
|
[NSApp terminate:self];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)makewin:(NSValue *)v
|
|
|
|
|
{
|
|
|
|
|
NSRect r, sr;
|
|
|
|
|
Rectangle wr;
|
|
|
|
|
int set;
|
|
|
|
|
char *s;
|
|
|
|
|
id<MTLLibrary> library;
|
|
|
|
|
MTLRenderPipelineDescriptor *pipelineDesc;
|
|
|
|
|
NSError *error;
|
|
|
|
|
|
|
|
|
|
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 setRestorable:NO];
|
|
|
|
|
[win setAcceptsMouseMovedEvents:YES];
|
|
|
|
|
[win setDelegate:myApp];
|
|
|
|
|
|
|
|
|
|
myContent = [DevDrawView new];
|
|
|
|
|
[win setContentView:myContent];
|
|
|
|
|
[myContent setWantsLayer:YES];
|
|
|
|
|
[myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
|
|
|
|
|
|
|
|
|
|
device = MTLCreateSystemDefaultDevice();
|
|
|
|
|
commandQueue = [device newCommandQueue];
|
|
|
|
|
|
|
|
|
|
layer = (DrawLayer *)[myContent layer];
|
|
|
|
|
layer.device = device;
|
|
|
|
|
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
|
|
|
layer.framebufferOnly = YES;
|
|
|
|
|
layer.opaque = YES;
|
|
|
|
|
|
|
|
|
|
renderPass = [MTLRenderPassDescriptor renderPassDescriptor];
|
|
|
|
|
renderPass.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
|
|
|
|
renderPass.colorAttachments[0].storeAction = MTLStoreActionDontCare;
|
|
|
|
|
|
|
|
|
|
library = [device newLibraryWithSource:metal options:nil error:&error];
|
|
|
|
|
if(!library)
|
|
|
|
|
sysfatal((char *)[[error localizedDescription] UTF8String]);
|
|
|
|
|
|
|
|
|
|
pipelineDesc = [MTLRenderPipelineDescriptor new];
|
|
|
|
|
pipelineDesc.alphaToOneEnabled = YES;
|
|
|
|
|
pipelineDesc.inputPrimitiveTopology = MTLPrimitiveTopologyClassTriangle;
|
|
|
|
|
pipelineDesc.rasterSampleCount = 1;
|
|
|
|
|
pipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
|
|
|
[pipelineDesc setVertexFunction: [library newFunctionWithName: @"renderVertex"]];
|
|
|
|
|
[pipelineDesc setFragmentFunction: [library newFunctionWithName: @"renderFragment"]];
|
|
|
|
|
|
|
|
|
|
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error];
|
|
|
|
|
if(!pipelineState)
|
|
|
|
|
sysfatal((char *)[[error localizedDescription] UTF8String]);
|
|
|
|
|
|
|
|
|
|
[NSEvent setMouseCoalescingEnabled:NO];
|
|
|
|
|
|
|
|
|
|
topwin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void)callkicklabel:(NSString *)s
|
|
|
|
|
{
|
|
|
|
|
LOG(@"callkicklabel(%@)", s);
|
|
|
|
|
[win setTitle:s];
|
|
|
|
|
[[NSApp dockTile] setBadgeLabel:s];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ (void)callsetNeedsDisplayInRect:(NSValue *)v
|
|
|
|
|
{
|
|
|
|
|
NSRect r;
|
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
[myContent enlargeLastInputRect:r];
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-15 20:28:56 -05:00
|
|
|
typedef struct Cursors Cursors;
|
|
|
|
|
struct Cursors {
|
|
|
|
|
Cursor *c;
|
|
|
|
|
Cursor2 *c2;
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-21 20:59:21 -05:00
|
|
|
+ (void)callsetcursor:(NSValue *)v
|
|
|
|
|
{
|
2018-11-15 20:28:56 -05:00
|
|
|
Cursors *cs;
|
2018-10-21 20:59:21 -05:00
|
|
|
Cursor *c;
|
2018-11-15 20:28:56 -05:00
|
|
|
Cursor2 *c2;
|
2018-10-21 20:59:21 -05:00
|
|
|
NSBitmapImageRep *r, *r2;
|
|
|
|
|
NSImage *i;
|
|
|
|
|
NSPoint p;
|
|
|
|
|
uchar *plane[5], *plane2[5];
|
2018-11-15 20:28:56 -05:00
|
|
|
int b;
|
2018-10-21 20:59:21 -05:00
|
|
|
|
2018-11-15 20:28:56 -05:00
|
|
|
cs = [v pointerValue];
|
|
|
|
|
c = cs->c;
|
2018-10-21 20:59:21 -05:00
|
|
|
if(!c)
|
|
|
|
|
c = &bigarrow;
|
2018-11-15 20:28:56 -05:00
|
|
|
c2 = cs->c2;
|
|
|
|
|
if(!c2)
|
|
|
|
|
c2 = &bigarrow2;
|
2018-10-21 20:59:21 -05:00
|
|
|
|
|
|
|
|
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];
|
2018-11-15 20:28:56 -05:00
|
|
|
for(b=0; b<nelem(c->set); b++){
|
2018-10-21 20:59:21 -05:00
|
|
|
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];
|
2018-11-15 20:28:56 -05:00
|
|
|
for(b=0; b<nelem(c2->set); b++){
|
|
|
|
|
plane2[0][b] = ~c2->set[b] & c2->clr[b];
|
|
|
|
|
plane2[1][b] = c2->set[b] | c2->clr[b];
|
2018-10-21 20:59:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For checking out the cursor bitmap image
|
|
|
|
|
/*
|
|
|
|
|
static BOOL saveimg = YES;
|
|
|
|
|
if(saveimg){
|
|
|
|
|
NSData *data = [r representationUsingType: NSBitmapImageFileTypeBMP properties: @{}];
|
|
|
|
|
[data writeToFile: @"/tmp/r.bmp" atomically: NO];
|
|
|
|
|
data = [r2 representationUsingType: NSBitmapImageFileTypeBMP properties: @{}];
|
|
|
|
|
[data writeToFile: @"/tmp/r2.bmp" atomically: NO];
|
|
|
|
|
saveimg = NO;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)];
|
|
|
|
|
[i addRepresentation:r2];
|
|
|
|
|
[i addRepresentation:r];
|
|
|
|
|
|
|
|
|
|
p = NSMakePoint(-c->offset.x, -c->offset.y);
|
|
|
|
|
currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p];
|
|
|
|
|
|
|
|
|
|
[win invalidateCursorRectsForView:myContent];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)applicationDidFinishLaunching:(id)arg
|
|
|
|
|
{
|
|
|
|
|
NSMenu *m, *sm;
|
|
|
|
|
NSData *d;
|
|
|
|
|
NSImage *i;
|
|
|
|
|
|
|
|
|
|
LOG(@"applicationDidFinishLaunching");
|
|
|
|
|
|
|
|
|
|
sm = [NSMenu new];
|
|
|
|
|
[sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
|
|
|
|
|
[sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"];
|
|
|
|
|
[sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
|
|
|
|
|
m = [NSMenu new];
|
|
|
|
|
[m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""];
|
|
|
|
|
[m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]];
|
|
|
|
|
[NSApp setMainMenu:m];
|
|
|
|
|
|
|
|
|
|
d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)];
|
|
|
|
|
i = [[NSImage alloc] initWithData:d];
|
|
|
|
|
[NSApp setApplicationIconImage:i];
|
|
|
|
|
[[NSApp dockTile] display];
|
|
|
|
|
|
|
|
|
|
[NSThread
|
|
|
|
|
detachNewThreadSelector:@selector(callservep9p:)
|
|
|
|
|
toTarget:[self class] withObject:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSApplicationPresentationOptions)window:(id)arg
|
|
|
|
|
willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions {
|
|
|
|
|
NSApplicationPresentationOptions o;
|
|
|
|
|
o = proposedOptions;
|
|
|
|
|
o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar);
|
|
|
|
|
o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 20:43:21 +02:00
|
|
|
- (void)windowDidBecomeKey:(id)arg
|
|
|
|
|
{
|
|
|
|
|
[myContent sendmouse:0];
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-21 20:59:21 -05:00
|
|
|
@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];
|
|
|
|
|
resizeimg();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)viewDidChangeBackingProperties
|
|
|
|
|
{
|
|
|
|
|
[super viewDidChangeBackingProperties];
|
|
|
|
|
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<NSAttributedStringKey> *)validAttributesForMarkedText
|
|
|
|
|
{
|
|
|
|
|
LOG(@"validAttributesForMarkedText");
|
|
|
|
|
return @[];
|
|
|
|
|
}
|
|
|
|
|
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r
|
|
|
|
|
actualRange:(NSRangePointer)actualRange
|
|
|
|
|
{
|
|
|
|
|
NSRange sr;
|
|
|
|
|
NSAttributedString *s;
|
|
|
|
|
|
|
|
|
|
LOG(@"attributedSubstringForProposedRange: (%ld, %ld) (%ld, %ld)",
|
|
|
|
|
r.location, r.length, actualRange->location, actualRange->length);
|
|
|
|
|
sr = NSMakeRange(0, [_tmpText length]);
|
|
|
|
|
sr = NSIntersectionRange(sr, r);
|
|
|
|
|
if(actualRange)
|
|
|
|
|
*actualRange = sr;
|
|
|
|
|
LOG(@"use range: %ld, %ld", sr.location, sr.length);
|
|
|
|
|
if(sr.length)
|
|
|
|
|
s = [[NSAttributedString alloc]
|
|
|
|
|
initWithString:[_tmpText substringWithRange:sr]];
|
|
|
|
|
LOG(@" return %@", s);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
- (void)insertText:(id)s
|
|
|
|
|
replacementRange:(NSRange)r
|
|
|
|
|
{
|
|
|
|
|
NSUInteger i;
|
|
|
|
|
NSUInteger len;
|
|
|
|
|
|
|
|
|
|
LOG(@"insertText: %@ replacementRange: %ld, %ld", s, r.location, r.length);
|
|
|
|
|
|
|
|
|
|
[self clearInput];
|
|
|
|
|
|
|
|
|
|
len = [s length];
|
|
|
|
|
for(i = 0; i < len; ++i)
|
|
|
|
|
keystroke([s characterAtIndex:i]);
|
|
|
|
|
[_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)];
|
|
|
|
|
_markedRange = NSMakeRange(NSNotFound, 0);
|
|
|
|
|
_selectedRange = NSMakeRange(0, 0);
|
|
|
|
|
}
|
|
|
|
|
- (NSUInteger)characterIndexForPoint:(NSPoint)point
|
|
|
|
|
{
|
|
|
|
|
LOG(@"characterIndexForPoint: %g, %g", point.x, point.y);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
- (NSRect)firstRectForCharacterRange:(NSRange)r
|
|
|
|
|
actualRange:(NSRangePointer)actualRange
|
|
|
|
|
{
|
|
|
|
|
LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)",
|
|
|
|
|
r.location, r.length, actualRange->location, actualRange->length);
|
|
|
|
|
if(actualRange)
|
|
|
|
|
*actualRange = r;
|
|
|
|
|
return [[self window] convertRectToScreen:_lastInputRect];
|
|
|
|
|
}
|
|
|
|
|
- (void)doCommandBySelector:(SEL)s
|
|
|
|
|
{
|
|
|
|
|
NSEvent *e;
|
|
|
|
|
NSEventModifierFlags m;
|
|
|
|
|
uint c, k;
|
|
|
|
|
|
|
|
|
|
LOG(@"doCommandBySelector (%@)", NSStringFromSelector(s));
|
|
|
|
|
|
|
|
|
|
e = [NSApp currentEvent];
|
|
|
|
|
c = [[e characters] characterAtIndex:0];
|
|
|
|
|
k = keycvt(c);
|
|
|
|
|
LOG(@"keyDown: character0: 0x%x -> 0x%x", c, k);
|
|
|
|
|
m = [e modifierFlags];
|
|
|
|
|
|
|
|
|
|
if(m & NSEventModifierFlagCommand){
|
|
|
|
|
if((m & NSEventModifierFlagShift) && 'a' <= k && k <= 'z')
|
|
|
|
|
k += 'A' - 'a';
|
|
|
|
|
if(' '<=k && k<='~')
|
|
|
|
|
k += Kcmd;
|
|
|
|
|
}
|
|
|
|
|
if(k>0)
|
|
|
|
|
keystroke(k);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper for managing input rect approximately
|
|
|
|
|
- (void)resetLastInputRect
|
|
|
|
|
{
|
|
|
|
|
LOG(@"resetLastInputRect");
|
|
|
|
|
_lastInputRect.origin.x = 0.0;
|
|
|
|
|
_lastInputRect.origin.y = 0.0;
|
|
|
|
|
_lastInputRect.size.width = 0.0;
|
|
|
|
|
_lastInputRect.size.height = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)enlargeLastInputRect:(NSRect)r
|
|
|
|
|
{
|
|
|
|
|
r.origin.y = [self bounds].size.height - r.origin.y - r.size.height;
|
|
|
|
|
_lastInputRect = NSUnionRect(_lastInputRect, r);
|
|
|
|
|
LOG(@"update last input rect (%g, %g, %g, %g)",
|
|
|
|
|
_lastInputRect.origin.x, _lastInputRect.origin.y,
|
|
|
|
|
_lastInputRect.size.width, _lastInputRect.size.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)clearInput
|
|
|
|
|
{
|
|
|
|
|
if(_tmpText.length){
|
|
|
|
|
uint i;
|
|
|
|
|
int l;
|
|
|
|
|
l = 1 + _tmpText.length - NSMaxRange(_selectedRange)
|
|
|
|
|
+ (_selectedRange.length > 0);
|
|
|
|
|
LOG(@"move right %d", l);
|
|
|
|
|
for(i = 0; i < l; ++i)
|
|
|
|
|
keystroke(Kright);
|
|
|
|
|
l = _tmpText.length+2+2*(_selectedRange.length > 0);
|
|
|
|
|
LOG(@"backspace %d", l);
|
|
|
|
|
for(uint i = 0; i < l; ++i)
|
|
|
|
|
keystroke(Kbs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation DrawLayer
|
|
|
|
|
|
|
|
|
|
- (void)display
|
|
|
|
|
{
|
|
|
|
|
id<MTLCommandBuffer> cbuf;
|
|
|
|
|
id<MTLRenderCommandEncoder> cmd;
|
|
|
|
|
|
|
|
|
|
LOG(@"display");
|
|
|
|
|
|
|
|
|
|
cbuf = [commandQueue commandBuffer];
|
|
|
|
|
|
|
|
|
|
LOG(@"display query drawable");
|
|
|
|
|
|
|
|
|
|
@autoreleasepool{
|
|
|
|
|
id<CAMetalDrawable> drawable;
|
|
|
|
|
|
|
|
|
|
drawable = [layer nextDrawable];
|
|
|
|
|
if(!drawable){
|
|
|
|
|
LOG(@"display couldn't get drawable");
|
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG(@"display got drawable");
|
|
|
|
|
|
|
|
|
|
renderPass.colorAttachments[0].texture = drawable.texture;
|
|
|
|
|
|
|
|
|
|
cmd = [cbuf renderCommandEncoderWithDescriptor:renderPass];
|
|
|
|
|
[cmd setRenderPipelineState:pipelineState];
|
|
|
|
|
[cmd setFragmentTexture:texture atIndex:0];
|
|
|
|
|
[cmd drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
|
|
|
|
[cmd endEncoding];
|
|
|
|
|
|
|
|
|
|
[cbuf presentDrawable:drawable];
|
|
|
|
|
drawable = nil;
|
|
|
|
|
}
|
|
|
|
|
[cbuf addCompletedHandler:^(id<MTLCommandBuffer> 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 '\b': return 127;
|
|
|
|
|
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);
|
2018-11-15 20:28:56 -05:00
|
|
|
setcursor(nil, nil);
|
2018-10-21 20:59:21 -05:00
|
|
|
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));
|
2019-04-05 12:44:47 -06:00
|
|
|
if(!rectinrect(r, Rect(0, 0, texture.width, texture.height))){
|
|
|
|
|
LOG(@"Rectangle is out of bounds, return.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-10-21 20:59:21 -05:00
|
|
|
|
|
|
|
|
@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
|
2018-11-15 20:28:56 -05:00
|
|
|
setcursor(Cursor *c, Cursor2 *c2)
|
2018-10-21 20:59:21 -05:00
|
|
|
{
|
2018-11-15 20:28:56 -05:00
|
|
|
Cursors cs;
|
|
|
|
|
|
|
|
|
|
cs.c = c;
|
|
|
|
|
cs.c2 = c2;
|
|
|
|
|
|
2018-10-21 20:59:21 -05:00
|
|
|
[AppDelegate
|
|
|
|
|
performSelectorOnMainThread:@selector(callsetcursor:)
|
2018-11-15 20:28:56 -05:00
|
|
|
withObject:[NSValue valueWithPointer:&cs]
|
2018-10-21 20:59:21 -05:00
|
|
|
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];
|
|
|
|
|
resizeimg();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
}
|