9vx/OSX: attempt at native GUI code - vx32 - Local 9vx git repository for patches.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit e769e79cbe9454c7a317f53db28954df0c6d6f9b
 (DIR) parent c7c041c8ea44d3c1cc93a9b882963caabf26b4a1
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Sat, 28 Jun 2008 15:08:36 -0400
       
       9vx/OSX: attempt at native GUI code
       
       Diffstat:
         src/9vx/Makefrag                    |      15 +++++++++++++--
         src/9vx/main.c                      |       4 ++++
         src/9vx/osx/draw.c                  |      54 +++++++++++++++++++++++++++++++
         src/9vx/osx/keycodes.h              |     189 +++++++++++++++++++++++++++++++
         src/9vx/osx/screen.c                |     632 +++++++++++++++++++++++++++++++
         src/9vx/stub.c                      |       5 +++++
       
       6 files changed, 897 insertions(+), 2 deletions(-)
       ---
 (DIR) diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag
       @@ -10,7 +10,8 @@ endif
        
        ifeq ($(OS),darwin)
        PLAN9VX=1
       -  # TODO: carbon
       +# TODO: osx works except that Carbon gets confused
       +# that we can survive an EXC_BAD_ACCESS
        PLAN9GUI=x11
        PLAN9AUDIO=none
        endif
       @@ -124,9 +125,16 @@ PLAN9_x11_OBJS = \
                        x11-kernel.o \
                        x11-keysym2rune.o \
                )
       -
        PLAN9_x11_LIBS = -L/usr/X11R6/lib -L/usr/local/lib -lX11
        
       +PLAN9_osx_OBJS =\
       +        $(addprefix 9vx/osx/, \
       +                screen.o \
       +                draw.o \
       +        )
       +PLAN9_osx_LIBS = -ggdb -framework Carbon -framework QuickTime
       +
       +
        PLAN9_GUI_OBJS = $(PLAN9_$(PLAN9GUI)_OBJS)
        PLAN9_GUI_LIBS = $(PLAN9_$(PLAN9GUI)_LIBS)
        
       @@ -149,6 +157,9 @@ PLAN9_DEPS = \
        9vx/x11/%.o: 9vx/x11/%.c
                $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -I/usr/X11R6/include -I/usr/local/include -Wall -Wno-missing-braces -c -o $@ $<
        
       +9vx/osx/%.o: 9vx/osx/%.c
       +        $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -Wall -Wno-missing-braces -c -o $@ $<
       +
        9vx/%.o: 9vx/%.c
                $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -Wall -Wno-missing-braces -c -o $@ $<
        
 (DIR) diff --git a/src/9vx/main.c b/src/9vx/main.c
       @@ -4,6 +4,10 @@
        
        #define        WANT_M
        
       +#ifdef __APPLE__
       +#define __DARWIN_UNIX03 0
       +#endif
       +
        #include        "u.h"
        #include        "libvx32/vx32.h"
        #include        <sys/mman.h>
 (DIR) diff --git a/src/9vx/osx/draw.c b/src/9vx/osx/draw.c
       @@ -0,0 +1,54 @@
       +#include "u.h"
       +#include "lib.h"
       +#include "draw.h"
       +#include "memdraw.h"
       +
       +Memimage*
       +allocmemimage(Rectangle r, ulong chan)
       +{
       +        return _allocmemimage(r, chan);
       +}
       +
       +void
       +freememimage(Memimage *i)
       +{
       +        _freememimage(i);
       +}
       +
       +void
       +memfillcolor(Memimage *i, ulong val)
       +{
       +        _memfillcolor(i, val);
       +}
       +
       +
       +int
       +cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
       +{
       +        return _cloadmemimage(i, r, data, ndata);
       +}
       +
       +void
       +memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op)
       +{
       +        _memimagedraw(_memimagedrawsetup(dst, r, src, sp, mask, mp, op));
       +}
       +
       +ulong
       +pixelbits(Memimage *m, Point p)
       +{
       +        return _pixelbits(m, p);
       +}
       +
       +int
       +loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
       +{
       +        return _loadmemimage(i, r, data, ndata);
       +}
       +
       +int
       +unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
       +{
       +        return _unloadmemimage(i, r, data, ndata);
       +}
       +
 (DIR) diff --git a/src/9vx/osx/keycodes.h b/src/9vx/osx/keycodes.h
       @@ -0,0 +1,189 @@
       +/* These are the Macintosh key scancode constants -- from Inside Macintosh */
       +#define QZ_ESCAPE                0x35
       +#define QZ_F1                        0x7A
       +#define QZ_F2                        0x78
       +#define QZ_F3                        0x63
       +#define QZ_F4                        0x76
       +#define QZ_F5                        0x60
       +#define QZ_F6                        0x61
       +#define QZ_F7                        0x62
       +#define QZ_F8                        0x64
       +#define QZ_F9                        0x65
       +#define QZ_F10                        0x6D
       +#define QZ_F11                        0x67
       +#define QZ_F12                        0x6F
       +#define QZ_PRINT                0x69
       +#define QZ_SCROLLOCK            0x6B
       +#define QZ_PAUSE                0x71
       +#define QZ_POWER                0x7F
       +#define QZ_BACKQUOTE                0x32
       +#define QZ_1                        0x12
       +#define QZ_2                        0x13
       +#define QZ_3                        0x14
       +#define QZ_4                        0x15
       +#define QZ_5                        0x17
       +#define QZ_6                        0x16
       +#define QZ_7                        0x1A
       +#define QZ_8                        0x1C
       +#define QZ_9                        0x19
       +#define QZ_0                        0x1D
       +#define QZ_MINUS                0x1B
       +#define QZ_EQUALS                0x18
       +#define QZ_BACKSPACE                0x33
       +#define QZ_INSERT                0x72
       +#define QZ_HOME                        0x73
       +#define QZ_PAGEUP                0x74
       +#define QZ_NUMLOCK                0x47
       +#define QZ_KP_EQUALS                0x51
       +#define QZ_KP_DIVIDE                0x4B
       +#define QZ_KP_MULTIPLY                0x43
       +#define QZ_TAB                        0x30
       +#define QZ_q                        0x0C
       +#define QZ_w                        0x0D
       +#define QZ_e                        0x0E
       +#define QZ_r                        0x0F
       +#define QZ_t                        0x11
       +#define QZ_y                        0x10
       +#define QZ_u                        0x20
       +#define QZ_i                        0x22
       +#define QZ_o                        0x1F
       +#define QZ_p                        0x23
       +#define QZ_LEFTBRACKET                0x21
       +#define QZ_RIGHTBRACKET                0x1E
       +#define QZ_BACKSLASH                0x2A
       +#define QZ_DELETE                0x75
       +#define QZ_END                        0x77
       +#define QZ_PAGEDOWN                0x79
       +#define QZ_KP7                        0x59
       +#define QZ_KP8                        0x5B
       +#define QZ_KP9                        0x5C
       +#define QZ_KP_MINUS                0x4E
       +#define QZ_CAPSLOCK                0x39
       +#define QZ_a                        0x00
       +#define QZ_s                        0x01
       +#define QZ_d                        0x02
       +#define QZ_f                        0x03
       +#define QZ_g                        0x05
       +#define QZ_h                        0x04
       +#define QZ_j                        0x26
       +#define QZ_k                        0x28
       +#define QZ_l                        0x25
       +#define QZ_SEMICOLON                0x29
       +#define QZ_QUOTE                0x27
       +#define QZ_RETURN                0x24
       +#define QZ_KP4                        0x56
       +#define QZ_KP5                        0x57
       +#define QZ_KP6                        0x58
       +#define QZ_KP_PLUS                0x45
       +#define QZ_LSHIFT                0x38
       +#define QZ_z                        0x06
       +#define QZ_x                        0x07
       +#define QZ_c                        0x08
       +#define QZ_v                        0x09
       +#define QZ_b                        0x0B
       +#define QZ_n                        0x2D
       +#define QZ_m                        0x2E
       +#define QZ_COMMA                0x2B
       +#define QZ_PERIOD                0x2F
       +#define QZ_SLASH                0x2C
       +/* These are the same as the left versions - use left by default */
       +#if 0
       +#define QZ_RSHIFT                0x38
       +#endif
       +#define QZ_UP                        0x7E
       +#define QZ_KP1                        0x53
       +#define QZ_KP2                        0x54
       +#define QZ_KP3                        0x55
       +#define QZ_KP_ENTER                0x4C
       +#define QZ_LCTRL                0x3B
       +#define QZ_LALT                        0x3A
       +#define QZ_LMETA                0x37
       +#define QZ_SPACE                0x31
       +/* These are the same as the left versions - use left by default */
       +#if 0
       +#define QZ_RMETA                0x37
       +#define QZ_RALT                        0x3A
       +#define QZ_RCTRL                0x3B
       +#endif
       +#define QZ_LEFT                        0x7B
       +#define QZ_DOWN                        0x7D
       +#define QZ_RIGHT                0x7C
       +#define QZ_KP0                        0x52
       +#define QZ_KP_PERIOD                0x41
       +
       +/* Wierd, these keys are on my iBook under MacOS X */
       +#define QZ_IBOOK_ENTER                0x34
       +#define QZ_IBOOK_LEFT                0x3B
       +#define QZ_IBOOK_RIGHT                0x3C
       +#define QZ_IBOOK_DOWN                0x3D
       +#define QZ_IBOOK_UP                0x3E
       +#define KEY_ENTER 13
       +#define KEY_TAB 9
       +
       +#define KEY_BASE 0x100
       +
       +/*  Function keys  */
       +#define KEY_F (KEY_BASE+64)
       +
       +/* Control keys */
       +#define KEY_CTRL (KEY_BASE)
       +#define KEY_BACKSPACE (KEY_CTRL+0)
       +#define KEY_DELETE (KEY_CTRL+1)
       +#define KEY_INSERT (KEY_CTRL+2)
       +#define KEY_HOME (KEY_CTRL+3)
       +#define KEY_END (KEY_CTRL+4)
       +#define KEY_PAGE_UP (KEY_CTRL+5)
       +#define KEY_PAGE_DOWN (KEY_CTRL+6)
       +#define KEY_ESC (KEY_CTRL+7)
       +
       +/* Control keys short name */
       +#define KEY_BS KEY_BACKSPACE
       +#define KEY_DEL KEY_DELETE
       +#define KEY_INS KEY_INSERT
       +#define KEY_PGUP KEY_PAGE_UP
       +#define KEY_PGDOWN KEY_PAGE_DOWN
       +#define KEY_PGDWN KEY_PAGE_DOWN
       +
       +/* Cursor movement */
       +#define KEY_CRSR (KEY_BASE+16)
       +#define KEY_RIGHT (KEY_CRSR+0)
       +#define KEY_LEFT (KEY_CRSR+1)
       +#define KEY_DOWN (KEY_CRSR+2)
       +#define KEY_UP (KEY_CRSR+3)
       +
       +/* Multimedia keyboard/remote keys */
       +#define KEY_MM_BASE (0x100+384)
       +#define KEY_POWER (KEY_MM_BASE+0)
       +#define KEY_MENU (KEY_MM_BASE+1)
       +#define KEY_PLAY (KEY_MM_BASE+2)
       +#define KEY_PAUSE (KEY_MM_BASE+3)
       +#define KEY_PLAYPAUSE (KEY_MM_BASE+4)
       +#define KEY_STOP (KEY_MM_BASE+5)
       +#define KEY_FORWARD (KEY_MM_BASE+6)
       +#define KEY_REWIND (KEY_MM_BASE+7)
       +#define KEY_NEXT (KEY_MM_BASE+8)
       +#define KEY_PREV (KEY_MM_BASE+9)
       +#define KEY_VOLUME_UP (KEY_MM_BASE+10)
       +#define KEY_VOLUME_DOWN (KEY_MM_BASE+11)
       +#define KEY_MUTE (KEY_MM_BASE+12)
       +
       +/* Keypad keys */
       +#define KEY_KEYPAD (KEY_BASE+32)
       +#define KEY_KP0 (KEY_KEYPAD+0)
       +#define KEY_KP1 (KEY_KEYPAD+1)
       +#define KEY_KP2 (KEY_KEYPAD+2)
       +#define KEY_KP3 (KEY_KEYPAD+3)
       +#define KEY_KP4 (KEY_KEYPAD+4)
       +#define KEY_KP5 (KEY_KEYPAD+5)
       +#define KEY_KP6 (KEY_KEYPAD+6)
       +#define KEY_KP7 (KEY_KEYPAD+7)
       +#define KEY_KP8 (KEY_KEYPAD+8)
       +#define KEY_KP9 (KEY_KEYPAD+9)
       +#define KEY_KPDEC (KEY_KEYPAD+10)
       +#define KEY_KPINS (KEY_KEYPAD+11)
       +#define KEY_KPDEL (KEY_KEYPAD+12)
       +#define KEY_KPENTER (KEY_KEYPAD+13)
       +
       +/* Special keys */
       +#define KEY_INTERN (0x1000)
       +#define KEY_CLOSE_WIN (KEY_INTERN+0)
 (DIR) diff --git a/src/9vx/osx/screen.c b/src/9vx/osx/screen.c
       @@ -0,0 +1,632 @@
       +#define Point OSXPoint
       +#define Rect OSXRect
       +#define Cursor OSXCursor
       +#include <Carbon/Carbon.h>
       +#include <QuickTime/QuickTime.h> // for full screen
       +#undef Rect
       +#undef Point
       +#undef Cursor
       +#undef offsetof
       +#undef nil
       +
       +#include "u.h"
       +#include "lib.h"
       +#include "mem.h"
       +#include "dat.h"
       +#include "fns.h"
       +#include "error.h"
       +#define Image IMAGE        /* kernel has its own Image */
       +#include <draw.h>
       +#include <memdraw.h>
       +#include <keyboard.h>
       +#include <cursor.h>
       +#include "screen.h"
       +#include "mouse.h"
       +#include "keycodes.h"
       +
       +struct {
       +        Rectangle fullscreenr;
       +        Rectangle screenr;
       +        Memimage *screenimage;
       +        int isfullscreen;
       +        Rectangle nonfullscreenr;
       +        
       +        Point xy;
       +        int buttons;
       +        int kbuttons;
       +
       +        CGDataProviderRef provider;
       +        MenuRef wmenu;
       +        MenuRef vmenu;
       +        WindowRef window;
       +        CGImageRef image;
       +        WindowGroupRef wingroup;
       +        PasteboardRef snarf;
       +} osx;
       +
       +enum
       +{
       +        WindowAttrs =
       +                kWindowCloseBoxAttribute |
       +                kWindowCollapseBoxAttribute |
       +                kWindowResizableAttribute |
       +                kWindowStandardHandlerAttribute |
       +                kWindowFullZoomAttribute
       +};
       +
       +static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
       +static void screenproc(void*);
       +static void eresized(int);
       +static void fullscreen(int);
       +
       +enum
       +{
       +        CmdFullScreen = 1,
       +};
       +
       +uchar*
       +attachscreen(Rectangle *r, ulong *chan, int *depth,
       +        int *width, int *softscreen, void **X)
       +{
       +        Memimage *m;
       +
       +        if(osx.screenimage == nil){
       +                screeninit();
       +                if(osx.screenimage == nil)
       +                        panic("cannot create OS X screen");
       +        }
       +        m = osx.screenimage;
       +        *r = m->r;
       +        *chan = m->chan;
       +        *depth = m->depth;
       +        *width = m->width;
       +        *X = nil;
       +        *softscreen = 1;
       +        return m->data->bdata;
       +}
       +
       +void
       +screeninit(void)
       +{
       +        CGRect cgr;
       +        OSXRect or;
       +
       +        _memimageinit();
       +
       +        ProcessSerialNumber psn = { 0, kCurrentProcess };
       +        TransformProcessType(&psn, kProcessTransformToForegroundApplication);
       +        SetFrontProcess(&psn);
       +
       +        cgr = CGDisplayBounds(CGMainDisplayID());
       +        osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
       +        
       +        InitCursor();
       +        
       +        // Create minimal menu with full-screen option.
       +        ClearMenuBar();
       +        CreateStandardWindowMenu(0, &osx.wmenu);
       +        InsertMenu(osx.wmenu, 0);
       +        MenuItemIndex ix;
       +        CreateNewMenu(1004, 0, &osx.vmenu);        // XXX 1004?
       +        SetMenuTitleWithCFString(osx.vmenu, CFSTR("View"));
       +        AppendMenuItemTextWithCFString(osx.vmenu,
       +                CFSTR("Full Screen"), 0, CmdFullScreen, &ix);
       +        SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F');
       +        AppendMenuItemTextWithCFString(osx.vmenu,
       +                CFSTR("Ctl-Opt exits full screen"),
       +                kMenuItemAttrDisabled, CmdFullScreen, &ix);
       +        InsertMenu(osx.vmenu, GetMenuID(osx.wmenu));
       +        DrawMenuBar();
       +
       +        // Create the window.
       +        or.left = 0;
       +        or.top = 30;
       +        or.bottom = Dy(osx.fullscreenr) - 100;
       +        or.right = Dx(osx.fullscreenr);
       +        CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window);
       +        CreateWindowGroup(0, &osx.wingroup);
       +        SetWindowGroup(osx.window, osx.wingroup);
       +        SetWindowTitleWithCFString(osx.window, CFSTR("Plan 9 VX"));
       +        
       +        // Set up the clip board.
       +        if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr)
       +                panic("pasteboard create");
       +        
       +        // Explain in great detail which events we want to handle.
       +        // Why can't we just have one handler?
       +        const EventTypeSpec quits[] = {
       +                { kEventClassApplication, kEventAppQuit }
       +        };
       +        const EventTypeSpec commands[] = {
       +                { kEventClassWindow, kEventWindowClosed },
       +                { kEventClassWindow, kEventWindowBoundsChanged },
       +                { kEventClassCommand, kEventCommandProcess }
       +        };
       +        const EventTypeSpec events[] = {
       +                { kEventClassKeyboard, kEventRawKeyDown },
       +                { kEventClassKeyboard, kEventRawKeyModifiersChanged },
       +                { kEventClassKeyboard, kEventRawKeyRepeat },
       +                { kEventClassMouse, kEventMouseDown },
       +                { kEventClassMouse, kEventMouseUp },
       +                { kEventClassMouse, kEventMouseMoved },
       +                { kEventClassMouse, kEventMouseDragged },
       +                { kEventClassMouse, kEventMouseWheelMoved },
       +        };
       +        InstallApplicationEventHandler(
       +                NewEventHandlerUPP(eventhandler),
       +                nelem(quits), quits, nil, nil);
       +        InstallApplicationEventHandler(
       +                NewEventHandlerUPP(eventhandler),
       +                nelem(events), events, osx.window, nil);
       +        InstallWindowEventHandler(osx.window,
       +                NewEventHandlerUPP(eventhandler),
       +                nelem(commands), commands, osx.window, nil);
       +        
       +        // Finally, put the window on the screen.
       +        ShowWindow(osx.window);
       +        ShowMenuBar();
       +        eresized(0);
       +        SelectWindow(osx.window);
       +        
       +        InitCursor();
       +
       +        kproc("*screen*", screenproc, nil);
       +}
       +
       +static void
       +screenproc(void *v)
       +{
       +        RunApplicationEventLoop();
       +}
       +
       +static OSStatus kbdevent(EventRef);
       +static OSStatus mouseevent(EventRef);
       +
       +static OSStatus
       +eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
       +{
       +        OSStatus result;
       +        
       +        result = CallNextEventHandler(next, event);
       +        
       +        switch(GetEventClass(event)){
       +        case kEventClassKeyboard:
       +                return kbdevent(event);
       +                break;
       +        
       +        case kEventClassMouse:
       +                return mouseevent(event);
       +                break;
       +        
       +        case kEventClassCommand:;
       +                HICommand cmd;
       +                GetEventParameter(event, kEventParamDirectObject,
       +                        typeHICommand, nil, sizeof cmd, nil, &cmd);
       +                switch(cmd.commandID){
       +                case kHICommandQuit:
       +                        exit(0);
       +                
       +                case CmdFullScreen:
       +                        fullscreen(1);
       +                        break;
       +                
       +                default:
       +                        return eventNotHandledErr;
       +                }
       +                break;
       +        
       +        case kEventClassWindow:;
       +                switch(GetEventKind(event)){
       +                case kEventWindowClosed:
       +                        exit(0);
       +                
       +                case kEventWindowBoundsChanged:
       +                        eresized(1);
       +                        break;
       +                
       +                default:
       +                        return eventNotHandledErr;
       +                }
       +                break;
       +        }
       +        
       +        return result;
       +}
       +
       +static OSStatus
       +mouseevent(EventRef event)
       +{
       +        int wheel;
       +        OSXPoint op;
       +        
       +        GetEventParameter(event, kEventParamMouseLocation,
       +                typeQDPoint, 0, sizeof op, 0, &op);
       +        osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min);
       +        wheel = 0;
       +
       +        switch(GetEventKind(event)){
       +        case kEventMouseWheelMoved:;
       +                SInt32 delta;
       +                GetEventParameter(event, kEventParamMouseWheelDelta,
       +                        typeSInt32, 0, sizeof delta, 0, &delta);
       +                if(delta > 0)
       +                        wheel = 8;
       +                else
       +                        wheel = 16;
       +                break;
       +        
       +        case kEventMouseDown:
       +        case kEventMouseUp:;
       +                UInt32 but, mod;
       +                GetEventParameter(event, kEventParamMouseChord,
       +                        typeUInt32, 0, sizeof but, 0, &but);
       +                GetEventParameter(event, kEventParamKeyModifiers,
       +                        typeUInt32, 0, sizeof mod, 0, &mod);
       +                
       +                // OS X swaps button 2 and 3
       +                but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
       +                
       +                // Apply keyboard modifiers and pretend it was a real mouse button.
       +                // (Modifiers typed while holding the button go into kbuttons,
       +                // but this one does not.)
       +                if(but == 1){
       +                        if(mod & optionKey)
       +                                but = 2;
       +                        else if(mod & cmdKey)
       +                                but = 4;
       +                }
       +                osx.buttons = but;
       +                break;
       +
       +        case kEventMouseMoved:
       +                break;
       +        
       +        default:
       +                return eventNotHandledErr;
       +        }
       +
       +        mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
       +        return noErr;        
       +}
       +
       +static int keycvt[] =
       +{
       +        [QZ_IBOOK_ENTER] '\n',
       +        [QZ_RETURN] '\n',
       +        [QZ_ESCAPE] 27,
       +        [QZ_BACKSPACE] '\b',
       +        [QZ_LALT] Kalt,
       +        [QZ_LCTRL] Kctl,
       +        [QZ_LSHIFT] Kshift,
       +        [QZ_F1] KF+1,
       +        [QZ_F2] KF+2,
       +        [QZ_F3] KF+3,
       +        [QZ_F4] KF+4,
       +        [QZ_F5] KF+5,
       +        [QZ_F6] KF+6,
       +        [QZ_F7] KF+7,
       +        [QZ_F8] KF+8,
       +        [QZ_F9] KF+9,
       +        [QZ_F10] KF+10,
       +        [QZ_F11] KF+11,
       +        [QZ_F12] KF+12,
       +        [QZ_INSERT] Kins,
       +        [QZ_DELETE] 0x7F,
       +        [QZ_HOME] Khome,
       +        [QZ_END] Kend,
       +        [QZ_KP_PLUS] '+',
       +        [QZ_KP_MINUS] '-',
       +        [QZ_TAB] '\t',
       +        [QZ_PAGEUP] Kpgup,
       +        [QZ_PAGEDOWN] Kpgdown,
       +        [QZ_UP] Kup,
       +        [QZ_DOWN] Kdown,
       +        [QZ_LEFT] Kleft,
       +        [QZ_RIGHT] Kright,
       +        [QZ_KP_MULTIPLY] '*',
       +        [QZ_KP_DIVIDE] '/',
       +        [QZ_KP_ENTER] '\n',
       +        [QZ_KP_PERIOD] '.',
       +        [QZ_KP0] '0',
       +        [QZ_KP1] '1',
       +        [QZ_KP2] '2',
       +        [QZ_KP3] '3',
       +        [QZ_KP4] '4',
       +        [QZ_KP5] '5',
       +        [QZ_KP6] '6',
       +        [QZ_KP7] '7',
       +        [QZ_KP8] '8',
       +        [QZ_KP9] '9',
       +};
       +
       +static void
       +kputc(int c)
       +{
       +        kbdputc(kbdq, c);
       +}
       +
       +static OSStatus
       +kbdevent(EventRef event)
       +{
       +        char ch;
       +        UInt32 code;
       +        UInt32 mod;
       +        int k;
       +
       +        GetEventParameter(event, kEventParamKeyMacCharCodes,
       +                typeChar, nil, sizeof ch, nil, &ch);
       +        GetEventParameter(event, kEventParamKeyCode,
       +                typeUInt32, nil, sizeof code, nil, &code);
       +        GetEventParameter(event, kEventParamKeyModifiers,
       +                typeUInt32, nil, sizeof mod, nil, &mod);
       +
       +        switch(GetEventKind(event)){
       +        case kEventRawKeyDown:
       +        case kEventRawKeyRepeat:
       +                if(mod == cmdKey){
       +                        if(ch == 'F' && osx.isfullscreen){
       +                                fullscreen(0);
       +                                break;
       +                        }
       +                        return eventNotHandledErr;
       +                }
       +                k = ch;
       +                if(code < nelem(keycvt) && keycvt[code])
       +                        k = keycvt[code];
       +                if(k >= 0)
       +                        latin1putc(k, kputc);
       +                break;
       +
       +        case kEventRawKeyModifiersChanged:
       +                if(!osx.buttons && !osx.kbuttons){
       +                        if(mod == optionKey)
       +                                kbdputc(kbdq, Kalt);
       +                        break;
       +                }
       +                
       +                // If the mouse button is being held down, treat 
       +                // changes in the keyboard modifiers as changes
       +                // in the mouse buttons.
       +                osx.kbuttons = 0;
       +                if(mod & optionKey)
       +                        osx.kbuttons |= 2;
       +                if(mod & cmdKey)
       +                        osx.kbuttons |= 4;
       +                mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
       +                break;
       +        }
       +        return noErr;
       +}
       +
       +static void
       +eresized(int new)
       +{
       +        Memimage *m;
       +        OSXRect or;
       +        ulong chan;
       +        Rectangle r;
       +        int bpl;
       +        CGDataProviderRef provider;
       +        CGImageRef image;
       +        
       +        GetWindowBounds(osx.window, kWindowContentRgn, &or);
       +        r = Rect(or.left, or.top, or.right, or.bottom);
       +        if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr)){
       +                // 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);
       +        image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
       +                CGColorSpaceCreateDeviceRGB(),
       +                kCGImageAlphaNoneSkipLast,
       +                provider, 0, 0, kCGRenderingIntentDefault);
       +        CGDataProviderRelease(provider);        // CGImageCreate did incref
       +        
       +        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
       +flushmemscreen(Rectangle r)
       +{
       +        CGRect cgr;
       +        CGContextRef context;
       +        CGImageRef subimg;
       +
       +        QDBeginCGContext(GetWindowPort(osx.window), &context);
       +        
       +        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);
       +        CGContextDrawImage(context, cgr, subimg);
       +        CGContextFlush(context);
       +        CGImageRelease(subimg);
       +
       +        QDEndCGContext(GetWindowPort(osx.window), &context);
       +}
       +
       +void
       +fullscreen(int x)
       +{
       +}
       +
       +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(struct Cursor *c)
       +{
       +        OSXCursor oc;
       +        int i;
       +
       +        // SetCursor is deprecated, but what replaces it?
       +        for(i=0; i<16; i++){
       +                oc.data[i] = ((ushort*)c->set)[i];
       +                oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i];
       +        }
       +        oc.hotSpot.h = - c->offset.x;
       +        oc.hotSpot.v = - c->offset.y;
       +        SetCursor(&oc);
       +}
       +
       +void
       +getcolor(ulong i, ulong *r, ulong *g, ulong *b)
       +{
       +        ulong v;
       +        
       +        v = 0;
       +        *r = (v>>16)&0xFF;
       +        *g = (v>>8)&0xFF;
       +        *b = v&0xFF;
       +}
       +
       +int
       +setcolor(ulong i, ulong r, ulong g, ulong b)
       +{
       +        /* no-op */
       +        return 0;
       +}
       +
       +
       +int
       +hwdraw(Memdrawparam *p)
       +{
       +        return 0;
       +}
       +
       +struct {
       +        QLock lk;
       +        char buf[SnarfSize];
       +        Rune rbuf[SnarfSize];
       +        PasteboardRef apple;
       +} clip;
       +
       +char*
       +getsnarf(void)
       +{
       +        char *s, *t;
       +        CFArrayRef flavors;
       +        CFDataRef data;
       +        CFIndex nflavor, ndata, j;
       +        CFStringRef type;
       +        ItemCount nitem;
       +        PasteboardItemID id;
       +        PasteboardSyncFlags flags;
       +        UInt32 i;
       +
       +/*        fprint(2, "applegetsnarf\n"); */
       +        qlock(&clip.lk);
       +        clip.apple = osx.snarf;
       +        if(clip.apple == nil){
       +                if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
       +                        fprint(2, "apple pasteboard create failed\n");
       +                        qunlock(&clip.lk);
       +                        return nil;
       +                }
       +        }
       +        flags = PasteboardSynchronize(clip.apple);
       +        if(flags&kPasteboardClientIsOwner){
       +                s = strdup(clip.buf);
       +                qunlock(&clip.lk);
       +                return s;
       +        }
       +        if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
       +                fprint(2, "apple pasteboard get item count failed\n");
       +                qunlock(&clip.lk);
       +                return nil;
       +        }
       +        for(i=1; i<=nitem; i++){
       +                if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
       +                        continue;
       +                if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
       +                        continue;
       +                nflavor = CFArrayGetCount(flavors);
       +                for(j=0; j<nflavor; j++){
       +                        type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
       +                        if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
       +                                continue;
       +                        if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
       +                                continue;
       +                        ndata = CFDataGetLength(data);
       +                        qunlock(&clip.lk);
       +                        s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data));
       +                        CFRelease(flavors);
       +                        CFRelease(data);
       +                        for(t=s; *t; t++)
       +                                if(*t == '\r')
       +                                        *t = '\n';
       +                        return s;
       +                }
       +                CFRelease(flavors);
       +        }
       +        qunlock(&clip.lk);
       +        return nil;                
       +}
       +
       +void
       +putsnarf(char *s)
       +{
       +        CFDataRef cfdata;
       +        PasteboardSyncFlags flags;
       +
       +/*        fprint(2, "appleputsnarf\n"); */
       +
       +        if(strlen(s) >= SnarfSize)
       +                return;
       +        qlock(&clip.lk);
       +        strcpy(clip.buf, s);
       +        runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
       +        clip.apple = osx.snarf;
       +        if(PasteboardClear(clip.apple) != noErr){
       +                fprint(2, "apple pasteboard clear failed\n");
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        flags = PasteboardSynchronize(clip.apple);
       +        if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
       +                fprint(2, "apple pasteboard cannot assert ownership\n");
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        cfdata = CFDataCreate(kCFAllocatorDefault, 
       +                (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2);
       +        if(cfdata == nil){
       +                fprint(2, "apple pasteboard cfdatacreate failed\n");
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
       +                CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
       +                fprint(2, "apple pasteboard putitem failed\n");
       +                CFRelease(cfdata);
       +                qunlock(&clip.lk);
       +                return;
       +        }
       +        /* CFRelease(cfdata); ??? */
       +        qunlock(&clip.lk);
       +}
       +
 (DIR) diff --git a/src/9vx/stub.c b/src/9vx/stub.c
       @@ -1,5 +1,10 @@
       +
        #define        WANT_M
        
       +#ifdef __APPLE__
       +#define __DARWIN_UNIX03 0
       +#endif
       +
        #include        "u.h"
        #include        <sched.h>
        #include        <signal.h>