code: drawterm

Download patch

ref: c50e30b59e81227198b3188183e55ae143498d71
parent: 416371c2d3ef3ca6e6d5e43433127927d53707f0
author: Russ Cox <rsc@swtch.com>
date: Tue Jan 9 17:17:21 EST 2007

Updates for 386 OS X, add native OS X graphics from Paul Lalonde

diff: cannot open b/gui-osx//null: file does not exist: 'b/gui-osx//null'
--- a/Make.osx
+++ b/Make.osx
@@ -3,13 +3,12 @@
 AR=ar
 AS=as
 RANLIB=ranlib
-X11=/usr/X11R6
 CC=gcc
-CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -I$(X11)/include -D_THREAD_SAFE $(PTHREAD) -O2
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -D_THREAD_SAFE $(PTHREAD) -O2
 O=o
 OS=posix
-GUI=x11
-LDADD=-L$(X11)/lib -lX11 -ggdb
+GUI=osx
+LDADD=-ggdb -framework Carbon -framework QuickTime
 LDFLAGS=$(PTHREAD)
 TARG=drawterm
 AUDIO=none
--- /dev/null
+++ b/Make.osx-x11
@@ -1,0 +1,21 @@
+# Mac OS X
+PTHREAD=	# for Mac
+AR=ar
+AS=as
+RANLIB=ranlib
+X11=/usr/X11R6
+CC=gcc
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -I$(X11)/include -D_THREAD_SAFE $(PTHREAD) -O2
+O=o
+OS=posix
+GUI=x11
+LDADD=-L$(X11)/lib -lX11 -ggdb
+LDFLAGS=$(PTHREAD)
+TARG=drawterm
+AUDIO=none
+
+all: default
+
+libmachdep.a:
+	arch=`uname -m|sed 's/i.86/386/;s/Power Macintosh/power/'`; \
+	(cd posix-$$arch &&  make)
--- /dev/null
+++ b/gui-osx/Makefile
@@ -1,0 +1,19 @@
+ROOT=..
+include ../Make.config
+LIB=libgui.a
+
+OFILES=\
+	alloc.$O\
+	cload.$O\
+	draw.$O\
+	load.$O\
+	screen.$O
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/gui-osx/alloc.c
@@ -1,0 +1,23 @@
+#include <u.h>
+#include <libc.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);
+}
+
--- /dev/null
+++ b/gui-osx/cload.c
@@ -1,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+int
+cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
+{
+	return _cloadmemimage(i, r, data, ndata);
+}
--- /dev/null
+++ b/gui-osx/draw.c
@@ -1,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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);
+}
+
+void
+memimageinit(void)
+{
+	_memimageinit();
+}
--- /dev/null
+++ b/gui-osx/keycodes.h
@@ -1,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)
--- /dev/null
+++ b/gui-osx/load.c
@@ -1,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+int
+loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
+{
+	return _loadmemimage(i, r, data, ndata);
+}
--- /dev/null
+++ b/gui-osx/screen.c
@@ -1,0 +1,666 @@
+// in this file, _Rect is os x Rect,
+// _Point is os x Point
+#undef Point
+#define Point _Point
+#undef Rect
+#define Rect _Rect
+
+#include <Carbon/Carbon.h>
+#include <QuickTime/QuickTime.h> // for full screen
+
+#undef Rect
+#undef Point
+
+#undef nil
+
+
+#include "u.h"
+#include "lib.h"
+#include "kern/dat.h"
+#include "kern/fns.h"
+#include "error.h"
+#include "user.h"
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+#include "keyboard.h"
+#include "keycodes.h"
+
+#define rWindowResource  128
+
+#define topLeft(r)  (((Point *) &(r))[0])
+#define botRight(r) (((Point *) &(r))[1])
+
+extern int mousequeue;
+static int depth;
+Boolean   gDone;
+RgnHandle gCursorRegionHdl;
+
+Memimage	*gscreen;
+Screeninfo	screen;
+
+static int readybit;
+static Rendez	rend;
+
+///
+// menu
+//
+static MenuRef windMenu;
+static MenuRef viewMenu;
+
+enum {
+	kQuitCmd = 1,
+	kFullScreenCmd = 2,
+};
+
+static WindowGroupRef winGroup = NULL;
+static WindowRef theWindow = NULL;
+static CGContextRef context;
+static CGDataProviderRef dataProviderRef;
+static CGImageRef fullScreenImage;
+static CGRect devRect;
+static CGRect bounds;
+static PasteboardRef appleclip;
+static _Rect winRect;
+
+
+static int
+isready(void*a)
+{
+	return readybit;
+}
+
+CGContextRef QuartzContext;
+
+void winproc(void *a);
+
+void screeninit(void)
+{
+	int fmt;
+	int dx, dy;
+	ProcessSerialNumber psn = { 0, kCurrentProcess };
+	TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+	SetFrontProcess(&psn);
+
+	memimageinit();
+	depth = 32; // That's all this code deals with for now
+	screen.depth = 32;
+	fmt = XBGR32; //XRGB32;
+
+	devRect = CGDisplayBounds(CGMainDisplayID());
+//	devRect.origin.x = 0;
+//	devRect.origin.y = 0;
+//	devRect.size.width = 1024;
+//	devRect.size.height = 768;
+	dx = devRect.size.width;
+	dy = devRect.size.height;
+
+	gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
+	dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata,
+						dx * dy * 4, 0);
+	fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4,
+				CGColorSpaceCreateDeviceRGB(),
+				kCGImageAlphaNoneSkipLast,
+				dataProviderRef, 0, 0, kCGRenderingIntentDefault);
+
+	kproc("osxscreen", winproc, 0);
+	ksleep(&rend, isready, 0);
+}
+
+static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
+
+void 
+window_resized()
+{
+	GetWindowBounds(theWindow, kWindowContentRgn, &winRect );
+
+	bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top);
+}
+
+
+void winproc(void *a)
+{
+	winRect.left = 30;
+	winRect.top = 60;
+	winRect.bottom = (devRect.size.height * 0.75) + winRect.top;
+	winRect.right = (devRect.size.width * 0.75) + winRect.left;
+
+	ClearMenuBar();
+	InitCursor();
+
+	CreateStandardWindowMenu(0, &windMenu);
+	InsertMenu(windMenu, 0);
+
+    MenuItemIndex index;
+	CreateNewMenu(1004, 0, &viewMenu);
+	SetMenuTitleWithCFString(viewMenu, CFSTR("View"));
+	AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0,
+			kFullScreenCmd, &index);
+	SetMenuItemCommandKey(viewMenu, index, 0, 'F');
+	AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"), 
+			kMenuItemAttrDisabled,
+			kFullScreenCmd, &index);
+	InsertMenu(viewMenu, GetMenuID(windMenu));
+
+	DrawMenuBar();
+	uint32_t windowAttrs = 0
+				| kWindowCloseBoxAttribute
+				| kWindowCollapseBoxAttribute
+				| kWindowResizableAttribute
+				| kWindowStandardHandlerAttribute
+				| kWindowFullZoomAttribute
+		;
+
+	CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
+	CreateWindowGroup(0, &winGroup);
+	SetWindowGroup(theWindow, winGroup);
+
+	SetWindowTitleWithCFString(theWindow, CFSTR("Drawterm"));
+
+	if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
+		sysfatal("pasteboard create failed");
+
+	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 (MainWindowEventHandler),
+								GetEventTypeCount(events),
+								events,
+								NULL,
+								NULL);
+	InstallWindowEventHandler (
+								theWindow,
+								NewEventHandlerUPP (MainWindowCommandHandler),
+								GetEventTypeCount(commands),
+								commands,
+								theWindow,
+								NULL);
+
+	ShowWindow(theWindow);
+	ShowMenuBar();
+	window_resized();
+	SelectWindow(theWindow);
+	terminit();
+	// Run the event loop
+	readybit = 1;
+	wakeup(&rend);
+	RunApplicationEventLoop();
+
+}
+
+static inline int convert_key(UInt32 key, UInt32 charcode)
+{
+	switch(key) {
+		case QZ_IBOOK_ENTER:
+		case QZ_RETURN: return '\n';
+		case QZ_ESCAPE: return 27;
+		case QZ_BACKSPACE: return '\b';
+		case QZ_LALT: return Kalt;
+		case QZ_LCTRL: return Kctl;
+		case QZ_LSHIFT: return Kshift;
+		case QZ_F1: return KF+1;
+		case QZ_F2: return KF+2;
+		case QZ_F3: return KF+3;
+		case QZ_F4: return KF+4;
+		case QZ_F5: return KF+5;
+		case QZ_F6: return KF+6;
+		case QZ_F7: return KF+7;
+		case QZ_F8: return KF+8;
+		case QZ_F9: return KF+9;
+		case QZ_F10: return KF+10;
+		case QZ_F11: return KF+11;
+		case QZ_F12: return KF+12;
+		case QZ_INSERT: return Kins;
+		case QZ_DELETE: return '0';
+		case QZ_HOME: return Khome;
+		case QZ_END: return Kend;
+		case QZ_KP_PLUS: return '+';
+		case QZ_KP_MINUS: return '-';
+		case QZ_TAB: return '\t';
+		case QZ_PAGEUP: return Kpgup;
+		case QZ_PAGEDOWN: return Kpgdown;
+		case QZ_UP: return Kup;
+		case QZ_DOWN: return Kdown;
+		case QZ_LEFT: return Kleft;
+		case QZ_RIGHT: return Kright;
+		case QZ_KP_MULTIPLY: return '*';
+		case QZ_KP_DIVIDE: return '/';
+		case QZ_KP_ENTER: return '\b';
+		case QZ_KP_PERIOD: return '.';
+		case QZ_KP0: return '0';
+		case QZ_KP1: return '1';
+		case QZ_KP2: return '2';
+		case QZ_KP3: return '3';
+		case QZ_KP4: return '4';
+		case QZ_KP5: return '5';
+		case QZ_KP6: return '6';
+		case QZ_KP7: return '7';
+		case QZ_KP8: return '8';
+		case QZ_KP9: return '9';
+		default: return charcode;
+	}
+}
+
+void
+sendbuttons(int b, int x, int y)
+{
+	int i;
+	lock(&mouse.lk);
+	i = mouse.wi;
+	if(mousequeue) {
+		if(i == mouse.ri || mouse.lastb != b || mouse.trans) {
+			mouse.wi = (i+1)%Mousequeue;
+			if(mouse.wi == mouse.ri)
+				mouse.ri = (mouse.ri+1)%Mousequeue;
+			mouse.trans = mouse.lastb != b;
+		} else {
+			i = (i-1+Mousequeue)%Mousequeue;
+		}
+	} else {
+		mouse.wi = (i+1)%Mousequeue;
+		mouse.ri = i;
+	}
+	mouse.queue[i].xy.x = x;
+	mouse.queue[i].xy.y = y;
+	mouse.queue[i].buttons = b;
+	mouse.queue[i].msec = ticks();
+	mouse.lastb = b;
+	unlock(&mouse.lk);
+	wakeup(&mouse.r);
+}
+
+static Ptr fullScreenRestore;
+static int amFullScreen = 0;
+static WindowRef oldWindow = NULL;
+
+static void
+leave_full_screen()
+{
+	if (amFullScreen) {
+		EndFullScreen(fullScreenRestore, 0);
+		theWindow = oldWindow;
+		ShowWindow(theWindow);
+		amFullScreen = 0;
+		window_resized();
+		Rectangle rect =  { { 0, 0 },
+ 							{ bounds.size.width,
+ 							  bounds.size.height} };
+ 		flushmemscreen(rect);
+	}
+}
+
+static void
+full_screen()
+{
+	if (!amFullScreen) {
+		oldWindow = theWindow;
+		HideWindow(theWindow);
+		BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0);
+		amFullScreen = 1;
+		window_resized();
+		Rectangle rect =  { { 0, 0 },
+ 							{ bounds.size.width,
+ 							  bounds.size.height} };
+ 		flushmemscreen(rect);
+	}
+}
+
+static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
+{
+	OSStatus result = noErr;
+	result = CallNextEventHandler(nextHandler, event);
+	UInt32 class = GetEventClass (event);
+	UInt32 kind = GetEventKind (event);
+	if(class == kEventClassKeyboard) {
+		char macCharCodes;
+		UInt32 macKeyCode;
+		UInt32 macKeyModifiers;
+
+		GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,
+							NULL, sizeof(macCharCodes), NULL, &macCharCodes);
+		GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
+							sizeof(macKeyCode), NULL, &macKeyCode);
+		GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
+							sizeof(macKeyModifiers), NULL, &macKeyModifiers);
+        switch(kind) {
+		case kEventRawKeyModifiersChanged:
+			if ( macKeyModifiers == 0x1800 ) leave_full_screen();
+			break;
+		case kEventRawKeyDown:
+		case kEventRawKeyRepeat: {
+			if(macKeyModifiers != 256) {
+				if (kind == kEventRawKeyRepeat || kind == kEventRawKeyDown) {
+					int key = convert_key(macKeyCode, macCharCodes);
+					if (key != -1) kbdputc(kbdq, key);
+				}
+			}
+			else
+				result = eventNotHandledErr;
+			break;
+		}
+		default:
+			break;
+		}
+	}
+	else if(class == kEventClassMouse) {
+		_Point mousePos;
+
+		GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
+							0, sizeof mousePos, 0, &mousePos);
+		
+		static uint32_t mousebuttons = 0; // bitmask of buttons currently down
+		
+		switch (kind) {
+			case kEventMouseWheelMoved:
+			{
+			    int32_t wheeldelta;
+				GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32,
+									0,sizeof(EventMouseButton), 0, &wheeldelta);
+				sendbuttons((int16_t)wheeldelta>0 ? 8 : 16,
+							mousePos.h - winRect.left,
+							mousePos.v - winRect.top);
+				break;
+			}
+			case kEventMouseUp:
+			case kEventMouseDown:
+			{
+				uint32_t buttons;
+				GetEventParameter(event, kEventParamMouseChord,
+					typeUInt32, 0, sizeof buttons, 0, &buttons);
+				mousebuttons = (buttons & 1)
+							 | ((buttons & 2)<<1)
+							 | ((buttons & 4)>>1);
+			} /* Fallthrough */
+			case kEventMouseMoved:
+			case kEventMouseDragged:
+			{
+				sendbuttons(mousebuttons,
+							mousePos.h - winRect.left,
+							mousePos.v - winRect.top);
+			}
+			break;
+
+			default:result = eventNotHandledErr;break;
+		}
+	}
+	return result;
+}
+
+
+//default window command handler (from menus)
+static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler,
+					EventRef event, void *userData)
+{
+	OSStatus result = noErr;
+	UInt32 class = GetEventClass (event);
+	UInt32 kind = GetEventKind (event);
+
+	result = CallNextEventHandler(nextHandler, event);
+
+	if(class == kEventClassCommand)
+	{
+		HICommand theHICommand;
+		GetEventParameter( event, kEventParamDirectObject, typeHICommand,
+							NULL, sizeof( HICommand ), NULL, &theHICommand );
+
+		switch ( theHICommand.commandID )
+		{
+			case kHICommandQuit:
+				exit(0);
+				break;
+
+			case kFullScreenCmd:
+				full_screen();
+				break;
+
+			default:
+				result = eventNotHandledErr;
+				break;
+		}
+	}
+	else if(class == kEventClassWindow)
+	{
+		WindowRef     window;
+		_Rect          rectPort = {0,0,0,0};
+
+		GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
+							NULL, sizeof(WindowRef), NULL, &window);
+
+		if(window)
+		{
+			GetPortBounds(GetWindowPort(window), &rectPort);
+		}
+
+		switch (kind)
+		{
+			case kEventWindowClosed:
+				theWindow = NULL;
+				exit(0); // only one window
+				break;
+
+			//resize window
+			case kEventWindowBoundsChanged:
+				window_resized();
+				Rectangle rect =  { { 0, 0 },
+ 									{ bounds.size.width,
+ 									  bounds.size.height} };
+ 				flushmemscreen(rect);
+				break;
+
+			default:
+				result = eventNotHandledErr;
+				break;
+		}
+	}
+
+	return result;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+	// sanity check.  Trips from the initial "terminal"
+    if (r.max.x < r.min.x || r.max.y < r.min.y) return;
+    
+	screenload(r, gscreen->depth, byteaddr(gscreen, ZP), ZP,
+		gscreen->width*sizeof(ulong));
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen, void **X)
+{
+	*r = gscreen->r;
+	*chan = gscreen->chan;
+	*depth = gscreen->depth;
+	*width = gscreen->width;
+	*softscreen = 1;
+
+	return gscreen->data->bdata;
+}
+
+// PAL - no palette handling.  Don't intend to either.
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+
+// PAL: Certainly wrong to return a grayscale.
+	 *r = i;
+	 *g = i;
+	 *b = i;
+}
+
+void
+setcolor(ulong index, ulong red, ulong green, ulong blue)
+{
+	assert(0);
+}
+
+
+static char snarf[3*SnarfSize+1];
+static Rune rsnarf[SnarfSize+1];
+
+char*
+clipread(void)
+{
+	CFDataRef cfdata;
+	OSStatus err = noErr;
+	ItemCount nItems;
+
+	// Wow.  This is ridiculously complicated.
+	PasteboardSynchronize(appleclip);
+	if((err = PasteboardGetItemCount(appleclip, &nItems)) != noErr) {
+		fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err);
+		return 0;
+	}
+
+	uint32_t i;
+	// Yes, based at 1.  Silly API.
+	for(i = 1; i <= nItems; ++i) {
+		PasteboardItemID itemID;
+		CFArrayRef flavorTypeArray;
+		CFIndex flavorCount;
+
+		if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){
+			fprint(2, "Can't get pasteboard item identifier: %d\n", err);
+			return 0;
+		}
+
+		if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){
+			fprint(2, "Can't copy pasteboard item flavors: %d\n", err);
+			return 0;
+		}
+
+		flavorCount = CFArrayGetCount(flavorTypeArray);
+		CFIndex flavorIndex;
+		for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){
+			CFStringRef flavorType;
+			flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+			if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){
+				if((err = PasteboardCopyItemFlavorData(appleclip, itemID,
+					CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){
+					fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err);
+					return 0;
+				}
+				CFIndex length = CFDataGetLength(cfdata);
+				if (length > sizeof rsnarf) length = sizeof rsnarf;
+				CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf);
+				snprint(snarf, sizeof snarf, "%S", rsnarf);
+				char *s = snarf;
+				while (*s) {
+					if (*s == '\r') *s = '\n';
+					s++;
+				}
+				CFRelease(cfdata);
+				return strdup(snarf);
+			}
+		}
+	}
+	return 0;
+}
+
+int
+clipwrite(char *snarf)
+{
+	CFDataRef cfdata;
+	PasteboardSyncFlags flags;
+
+	runesnprint(rsnarf, nelem(rsnarf), "%s", snarf);
+	if(PasteboardClear(appleclip) != noErr){
+		fprint(2, "apple pasteboard clear failed\n");
+		return 0;
+	}
+	flags = PasteboardSynchronize(appleclip);
+	if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
+		fprint(2, "apple pasteboard cannot assert ownership\n");
+		return 0;
+	}
+	cfdata = CFDataCreate(kCFAllocatorDefault, 
+		(uchar*)rsnarf, runestrlen(rsnarf)*2);
+	if(cfdata == nil){
+		fprint(2, "apple pasteboard cfdatacreate failed\n");
+		return 0;
+	}
+	if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
+		CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
+		fprint(2, "apple pasteboard putitem failed\n");
+		CFRelease(cfdata);
+		return 0;
+	}
+	CFRelease(cfdata);
+	return 1;
+}
+
+
+void
+mouseset(Point xy)
+{
+	CGPoint pnt;
+	pnt.x = xy.x + winRect.left;
+	pnt.y = xy.y + winRect.top;
+	CGWarpMouseCursorPosition(pnt);
+}
+
+void
+screenload(Rectangle r, int depth, uchar *p, Point pt, int step)
+{
+	CGRect rbounds;
+	rbounds.size.width = r.max.x - r.min.x;
+	rbounds.size.height = r.max.y - r.min.y;
+	rbounds.origin.x = r.min.x;
+	rbounds.origin.y = r.min.y;
+		
+	if(depth != gscreen->depth)
+		panic("screenload: bad ldepth");
+		
+	QDBeginCGContext( GetWindowPort(theWindow), &context);
+	
+	// The sub-image is relative to our whole screen image.
+	CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds);
+	
+	// Drawing the sub-image is relative to the window.
+	rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height;
+	CGContextDrawImage(context, rbounds, subimg);
+	CGContextFlush(context);
+	CGImageRelease(subimg);
+	QDEndCGContext( GetWindowPort(theWindow), &context);
+
+}
+
+// PAL: these don't work.
+// SetCursor and InitCursor are marked as deprecated in 10.4, and I can't for the
+// life of me find out what has replaced them.
+void
+setcursor(void)
+{
+    Cursor crsr;
+    int i;
+    
+	for(i=0; i<16; i++){
+		crsr.data[i] = ((ushort*)cursor.set)[i];
+		crsr.mask[i] = crsr.data[i] | ((ushort*)cursor.clr)[i];
+	}
+	crsr.hotSpot.h = -cursor.offset.x;
+	crsr.hotSpot.v = -cursor.offset.y;
+	SetCursor(&crsr);
+}
+
+void
+cursorarrow(void)
+{
+	InitCursor();
+}
--- /dev/null
+++ b/gui-osx/wstrtoutf.c
@@ -1,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+
+int
+wstrutflen(Rune *s)
+{
+	int n;
+	
+	for(n=0; *s; n+=runelen(*s),s++)
+		;
+	return n;
+}
+
+int
+wstrtoutf(char *s, Rune *t, int n)
+{
+	int i;
+	char *s0;
+
+	s0 = s;
+	if(n <= 0)
+		return wstrutflen(t)+1;
+	while(*t) {
+		if(n < UTFmax+1 && n < runelen(*t)+1) {
+			*s = 0;
+			return i+wstrutflen(t)+1;
+		}
+		i = runetochar(s, t);
+		s += i;
+		n -= i;
+		t++;
+	}
+	*s = 0;
+	return s-s0;
+}
--- a/gui-x11/alloc.c
+++ b/gui-x11/alloc.c
@@ -17,6 +17,8 @@
 	int d;
 	
 	m = _allocmemimage(r, chan);
+	if(m == nil)
+		return nil;
 	if(chan != GREY1 && chan != xscreenchan)
 		return m;
 
--- a/include/lib.h
+++ b/include/lib.h
@@ -223,6 +223,10 @@
 extern	Rune*	runesmprint(char*, ...);
 extern	Rune*	runevsmprint(char*, va_list);
 
+extern       Rune*	runestrchr(Rune*, Rune);
+extern       long	runestrlen(Rune*);
+extern       Rune*	runestrstr(Rune*, Rune*);
+
 extern	int	fmtfdinit(Fmt*, int, char*, int);
 extern	int	fmtfdflush(Fmt*);
 extern	int	fmtstrinit(Fmt*);
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -49,6 +49,9 @@
 	runesmprint.$O\
 	runesnprint.$O\
 	runesprint.$O\
+	runestrchr.$O\
+	runestrlen.$O\
+	runestrstr.$O\
 	runetype.$O\
 	runevseprint.$O\
 	runevsmprint.$O\
--- a/posix-386/Makefile
+++ b/posix-386/Makefile
@@ -20,10 +20,8 @@
 	$(AS) -o $*.$O $*.s
 
 md5block.s: md5block.spp
-	cpp md5block.spp >md5block.s
+	gcc -E md5block.spp >md5block.s
 
 sha1block.s: sha1block.spp
-	cpp sha1block.spp >sha1block.s
-
-
+	gcc -E sha1block.spp >sha1block.s
 
--- a/posix-386/md5block.spp
+++ b/posix-386/md5block.spp
@@ -43,7 +43,7 @@
 #define S43 15
 #define S44 21
 
-#define PAYME(x) $ ## x
+#define PAYME(x) $##x
 
 /*
  * SI is data
@@ -116,9 +116,13 @@
 	.text
 
 	.p2align 2,0x90
+#ifdef __Darwin__
+	.globl __md5block
+	__md5block:
+#else
 	.globl _md5block
-		.type _md5block, @function
 	_md5block:
+#endif
 
 	/* Prelude */
 	pushl %ebp
--- a/posix-386/sha1block.spp
+++ b/posix-386/sha1block.spp
@@ -1,9 +1,13 @@
 .text
 
 .p2align 2,0x90
+#ifdef __Darwin__
+.globl __sha1block
+__sha1block:
+#else
 .globl _sha1block
-	.type _sha1block, @function
 _sha1block:
+#endif
 
 /* x = (wp[off-f] ^ wp[off-8] ^ wp[off-14] ^ wp[off-16]) <<< 1;
  * wp[off] = x;