diff options
Diffstat (limited to 'src/cmd')
33 files changed, 2802 insertions, 1902 deletions
diff --git a/src/cmd/9term/FreeBSD.c b/src/cmd/9term/FreeBSD.c index eec79c28..e91f6ace 100644 --- a/src/cmd/9term/FreeBSD.c +++ b/src/cmd/9term/FreeBSD.c @@ -1 +1,17 @@ +#define getpts not_using_this_getpts #include "bsdpty.c" +#undef getpts + +#include <libutil.h> + +int +getpts(int fd[], char *slave) +{ + if(openpty(&fd[1], &fd[0], NULL, NULL, NULL) >= 0){ + fchmod(fd[1], 0620); + strcpy(slave, ttyname(fd[0])); + return 0; + } + sysfatal("no ptys"); + return 0; +} diff --git a/src/cmd/9term/rcstart.c b/src/cmd/9term/rcstart.c index 141b9b00..fddabc6d 100644 --- a/src/cmd/9term/rcstart.c +++ b/src/cmd/9term/rcstart.c @@ -34,7 +34,7 @@ int rcstart(int argc, char **argv, int *pfd, int *tfd) { int fd[2], i, pid; - char *cmd, *xargv[3]; + char *cmd, *xargv[4]; char slave[256]; int sfd; @@ -46,6 +46,11 @@ rcstart(int argc, char **argv, int *pfd, int *tfd) argv[0] = "rc"; argv[1] = "-i"; argv[2] = 0; + if(loginshell){ + argv[2] = "-l"; + argv[3] = 0; + argc = 3; + } } cmd = argv[0]; if(loginshell){ diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c index 742aabdf..12701f23 100644 --- a/src/cmd/acme/acme.c +++ b/src/cmd/acme/acme.c @@ -966,6 +966,74 @@ Cursor boxcursor = { 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} }; +Cursor2 boxcursor2 = { + {-15, -15}, + {0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0x00, 0x00, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00} +}; + void iconinit(void) { diff --git a/src/cmd/acme/cols.c b/src/cmd/acme/cols.c index 10e014ee..6215e044 100644 --- a/src/cmd/acme/cols.c +++ b/src/cmd/acme/cols.c @@ -250,8 +250,12 @@ colresize(Column *c, Rectangle r) w->maxlines = 0; if(i == c->nw-1) r1.max.y = r.max.y; - else - r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r); + else{ + r1.max.y = r1.min.y; + if(Dy(c->r) != 0){ + r1.max.y += (Dy(w->r)+Border)*Dy(r)/Dy(c->r); + } + } r1.max.y = max(r1.max.y, r1.min.y + Border+font->height); r2 = r1; r2.max.y = r2.min.y+Border; @@ -473,7 +477,7 @@ coldragwin(Column *c, Window *w, int but) Column *nc; clearmouse(); - setcursor(mousectl, &boxcursor); + setcursor2(mousectl, &boxcursor, &boxcursor2); b = mouse->buttons; op = mouse->xy; while(mouse->buttons == b) diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h index 451ba392..3a3d2e78 100644 --- a/src/cmd/acme/dat.h +++ b/src/cmd/acme/dat.h @@ -526,6 +526,7 @@ Image *button; Image *but2col; Image *but3col; Cursor boxcursor; +Cursor2 boxcursor2; Row row; int timerpid; Disk *disk; diff --git a/src/cmd/acme/rows.c b/src/cmd/acme/rows.c index 8cff0855..83c64594 100644 --- a/src/cmd/acme/rows.c +++ b/src/cmd/acme/rows.c @@ -148,7 +148,7 @@ rowdragcol(Row *row, Column *c, int _0) USED(_0); clearmouse(); - setcursor(mousectl, &boxcursor); + setcursor2(mousectl, &boxcursor, &boxcursor2); b = mouse->buttons; op = mouse->xy; while(mouse->buttons == b) diff --git a/src/cmd/devdraw/bigarrow.h b/src/cmd/devdraw/bigarrow.h index a46f19a0..1221ec8c 100644 --- a/src/cmd/devdraw/bigarrow.h +++ b/src/cmd/devdraw/bigarrow.h @@ -1,11 +1,83 @@ -Cursor bigarrow = { - {0, 0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, - 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, - 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, - 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40}, - {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, - 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, - 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, - 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00}, +Cursor bigarrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +Cursor2 bigarrow2 = { + { -2, -2 }, + { 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x1E, + 0xC0, 0x00, 0x00, 0x3C, + 0xC0, 0x00, 0x00, 0xF0, + 0xC0, 0x00, 0x03, 0xE0, + 0xC0, 0x00, 0x0F, 0x80, + 0xC0, 0x00, 0x0E, 0x00, + 0xC0, 0x00, 0x07, 0x00, + 0xC0, 0x00, 0x03, 0x80, + 0xC0, 0x00, 0x01, 0xC0, + 0xC0, 0x00, 0x00, 0xE0, + 0xC0, 0x00, 0x00, 0x70, + 0xC0, 0x00, 0x00, 0x38, + 0xC0, 0x00, 0x00, 0x1C, + 0xC0, 0x00, 0x00, 0x0E, + 0xC0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x03, + 0xC0, 0xC0, 0x00, 0x07, + 0xC0, 0xE0, 0x00, 0x0E, + 0xC1, 0xF0, 0x00, 0x1C, + 0xC1, 0xB8, 0x00, 0x38, + 0xC3, 0x9C, 0x00, 0x70, + 0xC3, 0x0E, 0x00, 0xE0, + 0xC7, 0x07, 0x01, 0xC0, + 0xCE, 0x03, 0x83, 0x80, + 0xCC, 0x01, 0xC7, 0x00, + 0xDC, 0x00, 0xEE, 0x00, + 0xF8, 0x00, 0x7C, 0x00, + 0xF0, 0x00, 0x38, 0x00, + }, + { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xF8, + 0x3F, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, + 0x3F, 0xFF, 0xF8, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0xFE, 0x00, + 0x3F, 0xFF, 0xFF, 0x00, + 0x3F, 0xFF, 0xFF, 0x80, + 0x3F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xF0, + 0x3F, 0xFF, 0xFF, 0xF8, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0x3F, 0xFF, 0xF8, + 0x3F, 0x1F, 0xFF, 0xF0, + 0x3E, 0x0F, 0xFF, 0xE0, + 0x3E, 0x07, 0xFF, 0xC0, + 0x3C, 0x03, 0xFF, 0x80, + 0x3C, 0x01, 0xFF, 0x00, + 0x38, 0x00, 0xFE, 0x00, + 0x30, 0x00, 0x7C, 0x00, + 0x30, 0x00, 0x38, 0x00, + 0x20, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + } }; diff --git a/src/cmd/devdraw/cocoa-screen-metal.m b/src/cmd/devdraw/cocoa-screen-metal.m new file mode 100644 index 00000000..049d1c5c --- /dev/null +++ b/src/cmd/devdraw/cocoa-screen-metal.m @@ -0,0 +1,1250 @@ +#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]; +} + +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]; + int 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; b<nelem(c->set); 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; b<nelem(c2->set); b++){ + plane2[0][b] = ~c2->set[b] & c2->clr[b]; + plane2[1][b] = c2->set[b] | c2->clr[b]; + } + + // For checking out the cursor bitmap image +/* + static BOOL saveimg = YES; + if(saveimg){ + NSData *data = [r representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; + [data writeToFile: @"/tmp/r.bmp" atomically: NO]; + data = [r2 representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; + [data writeToFile: @"/tmp/r2.bmp" atomically: NO]; + saveimg = NO; + } +*/ + + i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; + [i addRepresentation:r2]; + [i addRepresentation:r]; + + p = NSMakePoint(-c->offset.x, -c->offset.y); + currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; + + [win invalidateCursorRectsForView:myContent]; +} + +- (void)applicationDidFinishLaunching:(id)arg +{ + NSMenu *m, *sm; + NSData *d; + NSImage *i; + + LOG(@"applicationDidFinishLaunching"); + + sm = [NSMenu new]; + [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; + [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"]; + [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; + m = [NSMenu new]; + [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; + [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; + [NSApp setMainMenu:m]; + + d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)]; + i = [[NSImage alloc] initWithData:d]; + [NSApp setApplicationIconImage:i]; + [[NSApp dockTile] display]; + + [NSThread + detachNewThreadSelector:@selector(callservep9p:) + toTarget:[self class] withObject:nil]; +} + +- (NSApplicationPresentationOptions)window:(id)arg + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { + NSApplicationPresentationOptions o; + o = proposedOptions; + o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); + o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + return o; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return YES; +} + +@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); + 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)); + + @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]; + 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"); +} diff --git a/src/cmd/devdraw/cocoa-screen.h b/src/cmd/devdraw/cocoa-screen.h index 3c4c94c5..b5e3c701 100644 --- a/src/cmd/devdraw/cocoa-screen.h +++ b/src/cmd/devdraw/cocoa-screen.h @@ -2,7 +2,7 @@ Memimage *attachscreen(char*, char*); void setmouse(Point); -void setcursor(Cursor*); +void setcursor(Cursor*, Cursor2*); void setlabel(char*); char* getsnarf(void); void putsnarf(char*); @@ -16,5 +16,9 @@ void servep9p(void); void zlock(void); void zunlock(void); +void resizeimg(void); + Rectangle mouserect; -int mouseresized; + +int mouseresized; +void resizewindow(Rectangle); diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m index 8e141904..2b804c2f 100644 --- a/src/cmd/devdraw/cocoa-screen.m +++ b/src/cmd/devdraw/cocoa-screen.m @@ -1410,8 +1410,10 @@ kicklabel0(char *label) { } void -setcursor(Cursor *c) +setcursor(Cursor *c, Cursor2 *c2) { + USED(c2); + /* * No cursor change unless in main thread. */ @@ -1658,3 +1660,9 @@ setprocname(const char *s) if(err != noErr) fprint(2, "Call to set process name failed\n"); } + +void +resizewindow(Rectangle r) +{ + USED(r); +} diff --git a/src/cmd/devdraw/cocoa-srv.c b/src/cmd/devdraw/cocoa-srv.c index 197fd512..329dd71f 100644 --- a/src/cmd/devdraw/cocoa-srv.c +++ b/src/cmd/devdraw/cocoa-srv.c @@ -21,7 +21,7 @@ typedef struct Tagbuf Tagbuf; struct Kbdbuf { - Rune r[32]; + Rune r[256]; int ri; int wi; int stall; @@ -29,7 +29,7 @@ struct Kbdbuf struct Mousebuf { - Mouse m[32]; + Mouse m[256]; Mouse last; int ri; int wi; @@ -38,7 +38,7 @@ struct Mousebuf struct Tagbuf { - int t[32]; + int t[256]; int ri; int wi; }; @@ -97,7 +97,7 @@ servep9p(void) /* pick off messages one by one */ if(convM2W(mbuf, nn+4, &m) <= 0) sysfatal("cannot convert message"); - if(trace) fprint(2, "<- %W\n", &m); + if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); runmsg(&m); } } @@ -163,9 +163,9 @@ runmsg(Wsysmsg *m) case Tcursor: if(m->arrowcursor) - setcursor(nil); + setcursor(nil, nil); else - setcursor(&m->cursor); + setcursor(&m->cursor, &m->cursor2); replymsg(m); break; @@ -191,6 +191,7 @@ runmsg(Wsysmsg *m) break; case Trddraw: + zlock(); n = m->count; if(n > sizeof buf) n = sizeof buf; @@ -202,13 +203,16 @@ runmsg(Wsysmsg *m) 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: @@ -217,7 +221,7 @@ runmsg(Wsysmsg *m) break; case Tresize: - // _xresizewindow(m->rect); + resizewindow(m->rect); replymsg(m); break; } @@ -238,7 +242,7 @@ replymsg(Wsysmsg *m) if(m->type%2 == 0) m->type++; - if(trace) fprint(2, "-> %W\n", m); + if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m); /* copy to output buffer */ n = sizeW2M(m); @@ -293,11 +297,11 @@ matchmouse(void) mousetags.ri = 0; m.mouse = mouse.m[mouse.ri]; m.resized = mouseresized; + mouseresized = 0; /* if(m.resized) fprint(2, "sending resize\n"); */ - mouseresized = 0; mouse.ri++; if(mouse.ri == nelem(mouse.m)) mouse.ri = 0; @@ -367,8 +371,6 @@ abortcompose(void) keystroke(Kalt); } -void resizeimg(void); - void keystroke(int c) { diff --git a/src/cmd/devdraw/cocoa-thread.c b/src/cmd/devdraw/cocoa-thread.c index c9b280f7..92b92d2c 100644 --- a/src/cmd/devdraw/cocoa-thread.c +++ b/src/cmd/devdraw/cocoa-thread.c @@ -25,4 +25,11 @@ qunlock(QLock *q) { pthread_mutex_unlock(&q->m); } + +int +threadid(void) +{ + return pthread_mach_thread_np(pthread_self()); +} + #endif diff --git a/src/cmd/devdraw/cocoa-thread.h b/src/cmd/devdraw/cocoa-thread.h index c2f3e982..d5793f0a 100644 --- a/src/cmd/devdraw/cocoa-thread.h +++ b/src/cmd/devdraw/cocoa-thread.h @@ -30,4 +30,5 @@ void qlock(QLock*); void qunlock(QLock*); + int threadid(void); #endif diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile index cad244ac..7f0c2a20 100644 --- a/src/cmd/devdraw/mkfile +++ b/src/cmd/devdraw/mkfile @@ -3,6 +3,8 @@ TARG=devdraw +SHORTLIB=draw memdraw + WSYSOFILES=\ devdraw.$O\ latin1.$O\ @@ -35,6 +37,9 @@ latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd $O.macargv: $MACARGV $LD -o $target $prereq +cocoa-screen-metal-objc.$O: cocoa-screen-metal.m + $CC $CFLAGS $OBJCFLAGS -o $target cocoa-screen-metal.m + %-objc.$O: %.m $CC $CFLAGS -o $target $stem.m diff --git a/src/cmd/devdraw/mkwsysrules.sh b/src/cmd/devdraw/mkwsysrules.sh index 9c422261..e94afbd3 100644 --- a/src/cmd/devdraw/mkwsysrules.sh +++ b/src/cmd/devdraw/mkwsysrules.sh @@ -23,7 +23,8 @@ fi if [ "x$WSYSTYPE" = "x" ]; then if [ "x`uname`" = "xDarwin" ]; then if sw_vers | grep 'ProductVersion: 10\.[0-5]\.' >/dev/null; then - WSYSTYPE=osx + echo 1>&2 'OS X 10.5 and older are not supported' + exit 1 else #echo 1>&2 'WARNING: OS X Lion is not working. Copy binaries from a Snow Leopard system.' WSYSTYPE=osx-cocoa @@ -52,15 +53,13 @@ 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 ]; then - if [ -d /System/Library/PrivateFrameworks/MultitouchSupport.framework ]; then - echo 'CFLAGS=$CFLAGS -DMULTITOUCH' - echo 'LDFLAGS=$LDFLAGS -F/System/Library/PrivateFrameworks' - fi - echo 'WSYSOFILES=$WSYSOFILES osx-screen-carbon-objc.o osx-draw.o osx-srv.o' - echo 'MACARGV=macargv.o' elif [ $WSYSTYPE = osx-cocoa ]; then - echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-objc.o cocoa-srv.o cocoa-thread.o' + if sw_vers|awk '/ProductVersion/{split($2,a,".");exit(a[2]<14)}' >/dev/null; then # 0 is true in sh. + echo 'OBJCFLAGS=$OBJCFLAGS -fobjc-arc' + echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-metal-objc.o cocoa-srv.o cocoa-thread.o' + else + echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-objc.o cocoa-srv.o cocoa-thread.o' + fi echo 'MACARGV=macargv-objc.o' elif [ $WSYSTYPE = nowsys ]; then echo 'WSYSOFILES=nowsys.o' diff --git a/src/cmd/devdraw/osx-screen-carbon.m b/src/cmd/devdraw/osx-screen-carbon.m deleted file mode 100644 index d41e9d10..00000000 --- a/src/cmd/devdraw/osx-screen-carbon.m +++ /dev/null @@ -1,1302 +0,0 @@ -#define Point OSXPoint -#define Rect OSXRect -#define Cursor OSXCursor -#include <Carbon/Carbon.h> -#import <Foundation/Foundation.h> -#ifdef MULTITOUCH -#include <IOKit/IOKitLib.h> -#include <IOKit/hidsystem/IOHIDShared.h> -#endif -#undef Rect -#undef Point -#undef Cursor -#undef offsetof -#undef nil - -#include "u.h" -#include "libc.h" -#include <thread.h> -#include <draw.h> -#include <memdraw.h> -#include <keyboard.h> -#include "mouse.h" -#include <cursor.h> -#include "osx-screen.h" -#include "osx-keycodes.h" -#include "devdraw.h" -#include "glendapng.h" - -AUTOFRAMEWORK(Carbon) -AUTOFRAMEWORK(Cocoa) - -#ifdef MULTITOUCH -AUTOFRAMEWORK(MultitouchSupport) -AUTOFRAMEWORK(IOKit) -#endif - -#define panic sysfatal - -extern Rectangle mouserect; - -struct { - char *label; - char *winsize; - QLock labellock; - - Rectangle fullscreenr; - Rectangle screenr; - Memimage *screenimage; - int isfullscreen; - ulong fullscreentime; - - Point xy; - int buttons; - int kbuttons; - - CGDataProviderRef provider; - MenuRef wmenu; - MenuRef vmenu; - WindowRef window; - CGImageRef image; - CGContextRef windowctx; - PasteboardRef snarf; - int needflush; - QLock flushlock; - int active; - int infullscreen; - int kalting; // last keystroke was Kalt - int touched; // last mouse event was touchCallback - int collapsed; // parked in dock - int flushing; // flushproc has started - NSMutableArray* devicelist; -} osx; - -/* - These structs are required, in order to handle some parameters returned from the - Support.framework - */ -typedef struct { - float x; - float y; -}mtPoint; - -typedef struct { - mtPoint position; - mtPoint velocity; -}mtReadout; - -/* - Some reversed engineered informations from MultiTouchSupport.framework - */ -typedef struct -{ - int frame; //the current frame - double timestamp; //event timestamp - int identifier; //identifier guaranteed unique for life of touch per device - int state; //the current state (not sure what the values mean) - int unknown1; //no idea what this does - int unknown2; //no idea what this does either - mtReadout normalized; //the normalized position and vector of the touch (0,0 to 1,1) - float size; //the size of the touch (the area of your finger being tracked) - int unknown3; //no idea what this does - float angle; //the angle of the touch -| - float majorAxis; //the major axis of the touch -|-- an ellipsoid. you can track the angle of each finger! - float minorAxis; //the minor axis of the touch -| - mtReadout unknown4; //not sure what this is for - int unknown5[2]; //no clue - float unknown6; //no clue -}Touch; - -//a reference pointer for the multitouch device -typedef void *MTDeviceRef; - -//the prototype for the callback function -typedef int (*MTContactCallbackFunction)(int,Touch*,int,double,int); - -//returns a pointer to the default device (the trackpad?) -MTDeviceRef MTDeviceCreateDefault(void); - -//returns a CFMutableArrayRef array of all multitouch devices -CFMutableArrayRef MTDeviceCreateList(void); - -//registers a device's frame callback to your callback function -void MTRegisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction); -void MTUnregisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction); - -//start sending events -void MTDeviceStart(MTDeviceRef, int); -void MTDeviceStop(MTDeviceRef); - -MTDeviceRef MTDeviceCreateFromService(io_service_t); -io_service_t MTDeviceGetService(MTDeviceRef); - -#define kNTracks 10 -struct TouchTrack { - int id; - float firstThreshTime; - mtPoint pos; -} tracks[kNTracks]; - -#define kSizeSensitivity 1.25f -#define kTimeSensitivity 0.03f /* seconds */ -#define kButtonLimit 0.6f /* percentage from base of pad */ - -int -findTrack(int id) -{ - int i; - for(i = 0; i < kNTracks; ++i) - if(tracks[i].id == id) - return i; - return -1; -} - -#define kMoveSensitivity 0.05f - -int -moved(mtPoint a, mtPoint b) -{ - if(fabs(a.x - b.x) > kMoveSensitivity) - return 1; - if(fabs(a.y - b.y) > kMoveSensitivity) - return 1; - return 0; -} - -int -classifyTouch(Touch *t) -{ - mtPoint p; - int i; - - p = t->normalized.position; - - i = findTrack(t->identifier); - if(i == -1) { - i = findTrack(-1); - if(i == -1) - return 0; // No empty tracks. - tracks[i].id = t->identifier; - tracks[i].firstThreshTime = t->timestamp; - tracks[i].pos = p; - // we don't have a touch yet - we wait kTimeSensitivity before reporting it. - return 0; - } - - if(t->size == 0) { // lost touch - tracks[i].id = -1; - return 0; - } - if(t->size < kSizeSensitivity) { - tracks[i].firstThreshTime = t->timestamp; - } - if((t->timestamp - tracks[i].firstThreshTime) < kTimeSensitivity) { - return 0; - } - if(p.y > kButtonLimit && t->size > kSizeSensitivity) { - if(p.x < 0.35) - return 1; - if(p.x > 0.65) - return 4; - if(p.x > 0.35 && p.x < 0.65) - return 2; - } - return 0; -} - -static ulong msec(void); - -int -touchCallback(int device, Touch *data, int nFingers, double timestamp, int frame) -{ -#ifdef MULTITOUCH - int buttons, delta, i; - static int obuttons; - CGPoint p; - CGEventRef e; - - p.x = osx.xy.x+osx.screenr.min.x; - p.y = osx.xy.y+osx.screenr.min.y; - if(!ptinrect(Pt(p.x, p.y), osx.screenr)) - return 0; - osx.touched = 1; - buttons = 0; - for(i = 0; i < nFingers; ++i) - buttons |= classifyTouch(data+i); - delta = buttons ^ obuttons; - obuttons = buttons; - if(delta & 1) { - e = CGEventCreateMouseEvent(NULL, - (buttons & 1) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp, - p, - 29); - CGEventPost(kCGSessionEventTap, e); - CFRelease(e); - } - if(delta & 2) { - e = CGEventCreateMouseEvent(NULL, - (buttons & 2) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp, - p, - 30); - CGEventPost(kCGSessionEventTap, e); - CFRelease(e); - } - if(delta & 4){ - e = CGEventCreateMouseEvent(NULL, - (buttons & 4) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp, - p, - 31); - CGEventPost(kCGSessionEventTap, e); - CFRelease(e); - } - return delta != 0; -#else - return 0; -#endif -} - -extern int multitouch; - -enum -{ - WindowAttrs = - kWindowCloseBoxAttribute | - kWindowCollapseBoxAttribute | - kWindowResizableAttribute | - kWindowStandardHandlerAttribute | - kWindowFullZoomAttribute -}; - -enum -{ - P9PEventLabelUpdate = 1 -}; - -static void screenproc(void*); -static void eresized(int); -static void fullscreen(int); -static void seticon(void); -static void activated(int); - -static OSStatus quithandler(EventHandlerCallRef, EventRef, void*); -static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*); -static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*); - -enum -{ - CmdFullScreen = 1, -}; - -void screeninit(void); -void _flushmemscreen(Rectangle r); - -#ifdef MULTITOUCH -static void -RegisterMultitouch(void *ctx, io_iterator_t iter) -{ - io_object_t io; - MTDeviceRef dev; - - while((io = IOIteratorNext(iter)) != 0){ - dev = MTDeviceCreateFromService(io); - if (dev != nil){ - MTRegisterContactFrameCallback(dev, touchCallback); - [osx.devicelist addObject:dev]; - if(osx.active) - MTDeviceStart(dev, 0); - } - - IOObjectRelease(io); - } -} - -static void -UnregisterMultitouch(void *ctx, io_iterator_t iter) -{ - io_object_t io; - MTDeviceRef dev; - int i; - - while((io = IOIteratorNext(iter)) != 0){ - for(i = 0; i < [osx.devicelist count]; i++){ - dev = [osx.devicelist objectAtIndex:i]; - if(IOObjectIsEqualTo(MTDeviceGetService(dev), io)){ - if(osx.active) - MTDeviceStop(dev); - MTUnregisterContactFrameCallback(dev, touchCallback); - [osx.devicelist removeObjectAtIndex:i]; - break; - } - } - - IOObjectRelease(io); - } -} - -#endif /*MULTITOUCH*/ - -static void -InitMultiTouch() -{ -#ifdef MULTITOUCH - IONotificationPortRef port; - CFRunLoopSourceRef source; - io_iterator_t iter; - kern_return_t kr; - io_object_t obj; - int i; - - if(!multitouch) - return; - - osx.devicelist = [[NSMutableArray alloc] init]; - - for(i = 0; i < kNTracks; ++i) - tracks[i].id = -1; - - port = IONotificationPortCreate(kIOMasterPortDefault); - if(port == nil){ - fprint(2, "failed to get an IO notification port\n"); - return; - } - - source = IONotificationPortGetRunLoopSource(port); - if(source == nil){ - fprint(2, "failed to get loop source for port"); - return; - } - - CFRunLoopAddSource( - (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop()), - source, - kCFRunLoopDefaultMode); - - kr = IOServiceAddMatchingNotification( - port, kIOTerminatedNotification, - IOServiceMatching("AppleMultitouchDevice"), - &UnregisterMultitouch, - nil, &iter); - - if(kr != KERN_SUCCESS){ - fprint(2, "failed to add termination notification\n"); - return; - } - - /* Arm the notification */ - while((obj = IOIteratorNext(iter)) != 0) - IOObjectRelease(obj); - - kr = IOServiceAddMatchingNotification( - port, kIOMatchedNotification, - IOServiceMatching("AppleMultitouchDevice"), - &RegisterMultitouch, - nil, &iter); - - if(kr != KERN_SUCCESS){ - fprint(2, "failed to add matching notification\n"); - return; - } - - RegisterMultitouch(nil, iter); -#endif -} - -Memimage* -attachscreen(char *label, char *winsize) -{ - if(label == nil) - label = "gnot a label"; - osx.label = strdup(label); - osx.winsize = winsize; - if(osx.screenimage == nil){ - screeninit(); - if(osx.screenimage == nil) - panic("cannot create OS X screen"); - } - return osx.screenimage; -} - -extern int multitouch; - -void -_screeninit(void) -{ - CGRect cgr; - OSXRect or; - Rectangle r; - int havemin; - - memimageinit(); - - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); - - cgr = CGDisplayBounds(CGMainDisplayID()); - osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height); - - InitCursor(); - - // Create minimal menu with full-screen option. - ClearMenuBar(); - CreateStandardWindowMenu(0, &osx.wmenu); - InsertMenu(osx.wmenu, 0); - MenuItemIndex ix; - CreateNewMenu(1004, 0, &osx.vmenu); // XXX 1004? - SetMenuTitleWithCFString(osx.vmenu, CFSTR("View")); - AppendMenuItemTextWithCFString(osx.vmenu, - CFSTR("Full Screen"), 0, CmdFullScreen, &ix); - SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F'); - AppendMenuItemTextWithCFString(osx.vmenu, - CFSTR("Cmd-F exits full screen"), - kMenuItemAttrDisabled, CmdFullScreen, &ix); - InsertMenu(osx.vmenu, GetMenuID(osx.wmenu)); - DrawMenuBar(); - - // Create the window. - r = Rect(0, 0, Dx(osx.fullscreenr)*2/3, Dy(osx.fullscreenr)*2/3); - havemin = 0; - if(osx.winsize && osx.winsize[0]){ - if(parsewinsize(osx.winsize, &r, &havemin) < 0) - sysfatal("%r"); - } - if(!havemin) - r = rectaddpt(r, Pt((Dx(osx.fullscreenr)-Dx(r))/2, (Dy(osx.fullscreenr)-Dy(r))/2)); - or.left = r.min.x; - or.top = r.min.y; - or.right = r.max.x; - or.bottom = r.max.y; - CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window); - setlabel(osx.label); - seticon(); - - // Set up the clip board. - if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr) - panic("pasteboard create"); - - // Explain in great detail which events we want to handle. - // Why can't we just have one handler? - const EventTypeSpec quits[] = { - { kEventClassApplication, kEventAppQuit } - }; - const EventTypeSpec cmds[] = { - { kEventClassWindow, kEventWindowClosed }, - { kEventClassWindow, kEventWindowBoundsChanged }, - { kEventClassWindow, kEventWindowDrawContent }, - { kEventClassCommand, kEventCommandProcess }, - { kEventClassWindow, kEventWindowActivated }, - { kEventClassWindow, kEventWindowDeactivated }, - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowExpanded }, - }; - const EventTypeSpec events[] = { - { kEventClassApplication, kEventAppShown }, - { kEventClassKeyboard, kEventRawKeyDown }, - { kEventClassKeyboard, kEventRawKeyModifiersChanged }, - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - { kEventClassMouse, kEventMouseWheelMoved }, - { 'P9PE', P9PEventLabelUpdate} - }; - - InstallApplicationEventHandler( - NewEventHandlerUPP(quithandler), - nelem(quits), quits, nil, nil); - - InstallApplicationEventHandler( - NewEventHandlerUPP(eventhandler), - nelem(events), events, nil, nil); - - InstallWindowEventHandler(osx.window, - NewEventHandlerUPP(cmdhandler), - nelem(cmds), cmds, osx.window, nil); - - // Finally, put the window on the screen. - ShowWindow(osx.window); - ShowMenuBar(); - eresized(0); - SelectWindow(osx.window); - - if(multitouch) - InitMultiTouch(); - - // CoreGraphics pins mouse events to the destination point of a - // CGWarpMouseCursorPosition (see setmouse) for an interval of time - // following the move. Disable this by setting the interval to zero - // seconds. - CGSetLocalEventsSuppressionInterval(0.0); - - InitCursor(); -} - -static Rendez scr; -static QLock slock; - -void -screeninit(void) -{ - scr.l = &slock; - qlock(scr.l); - proccreate(screenproc, nil, 256*1024); - while(osx.window == nil) - rsleep(&scr); - qunlock(scr.l); -} - -static void -screenproc(void *v) -{ - qlock(scr.l); - _screeninit(); - rwakeup(&scr); - qunlock(scr.l); - RunApplicationEventLoop(); -} - -static OSStatus kbdevent(EventRef); -static OSStatus mouseevent(EventRef); - -static OSStatus -cmdhandler(EventHandlerCallRef next, EventRef event, void *arg) -{ - return eventhandler(next, event, arg); -} - -static OSStatus -quithandler(EventHandlerCallRef next, EventRef event, void *arg) -{ - exit(0); - return 0; -} - -static OSStatus -eventhandler(EventHandlerCallRef next, EventRef event, void *arg) -{ - OSStatus result; - - result = CallNextEventHandler(next, event); - - switch(GetEventClass(event)){ - - case 'P9PE': - if(GetEventKind(event) == P9PEventLabelUpdate) { - qlock(&osx.labellock); - setlabel(osx.label); - qunlock(&osx.labellock); - return noErr; - } else - return eventNotHandledErr; - - case kEventClassApplication:; - Rectangle r = Rect(0, 0, Dx(osx.screenr), Dy(osx.screenr)); - _flushmemscreen(r); - return eventNotHandledErr; - - case kEventClassKeyboard: - return kbdevent(event); - - case kEventClassMouse: - return mouseevent(event); - - case kEventClassCommand:; - HICommand cmd; - GetEventParameter(event, kEventParamDirectObject, - typeHICommand, nil, sizeof cmd, nil, &cmd); - switch(cmd.commandID){ - case kHICommandQuit: - exit(0); - - case CmdFullScreen: - fullscreen(1); - break; - - default: - return eventNotHandledErr; - } - break; - - case kEventClassWindow: - switch(GetEventKind(event)){ - case kEventWindowClosed: - exit(0); - - case kEventWindowBoundsChanged:; - // We see kEventWindowDrawContent - // if we grow a window but not if we shrink it. - UInt32 flags; - GetEventParameter(event, kEventParamAttributes, - typeUInt32, 0, sizeof flags, 0, &flags); - int new = (flags & kWindowBoundsChangeSizeChanged) != 0; - eresized(new); - break; - - case kEventWindowDrawContent: - // Tried using just flushmemimage here, but - // it causes an odd artifact in which making a window - // bigger in both width and height can then only draw - // on the new border: it's like the old window is stuck - // floating on top. Doing a full "get a new window" - // seems to solve the problem. - eresized(1); - break; - - case kEventWindowActivated: - if(!osx.collapsed) - activated(1); - return eventNotHandledErr; - - case kEventWindowDeactivated: - activated(0); - return eventNotHandledErr; - - case kEventWindowCollapsed: - osx.collapsed = 1; - activated(0); - return eventNotHandledErr; - - case kEventWindowExpanded: - osx.collapsed = 0; - activated(1); - return eventNotHandledErr; - - default: - return eventNotHandledErr; - } - break; - } - - return result; -} - -static ulong -msec(void) -{ - return nsec()/1000000; -} - -static OSStatus -mouseevent(EventRef event) -{ - int wheel; - OSXPoint op; - - GetEventParameter(event, kEventParamMouseLocation, - typeQDPoint, 0, sizeof op, 0, &op); - - osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min); - wheel = 0; - - switch(GetEventKind(event)){ - case kEventMouseWheelMoved:; - SInt32 delta; - GetEventParameter(event, kEventParamMouseWheelDelta, - typeSInt32, 0, sizeof delta, 0, &delta); - - // if I have any active touches in my region, I need to ignore the wheel motion. - //int i; - //for(i = 0; i < kNTracks; ++i) { - // if(tracks[i].id != -1 && tracks[i].pos.y > kButtonLimit) break; - //} - //if(i == kNTracks) { // No active touches, go ahead and scroll. - if(delta > 0) - wheel = 8; - else - wheel = 16; - //} - break; - - case kEventMouseDown: - case kEventMouseUp:; - UInt32 but, mod; - GetEventParameter(event, kEventParamMouseChord, - typeUInt32, 0, sizeof but, 0, &but); - GetEventParameter(event, kEventParamKeyModifiers, - typeUInt32, 0, sizeof mod, 0, &mod); - - // OS X swaps button 2 and 3 - but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1); - but = (but & ~((1<<10)-1)) | mouseswap(but & ((1<<10)-1)); - if(osx.touched) { - // in multitouch we use the clicks down to enable our - // virtual buttons. - if(but & 0x7) { - if(but>>29) - but = but >> 29; - } else - but = 0; - osx.touched = 0; - } - - // Apply keyboard modifiers and pretend it was a real mouse button. - // (Modifiers typed while holding the button go into kbuttons, - // but this one does not.) - if(but == 1){ - if(mod & optionKey) { - // Take the ALT away from the keyboard handler. - if(osx.kalting) { - osx.kalting = 0; - keystroke(Kalt); - } - but = 2; - } - else if(mod & cmdKey) - but = 4; - } - osx.buttons = but; - break; - - case kEventMouseMoved: - case kEventMouseDragged: - break; - - default: - return eventNotHandledErr; - } - - mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec()); - return noErr; -} - -static int keycvt[] = -{ - [QZ_IBOOK_ENTER] '\n', - [QZ_RETURN] '\n', - [QZ_ESCAPE] 27, - [QZ_BACKSPACE] '\b', - [QZ_LALT] Kalt, - [QZ_LCTRL] Kctl, - [QZ_LSHIFT] Kshift, - [QZ_F1] KF+1, - [QZ_F2] KF+2, - [QZ_F3] KF+3, - [QZ_F4] KF+4, - [QZ_F5] KF+5, - [QZ_F6] KF+6, - [QZ_F7] KF+7, - [QZ_F8] KF+8, - [QZ_F9] KF+9, - [QZ_F10] KF+10, - [QZ_F11] KF+11, - [QZ_F12] KF+12, - [QZ_INSERT] Kins, - [QZ_DELETE] 0x7F, - [QZ_HOME] Khome, - [QZ_END] Kend, - [QZ_KP_PLUS] '+', - [QZ_KP_MINUS] '-', - [QZ_TAB] '\t', - [QZ_PAGEUP] Kpgup, - [QZ_PAGEDOWN] Kpgdown, - [QZ_UP] Kup, - [QZ_DOWN] Kdown, - [QZ_LEFT] Kleft, - [QZ_RIGHT] Kright, - [QZ_KP_MULTIPLY] '*', - [QZ_KP_DIVIDE] '/', - [QZ_KP_ENTER] '\n', - [QZ_KP_PERIOD] '.', - [QZ_KP0] '0', - [QZ_KP1] '1', - [QZ_KP2] '2', - [QZ_KP3] '3', - [QZ_KP4] '4', - [QZ_KP5] '5', - [QZ_KP6] '6', - [QZ_KP7] '7', - [QZ_KP8] '8', - [QZ_KP9] '9', -}; - -static OSStatus -kbdevent(EventRef event) -{ - char ch; - UInt32 code; - UInt32 mod; - int k; - - GetEventParameter(event, kEventParamKeyMacCharCodes, - typeChar, nil, sizeof ch, nil, &ch); - GetEventParameter(event, kEventParamKeyCode, - typeUInt32, nil, sizeof code, nil, &code); - GetEventParameter(event, kEventParamKeyModifiers, - typeUInt32, nil, sizeof mod, nil, &mod); - - switch(GetEventKind(event)){ - case kEventRawKeyDown: - case kEventRawKeyRepeat: - osx.kalting = 0; - if(mod == cmdKey){ - if(ch == 'F' || ch == 'f'){ - if(osx.isfullscreen && msec() - osx.fullscreentime > 500) - fullscreen(0); - return noErr; - } - - // Pass most Cmd keys through as Kcmd + ch. - // OS X interprets a few no matter what we do, - // so it is useless to pass them through as keystrokes too. - switch(ch) { - case 'm': // minimize window - case 'h': // hide window - case 'H': // hide others - case 'q': // quit - return eventNotHandledErr; - } - if(' ' <= ch && ch <= '~') { - keystroke(Kcmd + ch); - return noErr; - } - return eventNotHandledErr; - } - k = ch; - if(code < nelem(keycvt) && keycvt[code]) - k = keycvt[code]; - if(k == 0) - return noErr; - if(k > 0) - keystroke(k); - else{ - UniChar uc; - OSStatus s; - - s = GetEventParameter(event, kEventParamKeyUnicodes, - typeUnicodeText, nil, sizeof uc, nil, &uc); - if(s == noErr) - keystroke(uc); - } - break; - - case kEventRawKeyModifiersChanged: - if(!osx.buttons && !osx.kbuttons){ - if(mod == optionKey) { - osx.kalting = 1; - keystroke(Kalt); - } - break; - } - - // If the mouse button is being held down, treat - // changes in the keyboard modifiers as changes - // in the mouse buttons. - osx.kbuttons = 0; - if(mod & optionKey) - osx.kbuttons |= 2; - if(mod & cmdKey) - osx.kbuttons |= 4; - mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec()); - break; - } - return noErr; -} - -static void -eresized(int new) -{ - Memimage *m; - OSXRect or; - ulong chan; - Rectangle r; - int bpl; - CGDataProviderRef provider; - CGImageRef image; - CGColorSpaceRef cspace; - - GetWindowBounds(osx.window, kWindowContentRgn, &or); - r = Rect(or.left, or.top, or.right, or.bottom); - if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr) && !new){ - // No need to make new image. - osx.screenr = r; - return; - } - - chan = XBGR32; - m = allocmemimage(Rect(0, 0, Dx(r), Dy(r)), chan); - if(m == nil) - panic("allocmemimage: %r"); - if(m->data == nil) - panic("m->data == nil"); - bpl = bytesperline(r, 32); - provider = CGDataProviderCreateWithData(0, - m->data->bdata, Dy(r)*bpl, 0); - //cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - cspace = CGColorSpaceCreateDeviceRGB(); - image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl, - cspace, - kCGImageAlphaNoneSkipLast, - provider, 0, 0, kCGRenderingIntentDefault); - CGColorSpaceRelease(cspace); - CGDataProviderRelease(provider); // CGImageCreate did incref - - mouserect = m->r; - if(new){ - mouseresized = 1; - mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec()); - } -// termreplacescreenimage(m); - _drawreplacescreenimage(m); // frees old osx.screenimage if any - if(osx.image) - CGImageRelease(osx.image); - osx.image = image; - osx.screenimage = m; - osx.screenr = r; - - if(new){ - qlock(&osx.flushlock); - QDEndCGContext(GetWindowPort(osx.window), &osx.windowctx); - osx.windowctx = nil; - qunlock(&osx.flushlock); - } -} - -void -flushproc(void *v) -{ - for(;;){ - if(osx.needflush && osx.windowctx && canqlock(&osx.flushlock)){ - if(osx.windowctx){ - CGContextFlush(osx.windowctx); - osx.needflush = 0; - } - qunlock(&osx.flushlock); - } - usleep(33333); - } -} - -void -_flushmemscreen(Rectangle r) -{ - CGRect cgr; - CGImageRef subimg; - - qlock(&osx.flushlock); - if(osx.windowctx == nil){ - QDBeginCGContext(GetWindowPort(osx.window), &osx.windowctx); - if(!osx.flushing) { - proccreate(flushproc, nil, 256*1024); - osx.flushing = 1; - } - } - - cgr.origin.x = r.min.x; - cgr.origin.y = r.min.y; - cgr.size.width = Dx(r); - cgr.size.height = Dy(r); - subimg = CGImageCreateWithImageInRect(osx.image, cgr); - cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense? - CGContextDrawImage(osx.windowctx, cgr, subimg); - osx.needflush = 1; - qunlock(&osx.flushlock); - CGImageRelease(subimg); -} - -void -activated(int active) -{ -#ifdef MULTITOUCH - int i; - if(active) { - for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices - MTDeviceStart([osx.devicelist objectAtIndex:i], 0); //start sending events - } - } else { - osx.xy.x = -10000; - for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices - MTDeviceStop([osx.devicelist objectAtIndex:i]); //stop sending events - } - for(i = 0; i<kNTracks; ++i) { - tracks[i].id = -1; - } - } -#endif - osx.active = active; -} - -void -fullscreen(int wascmd) -{ - static OSXRect oldrect; - GDHandle device; - OSXRect dr; - - if(!wascmd) - return; - - if(!osx.isfullscreen){ - GetWindowGreatestAreaDevice(osx.window, - kWindowTitleBarRgn, &device, nil); - dr = (*device)->gdRect; - if(dr.top == 0 && dr.left == 0) - HideMenuBar(); - GetWindowBounds(osx.window, kWindowContentRgn, &oldrect); - ChangeWindowAttributes(osx.window, - kWindowNoTitleBarAttribute, - kWindowResizableAttribute); - MoveWindow(osx.window, 0, 0, 1); - MoveWindow(osx.window, dr.left, dr.top, 0); - SizeWindow(osx.window, - dr.right - dr.left, - dr.bottom - dr.top, 0); - osx.isfullscreen = 1; - }else{ - ShowMenuBar(); - ChangeWindowAttributes(osx.window, - kWindowResizableAttribute, - kWindowNoTitleBarAttribute); - SizeWindow(osx.window, - oldrect.right - oldrect.left, - oldrect.bottom - oldrect.top, 0); - MoveWindow(osx.window, oldrect.left, oldrect.top, 0); - osx.isfullscreen = 0; - } - eresized(1); -} - -void -setmouse(Point p) -{ - CGPoint cgp; - - cgp.x = p.x + osx.screenr.min.x; - cgp.y = p.y + osx.screenr.min.y; - CGWarpMouseCursorPosition(cgp); - osx.xy = p; -} - -void -setcursor(Cursor *c) -{ - OSXCursor oc; - int i; - - if(c == nil){ - InitCursor(); - return; - } - - // SetCursor is deprecated, but what replaces it? - for(i=0; i<16; i++){ - oc.data[i] = ((ushort*)c->set)[i]; - oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i]; - } - oc.hotSpot.h = - c->offset.x; - oc.hotSpot.v = - c->offset.y; - SetCursor(&oc); -} - -void -getcolor(ulong i, ulong *r, ulong *g, ulong *b) -{ - ulong v; - - v = 0; - *r = (v>>16)&0xFF; - *g = (v>>8)&0xFF; - *b = v&0xFF; -} - -int -setcolor(ulong i, ulong r, ulong g, ulong b) -{ - /* no-op */ - return 0; -} - - -int -hwdraw(Memdrawparam *p) -{ - return 0; -} - -struct { - QLock lk; - char buf[SnarfSize]; - Rune rbuf[SnarfSize]; - PasteboardRef apple; -} clip; - -char* -getsnarf(void) -{ - char *s; - CFArrayRef flavors; - CFDataRef data; - CFIndex nflavor, ndata, j; - CFStringRef type; - ItemCount nitem; - PasteboardItemID id; - PasteboardSyncFlags flags; - UInt32 i; - u16int *u; - Fmt fmt; - Rune r; - -/* fprint(2, "applegetsnarf\n"); */ - qlock(&clip.lk); - clip.apple = osx.snarf; - if(clip.apple == nil){ - if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ - fprint(2, "apple pasteboard create failed\n"); - qunlock(&clip.lk); - return nil; - } - } - flags = PasteboardSynchronize(clip.apple); - if(flags&kPasteboardClientIsOwner){ - s = strdup(clip.buf); - qunlock(&clip.lk); - return s; - } - if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ - fprint(2, "apple pasteboard get item count failed\n"); - qunlock(&clip.lk); - return nil; - } - for(i=1; i<=nitem; i++){ - if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) - continue; - if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) - continue; - nflavor = CFArrayGetCount(flavors); - for(j=0; j<nflavor; j++){ - type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); - if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) - continue; - if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) - continue; - qunlock(&clip.lk); - ndata = CFDataGetLength(data)/2; - u = (u16int*)CFDataGetBytePtr(data); - fmtstrinit(&fmt); - // decode utf-16. what was apple thinking? - for(i=0; i<ndata; i++) { - r = u[i]; - if(0xd800 <= r && r < 0xdc00 && i+1 < ndata && 0xdc00 <= u[i+1] && u[i+1] < 0xe000) { - r = (((r - 0xd800)<<10) | (u[i+1] - 0xdc00)) + 0x10000; - i++; - } - else if(0xd800 <= r && r < 0xe000) - r = Runeerror; - if(r == '\r') - r = '\n'; - fmtrune(&fmt, r); - } - CFRelease(flavors); - CFRelease(data); - return fmtstrflush(&fmt); - } - CFRelease(flavors); - } - qunlock(&clip.lk); - return nil; -} - -void -putsnarf(char *s) -{ - CFDataRef cfdata; - PasteboardSyncFlags flags; - u16int *u, *p; - Rune r; - int i; - -/* fprint(2, "appleputsnarf\n"); */ - - if(strlen(s) >= SnarfSize) - return; - qlock(&clip.lk); - strcpy(clip.buf, s); - runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); - clip.apple = osx.snarf; - if(PasteboardClear(clip.apple) != noErr){ - fprint(2, "apple pasteboard clear failed\n"); - qunlock(&clip.lk); - return; - } - flags = PasteboardSynchronize(clip.apple); - if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ - fprint(2, "apple pasteboard cannot assert ownership\n"); - qunlock(&clip.lk); - return; - } - u = malloc(runestrlen(clip.rbuf)*4); - p = u; - for(i=0; clip.rbuf[i]; i++) { - r = clip.rbuf[i]; - // convert to utf-16 - if(0xd800 <= r && r < 0xe000) - r = Runeerror; - if(r >= 0x10000) { - r -= 0x10000; - *p++ = 0xd800 + (r>>10); - *p++ = 0xdc00 + (r & ((1<<10)-1)); - } else - *p++ = r; - } - cfdata = CFDataCreate(kCFAllocatorDefault, - (uchar*)u, (p-u)*2); - free(u); - if(cfdata == nil){ - fprint(2, "apple pasteboard cfdatacreate failed\n"); - qunlock(&clip.lk); - return; - } - if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, - CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ - fprint(2, "apple pasteboard putitem failed\n"); - CFRelease(cfdata); - qunlock(&clip.lk); - return; - } - CFRelease(cfdata); - qunlock(&clip.lk); -} - -void -setlabel(char *label) -{ - CFStringRef cs; - - cs = CFStringCreateWithBytes(nil, (uchar*)label, strlen(label), kCFStringEncodingUTF8, false); - SetWindowTitleWithCFString(osx.window, cs); - CFRelease(cs); -} - -void -kicklabel(char *label) -{ - char *p; - EventRef e; - - p = strdup(label); - if(p == nil) - return; - qlock(&osx.labellock); - free(osx.label); - osx.label = p; - qunlock(&osx.labellock); - - CreateEvent(nil, 'P9PE', P9PEventLabelUpdate, 0, kEventAttributeUserEvent, &e); - PostEventToQueue(GetMainEventQueue(), e, kEventPriorityStandard); - -} - -static void -seticon(void) -{ - CGImageRef im; - CGDataProviderRef d; - - d = CGDataProviderCreateWithData(nil, glenda_png, sizeof glenda_png, nil); - im = CGImageCreateWithPNGDataProvider(d, nil, true, kCGRenderingIntentDefault); - if(im) - SetApplicationDockTileImage(im); - CGImageRelease(im); - CGDataProviderRelease(d); -} - diff --git a/src/cmd/devdraw/osx-screen.h b/src/cmd/devdraw/osx-screen.h deleted file mode 100644 index f50d8dfe..00000000 --- a/src/cmd/devdraw/osx-screen.h +++ /dev/null @@ -1,18 +0,0 @@ -void zlock(void); -void zunlock(void); - -#define setcursor dsetcursor - -Memimage *attachscreen(char*, char*); -void setmouse(Point); -void setcursor(Cursor*); -void setlabel(char*); -char* getsnarf(void); -void putsnarf(char*); - -void mousetrack(int, int, int, int); -void keystroke(int); -void kicklabel(char*); - -extern Rectangle mouserect; -extern int mouseresized; diff --git a/src/cmd/devdraw/osx-srv.c b/src/cmd/devdraw/osx-srv.c deleted file mode 100644 index 6cbb5235..00000000 --- a/src/cmd/devdraw/osx-srv.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Window system protocol server. - */ - -#include <u.h> -#include <errno.h> -#include <sys/select.h> -#include <libc.h> -#include <thread.h> -#include <draw.h> -#include <memdraw.h> -#include <memlayer.h> -#include <keyboard.h> -#include <mouse.h> -#include <cursor.h> -#include <drawfcall.h> -#include "osx-screen.h" -#include "devdraw.h" - -#undef time - -#define MouseMask (\ - ButtonPressMask|\ - ButtonReleaseMask|\ - PointerMotionMask|\ - Button1MotionMask|\ - Button2MotionMask|\ - Button3MotionMask) - -#define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask - -typedef struct Kbdbuf Kbdbuf; -typedef struct Mousebuf Mousebuf; -typedef struct Fdbuf Fdbuf; -typedef struct Tagbuf Tagbuf; - -struct Kbdbuf -{ - Rune r[32]; - int ri; - int wi; - int stall; -}; - -struct Mousebuf -{ - Mouse m[32]; - Mouse last; - int ri; - int wi; - int stall; -}; - -struct Tagbuf -{ - int t[32]; - int ri; - int wi; -}; - -Kbdbuf kbd; -Mousebuf mouse; -Tagbuf kbdtags; -Tagbuf mousetags; - -void fdslide(Fdbuf*); -void runmsg(Wsysmsg*); -void replymsg(Wsysmsg*); -void matchkbd(void); -void matchmouse(void); -int fdnoblock(int); -Rectangle mouserect; -int mouseresized; - - -QLock lk; -void -zlock(void) -{ - qlock(&lk); -} - -void -zunlock(void) -{ - qunlock(&lk); -} - -int chatty; -int drawsleep; -int trace; -int multitouch = 1; - -void -usage(void) -{ - fprint(2, "usage: devdraw (don't run directly)\n"); - threadexitsall("usage"); -} - -void -bell(void *v, char *msg) -{ - if(strcmp(msg, "alarm") == 0) - drawsleep = drawsleep ? 0 : 1000; - noted(NCONT); -} - -void -threadmain(int argc, char **argv) -{ - uchar buf[4], *mbuf; - int nmbuf, n, nn; - Wsysmsg m; - - /* - * 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); - -//trace = 1; - fmtinstall('W', drawfcallfmt); - - ARGBEGIN{ - case 'D': - chatty++; - break; - case 'M': - multitouch = 0; - break; - default: - usage(); - }ARGEND - - /* - * Ignore arguments. They're only for good ps -a listings. - */ - - notify(bell); - - 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, "<- %W\n", &m); - runmsg(&m); - } - threadexitsall(0); -} - -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); - else - setcursor(&m->cursor); - 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: - 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); - } - break; - - case Twrdraw: - if(_drawmsgwrite(m->data, m->count) < 0) - replyerror(m); - else - replymsg(m); - break; - - case Ttop: - // _xtopwindow(); - replymsg(m); - break; - - case Tresize: - // _xresizewindow(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, "-> %W\n", 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; - /* - if(m.resized) - fprint(2, "sending resize\n"); - */ - mouseresized = 0; - mouse.ri++; - if(mouse.ri == nelem(mouse.m)) - mouse.ri = 0; - replymsg(&m); - } -} - -void -mousetrack(int x, int y, int b, int 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(); -} - -void -keystroke(int c) -{ - static Rune k[10]; - static int alting, nk; - int i; - - if(c == Kalt){ - alting = !alting; - 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<nk; i++) - kputc(k[i]); - nk = 0; - return; - } - // need more input - return; -} diff --git a/src/cmd/devdraw/x11-init.c b/src/cmd/devdraw/x11-init.c index 5363fb74..f09963dc 100644 --- a/src/cmd/devdraw/x11-init.c +++ b/src/cmd/devdraw/x11-init.c @@ -208,9 +208,9 @@ _xattach(char *label, char *winsize) * Parse the various X resources. Thanks to Peter Canning. */ char *screen_resources, *display_resources, *geom, - *geomrestype, *home, *file; + *geomrestype, *home, *file, *dpitype; XrmDatabase database; - XrmValue geomres; + XrmValue geomres, dpires; database = XrmGetDatabase(_x.display); screen_resources = XScreenResourceString(xscreen); @@ -230,6 +230,11 @@ _xattach(char *label, char *winsize) }else XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False); + if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) { + if (dpires.addr) { + displaydpi=atoi(dpires.addr); + } + } geom = smprint("%s.geometry", label); if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres)) mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height); diff --git a/src/cmd/draw/tweak.c b/src/cmd/draw/tweak.c index 9d7cd601..89fdef32 100644 --- a/src/cmd/draw/tweak.c +++ b/src/cmd/draw/tweak.c @@ -30,7 +30,7 @@ enum Up = 1, Down = 0, Mag = 4, - Maxmag = 10 + Maxmag = 20 }; enum @@ -161,7 +161,7 @@ Image *values[256]; Image *greyvalues[256]; uchar data[8192]; -Thing* tget(char*); +Thing* tget(char*, int); void mesg(char*, ...); void drawthing(Thing*, int); void xselect(void); @@ -184,6 +184,7 @@ main(volatile int argc, char **volatile argv) volatile int i; Event e; Thing *t; + Thing *nt; ARGBEGIN{ case 'W': @@ -209,9 +210,14 @@ main(volatile int argc, char **volatile argv) setjmp(err); for(; i<argc; i++){ file = argv[i]; - t = tget(argv[i]); - if(t) + t = tget(argv[i], 1); + if(t) { + nt = t->next; + t->next = 0; drawthing(t, 1); + if(nt) + drawthing(nt, 1); + } flushimage(display, 1); } file = 0; @@ -382,6 +388,8 @@ stext(Thing *t, char *l0, char *l1) }else if(t->s) sprint(l1, "offset(hex): %ux n:%d height:%d ascent:%d", t->off, t->s->n, t->s->height, t->s->ascent); + else if(t->face == CURSOR) + sprint(l0+strlen(l0), " cursor:%d", Dx(t->b->r)); } void @@ -569,7 +577,7 @@ tohex(int c) } Thing* -tget(char *file) +tget(char *file, int extra) { int i, j, fd, face, x, y, c, chan; Image *b; @@ -577,8 +585,9 @@ tget(char *file) Thing *t; Dir *volatile d; jmp_buf oerr; - uchar buf[256]; + uchar buf[300]; char *data; + Rectangle r; buf[0] = '\0'; errstr((char*)buf, sizeof buf); /* flush pending error message */ @@ -628,17 +637,15 @@ tget(char *file) close(fd); goto Err; } - b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill); - if(b == 0){ - mesg("image alloc failed file %s: %r", file); - free(data); - close(fd); - goto Err; - } i = 0; - for(x=0;x<64; ){ - if((c=data[i]) == '\0') - goto ill; + for(x=0;; ){ + if((c=data[i]) == '\0' || x > 256) { + if(x == 64 || x == 256) + goto HaveCursor; + mesg("ill-formed cursor file %s", file); + close(fd); + goto Err; + } if(c=='0' && data[i+1] == 'x'){ i += 2; continue; @@ -650,7 +657,19 @@ tget(char *file) } i++; } - loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf); + HaveCursor: + if(x == 64) + r = Rect(0, 0, 16, 32); + else + r = Rect(0, 0, 32, 64); + b = allocimage(display, r, GREY1, 0, DNofill); + if(b == 0){ + mesg("image alloc failed file %s: %r", file); + free(data); + close(fd); + goto Err; + } + loadimage(b, r, buf, sizeof buf); free(data); }else if(memcmp(buf, "0x", 2)==0){ /* @@ -752,7 +771,7 @@ tget(char *file) s = readsubfonti(display, file, fd, b, 0); } close(fd); - t = malloc(sizeof(Thing)); + t = mallocz(sizeof(Thing), 1); if(t == 0){ nomem: mesg("malloc failed: %r"); @@ -775,6 +794,40 @@ tget(char *file) t->c = -1; t->mag = 1; t->off = 0; + if(face == CURSOR && extra && Dx(t->b->r) == 16) { + // Make 32x32 cursor as second image. + Thing *nt; + Cursor c; + Cursor2 c2; + + nt = mallocz(sizeof(Thing), 1); + if(nt == 0) + goto nomem; + nt->name = strdup(""); + if(nt->name == 0) { + free(nt); + goto nomem; + } + b = allocimage(display, Rect(0, 0, 32, 64), GREY1, 0, DNofill); + if(b == nil) { + free(nt->name); + free(nt); + goto nomem; + } + memmove(c.clr, buf, 64); + scalecursor(&c2, &c); + memmove(buf, c2.clr, 256); + loadimage(b, b->r, buf, sizeof buf); + t->next = nt; + nt->b = b; + nt->s = 0; + nt->face = CURSOR; + nt->mod = 0; + nt->parent = 0; + nt->c = -1; + nt->mag = 1; + nt->off = 0; + } memmove(err, oerr, sizeof err); return t; } @@ -1637,18 +1690,13 @@ twrite(Thing *t) Bprint(&buf, "%.2x", data[i+j]); Bprint(&buf, ", "); } - if(t->face == CURSOR){ - switch(y){ - case 3: case 7: case 11: case 19: case 23: case 27: - Bprint(&buf, "\n "); - break; - case 15: + if(t->face == CURSOR) { + if(y == Dy(r)/2-1) Bprint(&buf, "},\n{"); - break; - case 31: + else if(y == Dy(r)-1) Bprint(&buf, "}\n"); - break; - } + else + Bprint(&buf, "\n\t"); }else Bprint(&buf, "\n"); } @@ -1759,7 +1807,7 @@ tread(Thing *t) if(t->parent) t = t->parent; - new = tget(t->name); + new = tget(t->name, 0); if(new == 0) return; nclosed = 0; @@ -2025,7 +2073,7 @@ menu(void) switch(sel){ case Mopen: if(type(buf, "file")){ - t = tget(buf); + t = tget(buf, 0); if(t) drawthing(t, 1); } diff --git a/src/cmd/file.c b/src/cmd/file.c index 0afb9ba3..32d580cd 100644 --- a/src/cmd/file.c +++ b/src/cmd/file.c @@ -519,10 +519,13 @@ Filemagic long0tab[] = { 0x32636170, 0xFFFF00FF, "pac4 audio file\n", OCTET, 0xBA010000, 0xFFFFFFFF, "mpeg system stream\n", OCTET, 0x30800CC0, 0xFFFFFFFF, "inferno .dis executable\n", OCTET, - 0x04034B50, 0xFFFFFFFF, "zip archive\n", "application/zip", + 0x04034B50, 0xFFFFFFFF, "zip archive\n", "application/zip\n", 070707, 0xFFFF, "cpio archive\n", OCTET, - 0x2F7, 0xFFFF, "tex dvi\n", "application/dvi", - 0xfffa0000, 0xfffe0000, "mp3 audio", "audio/mpeg", + 0x2F7, 0xFFFF, "tex dvi\n", "application/dvi\n", + 0xfffa0000, 0xfffe0000, "mp3 audio\n", "audio/mpeg\n", + 0xcafebabe, 0xFFFFFFFF, "Mach-O fat executable\n", "application/x-mach-binary\n", + 0xfeedface, 0xFFFFFFFE, "Mach-O executable\n", "application/x-mach-binary\n", + 0xbebafeca, 0xFFFFFFFF, "Java class\n", "application/x-java-applet\n", }; int diff --git a/src/cmd/fontsrv/a.h b/src/cmd/fontsrv/a.h index 3344d28e..164b1bd6 100644 --- a/src/cmd/fontsrv/a.h +++ b/src/cmd/fontsrv/a.h @@ -30,6 +30,5 @@ Memsubfont* mksubfont(XFont*, char*, int, int, int, int); extern XFont *xfont; extern int nxfont; void *emalloc9p(ulong); -extern Memsubfont *defont; void drawpjw(Memimage*, Fontchar*, int, int, int, int); diff --git a/src/cmd/fontsrv/main.c b/src/cmd/fontsrv/main.c index b00802d8..37f0da32 100644 --- a/src/cmd/fontsrv/main.c +++ b/src/cmd/fontsrv/main.c @@ -13,8 +13,6 @@ #include "a.h" -Memsubfont *defont; - void usage(void) { @@ -526,7 +524,6 @@ main(int argc, char **argv) fmtinstall('R', Rfmt); fmtinstall('P', Pfmt); memimageinit(); - defont = getmemdefont(); loadfonts(); qsort(xfont, nxfont, sizeof xfont[0], fontcmp); diff --git a/src/cmd/fontsrv/mkfile b/src/cmd/fontsrv/mkfile index eed0355a..8ad99dbc 100644 --- a/src/cmd/fontsrv/mkfile +++ b/src/cmd/fontsrv/mkfile @@ -1,4 +1,5 @@ <$PLAN9/src/mkhdr +<|osxvers <|sh ../devdraw/mkwsysrules.sh <|sh freetyperules.sh $WSYSTYPE $X11H diff --git a/src/cmd/fontsrv/osx.c b/src/cmd/fontsrv/osx.c index b28ee252..c01ae6ce 100644 --- a/src/cmd/fontsrv/osx.c +++ b/src/cmd/fontsrv/osx.c @@ -277,6 +277,9 @@ mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias) 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++) { diff --git a/src/cmd/fontsrv/x11.c b/src/cmd/fontsrv/x11.c index a097ca4d..4137f9ca 100644 --- a/src/cmd/fontsrv/x11.c +++ b/src/cmd/fontsrv/x11.c @@ -40,7 +40,7 @@ loadfonts(void) int index; FcPattern *pat = sysfonts->fonts[i]; - if(FcPatternGetString(pat, FC_FULLNAME, 0, &fullname) != FcResultMatch || + if(FcPatternGetString(pat, FC_POSTSCRIPT_NAME, 0, &fullname) != FcResultMatch || FcPatternGetString(pat, FC_FILE, 0, &fontfile) != FcResultMatch || FcPatternGetInteger(pat, FC_INDEX, 0, &index) != FcResultMatch) continue; @@ -66,21 +66,18 @@ load(XFont *f) return; e = FT_New_Face(lib, f->fontfile, f->index, &face); - if(e){ fprint(2, "load failed for %s (%s) index:%d\n", f->name, f->fontfile, f->index); return; } - if(!FT_IS_SCALABLE(face)) { fprint(2, "%s is a non scalable font, skipping\n", f->name); FT_Done_Face(face); - f->loaded = 1; + f->loaded = 1; return; } - f->unit = face->units_per_EM; - f->height = (int)((face->ascender - face->descender) * 1.2); + f->height = (int)((face->ascender - face->descender) * 1.35); f->originy = face->descender; // bbox.yMin (or descender) is negative, becase the baseline is y-coord 0 for(charcode=FT_Get_First_Char(face, &glyph_index); glyph_index != 0; @@ -96,7 +93,11 @@ load(XFont *f) f->nrange++; } } - + // libdraw expects U+0000 to be present + if(!f->range[0]) { + f->range[0] = 1; + f->nrange++; + } FT_Done_Face(face); f->loaded = 1; } @@ -108,14 +109,13 @@ mksubfont(XFont *xf, char *name, int lo, int hi, int size, int antialias) FT_Error e; Memimage *m, *mc, *m1; double pixel_size; - int x, y, y0; + int w, x, y, y0; int i; Fontchar *fc, *fc0; Memsubfont *sf; //Point rect_points[4]; e = FT_New_Face(lib, xf->fontfile, xf->index, &face); - if(e){ fprint(2, "load failed for %s (%s) index:%d\n", xf->name, xf->fontfile, xf->index); return nil; @@ -129,16 +129,16 @@ mksubfont(XFont *xf, char *name, int lo, int hi, int size, int antialias) } pixel_size = (dpi*size)/72.0; - x = (int)((face->max_advance_width) * pixel_size/xf->unit + 0.99999999); + w = x = (int)((face->max_advance_width) * pixel_size/xf->unit + 0.99999999); y = (int)((face->ascender - face->descender) * pixel_size/xf->unit + 0.99999999); y0 = (int)(-face->descender * pixel_size/xf->unit + 0.99999999); - m = allocmemimage(Rect(0, 0, x*(hi+1-lo), y), antialias ? GREY8 : GREY1); + m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), antialias ? GREY8 : GREY1); if(m == nil) { FT_Done_Face(face); return nil; } - mc = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); + mc = allocmemimage(Rect(0, 0, x+1, y+1), antialias ? GREY8 : GREY1); if(mc == nil) { freememimage(m); FT_Done_Face(face); @@ -165,41 +165,42 @@ mksubfont(XFont *xf, char *name, int lo, int hi, int size, int antialias) x = 0; for(i=lo; i<=hi; i++, fc++) { - int r; + int k, r; int advance; memfillcolor(mc, DBlack); - e = FT_Load_Char(face, i, FT_LOAD_RENDER|FT_LOAD_NO_HINTING|(antialias ? 0:FT_LOAD_TARGET_MONO)); - if(e){ - fprint(2, "FT_Load_Char failed for %d\n", i); - //mempoly(mc, rect_points, 4, Endsquare, Endsquare, 0, memopaque, ZP, S); - memimageline(mc, m->r.min, Pt(m->r.max.x, m->r.min.y), Endsquare, Endsquare, 0, memopaque, ZP, S); - memimageline(mc, m->r.min, Pt(m->r.min.x, m->r.max.y), Endsquare, Endsquare, 0, memopaque, ZP, S); - memimageline(mc, Pt(m->r.max.x, m->r.min.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); - memimageline(mc, Pt(m->r.min.x, m->r.max.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); - memimageline(mc, m->r.min, m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); - advance = Dx(m->r); - - memimagedraw(m, Rect(x, 0, x + advance, y), mc, ZP, memopaque, ZP, S); - } else { - FT_Bitmap *bitmap = &face->glyph->bitmap; - uchar *base = byteaddr(mc, mc->r.min); - advance = (face->glyph->advance.x+32) >> 6; - - for(r=0; r < bitmap->rows; r++) - memmove(base + r*mc->width*sizeof(u32int), bitmap->buffer + r*bitmap->pitch, bitmap->pitch); - - memimagedraw(m, Rect(x, 0, x + advance, y), mc, - Pt(-face->glyph->bitmap_left, -(y - y0 - face->glyph->bitmap_top)), - memopaque, ZP, S); - } - fc->x = x; fc->top = 0; - fc->bottom = y; - fc->left = 0; + fc->bottom = Dy(m->r); + e = 1; + k = FT_Get_Char_Index(face, i); + if(k != 0) { + e = FT_Load_Glyph(face, k, FT_LOAD_RENDER|FT_LOAD_NO_HINTING|(antialias ? 0:FT_LOAD_TARGET_MONO)); + } + if(e || face->glyph->advance.x <= 0) { + fc->width = 0; + fc->left = 0; + if(i == 0) { + drawpjw(m, fc, x, w, y, y - y0); + x += fc->width; + } + continue; + } + + FT_Bitmap *bitmap = &face->glyph->bitmap; + uchar *base = byteaddr(mc, mc->r.min); + advance = (face->glyph->advance.x+32) >> 6; + + for(r=0; r < bitmap->rows; r++) + memmove(base + r*mc->width*sizeof(u32int), bitmap->buffer + r*bitmap->pitch, bitmap->pitch); + + memimagedraw(m, Rect(x, 0, x + advance, y), mc, + Pt(-face->glyph->bitmap_left, -(y - y0 - face->glyph->bitmap_top)), + memopaque, ZP, S); + fc->width = advance; + fc->left = 0; x += advance; #ifdef DEBUG_FT_BITMAP @@ -229,6 +230,10 @@ mksubfont(XFont *xf, char *name, int lo, int hi, int size, int antialias) // 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 diff --git a/src/cmd/paint/eenter.c b/src/cmd/paint/eenter.c new file mode 100644 index 00000000..6645820c --- /dev/null +++ b/src/cmd/paint/eenter.c @@ -0,0 +1,258 @@ +/* +This code was taken from 9front repository (https://code.9front.org/hg/plan9front). +It is subject to license from 9front, below is a reproduction of the license. + +Copyright (c) 20XX 9front + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <keyboard.h> + +/* additional keyboard codes needed - defined here to avoid API change */ +enum { + Spec= 0xF800, + Knack= 0x15, + Ksoh= 0x01, + Kenq= 0x05, + Ketb= 0x17 +}; + +int +eenter(char *ask, char *buf, int len, Mouse *m) +{ + int done, down, tick, n, h, w, l, i; + Image *b, *save, *backcol, *bordcol; + Point p, o, t; + Rectangle r, sc; + Event ev; + Rune k; + + o = screen->r.min; + backcol = allocimagemix(display, DPurpleblue, DWhite); + bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); + if(backcol == nil || bordcol == nil) + return -1; + + while(ecankbd()) + ekbd(); + + if(m) o = m->xy; + + if(buf && len > 0) + n = strlen(buf); + else { + buf = nil; + len = 0; + n = 0; + } + + k = -1; + tick = n; + save = nil; + done = down = 0; + + p = stringsize(font, " "); + h = p.y; + w = p.x; + + b = screen; + sc = b->clipr; + replclipr(b, 0, b->r); + + while(!done){ + p = stringsize(font, buf ? buf : ""); + if(ask && ask[0]){ + if(buf) p.x += w; + p.x += stringwidth(font, ask); + } + r = rectaddpt(insetrect(Rpt(ZP, p), -4), o); + p.x = 0; + r = rectsubpt(r, p); + + p = ZP; + if(r.min.x < screen->r.min.x) + p.x = screen->r.min.x - r.min.x; + if(r.min.y < screen->r.min.y) + p.y = screen->r.min.y - r.min.y; + r = rectaddpt(r, p); + p = ZP; + if(r.max.x > screen->r.max.x) + p.x = r.max.x - screen->r.max.x; + if(r.max.y > screen->r.max.y) + p.y = r.max.y - screen->r.max.y; + r = rectsubpt(r, p); + + r = insetrect(r, -2); + if(save == nil){ + save = allocimage(display, r, b->chan, 0, DNofill); + if(save == nil){ + n = -1; + break; + } + draw(save, r, b, nil, r.min); + } + draw(b, r, backcol, nil, ZP); + border(b, r, 2, bordcol, ZP); + p = addpt(r.min, Pt(6, 6)); + if(ask && ask[0]){ + p = string(b, p, bordcol, ZP, font, ask); + if(buf) p.x += w; + } + if(buf){ + t = p; + p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, tick)); + draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP); + draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP); + draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP); + p = string(b, p, display->black, ZP, font, buf+tick); + } + flushimage(display, 1); + +nodraw: + i = Ekeyboard; + if(m != nil) + i |= Emouse; + + replclipr(b, 0, sc); + i = eread(i, &ev); + + /* screen might have been resized */ + if(b != screen || !eqrect(screen->clipr, sc)){ + freeimage(save); + save = nil; + } + b = screen; + sc = b->clipr; + replclipr(b, 0, b->r); + + switch(i){ + default: + done = 1; + n = -1; + break; + case Ekeyboard: + k = ev.kbdc; + if(buf == nil || k == Keof || k == '\n'){ + done = 1; + break; + } + if(k == Knack || k == Kesc){ + done = !n; + buf[n = tick = 0] = 0; + break; + } + if(k == Ksoh || k == Khome){ + tick = 0; + continue; + } + if(k == Kenq || k == Kend){ + tick = n; + continue; + } + if(k == Kright){ + if(tick < n) + tick += chartorune(&k, buf+tick); + continue; + } + if(k == Kleft){ + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+tick); + if(i+l >= tick){ + tick = i; + break; + } + } + continue; + } + if(k == Ketb){ + while(tick > 0){ + tick--; + if(tick == 0 || + strchr(" !\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", buf[tick-1])) + break; + } + buf[n = tick] = 0; + break; + } + if(k == Kbs){ + if(tick <= 0) + continue; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + if(i+l >= tick){ + memmove(buf+i, buf+i+l, n - (i+l)); + buf[n -= l] = 0; + tick -= l; + break; + } + } + break; + } + if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec) + continue; + if((len-n) <= (l = runelen(k))) + continue; + memmove(buf+tick+l, buf+tick, n - tick); + runetochar(buf+tick, &k); + buf[n += l] = 0; + tick += l; + break; + case Emouse: + *m = ev.mouse; + if(!ptinrect(m->xy, r)){ + down = 0; + goto nodraw; + } + if(m->buttons & 7){ + down = 1; + if(buf && m->xy.x >= (t.x - w)){ + down = 0; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + t.x += stringnwidth(font, buf+i, 1); + if(t.x > m->xy.x) + break; + } + tick = i; + } + continue; + } + done = down; + break; + } + if(save){ + draw(b, save->r, save, nil, save->r.min); + freeimage(save); + save = nil; + } + } + + replclipr(b, 0, sc); + + freeimage(backcol); + freeimage(bordcol); + flushimage(display, 1); + + return n; +} + diff --git a/src/cmd/paint/mkfile b/src/cmd/paint/mkfile new file mode 100644 index 00000000..272db4b4 --- /dev/null +++ b/src/cmd/paint/mkfile @@ -0,0 +1,11 @@ +<$PLAN9/src/mkhdr + +TARG=paint + +OFILES=\ + eenter.$O\ + paint.$O\ + +HFILES=paint.h\ + +<$PLAN9/src/mkone diff --git a/src/cmd/paint/paint.c b/src/cmd/paint/paint.c new file mode 100644 index 00000000..7dce3710 --- /dev/null +++ b/src/cmd/paint/paint.c @@ -0,0 +1,859 @@ +/* +This code was taken from 9front repository (https://code.9front.org/hg/plan9front). +It is subject to license from 9front, below is a reproduction of the license. + +Copyright (c) 20XX 9front + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <keyboard.h> + +/* additional libdraw function needed - defined here to avoid API change */ +extern int eenter(char*, char*, int, Mouse*); + +char *filename; +int zoom = 1; +int brush = 1; +Point spos; /* position on screen */ +Point cpos; /* position on canvas */ +Image *canvas; +Image *ink; +Image *back; +Image *pal[16]; /* palette */ +Rectangle palr; /* palette rect on screen */ +Rectangle penr; /* pen size rect on screen */ + +enum { + NBRUSH = 10+1, +}; + +int nundo = 0; +Image *undo[1024]; + +int c64[] = { /* c64 color palette */ + 0x000000, + 0xFFFFFF, + 0x68372B, + 0x70A4B2, + 0x6F3D86, + 0x588D43, + 0x352879, + 0xB8C76F, + 0x6F4F25, + 0x433900, + 0x9A6759, + 0x444444, + 0x6C6C6C, + 0x9AD284, + 0x6C5EB5, + 0x959595, +}; + +/* + * get bounding rectnagle for stroke from r.min to r.max with + * specified brush (size). + */ +static Rectangle +strokerect(Rectangle r, int brush) +{ + r = canonrect(r); + return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1); +} + +/* + * draw stroke from r.min to r.max to dst with color ink and + * brush (size). + */ +static void +strokedraw(Image *dst, Rectangle r, Image *ink, int brush) +{ + if(!eqpt(r.min, r.max)) + line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP); + fillellipse(dst, r.max, brush, brush, ink, ZP); +} + +/* + * A draw operation that touches only the area contained in bot but not in top. + * mp and sp get aligned with bot.min. + */ +static void +gendrawdiff(Image *dst, Rectangle bot, Rectangle top, + Image *src, Point sp, Image *mask, Point mp, int op) +{ + Rectangle r; + Point origin; + Point delta; + + if(Dx(bot)*Dy(bot) == 0) + return; + + /* no points in bot - top */ + if(rectinrect(bot, top)) + return; + + /* bot - top ≡ bot */ + if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ + gendrawop(dst, bot, src, sp, mask, mp, op); + return; + } + + origin = bot.min; + /* split bot into rectangles that don't intersect top */ + /* left side */ + if(bot.min.x < top.min.x){ + r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.x = top.min.x; + } + + /* right side */ + if(bot.max.x > top.max.x){ + r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.x = top.max.x; + } + + /* top */ + if(bot.min.y < top.min.y){ + r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.y = top.min.y; + } + + /* bottom */ + if(bot.max.y > top.max.y){ + r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.y = top.max.y; + } +} + +int +alphachan(ulong chan) +{ + for(; chan; chan >>= 8) + if(TYPE(chan) == CAlpha) + return 1; + return 0; +} + +void +zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f) +{ + Rectangle dr; + Image *t; + Point a; + int w; + + a = ZP; + if(r.min.x < d->r.min.x){ + sp.x += (d->r.min.x - r.min.x)/f; + a.x = (d->r.min.x - r.min.x)%f; + r.min.x = d->r.min.x; + } + if(r.min.y < d->r.min.y){ + sp.y += (d->r.min.y - r.min.y)/f; + a.y = (d->r.min.y - r.min.y)%f; + r.min.y = d->r.min.y; + } + rectclip(&r, d->r); + w = s->r.max.x - sp.x; + if(w > Dx(r)) + w = Dx(r); + dr = r; + dr.max.x = dr.min.x+w; + if(!alphachan(s->chan)) + b = nil; + if(f <= 1){ + if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD); + gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD); + return; + } + if((t = allocimage(display, dr, s->chan, 0, 0)) == nil) + return; + for(; dr.min.y < r.max.y; dr.min.y++){ + dr.max.y = dr.min.y+1; + draw(t, dr, s, nil, sp); + if(++a.y == f){ + a.y = 0; + sp.y++; + } + } + dr = r; + for(sp=dr.min; dr.min.x < r.max.x; sp.x++){ + dr.max.x = dr.min.x+1; + if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD); + gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD); + for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){ + dr.max.x = dr.min.x+1; + gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD); + } + a.x = 0; + } + freeimage(t); +} + +Point +s2c(Point p){ + p = subpt(p, spos); + if(p.x < 0) p.x -= zoom-1; + if(p.y < 0) p.y -= zoom-1; + return addpt(divpt(p, zoom), cpos); +} + +Point +c2s(Point p){ + return addpt(mulpt(subpt(p, cpos), zoom), spos); +} + +Rectangle +c2sr(Rectangle r){ + return Rpt(c2s(r.min), c2s(r.max)); +} + +void +update(Rectangle *rp){ + if(canvas==nil) + draw(screen, screen->r, back, nil, ZP); + else { + if(rp == nil) + rp = &canvas->r; + gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD); + zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom); + } + flushimage(display, 1); +} + +void +expand(Rectangle r) +{ + Rectangle nr; + Image *tmp; + + if(canvas==nil){ + if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil) + sysfatal("allocimage: %r"); + draw(canvas, canvas->r, back, nil, ZP); + return; + } + nr = canvas->r; + combinerect(&nr, r); + if(eqrect(nr, canvas->r)) + return; + if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil) + return; + draw(tmp, canvas->r, canvas, nil, canvas->r.min); + gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD); + freeimage(canvas); + canvas = tmp; +} + +void +save(Rectangle r, int mark) +{ + Image *tmp; + int x; + + if(mark){ + x = nundo++ % nelem(undo); + if(undo[x]) + freeimage(undo[x]); + undo[x] = nil; + } + if(canvas==nil || nundo<0) + return; + if(!rectclip(&r, canvas->r)) + return; + if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil) + return; + draw(tmp, r, canvas, nil, r.min); + x = nundo++ % nelem(undo); + if(undo[x]) + freeimage(undo[x]); + undo[x] = tmp; +} + +void +restore(int n) +{ + Image *tmp; + int x; + + while(nundo > 0){ + if(n-- == 0) + return; + x = --nundo % nelem(undo); + if((tmp = undo[x]) == nil) + return; + undo[x] = nil; + if(canvas == nil || canvas->chan != tmp->chan){ + freeimage(canvas); + canvas = tmp; + update(nil); + } else { + expand(tmp->r); + draw(canvas, tmp->r, tmp, nil, tmp->r.min); + update(&tmp->r); + freeimage(tmp); + } + } +} + +typedef struct { + Rectangle r; + Rectangle r0; + Image* dst; + + int yscan; /* current scanline */ + int wscan; /* bscan width in bytes */ + Image* iscan; /* scanline image */ + uchar* bscan; /* scanline buffer */ + + int nmask; /* size of bmask in bytes */ + int wmask; /* width of bmask in bytes */ + Image* imask; /* mask image */ + uchar* bmask; /* mask buffer */ + + int ncmp; + uchar bcmp[4]; +} Filldata; + +void +fillscan(Filldata *f, Point p0) +{ + int x, y; + uchar *b; + + x = p0.x; + y = p0.y; + b = f->bmask + y*f->wmask; + if(b[x/8] & 0x80>>(x%8)) + return; + + if(f->yscan != y){ + draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y)); + if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0) + return; + f->yscan = y; + } + + for(x = p0.x; x >= 0; x--){ + if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp)) + break; + b[x/8] |= 0x80>>(x%8); + } + for(x = p0.x+1; x < f->r0.max.x; x++){ + if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp)) + break; + b[x/8] |= 0x80>>(x%8); + } + + y = p0.y-1; + if(y >= 0){ + for(x = p0.x; x >= 0; x--){ + if((b[x/8] & 0x80>>(x%8)) == 0) + break; + fillscan(f, Pt(x, y)); + } + for(x = p0.x+1; x < f->r0.max.x; x++){ + if((b[x/8] & 0x80>>(x%8)) == 0) + break; + fillscan(f, Pt(x, y)); + } + } + + y = p0.y+1; + if(y < f->r0.max.y){ + for(x = p0.x; x >= 0; x--){ + if((b[x/8] & 0x80>>(x%8)) == 0) + break; + fillscan(f, Pt(x, y)); + } + for(x = p0.x+1; x < f->r0.max.x; x++){ + if((b[x/8] & 0x80>>(x%8)) == 0) + break; + fillscan(f, Pt(x, y)); + } + } +} + +void +floodfill(Image *dst, Rectangle r, Point p, Image *src) +{ + Filldata f; + + if(!rectclip(&r, dst->r)) + return; + if(!ptinrect(p, r)) + return; + memset(&f, 0, sizeof(f)); + f.dst = dst; + f.r = r; + f.r0 = rectsubpt(r, r.min); + f.wmask = bytesperline(f.r0, 1); + f.nmask = f.wmask*f.r0.max.y; + if((f.bmask = mallocz(f.nmask, 1)) == nil) + goto out; + if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil) + goto out; + + r = f.r0; + r.max.y = 1; + if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil) + goto out; + f.yscan = -1; + f.wscan = bytesperline(f.iscan->r, f.iscan->depth); + if((f.bscan = mallocz(f.wscan, 0)) == nil) + goto out; + + r = Rect(0,0,1,1); + f.ncmp = (f.iscan->depth+7) / 8; + draw(f.iscan, r, dst, nil, p); + if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0) + goto out; + + fillscan(&f, subpt(p, f.r.min)); + + loadimage(f.imask, f.imask->r, f.bmask, f.nmask); + draw(f.dst, f.r, src, f.imask, f.imask->r.min); +out: + free(f.bmask); + free(f.bscan); + if(f.iscan) + freeimage(f.iscan); + if(f.imask) + freeimage(f.imask); +} + +void +translate(Point d) +{ + Rectangle r, nr; + + if(canvas==nil || d.x==0 && d.y==0) + return; + r = c2sr(canvas->r); + nr = rectaddpt(r, d); + rectclip(&r, screen->clipr); + draw(screen, rectaddpt(r, d), screen, nil, r.min); + zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom); + gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD); + spos = addpt(spos, d); + flushimage(display, 1); +} + +void +setzoom(Point o, int z) +{ + if(z < 1) + return; + cpos = s2c(o); + spos = o; + zoom = z; + update(nil); +} + +void +center(void) +{ + cpos = ZP; + if(canvas) + cpos = addpt(canvas->r.min, + divpt(subpt(canvas->r.max, canvas->r.min), 2)); + spos = addpt(screen->r.min, + divpt(subpt(screen->r.max, screen->r.min), 2)); + update(nil); +} + +void +drawpal(void) +{ + Rectangle r, rr; + int i; + + r = screen->r; + r.min.y = r.max.y - 20; + replclipr(screen, 0, r); + + penr = r; + penr.min.x = r.max.x - NBRUSH*Dy(r); + + palr = r; + palr.max.x = penr.min.x; + + r = penr; + draw(screen, r, back, nil, ZP); + for(i=0; i<NBRUSH; i++){ + r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH; + rr = r; + if(i == brush) + rr.min.y += Dy(r)/3; + if(i == NBRUSH-1){ + /* last is special brush for fill draw */ + draw(screen, rr, ink, nil, ZP); + } else { + rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)); + rr.max = rr.min; + strokedraw(screen, rr, ink, i); + } + r.min.x = r.max.x; + } + + r = palr; + for(i=1; i<=nelem(pal); i++){ + r.max.x = palr.min.x + i*Dx(palr) / nelem(pal); + rr = r; + if(ink == pal[i-1]) + rr.min.y += Dy(r)/3; + draw(screen, rr, pal[i-1], nil, ZP); + gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD); + r.min.x = r.max.x; + } + + r = screen->r; + r.max.y -= Dy(palr); + replclipr(screen, 0, r); +} + +int +hitpal(Mouse m) +{ + if(ptinrect(m.xy, penr)){ + if(m.buttons & 7){ + brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr); + drawpal(); + } + return 1; + } + if(ptinrect(m.xy, palr)){ + Image *col; + + col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)]; + switch(m.buttons & 7){ + case 1: + ink = col; + drawpal(); + break; + case 2: + back = col; + drawpal(); + update(nil); + break; + } + return 1; + } + return 0; +} + +void +catch(void * _, char *msg) +{ + USED(_); + if(strstr(msg, "closed pipe")) + noted(NCONT); + noted(NDFLT); +} + +int +pipeline(char *fmt, ...) +{ + char buf[1024]; + va_list a; + int p[2]; + + va_start(a, fmt); + vsnprint(buf, sizeof(buf), fmt, a); + va_end(a); + if(pipe(p) < 0) + return -1; + switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){ // RFEND not available in libc port + case -1: + close(p[0]); + close(p[1]); + return -1; + case 0: + close(p[1]); + dup(p[0], 0); + dup(p[0], 1); + close(p[0]); + execl("/bin/rc", "rc", "-c", buf, nil); + exits("exec"); + } + close(p[0]); + return p[1]; +} + +void +usage(void) +{ + fprint(2, "usage: %s [ file ]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + char *s, buf[1024]; + Rectangle r; + Image *img; + int i, fd; + Event e; + Mouse m; + Point p, d; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if(argc == 1) + filename = strdup(argv[0]); + else if(argc != 0) + usage(); + + if(initdraw(0, 0, "paint") < 0) + sysfatal("initdraw: %r"); + + if(filename){ + if((fd = open(filename, OREAD)) < 0) + sysfatal("open: %r"); + if((canvas = readimage(display, fd, 0)) == nil) + sysfatal("readimage: %r"); + close(fd); + } + + /* palette initialization */ + for(i=0; i<nelem(pal); i++){ + pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, + c64[i % nelem(c64)]<<8 | 0xFF); + if(pal[i] == nil) + sysfatal("allocimage: %r"); + } + ink = pal[0]; + back = pal[1]; + drawpal(); + center(); + + einit(Emouse | Ekeyboard); + + notify(catch); + for(;;) { + switch(event(&e)){ + case Emouse: + if(hitpal(e.mouse)) + continue; + + img = ink; + switch(e.mouse.buttons & 7){ + case 2: + img = back; + /* no break */ + case 1: + p = s2c(e.mouse.xy); + if(brush == NBRUSH-1){ + /* flood fill brush */ + if(canvas == nil || !ptinrect(p, canvas->r)){ + back = img; + drawpal(); + update(nil); + break; + } + r = canvas->r; + save(r, 1); + floodfill(canvas, r, p, img); + update(&r); + + /* wait for mouse release */ + while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0) + ; + break; + } + r = strokerect(Rpt(p, p), brush); + expand(r); + save(r, 1); + strokedraw(canvas, Rpt(p, p), img, brush); + update(&r); + for(;;){ + m = e.mouse; + if(event(&e) != Emouse) + break; + if((e.mouse.buttons ^ m.buttons) & 7) + break; + d = s2c(e.mouse.xy); + if(eqpt(d, p)) + continue; + r = strokerect(Rpt(p, d), brush); + expand(r); + save(r, 0); + strokedraw(canvas, Rpt(p, d), img, brush); + update(&r); + p = d; + } + break; + case 4: + for(;;){ + m = e.mouse; + if(event(&e) != Emouse) + break; + if((e.mouse.buttons & 7) != 4) + break; + translate(subpt(e.mouse.xy, m.xy)); + } + break; + } + break; + case Ekeyboard: + switch(e.kbdc){ + case Kesc: + zoom = 1; + center(); + break; + case '+': + if(zoom < 0x1000) + setzoom(e.mouse.xy, zoom*2); + break; + case '-': + if(zoom > 1) + setzoom(e.mouse.xy, zoom/2); + break; + case 'c': + if(canvas == nil) + break; + save(canvas->r, 1); + freeimage(canvas); + canvas = nil; + update(nil); + break; + case 'u': + restore(16); + break; + case 'f': + brush = NBRUSH-1; + drawpal(); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + brush = e.kbdc - '0'; + drawpal(); + break; + default: + if(e.kbdc == Kdel) + e.kbdc = 'q'; + buf[0] = 0; + if(filename && (e.kbdc == 'r' || e.kbdc == 'w')) + snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename); + else if(e.kbdc > 0x20 && e.kbdc < 0x7f) + snprint(buf, sizeof(buf), "%C", e.kbdc); + if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0) + break; + if(strcmp(buf, "q") == 0) + exits(nil); + s = buf+1; + while(*s == ' ' || *s == '\t') + s++; + if(*s == 0) + break; + switch(buf[0]){ + case 'r': + if((fd = open(s, OREAD)) < 0){ + Error: + snprint(buf, sizeof(buf), "%r"); + eenter(buf, nil, 0, &e.mouse); + break; + } + free(filename); + filename = strdup(s); + Readimage: + unlockdisplay(display); + img = readimage(display, fd, 1); + close(fd); + lockdisplay(display); + if(img == nil){ + werrstr("readimage: %r"); + goto Error; + } + if(canvas){ + save(canvas->r, 1); + freeimage(canvas); + } + canvas = img; + center(); + break; + case 'w': + if((fd = create(s, OWRITE, 0660)) < 0) + goto Error; + free(filename); + filename = strdup(s); + Writeimage: + if(canvas) + if(writeimage(fd, canvas, 0) < 0){ + close(fd); + werrstr("writeimage: %r"); + goto Error; + } + close(fd); + break; + case '<': + if((fd = pipeline("%s", s)) < 0) + goto Error; + goto Readimage; + case '>': + if((fd = pipeline("%s", s)) < 0) + goto Error; + goto Writeimage; + case '|': + if(canvas == nil) + break; + if((fd = pipeline("%s", s)) < 0) + goto Error; + switch(rfork(RFMEM|RFPROC|RFFDG)){ + case -1: + close(fd); + werrstr("rfork: %r"); + goto Error; + case 0: + writeimage(fd, canvas, 1); + exits(nil); + } + goto Readimage; + } + break; + } + break; + } + } +} + +void +eresized(int _) +{ + USED(_); + if(getwindow(display, Refnone) < 0) + sysfatal("resize failed"); + drawpal(); + update(nil); +} diff --git a/src/cmd/rio/mkriorules.sh b/src/cmd/rio/mkriorules.sh index 76dda36b..25cacc79 100644 --- a/src/cmd/rio/mkriorules.sh +++ b/src/cmd/rio/mkriorules.sh @@ -1,6 +1,8 @@ if [ "x$WSYSTYPE" != xx11 ]; then - echo 'all install clean nuke:Q:' - echo ' #' + echo 'default:V: all' + echo + echo 'all install clean nuke:' + echo ' # WSYSTYPE is not x11, and rio is only for x11' exit 0 fi cat $PLAN9/src/mkmany diff --git a/src/cmd/upas/nfs/imap.c b/src/cmd/upas/nfs/imap.c index 2cbe99e1..8d43fe79 100644 --- a/src/cmd/upas/nfs/imap.c +++ b/src/cmd/upas/nfs/imap.c @@ -214,7 +214,7 @@ imaplogin(Imap *z) return -1; } - sx = imapcmdsx(z, nil, "LOGIN %Z %Z", up->user, up->passwd); + sx = imapcmdsx(z, nil, "LOGIN %#Z %#Z", up->user, up->passwd); freeup(up); if(sx == nil) return -1; diff --git a/src/cmd/upas/smtp/mxdial.c b/src/cmd/upas/smtp/mxdial.c index f3a2a209..56962dcd 100644 --- a/src/cmd/upas/smtp/mxdial.c +++ b/src/cmd/upas/smtp/mxdial.c @@ -2,6 +2,7 @@ #include <ndb.h> #include "smtp.h" /* to publish dial_string_parse */ #include <ip.h> +#include <thread.h> enum { @@ -27,6 +28,45 @@ static int callmx(DS*, char*, char*); static void expand_meta(DS *ds); extern int cistrcmp(char*, char*); +/* Taken from imapdial, replaces tlsclient call with stunnel */ +static int +smtpdial(char *server) +{ + int p[2]; + int fd[3]; + char *tmp; + char *fpath; + + if(pipe(p) < 0) + return -1; + fd[0] = dup(p[0], -1); + fd[1] = dup(p[0], -1); + fd[2] = dup(2, -1); +#ifdef PLAN9PORT + tmp = smprint("%s:587", server); + fpath = searchpath("stunnel3"); + if (!fpath) { + werrstr("stunnel not found. it is required for tls support."); + return -1; + } + if(threadspawnl(fd, fpath, "stunnel", "-n", "smtp" , "-c", "-r", tmp, nil) < 0) { +#else + tmp = smprint("tcp!%s!587", server); + if(threadspawnl(fd, "/bin/tlsclient", "tlsclient", tmp, nil) < 0){ +#endif + free(tmp); + close(p[0]); + close(p[1]); + close(fd[0]); + close(fd[1]); + close(fd[2]); + return -1; + } + free(tmp); + close(p[0]); + return p[1]; +} + int mxdial(char *addr, char *ddomain, char *gdomain) { @@ -100,13 +140,21 @@ callmx(DS *ds, char *dest, char *domain) } /* dial each one in turn */ for(i = 0; i < nmx; i++){ +#ifdef PLAN9PORT + snprint(addr, sizeof(addr), "%s", mx[i].host); +#else snprint(addr, sizeof(addr), "%s!%s!%s", ds->proto, mx[i].host, ds->service); +#endif if(debug) fprint(2, "mxdial trying %s (%d)\n", addr, i); atnotify(timeout, 1); alarm(10*1000); +#ifdef PLAN9PORT + fd = smtpdial(addr); +#else fd = dial(addr, 0, 0, 0); +#endif alarm(0); atnotify(timeout, 0); if(fd >= 0) diff --git a/src/cmd/upas/smtp/smtp.c b/src/cmd/upas/smtp/smtp.c index 9dd05596..92873723 100644 --- a/src/cmd/upas/smtp/smtp.c +++ b/src/cmd/upas/smtp/smtp.c @@ -467,6 +467,7 @@ hello(char *me, int encrypted) } ehlo = 1; + encrypted = 1; Again: if(ehlo) dBprint("EHLO %s\r\n", me); |