commit 218811d066c15fb10060f586b466d91001dcc0bb
parent 51dc3331748d1f24cb610792fa49200e561a15b0
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:
10 files changed, 341 insertions(+), 33 deletions(-)
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 \
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. */
+typedef struct Psleep Psleep;
+struct Psleep
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
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*);
+
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
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);
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*)®s, &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(®s);
+ regs.cs = normal.cs;
+ regs.ds = normal.ds;
+ regs.es = normal.es;
+ regs.ss = normal.ss;
+
+ stk_siginfo = alloc(®s, sizeof *stk_siginfo);
+ stk_mcontext = alloc(®s, 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(®s, 3*4);
+ sp[0] = (uint)stk_siginfo;
+ sp[1] = (uint)stk_mcontext;
+ sp[2] = (uint)sigsegv;
+
+ push(®s, regs.eip); /* for debugger; wrapper won't return */
+ regs.eip = (uint)wrapper;
+
+ ret = thread_set_state(thread, x86_THREAD_STATE32,
+ (void*)®s, 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(®s);
+ 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);
+}
+
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. */
-typedef 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);
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);
}
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);
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();
}
+