9vx/OSX: add Mach signal handler, fix up native GUI code - vx32 - Local 9vx git repository for patches.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 218811d066c15fb10060f586b466d91001dcc0bb
 (DIR) parent 51dc3331748d1f24cb610792fa49200e561a15b0
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Sat, 28 Jun 2008 21:36:01 -0400
       
       9vx/OSX: add Mach signal handler, fix up native GUI code
       
       TODO: Perhaps the Mach signal handler belongs in libvx32?
       
       Diffstat:
         src/9vx/Makefrag                    |       8 +++++---
         src/9vx/a/dat.h                     |       9 +++++++++
         src/9vx/a/fns.h                     |       6 ++++++
         src/9vx/main.c                      |      13 +++++++++++--
         src/9vx/osx/screen.c                |      66 ++++++++++++++++++++++++-------
         src/9vx/osx/signal.c                |     237 +++++++++++++++++++++++++++++++
         src/9vx/sched.c                     |      17 +++++++----------
         src/9vx/stub.c                      |       3 ---
         src/9vx/term.c                      |       9 +++++++++
         src/9vx/x11/x11-kernel.c            |       6 ++++++
       
       10 files changed, 341 insertions(+), 33 deletions(-)
       ---
 (DIR) diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag
       @@ -10,9 +10,7 @@ endif
        
        ifeq ($(OS),darwin)
        PLAN9VX=1
       -# TODO: osx works except that Carbon gets confused
       -# that we can survive an EXC_BAD_ACCESS
       -PLAN9GUI=x11
       +PLAN9GUI=osx
        PLAN9AUDIO=none
        endif
        
       @@ -55,6 +53,10 @@ PLAN9_OBJS = \
                        vx32.o \
                )
        
       +ifeq ($(OS),darwin)
       +PLAN9_OBJS := $(PLAN9_OBJS) 9vx/osx/signal.o
       +endif
       +
        PLAN9_A_OBJS = \
                $(addprefix 9vx/a/, \
                        allocb.o \
 (DIR) diff --git a/src/9vx/a/dat.h b/src/9vx/a/dat.h
       @@ -349,3 +349,12 @@ extern int traceprocs;
        extern int tracesyscalls;
        extern uchar *uzero;
        extern int doabort;
       +
       +/* Pthreads-based sleep and wakeup. */
       +ttypedef struct Psleep Psleep;
       +struct Psleep
       +{
       +        pthread_mutex_t mutex;
       +        pthread_cond_t cond;
       +};
       +
 (DIR) diff --git a/src/9vx/a/fns.h b/src/9vx/a/fns.h
       @@ -166,3 +166,9 @@ void        uartinit(int);
        void        *uvalidaddr(ulong addr, ulong len, int write);
        int        isuaddr(void*);
        void        setsigsegv(int invx32);
       +
       +void        plock(Psleep*);
       +void        punlock(Psleep*);
       +void        pwakeup(Psleep*);
       +void        psleep(Psleep*);
       +
 (DIR) diff --git a/src/9vx/main.c b/src/9vx/main.c
       @@ -144,6 +144,7 @@ main(int argc, char **argv)
        
                if(username == nil && (username = getuser()) == nil)
                        username = "tor";
       +        kstrdup(&eve, username);
        
                mach0init();
                mmuinit();
       @@ -155,9 +156,12 @@ main(int argc, char **argv)
                 * fork into the background.  This is a little dicey, since we
                 * haven't allocated the screen yet, but that would kick off
                 * a kproc, and we need to fork before we start any pthreads.
       +         * Cannot fork on OS X - it can't handle it.
                 */
       +#ifndef __APPLE__
                if(!usetty && !nofork && fork() > 0)
                        _exit(0);
       +#endif
        
                /*
                 * Have to do this after fork; on OS X child does
       @@ -182,6 +186,12 @@ main(int argc, char **argv)
                }
                bootinit();
                pageinit();
       +#ifdef __APPLE__
       +        if(conf.monitor)
       +                screeninit();
       +        extern void machsiginit(void);
       +        machsiginit();
       +#endif
                userinit();
                terminit(!usetty);
                uartinit(usetty);
       @@ -473,7 +483,7 @@ init0(void)
                touser(sp);        /* infinity, and beyond. */
        }
        
       -static int invx32;
       +int invx32;        /* shared with sbcl-signal.c */
        
        /*
         * SIGSEGV handler.  If we get SIGSEGV while executing
       @@ -680,7 +690,6 @@ siginit(void)
                act.sa_flags = SA_SIGINFO;
                sigaction(SIGUSR1, &act, nil);
                sigaction(SIGURG, &act, nil);
       -
        }
        
        void
 (DIR) diff --git a/src/9vx/osx/screen.c b/src/9vx/osx/screen.c
       @@ -54,11 +54,14 @@ enum
                        kWindowFullZoomAttribute
        };
        
       -static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
        static void screenproc(void*);
        static void eresized(int);
        static void fullscreen(int);
        
       +static OSStatus quithandler(EventHandlerCallRef, EventRef, void*);
       +static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
       +static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*);
       +
        enum
        {
                CmdFullScreen = 1,
       @@ -86,7 +89,7 @@ attachscreen(Rectangle *r, ulong *chan, int *depth,
        }
        
        void
       -screeninit(void)
       +_screeninit(void)
        {
                CGRect cgr;
                OSXRect or;
       @@ -131,13 +134,13 @@ screeninit(void)
                // 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[] = {
       +        const EventTypeSpec cmds[] = {
                        { kEventClassWindow, kEventWindowClosed },
                        { kEventClassWindow, kEventWindowBoundsChanged },
                        { kEventClassCommand, kEventCommandProcess }
       @@ -152,16 +155,19 @@ screeninit(void)
                        { kEventClassMouse, kEventMouseDragged },
                        { kEventClassMouse, kEventMouseWheelMoved },
                };
       +
                InstallApplicationEventHandler(
       -                NewEventHandlerUPP(eventhandler),
       +                NewEventHandlerUPP(quithandler),
                        nelem(quits), quits, nil, nil);
       -        InstallApplicationEventHandler(
       -                NewEventHandlerUPP(eventhandler),
       -                nelem(events), events, osx.window, nil);
       +
       +         InstallApplicationEventHandler(
       +                 NewEventHandlerUPP(eventhandler),
       +                nelem(events), events, nil, nil);
       +
                InstallWindowEventHandler(osx.window,
       -                NewEventHandlerUPP(eventhandler),
       -                nelem(commands), commands, osx.window, nil);
       -        
       +                NewEventHandlerUPP(cmdhandler),
       +                nelem(cmds), cmds, osx.window, nil);
       +
                // Finally, put the window on the screen.
                ShowWindow(osx.window);
                ShowMenuBar();
       @@ -169,34 +175,60 @@ screeninit(void)
                SelectWindow(osx.window);
                
                InitCursor();
       +}
        
       +static Psleep scr;
       +
       +void
       +screeninit(void)
       +{
       +        plock(&scr);
                kproc("*screen*", screenproc, nil);
       +        while(osx.window == nil)
       +                psleep(&scr);
       +        punlock(&scr);
        }
        
        static void
        screenproc(void *v)
        {
       +        plock(&scr);
       +        _screeninit();
       +        pwakeup(&scr);
       +        punlock(&scr);
                RunApplicationEventLoop();
       +        iprint("screenproc exited!\n");
        }
        
        static OSStatus kbdevent(EventRef);
        static OSStatus mouseevent(EventRef);
        
        static OSStatus
       +cmdhandler(EventHandlerCallRef next, EventRef event, void *arg)
       +{
       +        return eventhandler(next, event, arg);
       +}
       +
       +static OSStatus
       +quithandler(EventHandlerCallRef next, EventRef event, void *arg)
       +{
       +        exit(0);
       +        return 0;
       +}
       +
       +static OSStatus
        eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
        {
                OSStatus result;
       -        
       +
                result = CallNextEventHandler(next, event);
       -        
       +
                switch(GetEventClass(event)){
                case kEventClassKeyboard:
                        return kbdevent(event);
       -                break;
                
                case kEventClassMouse:
                        return mouseevent(event);
       -                break;
                
                case kEventClassCommand:;
                        HICommand cmd;
       @@ -241,6 +273,7 @@ mouseevent(EventRef event)
                
                GetEventParameter(event, kEventParamMouseLocation,
                        typeQDPoint, 0, sizeof op, 0, &op);
       +
                osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min);
                wheel = 0;
        
       @@ -279,6 +312,7 @@ mouseevent(EventRef event)
                        break;
        
                case kEventMouseMoved:
       +        case kEventMouseDragged:
                        break;
                
                default:
       @@ -432,6 +466,7 @@ eresized(int new)
                        provider, 0, 0, kCGRenderingIntentDefault);
                CGDataProviderRelease(provider);        // CGImageCreate did incref
                
       +        mouserect = m->r;
                termreplacescreenimage(m);
                drawreplacescreenimage(m);        // frees old osx.screenimage if any
                if(osx.image)
       @@ -455,6 +490,7 @@ flushmemscreen(Rectangle r)
                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(context, cgr, subimg);
                CGContextFlush(context);
                CGImageRelease(subimg);
 (DIR) diff --git a/src/9vx/osx/signal.c b/src/9vx/osx/signal.c
       @@ -0,0 +1,237 @@
       +/*
       + * Truly awful code to simulate Unix signal handler dispatch
       + * using Mach signal handler dispatch.  The BSD support routines
       + * can't deal with our SIGSEGVs properly.  Among other things,
       + * they keep waking up other threads and they cause a popup 
       + * about the application quitting when it hasn't.
       + *
       + * This code is inspired by similar code in SBCL.
       + * See also http://paste.lisp.org/display/19593.
       + * See also http://lists.apple.com/archives/darwin-dev/2006/Oct/msg00122.html
       + */
       +
       +#define __DARWIN_UNIX_03 0
       +
       +#include <mach/mach.h>
       +#include <sys/ucontext.h>
       +#include <pthread.h>
       +#include <signal.h>
       +#include <sys/signal.h>
       +
       +#include "u.h"
       +#include "lib.h"
       +#include "mem.h"
       +#include "dat.h"
       +#include "fns.h"
       +#include "error.h"
       +
       +extern void sigsegv(int, siginfo_t*, void*);        /* provided by Plan 9 VX */
       +extern int invx32;
       +
       +static x86_thread_state32_t normal;        /* normal segment registers */
       +static void *altstack;
       +
       +/*
       + * Manipulate stack in regs.
       + */
       +static void
       +push(x86_thread_state32_t *regs, ulong data)
       +{
       +        uint *sp;
       +        
       +        sp = (uint*)regs->esp;
       +        *--sp = data;
       +        regs->esp = (uint)sp;
       +}
       +
       +static void
       +align(x86_thread_state32_t *regs)
       +{
       +        uint *sp;
       +        
       +        sp = (uint*)regs->esp;
       +        while((ulong)sp & 15)
       +                sp--;
       +        regs->esp = (uint)sp;
       +}
       +
       +static void*
       +alloc(x86_thread_state32_t *regs, int n)
       +{
       +        n = (n+15) & ~15;
       +        regs->esp -= n;
       +        return (void*)regs->esp;
       +}
       +
       +/*
       + * Signal handler wrapper.  Calls handler and then
       + * causes an illegal instruction exception to jump
       + * back to us.
       + */
       +static void
       +wrapper(siginfo_t *siginfo,
       +        mcontext_t mcontext,
       +        void (*handler)(int, siginfo_t*, void*))
       +{
       +        ucontext_t ucontext;
       +
       +        memset(&ucontext, 0, sizeof ucontext);
       +        ucontext.uc_mcontext = mcontext;
       +        handler(siginfo->si_signo, siginfo, &ucontext);
       +
       +        /* Cause EXC_BAD_INSTRUCTION to "exit" signal handler */
       +        asm volatile(
       +                "movl %0, %%eax\n"
       +                "movl $0xdeadbeef, %%esp\n"
       +                ".long 0xffff0b0f\n"
       +                : : "r" (mcontext));
       +}
       +
       +void
       +dumpmcontext(mcontext_t m)
       +{
       +        x86_thread_state32_t *ureg;
       +        
       +        ureg = &m->ss;
       +        iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX CR2=%luX\n",
       +                ureg->eflags, m->es.trapno, m->es.err, ureg->eip, m->es.faultvaddr);
       +        iprint("  AX %8.8luX  BX %8.8luX  CX %8.8luX  DX %8.8luX\n",
       +                ureg->eax, ureg->ebx, ureg->ecx, ureg->edx);
       +        iprint("  SI %8.8luX  DI %8.8luX  BP %8.8luX  SP %8.8luX\n",
       +                ureg->esi, ureg->edi, ureg->ebp, ureg->esp);
       +}
       +
       +void
       +dumpregs1(x86_thread_state32_t *ureg)
       +{
       +        iprint("FLAGS=%luX PC=%luX\n",
       +                ureg->eflags, ureg->eip);
       +        iprint("  AX %8.8luX  BX %8.8luX  CX %8.8luX  DX %8.8luX\n",
       +                ureg->eax, ureg->ebx, ureg->ecx, ureg->edx);
       +        iprint("  SI %8.8luX  DI %8.8luX  BP %8.8luX  SP %8.8luX\n",
       +                ureg->esi, ureg->edi, ureg->ebp, ureg->esp);
       +}
       +
       +
       +/*
       + * Called by mach loop in exception handling thread.
       + */
       +kern_return_t
       +catch_exception_raise(mach_port_t exception_port,
       +                      mach_port_t thread,
       +                      mach_port_t task,
       +                      exception_type_t exception,
       +                      exception_data_t code_vector,
       +                      mach_msg_type_number_t code_count)
       +{
       +        mach_msg_type_number_t n;
       +        x86_thread_state32_t regs, save_regs;
       +        siginfo_t *stk_siginfo;
       +        kern_return_t ret;
       +        uint *sp;
       +        mcontext_t stk_mcontext;
       +
       +        n = x86_THREAD_STATE32_COUNT;
       +        ret = thread_get_state(thread, x86_THREAD_STATE32, (void*)&regs, &n);
       +        if(ret != KERN_SUCCESS)
       +                panic("mach get regs failed: %d", ret);
       +
       +        switch(exception){
       +        case EXC_BAD_ACCESS:
       +                save_regs = regs;
       +                if(invx32)
       +                        regs.esp = (uint)altstack;
       +                else if(regs.ss != normal.ss)
       +                        panic("not in vx32 but bogus ss");
       +                align(&regs);
       +                regs.cs = normal.cs;
       +                regs.ds = normal.ds;
       +                regs.es = normal.es;
       +                regs.ss = normal.ss;
       +
       +                stk_siginfo = alloc(&regs, sizeof *stk_siginfo);
       +                stk_mcontext = alloc(&regs, sizeof *stk_mcontext);
       +
       +                memset(stk_siginfo, 0, sizeof *stk_siginfo);
       +                stk_siginfo->si_signo = SIGBUS;
       +                stk_siginfo->si_addr = (void*)code_vector[1];
       +
       +                stk_mcontext->ss = save_regs;
       +                n = x86_FLOAT_STATE32_COUNT;
       +                ret = thread_get_state(thread, x86_FLOAT_STATE32, (void*)&stk_mcontext->fs, &n);
       +                if(ret != KERN_SUCCESS)
       +                        panic("mach get fpregs failed: %d", ret);
       +                n = x86_EXCEPTION_STATE32_COUNT;
       +                ret = thread_get_state(thread, x86_EXCEPTION_STATE32, (void*)&stk_mcontext->es, &n);
       +                if(ret != KERN_SUCCESS)
       +                        panic("mach get eregs: %d", ret);
       +
       +                sp = alloc(&regs, 3*4);
       +                sp[0] = (uint)stk_siginfo;
       +                sp[1] = (uint)stk_mcontext;
       +                sp[2] = (uint)sigsegv;
       +                
       +                push(&regs, regs.eip);        /* for debugger; wrapper won't return */
       +                regs.eip = (uint)wrapper;
       +
       +                ret = thread_set_state(thread, x86_THREAD_STATE32,
       +                        (void*)&regs, x86_THREAD_STATE32_COUNT);
       +                if(ret != KERN_SUCCESS)
       +                        panic("mach set regs failed: %d", ret);
       +                return KERN_SUCCESS;
       +        
       +        case EXC_BAD_INSTRUCTION:
       +                /* Thread signalling that it's done with sigsegv. */
       +                if(regs.esp != 0xdeadbeef){
       +                        dumpregs1(&regs);
       +                        return KERN_INVALID_RIGHT;
       +                        panic("bad instruction eip=%p", regs.eip);
       +                }
       +                stk_mcontext = (mcontext_t)regs.eax;
       +                ret = thread_set_state(thread, x86_THREAD_STATE32,
       +                        (void*)&stk_mcontext->ss, x86_THREAD_STATE32_COUNT);
       +                if(ret != KERN_SUCCESS)
       +                        panic("mach set regs1 failed: %d", ret);
       +                ret = thread_set_state(thread, x86_FLOAT_STATE32,
       +                        (void*)&stk_mcontext->fs, x86_FLOAT_STATE32_COUNT);
       +                if(ret != KERN_SUCCESS)
       +                        panic("mach set fpregs failed: %d", ret);
       +                return KERN_SUCCESS;
       +        }
       +        return KERN_INVALID_RIGHT;
       +}
       +
       +static void*
       +handler(void *v)
       +{
       +        extern boolean_t exc_server();
       +        mach_port_t port;
       +        
       +        setmach(machp[0]);
       +        port = (mach_port_t)v;
       +        mach_msg_server(exc_server, 2048, port, 0);
       +        return 0;        /* not reached */
       +}
       +
       +void
       +machsiginit(void)
       +{
       +        mach_port_t port;
       +        pthread_t pid;
       +        stack_t ss;
       +        int ret;
       +        
       +        extern int vx32_getcontext(x86_thread_state32_t*);
       +        vx32_getcontext(&normal);
       +
       +        if(sigaltstack(nil, &ss) < 0 || (ss.ss_flags & SS_DISABLE))
       +                panic("machsiginit: no alt stack");
       +        altstack = ss.ss_sp + ss.ss_size;
       +
       +        mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
       +        mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
       +        ret = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION, port,
       +                EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
       +        pthread_create(&pid, nil, handler, (void*)port);
       +}
       +
 (DIR) diff --git a/src/9vx/sched.c b/src/9vx/sched.c
       @@ -21,27 +21,24 @@
        #include        "error.h"
        #include        "trace.h"
        
       -/* Pthreads-based sleep and wakeup. */
       -ttypedef struct Psleep Psleep;
       -struct Psleep
       +void
       +pinit(Psleep *p)
        {
       -        pthread_mutex_t mutex;
       -        pthread_cond_t cond;
       -};
       +}
        
       -static void
       +void
        plock(Psleep *p)
        {
                pthread_mutex_lock(&p->mutex);
        }
        
       -static void
       +void
        punlock(Psleep *p)
        {
                pthread_mutex_unlock(&p->mutex);
        }
        
       -static void
       +void
        psleep(Psleep *p)
        {
                /*
       @@ -58,7 +55,7 @@ psleep(Psleep *p)
                pthread_cond_wait(&p->cond, &p->mutex);
        }
        
       -static void
       +void
        pwakeup(Psleep *p)
        {
                pthread_cond_signal(&p->cond);
 (DIR) diff --git a/src/9vx/stub.c b/src/9vx/stub.c
       @@ -544,10 +544,7 @@ panic(char *fmt, ...)
                if(doabort){
                        for(;;)
                                microdelay(1000000);
       -                signal(SIGSEGV, SIG_IGN);
       -                *(int*)0 = 0;
                }
       -        //        abort();
                exit(0);
        }
        
 (DIR) diff --git a/src/9vx/term.c b/src/9vx/term.c
       @@ -67,6 +67,9 @@ terminit(int printing)
                /* x11 will call termreplacescreenimage when it is ready */
                unlock(&term.lk);
                
       +        /* maybe it already has */
       +        if(term.screen)
       +                termreplacescreenimage(term.screen);
        
                /* If we're the output mechanism, set it up and kick off the screen. */
                if(printing)
       @@ -106,6 +109,12 @@ _termreplacescreenimage(Memimage *m)
                int h;
                Rectangle r, r0;
        
       +        if(term.bg == nil){
       +                /* not yet init */
       +                term.screen = m;
       +                return;
       +        }
       +
                /* white background */
                if(!mouse.open)
                        memimagedraw(m, m->r, term.bg, ZP, memopaque, ZP, S);
 (DIR) diff --git a/src/9vx/x11/x11-kernel.c b/src/9vx/x11/x11-kernel.c
       @@ -110,6 +110,11 @@ _xproc(void *v)
                }
        }
        
       +void
       +screeninit(void)
       +{
       +}
       +
        uchar*
        attachscreen(Rectangle *r, ulong *chan, int *depth,
                int *width, int *softscreen, void **X)
       @@ -175,3 +180,4 @@ setmouse(Point p)
                _xmoveto(p);
                drawqunlock();
        }
       +