tdevdraw: more fixes (thanks David Jeannot) - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit d0a596c5c8669950a2499d3012c340cfbf0eeed5
 (DIR) parent 210d461c87a6c5f598ef958b303a7f24d4e4a93b
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Wed, 12 Oct 2011 13:40:35 -0400
       
       devdraw: more fixes (thanks David Jeannot)
       
       Diffstat:
         M man/man1/snarfer.1                  |       2 +-
         M src/cmd/devdraw/cocoa-screen.m      |     651 ++++++++++++++++---------------
         M src/cmd/devdraw/cocoa-srv.c         |       2 --
         M src/cmd/devdraw/cocoa-thread.c      |       3 +++
         M src/cmd/devdraw/cocoa-thread.h      |      41 ++++++++++++++++++++++---------
       
       5 files changed, 379 insertions(+), 320 deletions(-)
       ---
 (DIR) diff --git a/man/man1/snarfer.1 b/man/man1/snarfer.1
       t@@ -33,7 +33,7 @@ See
        .IR getsnarf (3)
        for more details.
        .SH SOURCE
       -.B \*9/src/cmd/snarfer.c
       +.B \*9/src/cmd/snarfer
        .SH SEE ALSO
        Unix's \fIxclipboard\fR(1),
        .IR getsnarf (3)
 (DIR) diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m
       t@@ -1,20 +1,20 @@
        /*
       - * Cocoa's event loop must be in the main thread.
       + * Cocoa's event loop must be in main thread.
         */
        
       +#define Cursor OSXCursor
        #define Point OSXPoint
        #define Rect OSXRect
       -#define Cursor OSXCursor
        
        #import <Cocoa/Cocoa.h>
        
       -#undef Rect
       -#undef Point
        #undef Cursor
       +#undef Point
       +#undef Rect
        
        #include <u.h>
        #include <libc.h>
       -#include  "cocoa-thread.h"        // try libthread when possible
       +#include  "cocoa-thread.h"
        #include <draw.h>
        #include <memdraw.h>
        #include <keyboard.h>
       t@@ -28,26 +28,21 @@ AUTOFRAMEWORK(Cocoa)
        
        #define panic sysfatal
        
       -/*
       - * Incompatible with Magic Mouse?
       - */
       -int reimplementswipe = 0;
       -        int usecopygesture = 0;
       -
       +int usegestures = 0;
        int useoldfullscreen = 0;
        
        void
        usage(void)
        {
                fprint(2, "usage: devdraw (don't run directly)\n");
       -        exits("usage");
       +        threadexitsall("usage");
        }
        
        @interface appdelegate : NSObject
        @end
        
        void
       -main(int argc, char **argv)
       +threadmain(int argc, char **argv)
        {
                /*
                 * Move the protocol off stdin/stdout so that
       t@@ -60,18 +55,19 @@ main(int argc, char **argv)
                open("/dev/null", OREAD);
                open("/dev/null", OWRITE);
        
       -        // Libdraw doesn't permit arguments currently.
       -
                ARGBEGIN{
       -        case 'D':                // only for good ps -a listings
       +        case 'D':                /* for good ps -a listings */
       +                break;
       +        case 'f':
       +                useoldfullscreen = 1;
       +                break;
       +        case 'g':
       +                usegestures = 1;
                        break;
                default:
                        usage();
                }ARGEND
        
       -        if(usecopygesture)
       -                reimplementswipe = 1;
       -
                if(OSX_VERSION < 100700)
                        [NSAutoreleasePool new];
        
       t@@ -82,28 +78,41 @@ main(int argc, char **argv)
                [NSApp run];
        }
        
       -struct {
       -        NSWindow                *std;
       -        NSWindow                *ofs;                        /* old fullscreen */
       -        NSWindow                *p;
       -        NSView                        *content;
       -        Cursor                        *cursor;
       -        char                                *rectstr;
       +#define WIN        win.ofs[win.isofs]
       +
       +struct
       +{
       +        NSWindow        *ofs[2];        /* ofs[1] for old fullscreen; ofs[0] else */
       +        int                        isofs;
       +        NSView                *content;
       +        char                        *rectstr;
                NSBitmapImageRep        *img;
       -        NSRect                        flushrect;
       -        int                                needflush;
       +        NSRect                flushrect;
       +        int                        needflush;
       +        NSCursor                *cursor;
       +        QLock                cursorl;
        } win;
        
       +struct
       +{
       +        int                kalting;
       +        int                kbuttons;
       +        int                mbuttons;
       +        Point                mpos;
       +        int                mscroll;
       +        int                undo;
       +        int                touchevent;
       +} in;
       +
        static void autohide(int);
        static void drawimg(void);
        static void flushwin(void);
       +static void followzoombutton(NSRect);
        static void getmousepos(void);
        static void makeicon(void);
        static void makemenu(void);
        static void makewin(void);
       -static void resize(void);
        static void sendmouse(void);
       -static void setcursor0(void);
        static void togglefs(void);
        
        @implementation appdelegate
       t@@ -117,6 +126,8 @@ static void togglefs(void);
        }
        - (void)windowDidBecomeKey:(id)arg
        {
       +        in.touchevent = 0;
       +
                getmousepos();
                sendmouse();
        }
       t@@ -124,30 +135,21 @@ static void togglefs(void);
        {
                getmousepos();
                sendmouse();
       -
       -        if([win.p inLiveResize])
       -                return;
       -
       -        resize();
        }
        - (void)windowDidEndLiveResize:(id)arg
        {
       -        resize();
       -}
       -- (void)windowDidDeminiaturize:(id)arg
       -{
       -        resize();
       -}
       -- (void)windowDidChangeScreenProfile:(id)arg
       -{
       -        resize();
       +        [win.content display];
        }
        - (void)windowDidChangeScreen:(id)arg
        {
       -        if(win.p == win.ofs)
       +        if(win.isofs)
                        autohide(1);
       -        [win.ofs setFrame:[[win.p screen] frame] display:YES];
       -        resize();
       +        [win.ofs[1] setFrame:[[WIN screen] frame] display:YES];
       +}
       +- (BOOL)windowShouldZoom:(id)arg toFrame:(NSRect)r
       +{
       +        followzoombutton(r);
       +        return YES;
        }
        - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(id)arg
        {
       t@@ -161,11 +163,10 @@ static void togglefs(void);
        + (void)calldrawimg:(id)arg{ drawimg();}
        + (void)callflushwin:(id)arg{ flushwin();}
        + (void)callmakewin:(id)arg{ makewin();}
       -+ (void)callsetcursor0:(id)arg{ setcursor0();}
        - (void)calltogglefs:(id)arg{ togglefs();}
        @end
        
       -static Memimage* makeimg(void);
       +static Memimage* initimg(void);
        
        Memimage*
        attachscreen(char *label, char *winsize)
       t@@ -182,8 +183,10 @@ attachscreen(char *label, char *winsize)
        
                win.rectstr = strdup(winsize);
        
       -//        Create window in main thread,
       -//        else no cursor change when resizing.
       +        /*
       +         * Create window in main thread, else no cursor
       +         * change while resizing.
       +         */
                [appdelegate
                        performSelectorOnMainThread:@selector(callmakewin:)
                        withObject:nil
       t@@ -191,7 +194,7 @@ attachscreen(char *label, char *winsize)
        //        makewin();
        
                kicklabel(label);
       -        return makeimg();
       +        return initimg();
        }
        
        @interface appview : NSView
       t@@ -207,8 +210,7 @@ attachscreen(char *label, char *winsize)
        }
        - (BOOL)canBecomeKeyWindow
        {
       -        // just keyboard? or all inputs?
       -        return YES;        // else no keyboard focus with NSBorderlessWindowMask
       +        return YES;        /* else no keyboard with old fullscreen */
        }
        @end
        
       t@@ -224,9 +226,10 @@ static void
        makewin(void)
        {
                NSRect r, sr;
       +        NSWindow *w;
                Rectangle wr;
                char *s;
       -        int set;
       +        int i, set;
        
                s = win.rectstr;
                sr = [[NSScreen mainScreen] frame];
       t@@ -238,89 +241,73 @@ makewin(void)
                        wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3);
                        set = 0;
                }
       -//        The origin is the left-bottom corner with Cocoa.
       -        r = NSMakeRect(wr.min.x, r.size.height-wr.min.y, Dx(wr), Dy(wr));
        
       -        win.std = [[appwin alloc]
       +        /*
       +         * The origin is the left bottom corner for Cocoa.
       +         */
       +        r.origin.y = sr.size.height-wr.max.y;
       +        r = NSMakeRect(wr.min.x, r.origin.y, Dx(wr), Dy(wr));
       +        r = [NSWindow contentRectForFrameRect:r
       +                styleMask:Winstyle];
       +
       +        w = [[appwin alloc]
                        initWithContentRect:r
                        styleMask:Winstyle
                        backing:NSBackingStoreBuffered defer:NO];
                if(!set)
       -                [win.std center];
       +                [w center];
        #if OSX_VERSION >= 100700
       -        [win.std setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
       +        [w setCollectionBehavior:
       +                NSWindowCollectionBehaviorFullScreenPrimary];
        #endif
       -        [win.std setMinSize:NSMakeSize(128,128)];
       -        [win.std setAcceptsMouseMovedEvents:YES];
       -        [win.std setDelegate:[NSApp delegate]];
       +        [w setContentMinSize:NSMakeSize(128,128)];
        
       -        win.ofs = [[appwin alloc]
       +        win.ofs[0] = w;
       +        win.ofs[1] = [[appwin alloc]
                        initWithContentRect:sr
                        styleMask:NSBorderlessWindowMask
       -                backing:NSBackingStoreBuffered defer:NO];
       -        [win.ofs setAcceptsMouseMovedEvents:YES];
       -        [win.ofs setDelegate:[NSApp delegate]];
       -
       +                backing:NSBackingStoreBuffered defer:YES];
       +        for(i=0; i<2; i++){
       +                [win.ofs[i] setAcceptsMouseMovedEvents:YES];
       +                [win.ofs[i] setDelegate:[NSApp delegate]];
       +                [win.ofs[i] setDisplaysWhenScreenProfileChanges:NO];
       +        }
       +        win.isofs = 0;
                win.content = [appview new];
                [win.content setAcceptsTouchEvents:YES];
       -        win.p = win.std;
       -        [win.p setContentView:win.content];
       -        [win.p makeKeyAndOrderFront:nil];
       +        [WIN setContentView:win.content];
       +        [WIN makeKeyAndOrderFront:nil];
        }
        
       -// explain the bottom-corner bug here (osx-screen-carbon.m:/^eresized)
        static Memimage*
       -makeimg(void)
       +initimg(void)
        {
       -        static int first = 1;
       -        Memimage *m;
       +        Memimage *i;
                NSSize size;
                Rectangle r;
       -        uint ch;
        
       -        if(first){
       -                memimageinit();
       -                first = 0;
       -        }
                size = [win.content bounds].size;
        
       -        if(size.width<=0 || size.height<=0){
       -                NSLog(@"bad content size: %.0f %.0f", size.width, size.height);
       -                return nil;
       -        }
                r = Rect(0, 0, size.width, size.height);
       -        ch = XBGR32;
       -        m = allocmemimage(r, ch);
       -        if(m == nil)
       +        i = allocmemimage(r, XBGR32);
       +        if(i == nil)
                        panic("allocmemimage: %r");
       -        if(m->data == nil)
       -                panic("m->data == nil");
       +        if(i->data == nil)
       +                panic("i->data == nil");
        
       -        if(win.img)
       -                [win.img release];
                win.img = [[NSBitmapImageRep alloc]
       -                initWithBitmapDataPlanes:&m->data->bdata
       +                initWithBitmapDataPlanes:&i->data->bdata
                        pixelsWide:Dx(r)
                        pixelsHigh:Dy(r)
                        bitsPerSample:8
       -                samplesPerPixel:4
       -                hasAlpha:YES
       +                samplesPerPixel:3
       +                hasAlpha:NO
                        isPlanar:NO
                        colorSpaceName:NSDeviceRGBColorSpace
                        bytesPerRow:bytesperline(r, 32)
                        bitsPerPixel:32];
        
       -        _drawreplacescreenimage(m);
       -        return m;
       -}
       -
       -static void
       -resize(void)
       -{
       -        makeimg();
       -
       -        mouseresized = 1;
       -        sendmouse();
       +        return i;
        }
        
        void
       t@@ -328,9 +315,14 @@ _flushmemscreen(Rectangle r)
        {
                win.flushrect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
        
       -//        Call "lockFocusIfCanDraw" from main thread, else
       -//        we deadlock while synchronizing both threads with
       -//        qlock(): main thread must apparently be idle while we call it.
       +        /*
       +         * Call "lockFocusIfCanDraw" from main thread, else
       +         * we deadlock while synchronizing both threads with
       +         * qlock(): main thread must apparently be idle while
       +         * we call it.  (This is also why Devdraw shows
       +         * occasionally an empty window: I found no
       +         * satisfactory way to wait for P9P's image.)
       +         */
                [appdelegate
                        performSelectorOnMainThread:@selector(calldrawimg:)
                        withObject:nil
       t@@ -356,11 +348,20 @@ drawimg(void)
                sr =  [win.content convertRect:dr fromView:nil];
        
                if([win.content lockFocusIfCanDraw]){
       +
       +                /*
       +                 * To round the window's bottom corners, we can use
       +                 * "NSCompositeSourceIn", but this slows down
       +                 * trackpad scrolling considerably in Acme.  Else we
       +                 * can use "bezierPathWithRoundedRect" with "addClip",
       +                 * but it's still too slow for wide Acme windows.
       +                 */
                        [win.img drawInRect:dr fromRect:sr
       +//                        operation:NSCompositeSourceIn fraction:1
                                operation:NSCompositeCopy fraction:1
                                respectFlipped:YES hints:nil];
        
       -                if(OSX_VERSION<100700 && win.p==win.std)
       +                if(OSX_VERSION<100700 && win.isofs==0)
                                drawresizehandle();
        
                        [win.content unlockFocus];
       t@@ -372,7 +373,7 @@ static void
        flushwin(void)
        {
                if(win.needflush){
       -                [win.p flushWindow];
       +                [WIN flushWindow];
                        win.needflush = 0;
                }
        }
       t@@ -380,44 +381,51 @@ flushwin(void)
        enum
        {
                Pixel = 1,
       -        Handlesize = 16*Pixel,
       +        Barsize = 4*Pixel,
       +        Handlesize = 3*Barsize + 1*Pixel,
        };
        
        static void
        drawresizehandle(void)
        {
       -        NSBezierPath *p;
       +        NSColor *color[Barsize];
       +        NSPoint a,b;
                NSRect r;
                NSSize size;
       -        Point o;
       +        Point c;
       +        int i,j;
        
                size = [win.img size];
       -        o = Pt(size.width+1-Handlesize, size.height+1-Handlesize);
       -        r = NSMakeRect(o.x, o.y, Handlesize, Handlesize);
       +        c = Pt(size.width, size.height);
       +        r = NSMakeRect(0, 0, Handlesize, Handlesize);
       +        r.origin = NSMakePoint(c.x-Handlesize, c.y-Handlesize);
                if(NSIntersectsRect(r, win.flushrect) == 0)
                        return;
        
       -        [[NSColor whiteColor] setFill];
       -        [[NSColor lightGrayColor] setStroke];
       -
       -        [NSBezierPath fillRect:r];
       -        [NSBezierPath strokeRect:r];
       -
       -
       -        [[NSColor darkGrayColor] setStroke];
       +        [[WIN graphicsContext] setShouldAntialias:NO];
        
       -        p = [NSBezierPath bezierPath];
       +        color[0] = [NSColor clearColor];
       +        color[1] = [NSColor darkGrayColor];
       +        color[2] = [NSColor lightGrayColor];
       +        color[3] = [NSColor whiteColor];
        
       -        [p moveToPoint:NSMakePoint(o.x+4, o.y+13)];
       -        [p lineToPoint:NSMakePoint(o.x+13, o.y+4)];
       -
       -        [p moveToPoint:NSMakePoint(o.x+8, o.y+13)];
       -        [p lineToPoint:NSMakePoint(o.x+13, o.y+8)];
       -
       -        [p moveToPoint:NSMakePoint(o.x+12, o.y+13)];
       -        [p lineToPoint:NSMakePoint(o.x+13, o.y+12)];
       +        for(i=1; i+Barsize <= Handlesize; )
       +                for(j=0; j<Barsize; j++){
       +                        [color[j] setStroke];
       +                        i++;
       +                        a = NSMakePoint(c.x-i, c.y-1);
       +                        b = NSMakePoint(c.x-2, c.y+1-i);
       +                        [NSBezierPath strokeLineFromPoint:a toPoint:b];
       +                }
       +}
        
       -        [p stroke];
       +static void
       +resizeimg()
       +{
       +        [win.img release];
       +        _drawreplacescreenimage(initimg());
       +        mouseresized = 1;
       +        sendmouse();
        }
        
        static void getgesture(NSEvent*);
       t@@ -429,15 +437,38 @@ static void gettouch(NSEvent*, int);
        
        - (void)drawRect:(NSRect)r
        {
       -        // else no window background
       +        static int first = 1;
       +
       +        if([WIN inLiveResize])
       +                return;
       +
       +        if(first)
       +                first = 0;
       +        else
       +                resizeimg();
       +
       +        /* We should wait for P9P's image here. */
       +}
       +- (void)resetCursorRects
       +{
       +        NSCursor *c;
       +
       +        qlock(&win.cursorl);
       +
       +        c = win.cursor;
       +        if(c == nil)
       +                c = [NSCursor arrowCursor];
       +        [self addCursorRect:[self bounds] cursor:c];
       +
       +        qunlock(&win.cursorl);
        }
        - (BOOL)isFlipped
        {
       -        return YES;        // to have the origin at top left
       +        return YES;        /* to have the origin at top left */
        }
        - (BOOL)acceptsFirstResponder
        {
       -        return YES;        // to receive mouseMoved events
       +        return YES;        /* to receive mouseMoved events */
        }        
        - (void)mouseMoved:(NSEvent*)e{ getmouse(e);}
        - (void)mouseDown:(NSEvent*)e{ getmouse(e);}
       t@@ -475,15 +506,6 @@ static void gettouch(NSEvent*, int);
        }
        @end
        
       -struct {
       -        int                kalting;
       -        int                kbuttons;
       -        int                mbuttons;
       -        Point                mpos;
       -        int                mscroll;
       -        int                undo;
       -} in;
       -
        static int keycvt[] =
        {
                [QZ_IBOOK_ENTER] '\n',
       t@@ -537,6 +559,7 @@ static int keycvt[] =
        static void
        getkeyboard(NSEvent *e)
        {
       +        NSString *s;
                char c;
                int k, m;
                uint code;
       t@@ -546,24 +569,25 @@ getkeyboard(NSEvent *e)
                switch([e type]){
                case NSKeyDown:
                        in.kalting = 0;
       -                c = [[e characters] characterAtIndex:0];
       +
       +                s = [e characters];
       +                c = [s UTF8String][0];
       +
                        if(m & NSCommandKeyMask){
       -                        if(' '<=c && c<='~'){
       +                        if(' '<=c && c<='~')
                                        keystroke(Kcmd+c);
       -                        }
       -                        return;
       +                        break;
                        }
       -//                to understand
                        k = c;
                        code = [e keyCode];
       -                if(code < nelem(keycvt) && keycvt[code])
       +                if(code<nelem(keycvt) && keycvt[code])
                                k = keycvt[code];
       -                if(k == 0)
       -                        return;
       -                if(k > 0)
       +                if(k==0)
       +                        break;
       +                if(k>0)
                                keystroke(k);
                        else
       -                        keystroke(c);
       +                        keystroke([s characterAtIndex:0]);
                        break;
        
                case NSFlagsChanged:
       t@@ -591,7 +615,7 @@ getmousepos(void)
        {
                NSPoint p;
        
       -        p = [win.p mouseLocationOutsideOfEventStream];
       +        p = [WIN mouseLocationOutsideOfEventStream];
                p = [win.content convertPoint:p fromView:nil];
                in.mpos = Pt(p.x, p.y);
        }
       t@@ -656,23 +680,50 @@ getmouse(NSEvent *e)
                sendmouse();
        }
        
       -static void sendswipe(int, int);
       +#define Minpinch        0.050
       +
       +enum
       +{
       +        Left                = -1,
       +        Right        = +1,
       +        Up                = +2,
       +        Down        = -2,
       +};
       +
       +static int
       +getdir(int dx, int dy)
       +{
       +        return dx + 2*dy;
       +}
       +
       +static void interpretswipe(int);
        
        static void
        getgesture(NSEvent *e)
        {
       +        static float sum;
       +        int dir;
       +
       +        if(usegestures == 0)
       +                return;
       +
                switch([e type]){
        
                case NSEventTypeMagnify:
       -//                if(fabs([e magnification]) > 0.025)
       +                sum += [e magnification];
       +                if(fabs(sum) > Minpinch){
                                togglefs();
       +                        sum = 0;
       +                }
                        break;
        
                case NSEventTypeSwipe:
       -                if(reimplementswipe)
       -                        break;
       +                dir = getdir(-[e deltaX], [e deltaY]);
        
       -                sendswipe(-[e deltaX], -[e deltaY]);
       +                if(in.touchevent)
       +                        if(dir==Up || dir==Down)
       +                                break;
       +                interpretswipe(dir);
                        break;
                }
        }
       t@@ -687,35 +738,43 @@ msec(void)
                return nsec()/1000000;
        }
        
       +#define Inch                72
       +#define Cm                Inch/2.54
       +
       +#define Mindelta        0.0*Cm
       +#define Xminswipe        0.5*Cm
       +#define Yminswipe        0.1*Cm
       +
        enum
        {
       +        Finger = 1,
                Msec = 1,
       +
                Maxtap = 400*Msec,
       -        Maxtouch = 3,
       -        Mindelta = 0,
       -        Minswipe = 15,
       +        Maxtouch = 3*Finger,
        };
        
        static void
        gettouch(NSEvent *e, int type)
        {
       -        static NSPoint delta, odelta;
       +        static NSPoint delta;
                static NSTouch *toucha[Maxtouch];
                static NSTouch *touchb[Maxtouch];
       -        static int done, ntouch, tapping;
       +        static int done, ntouch, odir, tapping;
                static uint taptime;
                NSArray *a;
                NSPoint d;
                NSSet *set;
                NSSize s;
       -        int i, p;
       +        int dir, i, p;
        
       -        if(reimplementswipe==0 && type!=NSTouchPhaseEnded)
       +        if(usegestures == 0)
                        return;
        
                switch(type){
        
                case NSTouchPhaseBegan:
       +                in.touchevent = 1;
                        p = NSTouchPhaseTouching;
                        set = [e touchesMatchingPhase:p inView:nil];
                        if(set.count == 3){
       t@@ -735,7 +794,7 @@ gettouch(NSEvent *e, int type)
                        if(ntouch==0){
                                ntouch = set.count;
                                for(i=0; i<ntouch; i++){
       -                                assert(toucha[i] == nil);
       +//                                assert(toucha[i] == nil);
                                        toucha[i] = [[a objectAtIndex:i] retain];
                                }
                                return;
       t@@ -747,7 +806,7 @@ gettouch(NSEvent *e, int type)
        
                        d = NSMakePoint(0,0);
                        for(i=0; i<ntouch; i++){
       -                        assert(touchb[i] == nil);
       +//                        assert(touchb[i] == nil);
                                touchb[i] = [a objectAtIndex:i];
                                d.x += touchb[i].normalizedPosition.x;
                                d.y += touchb[i].normalizedPosition.y;
       t@@ -765,18 +824,17 @@ gettouch(NSEvent *e, int type)
                                }
                                delta = NSMakePoint(delta.x+d.x, delta.y+d.y);
                                d = NSMakePoint(fabs(delta.x), fabs(delta.y));
       -                        if(d.x>Minswipe || d.y>Minswipe){
       +                        if(d.x>Xminswipe || d.y>Yminswipe){
                                        if(d.x > d.y)
       -                                        delta = NSMakePoint(-copysign(1,delta.x), 0);
       +                                        dir = delta.x>0? Right : Left;
                                        else
       -                                        delta = NSMakePoint(0, copysign(1,delta.y));
       -
       -                                if(! NSEqualPoints(delta, odelta)){
       +                                        dir = delta.y>0? Up : Down;
       +                                if(dir != odir){
        //                                        if(ntouch == 3)
       -                                                sendswipe(-delta.x, -delta.y);
       -                                        odelta = delta;
       +                                                if(dir==Up || dir==Down)
       +                                                        interpretswipe(dir);
       +                                        odir = dir;
                                        }
       -                                done = 1;
                                        goto Return;
                                }
                                for(i=0; i<ntouch; i++){
       t@@ -793,13 +851,12 @@ Return:
                        p = NSTouchPhaseTouching;
                        set = [e touchesMatchingPhase:p inView:nil];
                        if(set.count == 0){
       -                        in.undo = 0;
       -
       -                        if(usecopygesture)
       -                                if(tapping && msec()-taptime<Maxtap)
       -                                        sendclick(2);
       +                        if(tapping && msec()-taptime<Maxtap)
       +                                sendclick(2);
       +                        odir = 0;
                                tapping = 0;
       -                        odelta = NSMakePoint(0,0);
       +                        in.undo = 0;
       +                        in.touchevent = 0;
                        }
                        break;
        
       t@@ -807,45 +864,37 @@ Return:
                        break;
        
                default:
       -                panic("gettouch: unexpected event type: %d", type);
       +                panic("gettouch: unexpected event type");
                }
                for(i=0; i<ntouch; i++){
                        [toucha[i] release];
                        toucha[i] = nil;
                }
       -        for(i=0; i<ntouch; i++){
       -                assert(toucha[i] == nil);
       -                assert(touchb[i] == nil);
       -        }
       -        ntouch = 0;
                delta = NSMakePoint(0,0);
                done = 0;
       +        ntouch = 0;
        }
        
        static void
       -sendswipe(int dx, int dy)
       +interpretswipe(int dir)
        {
       -        if(dx == -1){
       +        if(dir == Left)
                        sendcmd('x');
       -        }else
       -        if(dx == +1){
       +        else
       +        if(dir == Right)
                        sendcmd('v');
       -        }else
       -        if(dy == -1){
       -                if(usecopygesture)
       -                        sendcmd('c');
       -                else
       -                        sendclick(2);
       -        }else
       -        if(dy == +1){
       +        else
       +        if(dir == Up)
       +                sendcmd('c');
       +        else
       +        if(dir == Down)
                        sendchord(2,1);
       -        }
        }
        
        static void
        sendcmd(int c)
        {
       -        if(c=='x' || c=='v'){
       +        if(in.touchevent && (c=='x' || c=='v')){
                        if(in.undo)
                                c = 'z';
                        in.undo = ! in.undo;
       t@@ -879,7 +928,7 @@ sendmouse(void)
                NSSize size;
                int b;
        
       -        size = [win.img size];
       +        size = [win.content bounds].size;
                mouserect = Rect(0, 0, size.width, size.height);
        
                b = in.kbuttons | in.mbuttons | in.mscroll;
       t@@ -895,21 +944,35 @@ setmouse(Point p)
                NSRect r;
        
                if(first){
       -//                try to move Acme's scrollbars without that!
       +                /* Try to move Acme's scrollbars without that! */
                        CGSetLocalEventsSuppressionInterval(0);
                        first = 0;
                }
       -        r = [[win.p screen] frame];
       +        r = [[WIN screen] frame];
        
       -        q = NSMakePoint(p.x,p.y);
       +        q = NSMakePoint(p.x, p.y);
                q = [win.content convertPoint:q toView:nil];
       -        q = [win.p convertBaseToScreen:q];
       +        q = [WIN convertBaseToScreen:q];
                q.y = r.size.height - q.y;
        
                CGWarpMouseCursorPosition(NSPointToCGPoint(q));
        
       -//        race condition
       -        in.mpos = p;
       +        in.mpos = p;        // race condition
       +}
       +
       +static void
       +followzoombutton(NSRect r)
       +{
       +        NSRect wr;
       +        Point p;
       +
       +        wr = [WIN frame];
       +        wr.origin.y += wr.size.height;
       +        r.origin.y += r.size.height;
       +
       +        p.x = (r.origin.x - wr.origin.x) + in.mpos.x;
       +        p.y = -(r.origin.y - wr.origin.y) + in.mpos.y;
       +        setmouse(p);
        }
        
        static void
       t@@ -917,33 +980,28 @@ togglefs(void)
        {
        #if OSX_VERSION >= 100700
                if(useoldfullscreen == 0){
       -                [win.p toggleFullScreen:nil];
       +                [WIN toggleFullScreen:nil];
                        return;
                }
        #endif
                NSScreen *screen;
                int willfs;
        
       -        screen = [win.p screen];
       +        screen = [WIN screen];
        
       -        willfs = !NSEqualRects([win.p frame], [screen frame]);
       +        willfs = !NSEqualRects([WIN frame], [screen frame]);
        
                autohide(willfs);
        
                [win.content retain];
       -        [win.p orderOut:nil];
       -        [win.p setContentView:nil];
       +        [WIN orderOut:nil];
       +        [WIN setContentView:nil];
        
       -        if(willfs)
       -                win.p = win.ofs;
       -        else
       -                win.p = win.std;
       +        win.isofs = willfs;
        
       -        [win.p setContentView:win.content];
       -        [win.p makeKeyAndOrderFront:nil];
       +        [WIN setContentView:win.content];
       +        [WIN makeKeyAndOrderFront:nil];
                [win.content release];
       -
       -        resize();
        }
        
        static void
       t@@ -952,7 +1010,7 @@ autohide(int set)
                NSScreen *s,*s0;
                int opt;
        
       -        s = [win.p screen];
       +        s = [WIN screen];
                s0 = [[NSScreen screens] objectAtIndex:0];
        
                if(set && s==s0)
       t@@ -964,40 +1022,35 @@ autohide(int set)
                [NSApp setPresentationOptions:opt];
        }
        
       -//        Rewrite this function
       -//        See ./osx-delegate.m implementation (NSLocalizedString)
        static void
        makemenu(void)
        {
       -        NSString *title;
       -        NSMenu *menu;
       -        NSMenuItem *appmenu, *item;
       -
       -        menu = [NSMenu new];
       -        appmenu = [NSMenuItem new];
       -        [menu addItem:appmenu];
       -        [NSApp setMenu:menu];
       -        [menu release];
       -
       -        menu = [NSMenu new];
       -
       -        title = @"Full Screen";
       -        item = [[NSMenuItem alloc]
       -                initWithTitle:title
       -                action:@selector(calltogglefs:) keyEquivalent:@"f"];
       -        [menu addItem:item];
       -        [item release];
       -
       -        title = @"Quit";
       -        item = [[NSMenuItem alloc]
       -                initWithTitle:title
       -                action:@selector(terminate:) keyEquivalent:@"q"];
       -        [menu addItem:item];
       -        [item release];
       -
       -        [appmenu setSubmenu:menu];
       -        [appmenu release];
       -        [menu release];
       +        NSMenu *m;
       +        NSMenuItem *i,*i0;
       +
       +        m = [NSMenu new];
       +        i0 = [NSMenuItem new];
       +        [m addItem:i0];
       +        [NSApp setMainMenu:m];
       +        [m release];
       +
       +        m = [NSMenu new];
       +
       +        i = [[NSMenuItem alloc] initWithTitle:@"Full Screen"
       +                action:@selector(calltogglefs:)
       +                keyEquivalent:@"f"];
       +        [m addItem:i];
       +        [i release];
       +
       +        i = [[NSMenuItem alloc] initWithTitle:@"Quit"
       +                action:@selector(terminate:)
       +                keyEquivalent:@"q"];
       +        [m addItem:i];
       +        [i release];
       +
       +        [i0 setSubmenu:m];
       +        [i0 release];
       +        [m release];
        }
        
        static void
       t@@ -1068,69 +1121,55 @@ kicklabel(char *label)
                        return;
        
                s = [[NSString alloc] initWithUTF8String:label];
       -        [win.std setTitle:s];
       -        [win.ofs setTitle:s];
       +        [win.ofs[0] setTitle:s];
       +        [win.ofs[1] setTitle:s];
                [[NSApp dockTile] setBadgeLabel:s];
                [s release];
        }
        
        void
       -setcursor(Cursor *cursor)
       -{
       -        win.cursor = cursor;
       -
       -//        no cursor change unless in main thread
       -        [appdelegate
       -                performSelectorOnMainThread:@selector(callsetcursor0:)
       -                withObject:nil
       -                waitUntilDone:YES];
       -//        setcursor0();
       -
       -        win.cursor = nil;
       -}
       -
       -static void
       -setcursor0(void)
       +setcursor(Cursor *c)
        {
       -        Cursor *c;
                NSBitmapImageRep *r;
       -        NSCursor *d;
                NSImage *i;
                NSPoint p;
                int b;
                uchar *plane[5];
        
       -        c = win.cursor;
       +        qlock(&win.cursorl);
        
       -        if(c == nil){
       -                [[NSCursor arrowCursor] set];
       -                return;
       -        }
       -        r = [[NSBitmapImageRep alloc]
       -                initWithBitmapDataPlanes:nil
       -                pixelsWide:16
       -                pixelsHigh:16
       -                bitsPerSample:1
       -                samplesPerPixel:2
       -                hasAlpha:YES
       -                isPlanar:YES
       -                colorSpaceName:NSDeviceBlackColorSpace
       -                bytesPerRow:2
       -                bitsPerPixel:1];
       -
       -        [r getBitmapDataPlanes:plane];
       -
       -        for(b=0; b<2*16; b++){
       -                plane[0][b] = c->set[b];
       -                plane[1][b] = c->clr[b];
       +        if(win.cursor){
       +                [win.cursor release];
       +                win.cursor = nil;
                }
       -        p = NSMakePoint(-c->offset.x, -c->offset.y);
       -        i = [NSImage new];
       -        [i addRepresentation:r];
       -        d = [[NSCursor alloc] initWithImage:i hotSpot:p];
       -        [d set];
       +        if(c){
       +                r = [[NSBitmapImageRep alloc]
       +                        initWithBitmapDataPlanes:nil
       +                        pixelsWide:16
       +                        pixelsHigh:16
       +                        bitsPerSample:1
       +                        samplesPerPixel:2
       +                        hasAlpha:YES
       +                        isPlanar:YES
       +                        colorSpaceName:NSDeviceBlackColorSpace
       +                        bytesPerRow:2
       +                        bitsPerPixel:1];
       +
       +                [r getBitmapDataPlanes:plane];
       +
       +                for(b=0; b<2*16; b++){
       +                        plane[0][b] = c->set[b];
       +                        plane[1][b] = c->clr[b];
       +                }
       +                p = NSMakePoint(-c->offset.x, -c->offset.y);
       +                i = [NSImage new];
       +                [i addRepresentation:r];
        
       -        [d release];
       -        [r release];
       -        [i release];
       +                win.cursor = [[NSCursor alloc] initWithImage:i hotSpot:p];
       +
       +                [i release];
       +                [r release];
       +        }
       +        qunlock(&win.cursorl);
       +        [WIN invalidateCursorRectsForView:win.content];
        }
 (DIR) diff --git a/src/cmd/devdraw/cocoa-srv.c b/src/cmd/devdraw/cocoa-srv.c
       t@@ -78,8 +78,6 @@ servep9p(void)
        
                fmtinstall('W', drawfcallfmt);
                
       -//        notify(bell);
       -
                mbuf = nil;
                nmbuf = 0;
                while((n = read(3, buf, 4)) == 4){
 (DIR) diff --git a/src/cmd/devdraw/cocoa-thread.c b/src/cmd/devdraw/cocoa-thread.c
       t@@ -2,6 +2,8 @@
        #include <libc.h>
        #include "cocoa-thread.h"
        
       +#ifndef TRY_LIBTHREAD
       +
        static pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER;
        
        void
       t@@ -23,3 +25,4 @@ qunlock(QLock *q)
        {
                pthread_mutex_unlock(&q->m);
        }
       +#endif
 (DIR) diff --git a/src/cmd/devdraw/cocoa-thread.h b/src/cmd/devdraw/cocoa-thread.h
       t@@ -1,14 +1,33 @@
       -#define QLock DQLock
       -#define qlock dqlock
       -#define qunlock dqunlock
       +/*
       + * I am too ignorant to know if Cocoa and Libthread
       + * can coexist: if I try to include thread.h, now
       + * that Devdraw uses Cocoa's threads (and timers), it
       + * crashes immediately; when Devdraw was using
       + * proccreate(), it could run a little while before to
       + * crash; the origin of those crashes is hard to
       + * ascertain, because other programs using Libthread
       + * (such as 9term, Acme, Plumber, and Sam) currently
       + * don't run when compiled with Xcode 4.1.
       + */
       +//#define TRY_LIBTHREAD
        
       -typedef struct QLock QLock;
       +#ifdef TRY_LIBTHREAD
       +        #include <thread.h>
       +#else
       +        #define QLock DQLock
       +        #define qlock dqlock
       +        #define qunlock dqunlock
       +        #define threadexitsall exits
       +        #define threadmain main
        
       -struct QLock
       -{
       -        pthread_mutex_t m;
       -        int init;
       -};
       +        typedef struct QLock QLock;
        
       -void        qlock(QLock*);
       -void        qunlock(QLock*);
       +        struct QLock
       +        {
       +                int init;
       +                pthread_mutex_t m;
       +        };
       +
       +        void        qlock(QLock*);
       +        void        qunlock(QLock*);
       +#endif