tdevdraw: more fixes (thanks David Jeannot) - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit d0a596c5c8669950a2499d3012c340cfbf0eeed5
parent 210d461c87a6c5f598ef958b303a7f24d4e4a93b
Author: Russ Cox 
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(-)
---
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)
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 
 
-#undef Rect
-#undef Point
 #undef Cursor
+#undef Point
+#undef Rect
 
 #include 
 #include 
-#include  "cocoa-thread.h"        // try libthread when possible
+#include  "cocoa-thread.h"
 #include 
 #include 
 #include 
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 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; iMinswipe || 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= 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];
 }
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){
diff --git a/src/cmd/devdraw/cocoa-thread.c b/src/cmd/devdraw/cocoa-thread.c
t@@ -2,6 +2,8 @@
 #include 
 #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
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 
+#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