URI: 
       tdevdraw: draft of new Cocoa-based devdraw - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 113867b836eaa85215e4b2ece5ccf612f34c3e03
   DIR parent daea2c7d501c3e825bede80992ade6b241efdce1
  HTML Author: Jeff Sickel <jas@corpus-callosum.com>
       Date:   Tue, 29 Sep 2009 09:35:23 -0700
       
       devdraw: draft of new Cocoa-based devdraw
       
       Can test with
               cd $PLAN9/src/cmd/devdraw
               mk devdraw-cocoa
               DEVDRAW=devdraw-cocoa colors
       
       Diffstat:
         M src/cmd/devdraw/mkfile              |      11 ++++++++++-
         A src/cmd/devdraw/osx-delegate.h      |      15 +++++++++++++++
         A src/cmd/devdraw/osx-delegate.m      |     282 +++++++++++++++++++++++++++++++
         A src/cmd/devdraw/osx-screen.m        |     680 +++++++++++++++++++++++++++++++
         A src/cmd/devdraw/osx-srv.m           |     452 +++++++++++++++++++++++++++++++
       
       5 files changed, 1439 insertions(+), 1 deletion(-)
       ---
   DIR diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile
       t@@ -7,7 +7,7 @@ WSYSOFILES=\
                latin1.$O\
                mouseswap.$O\
                winsize.$O\
       -        
       +
        <|sh ./mkwsysrules.sh
        
        OFILES=$WSYSOFILES
       t@@ -32,3 +32,12 @@ latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd
                ./$O.mklatinkbd -r $PLAN9/lib/keyboard | sed 's/, }/ }/' >$target
        
        CLEANFILES=latin1.h $O.mklatinkbd
       +
       +# Still in progress: Cocoa/Objective C version of devdraw
       +
       +%-objc.$O: %.m
       +        $CC $CFLAGS -o $target $stem.m
       +
       +devdraw-cocoa: devdraw.o latin1.o mouseswap.o winsize.o osx-screen-objc.o osx-draw.o osx-srv-objc.o osx-delegate-objc.o
       +        $LD -o $target $prereq
       +
   DIR diff --git a/src/cmd/devdraw/osx-delegate.h b/src/cmd/devdraw/osx-delegate.h
       t@@ -0,0 +1,15 @@
       +#import <Foundation/NSObject.h>
       +#import <AppKit/NSMenu.h>
       +
       +@class NSFileHandle;
       +
       +@interface DevdrawDelegate : NSObject
       +{
       +        NSFileHandle *readHandle;
       +}
       ++(void)populateMainMenu;
       ++(void)populateApplicationMenu:(NSMenu *)aMenu;
       ++(void)populateViewMenu:(NSMenu *)aMenu;
       ++(void)populateWindowMenu:(NSMenu *)aMenu;
       ++(void)populateHelpMenu:(NSMenu *)aMenu;
       +@end
   DIR diff --git a/src/cmd/devdraw/osx-delegate.m b/src/cmd/devdraw/osx-delegate.m
       t@@ -0,0 +1,282 @@
       +#define Point OSXPoint
       +#define Rect OSXRect
       +#define Cursor OSXCursor
       +#import "osx-delegate.h"
       +#import <Foundation/Foundation.h>
       +#import <AppKit/AppKit.h>
       +#undef Cursor
       +#undef Rect
       +#undef Point
       +
       +#include <u.h>
       +#include <errno.h>
       +#include <sys/select.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <memdraw.h>
       +#include <memlayer.h>
       +#include <keyboard.h>
       +#include <mouse.h>
       +#include <cursor.h>
       +#include <drawfcall.h>
       +
       +AUTOFRAMEWORK(Foundation)
       +AUTOFRAMEWORK(AppKit)
       +
       +extern int trace;
       +
       +extern void fullscreen(int);
       +extern void kbdevent(NSEvent *event);
       +extern void mouseevent(NSEvent *event);
       +extern void eresized(int);
       +
       +extern void runmsg(Wsysmsg *m);
       +extern void seticon();
       +
       +@implementation DevdrawDelegate
       ++(void)populateMainMenu
       +{
       +        NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"];
       +        NSMenuItem *menuItem;
       +        NSMenu *submenu;
       +
       +        menuItem = [mainMenu addItemWithTitle:@"Apple" action:NULL keyEquivalent:@""];
       +        submenu = [[NSMenu alloc] initWithTitle:@"Apple"];
       +        [NSApp performSelector:@selector(setAppleMenu:) withObject:submenu];
       +        [self populateApplicationMenu:submenu];
       +        [mainMenu setSubmenu:submenu forItem:menuItem];
       +
       +        menuItem = [mainMenu addItemWithTitle:@"View" action:NULL keyEquivalent:@""];
       +        submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"View", "@The View menu")];
       +        [self populateViewMenu:submenu];
       +        [mainMenu setSubmenu:submenu forItem:menuItem];
       +
       +        menuItem = [mainMenu addItemWithTitle:@"Window" action:NULL keyEquivalent:@""];
       +        submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", @"The Window menu")];
       +        [self populateWindowMenu:submenu];
       +        [mainMenu setSubmenu:submenu forItem:menuItem];
       +        [NSApp setWindowsMenu:submenu];
       +
       +        menuItem = [mainMenu addItemWithTitle:@"Help" action:NULL keyEquivalent:@""];
       +        submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", @"The Help menu")];
       +        [self populateHelpMenu:submenu];
       +        [mainMenu setSubmenu:submenu forItem:menuItem];
       +
       +        [NSApp setMainMenu:mainMenu];
       +}
       +
       ++(void)populateApplicationMenu:(NSMenu *)aMenu
       +{
       +        NSString *applicationName = [[NSProcessInfo processInfo] processName];
       +        NSMenuItem *menuItem;
       +        
       +        menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"About", nil), applicationName]
       +                                                                action:@selector(orderFrontStandardAboutPanel:)
       +                                                 keyEquivalent:@""];
       +        [menuItem setTarget:NSApp];
       +        
       +        [aMenu addItem:[NSMenuItem separatorItem]];
       +        
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Preferences...", nil)
       +                                                                action:NULL
       +                                                 keyEquivalent:@","];
       +        
       +        [aMenu addItem:[NSMenuItem separatorItem]];
       +        
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Services", nil)
       +                                                                action:NULL
       +                                                 keyEquivalent:@""];
       +        NSMenu * servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
       +        [aMenu setSubmenu:servicesMenu forItem:menuItem];
       +        [NSApp setServicesMenu:servicesMenu];
       +        
       +        [aMenu addItem:[NSMenuItem separatorItem]];
       +        
       +        menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Hide", nil), applicationName]
       +                                                                action:@selector(hide:)
       +                                                 keyEquivalent:@"h"];
       +        [menuItem setTarget:NSApp];
       +        
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Hide Others", nil)
       +                                                                action:@selector(hideOtherApplications:)
       +                                                 keyEquivalent:@"h"];
       +        [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
       +        [menuItem setTarget:NSApp];
       +        
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Show All", nil)
       +                                                                action:@selector(unhideAllApplications:)
       +                                                 keyEquivalent:@""];
       +        [menuItem setTarget:NSApp];
       +        
       +        [aMenu addItem:[NSMenuItem separatorItem]];
       +        
       +        menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Quit", nil), applicationName]
       +                                                                action:@selector(terminate:)
       +                                                 keyEquivalent:@"q"];
       +        [menuItem setTarget:NSApp];
       +}
       +
       ++(void)populateViewMenu:(NSMenu *)aMenu
       +{
       +        NSMenuItem *menuItem;
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Full Screen", nil)
       +                                action:@selector(fullscreen:) keyEquivalent:@"F"];
       +        [menuItem setTarget:NSApp];
       +
       +        menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Cmd-F exits full screen", nil)
       +                                action:NULL keyEquivalent:@""];
       +}
       +
       ++(void)populateWindowMenu:(NSMenu *)aMenu
       +{
       +}
       +
       ++(void)populateHelpMenu:(NSMenu *)aMenu
       +{
       +}
       +
       +- (void)applicationWillFinishLaunching:(NSNotification *)notification
       +{
       +        seticon();
       +}
       +
       +- (void)applicationDidFinishLaunching:(NSNotification *)notification
       +{
       +        [DevdrawDelegate populateMainMenu];
       +
       +//        [NSThread detachNewThreadSelector:@selector(devdrawMain)
       +//                toTarget:self withObject:nil];
       +//        [NSApplication detachDrawingThread:@selector(devdrawMain)
       +//                toTarget:self withObject:nil];
       +        [readHandle waitForDataInBackgroundAndNotify];
       +}
       +
       +- (id)init
       +{
       +        if(self = [super init]){
       +                readHandle = [[NSFileHandle alloc] initWithFileDescriptor:3 closeOnDealloc:YES];
       +                [[NSNotificationCenter defaultCenter] addObserver:self
       +                        selector:@selector(devdrawMain:)
       +                        name:NSFileHandleDataAvailableNotification
       +                        object:readHandle];
       +                [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
       +                        selector:@selector(receiveWake:)
       +                        name:NSWorkspaceDidWakeNotification
       +                        object:NULL];
       +        }
       +        return self;
       +}
       +
       +- (void)dealloc
       +{
       +        [[NSNotificationCenter defaultCenter] removeObserver:self];
       +        [readHandle release];
       +        return [super dealloc];
       +}
       +
       +- (void)devdrawMain:(NSNotification *)notification
       +{
       +        uchar buf[4], *mbuf;
       +        int nmbuf, n, nn;
       +        Wsysmsg m;
       +        NSData *data;
       +
       +        mbuf = nil;
       +        nmbuf = 0;
       +
       +        data = [readHandle readDataOfLength:4];
       +        if([data length] == 4){
       +                [data getBytes:buf length: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);
       +                data = [readHandle readDataOfLength:(n-4)];
       +                [data getBytes:(mbuf+4)];
       +                nn = [data length];
       +                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);
       +        } else {
       +                [NSApp terminate:self];
       +        }
       +        [readHandle waitForDataInBackgroundAndNotify];
       +
       +return;
       +
       +        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);
       +        }
       +}
       +
       +#pragma mark Notifications
       +
       +- (void)fullscreen:(NSNotification *)notification
       +{
       +        fullscreen(1);
       +}
       +
       +- (void)windowWillClose:(NSNotification *)notification
       +{
       +//        if(osx.window == [notification object]){
       +                [[NSNotificationCenter defaultCenter] removeObserver:self];
       +                [NSApp terminate:self];
       +//        }
       +}
       +
       +- (void)windowDidResize:(NSNotification *)notification
       +{
       +//        if(osx.window == [notification object]) {
       +                eresized(1);
       +//        }
       +}
       +
       +- (void)receiveWake:(NSNotification *)notification
       +{
       +        if(trace) NSLog(@"%s:%d %@", __FILE__, __LINE__, notification);
       +        // redraw
       +}
       +
       +- (void)mouseDown:(NSEvent *)anEvent
       +{
       +        mouseevent(anEvent);
       +}
       +
       +- (void)mouseDragged:(NSEvent *)anEvent
       +{
       +        mouseevent(anEvent);
       +}
       +
       +- (void)keydown:(NSEvent *)anEvent
       +{
       +        kbdevent(anEvent);
       +}
       +
       +@end
   DIR diff --git a/src/cmd/devdraw/osx-screen.m b/src/cmd/devdraw/osx-screen.m
       t@@ -0,0 +1,680 @@
       +#define Point OSXPoint
       +#define Rect OSXRect
       +#define Cursor OSXCursor
       +#import <Cocoa/Cocoa.h>
       +#import <AppKit/AppKit.h>
       +#undef Rect
       +#undef Point
       +#undef Cursor
       +#undef offsetof
       +#undef nil
       +
       +#include <u.h>
       +#include <libc.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(Cocoa)
       +
       +#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;
       +        NSWindow *window;
       +        CGImageRef image;
       +        CGContextRef windowctx;
       +
       +        int needflush;
       +        QLock flushlock;
       +        int active;
       +        int infullscreen;
       +        int kalting;                // last keystroke was Kalt
       +} osx;
       +
       +enum
       +{
       +        WindowAttrs = NSClosableWindowMask |
       +                NSTitledWindowMask |
       +                NSMiniaturizableWindowMask |
       +                NSResizableWindowMask
       +};
       +
       +static void screenproc(void*);
       + void eresized(int);
       + void fullscreen(int);
       + void seticon(void);
       +static void activated(int);
       +
       +enum
       +{
       +        CmdFullScreen = 1,
       +};
       +
       +@interface P9View : NSView
       +{}
       +@end
       +
       +@implementation P9View
       +- (BOOL)acceptsFirstResponder
       +{
       +        return YES;
       +}
       +@end
       +
       +void screeninit(void);
       +void _flushmemscreen(Rectangle r);
       +
       +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;
       +}
       +
       +void
       +_screeninit(void)
       +{
       +        CGRect cgr;
       +        NSRect or;
       +        Rectangle r;
       +        int havemin;
       +
       +        memimageinit();
       +
       +        cgr = CGDisplayBounds(CGMainDisplayID());
       +        osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
       +        
       +        // 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 = NSMakeRect(r.min.x, r.min.y, r.max.x, r.max.y);
       +        osx.window = [[NSWindow alloc] initWithContentRect:or styleMask:WindowAttrs
       +                 backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
       +        [osx.window setDelegate:[NSApp delegate]];
       +        [osx.window setAcceptsMouseMovedEvents:YES];
       +
       +        P9View *view = [[P9View alloc] initWithFrame:or];
       +        [osx.window setContentView:view];
       +        [view release];
       +
       +        setlabel(osx.label);
       +        seticon();
       +        
       +        // Finally, put the window on the screen.
       +        eresized(0);
       +        [osx.window makeKeyAndOrderFront:nil];
       +
       +        [NSCursor unhide];
       +}
       +
       +static Rendez scr;
       +static QLock slock;
       +
       +void
       +screeninit(void)
       +{
       +        scr.l = &slock;
       +        qlock(scr.l);
       +//        proccreate(screenproc, nil, 256*1024);
       +        screenproc(NULL);
       +        while(osx.window == nil)
       +                rsleep(&scr);
       +        qunlock(scr.l);
       +}
       +
       +static void
       +screenproc(void *v)
       +{
       +        qlock(scr.l);
       +        _screeninit();
       +        rwakeup(&scr);
       +        qunlock(scr.l);
       +}
       +
       +static ulong
       +msec(void)
       +{
       +        return nsec()/1000000;
       +}
       +
       +//static void
       +void
       +mouseevent(NSEvent *event)
       +{
       +        int wheel;
       +        NSPoint op;
       +        
       +        op = [event locationInWindow];
       +
       +        osx.xy = subpt(Pt(op.x, op.y), osx.screenr.min);
       +        wheel = 0;
       +
       +        switch([event type]){
       +        case NSScrollWheel:;
       +                CGFloat delta = [event deltaY];
       +                if(delta > 0)
       +                        wheel = 8;
       +                else
       +                        wheel = 16;
       +                break;
       +        
       +        case NSLeftMouseDown:
       +        case NSRightMouseDown:
       +        case NSOtherMouseDown:
       +        case NSLeftMouseUp:
       +        case NSRightMouseUp:
       +        case NSOtherMouseUp:;
       +                NSInteger but;
       +                NSUInteger  mod;
       +                but = [event buttonNumber];
       +                mod = [event modifierFlags];
       +                
       +                // OS X swaps button 2 and 3
       +                but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
       +                but = mouseswap(but);
       +                
       +                // 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 & NSAlternateKeyMask) {
       +                                // Take the ALT away from the keyboard handler.
       +                                if(osx.kalting) {
       +                                        osx.kalting = 0;
       +                                        keystroke(Kalt);
       +                                }
       +                                but = 2;
       +                        }
       +                        else if(mod & NSCommandKeyMask)
       +                                but = 4;
       +                }
       +                osx.buttons = but;
       +                break;
       +
       +        case NSMouseMoved:
       +        case NSLeftMouseDragged:
       +        case NSRightMouseDragged:
       +        case NSOtherMouseDragged:
       +                break;
       +        
       +        default:
       +                return;
       +        }
       +
       +        mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
       +}
       +
       +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 void
       +void
       +kbdevent(NSEvent *event)
       +{
       +        char ch;
       +        UInt32 code;
       +        UInt32 mod;
       +        int k;
       +
       +        ch = [[event characters] characterAtIndex:0];
       +        code = [event keyCode];
       +        mod = [event modifierFlags];
       +
       +        switch([event type]){
       +        case NSKeyDown:
       +                osx.kalting = 0;
       +                if(mod == NSCommandKeyMask){
       +                        if(ch == 'F' || ch == 'f'){
       +                                if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
       +                                        fullscreen(0);
       +                                return;
       +                        }
       +                        
       +                        // 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;
       +                        }
       +                        if(' ' <= ch && ch <= '~') {
       +                                keystroke(Kcmd + ch);
       +                                return;
       +                        }
       +                        return;
       +                }
       +                k = ch;
       +                if(code < nelem(keycvt) && keycvt[code])
       +                        k = keycvt[code];
       +                if(k >= 0)
       +                        keystroke(k);
       +                else{
       +                        keystroke(ch);
       +                }
       +                break;
       +
       +        case NSFlagsChanged:
       +                if(!osx.buttons && !osx.kbuttons){
       +                        if(mod == NSAlternateKeyMask) {
       +                                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 & NSAlternateKeyMask)
       +                        osx.kbuttons |= 2;
       +                if(mod & NSCommandKeyMask)
       +                        osx.kbuttons |= 4;
       +                mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
       +                break;
       +        }
       +        return;
       +}
       +
       +//static void
       +void
       +eresized(int new)
       +{
       +        Memimage *m;
       +        NSRect or;
       +        ulong chan;
       +        Rectangle r;
       +        int bpl;
       +        CGDataProviderRef provider;
       +        CGImageRef image;
       +        CGColorSpaceRef cspace;
       +
       +        or = [[osx.window contentView] bounds];
       +        r = Rect(or.origin.x, or.origin.y, or.size.width, or.size.height);
       +        if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr)){
       +                // 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;
       +}
       +
       +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){
       +                osx.windowctx = [[osx.window graphicsContext] graphicsPort];
       +//                [osx.window flushWindow];
       +//                proccreate(flushproc, nil, 256*1024);
       +        }
       +        
       +        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)
       +{
       +        osx.active = active;
       +}
       +
       +void
       +fullscreen(int wascmd)
       +{
       +        NSView *view = [osx.window contentView];
       +
       +        if(osx.isfullscreen){
       +                [view exitFullScreenModeWithOptions:nil];
       +                osx.isfullscreen = 0;
       +        }else{
       +                [view enterFullScreenMode:[osx.window screen] withOptions:nil];
       +                osx.isfullscreen = 1;
       +                osx.fullscreentime = msec();
       +        }
       +        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);
       +}
       +
       +void
       +setcursor(Cursor *c)
       +{
       +        NSImage *image;
       +        NSBitmapImageRep *bitmap;
       +        NSCursor *nsc;
       +        unsigned char *planes[5];
       +        int i;
       +
       +        if(c == nil){
       +                [NSCursor pop];
       +                return;
       +        }
       +        
       +        image = [[NSImage alloc] initWithSize:NSMakeSize(16.0, 16.0)];
       +        bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
       +                pixelsWide:16
       +                pixelsHigh:16
       +                bitsPerSample:1
       +                samplesPerPixel:2
       +                hasAlpha:YES
       +                isPlanar:YES
       +                colorSpaceName:NSCalibratedWhiteColorSpace
       +                bytesPerRow:2
       +                bitsPerPixel:1];
       +
       +        [bitmap getBitmapDataPlanes:planes];
       +
       +        for(i=0; i<16; i++){
       +                planes[0][i] = ((ushort*)c->set)[i];
       +                planes[1][i] = planes[0][i] | ((ushort*)c->clr)[i];
       +        }
       +
       +        [image addRepresentation:bitmap];
       +
       +        nsc = [[NSCursor alloc] initWithImage:image
       +                hotSpot:NSMakePoint(c->offset.x, c->offset.y)];
       +        [nsc push];
       +
       +        [image release];
       +        [bitmap release];
       +        [nsc release];
       +}
       +
       +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];
       +        NSPasteboard *apple;
       +} clip;
       +
       +char*
       +getsnarf(void)
       +{
       +        char *s, *t;
       +        NSArray *types;
       +        NSString *string;
       +        NSData * data;
       +        NSUInteger ndata;
       +
       +/*        fprint(2, "applegetsnarf\n"); */
       +        qlock(&clip.lk);
       +
       +        clip.apple = [NSPasteboard generalPasteboard];
       +        types = [clip.apple types];
       +
       +        string = [clip.apple stringForType:NSStringPboardType];
       +        if(string == nil){
       +                fprint(2, "apple pasteboard get item type failed\n");
       +                qunlock(&clip.lk);
       +                return nil;
       +        }
       +
       +        data = [string dataUsingEncoding:NSUnicodeStringEncoding];
       +        if(data != nil){
       +                ndata = [data length];
       +                qunlock(&clip.lk);
       +                s = smprint("%.*S", ndata/2, (Rune*)[data bytes]);
       +                for(t=s; *t; t++)
       +                        if(*t == '\r')
       +                                *t = '\n';
       +                return s;
       +        }
       +
       +        qunlock(&clip.lk);
       +        return nil;                
       +}
       +
       +void
       +putsnarf(char *s)
       +{
       +        NSArray *pboardTypes;
       +        NSString *string;
       +
       +/*        fprint(2, "appleputsnarf\n"); */
       +
       +        if(strlen(s) >= SnarfSize)
       +                return;
       +        qlock(&clip.lk);
       +        strcpy(clip.buf, s);
       +        runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
       +
       +        pboardTypes = [NSArray arrayWithObject:NSStringPboardType];
       +
       +        clip.apple = [NSPasteboard generalPasteboard];
       +        [clip.apple declareTypes:pboardTypes owner:nil];
       +
       +        assert(sizeof(clip.rbuf[0]) == 2);
       +        string = [NSString stringWithCharacters:clip.rbuf length:runestrlen(clip.rbuf)*2];
       +        if(string == nil){
       +                fprint(2, "apple pasteboard data create failed\n");
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        if(![clip.apple setString:string forType:NSStringPboardType]){
       +                fprint(2, "apple pasteboard putitem failed\n");
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        qunlock(&clip.lk);
       +}
       +
       +void
       +setlabel(char *label)
       +{
       +        CFStringRef cs;
       +        cs = CFStringCreateWithBytes(nil, (uchar*)label, strlen(osx.label), kCFStringEncodingUTF8, false);
       +        [osx.window setTitle:(NSString*)cs];
       +        CFRelease(cs);
       +}
       +
       +void
       +kicklabel(char *label)
       +{
       +        char *p;
       +
       +        p = strdup(label);
       +        if(p == nil)
       +                return;
       +        qlock(&osx.labellock);
       +        free(osx.label);
       +        osx.label = p;
       +        qunlock(&osx.labellock);
       +
       +        setlabel(label);        
       +}
       +
       +// static void
       +void
       +seticon(void)
       +{
       +        NSImage *im;
       +        NSData *d;
       +
       +        d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)];
       +        im = [[NSImage alloc] initWithData:d];
       +        if(im){
       +                NSLog(@"here");
       +                [NSApp setApplicationIconImage:im];
       +                [[NSApp dockTile] setShowsApplicationBadge:YES];
       +                [[NSApp dockTile] display];
       +        }
       +        [d release];
       +        [im release];
       +}
   DIR diff --git a/src/cmd/devdraw/osx-srv.m b/src/cmd/devdraw/osx-srv.m
       t@@ -0,0 +1,452 @@
       +#define Point OSXPoint
       +#define Rect OSXRect
       +#define Cursor OSXCursor
       +#import <AppKit/AppKit.h>
       +#undef Rect
       +#undef Point
       +#undef Cursor
       +
       +/*
       + * Window system protocol server.
       + */
       +
       +#include <u.h>
       +#include <errno.h>
       +#include <sys/select.h>
       +#include <libc.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"
       +
       +AUTOFRAMEWORK(AppKit)
       +
       +#import "osx-delegate.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;
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: devdraw (don't run directly)\n");
       +        exits("usage");
       +}
       +
       +void
       +bell(void *v, char *msg)
       +{
       +        if(strcmp(msg, "alarm") == 0)
       +                drawsleep = drawsleep ? 0 : 1000;
       +        noted(NCONT);
       +}
       +
       +int
       +main(int argc, char **argv)
       +{
       +        NSAutoreleasePool *pool = nil;
       +        NSApplication *application = nil;
       +
       +        /*
       +         * 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;
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        /*
       +         * Ignore arguments.  They're only for good ps -a listings.
       +         */
       +        
       +        notify(bell);
       +
       +        pool = [[NSAutoreleasePool alloc] init];
       +        application = [NSApplication sharedApplication];
       +        [application setDelegate:[[DevdrawDelegate alloc] init]];
       +        [application run];
       +        [application setDelegate:nil];
       +        [pool release];
       +        return 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;
       +}