911 lines · 19.0 KB · C
RawDownload
#define Point OSXPoint#define Rect OSXRect#define Cursor OSXCursor#include <Carbon/Carbon.h>#undef Rect#undef Point#undef Cursor#undef offsetof#undef nil #include "u.h"#include "libc.h"#include <thread.h>#include <draw.h>#include <memdraw.h>#include <keyboard.h>#include "mouse.h"#include <cursor.h>#include "osx-screen.h"#include "osx-keycodes.h"#include "devdraw.h"#include "glendapng.h" AUTOFRAMEWORK(Carbon) #define panic sysfatal extern Rectangle mouserect; struct {	char *label;	char *winsize;	QLock labellock; 	Rectangle fullscreenr;	Rectangle screenr;	Memimage *screenimage;	int isfullscreen;	ulong fullscreentime;		Point xy;	int buttons;	int kbuttons; 	CGDataProviderRef provider;	MenuRef wmenu;	MenuRef vmenu;	WindowRef window;	CGImageRef image;	CGContextRef windowctx;	PasteboardRef snarf;	int needflush;	QLock flushlock;	int active;	int infullscreen;	int kalting;		// last keystroke was Kalt} osx; enum{	WindowAttrs =		kWindowCloseBoxAttribute |		kWindowCollapseBoxAttribute |		kWindowResizableAttribute |		kWindowStandardHandlerAttribute |		kWindowFullZoomAttribute}; enum{	P9PEventLabelUpdate = 1}; static void screenproc(void*);static void eresized(int);static void fullscreen(int);static void seticon(void);static void activated(int); static OSStatus quithandler(EventHandlerCallRef, EventRef, void*);static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*); enum{	CmdFullScreen = 1,}; void screeninit(void);void _flushmemscreen(Rectangle r); Memimage*attachscreen(char *label, char *winsize){	if(label == nil)		label = "gnot a label";	osx.label = strdup(label);	osx.winsize = winsize;	if(osx.screenimage == nil){		screeninit();		if(osx.screenimage == nil)			panic("cannot create OS X screen");	}	return osx.screenimage;} void_screeninit(void){	CGRect cgr;	OSXRect or;	Rectangle r;	int havemin; 	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("Cmd-F exits full screen"),		kMenuItemAttrDisabled, CmdFullScreen, &ix);	InsertMenu(osx.vmenu, GetMenuID(osx.wmenu));	DrawMenuBar(); 	// Create the window.	r = Rect(0, 0, Dx(osx.fullscreenr)*2/3, Dy(osx.fullscreenr)*2/3);	havemin = 0;	if(osx.winsize && osx.winsize[0]){		if(parsewinsize(osx.winsize, &r, &havemin) < 0)			sysfatal("%r");	}	if(!havemin)		r = rectaddpt(r, Pt((Dx(osx.fullscreenr)-Dx(r))/2, (Dy(osx.fullscreenr)-Dy(r))/2));	or.left = r.min.x;	or.top = r.min.y;	or.right = r.max.x;	or.bottom = r.max.y;	CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window);	setlabel(osx.label);	seticon();		// 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 cmds[] = {		{ kEventClassWindow, kEventWindowClosed },		{ kEventClassWindow, kEventWindowBoundsChanged },		{ kEventClassCommand, kEventCommandProcess },		{ kEventClassWindow, kEventWindowActivated },		{ kEventClassWindow, kEventWindowDeactivated },	};	const EventTypeSpec events[] = {		{ kEventClassApplication, kEventAppShown },		{ kEventClassKeyboard, kEventRawKeyDown },		{ kEventClassKeyboard, kEventRawKeyModifiersChanged },		{ kEventClassKeyboard, kEventRawKeyRepeat },		{ kEventClassMouse, kEventMouseDown },		{ kEventClassMouse, kEventMouseUp },		{ kEventClassMouse, kEventMouseMoved },		{ kEventClassMouse, kEventMouseDragged },		{ kEventClassMouse, kEventMouseWheelMoved },		{ 'P9PE', P9PEventLabelUpdate}	}; 	InstallApplicationEventHandler(		NewEventHandlerUPP(quithandler),		nelem(quits), quits, nil, nil);  	InstallApplicationEventHandler( 		NewEventHandlerUPP(eventhandler),		nelem(events), events, nil, nil); 	InstallWindowEventHandler(osx.window,		NewEventHandlerUPP(cmdhandler),		nelem(cmds), cmds, osx.window, nil); 	// Finally, put the window on the screen.	ShowWindow(osx.window);	ShowMenuBar();	eresized(0);	SelectWindow(osx.window);		InitCursor();} static Rendez scr;static QLock slock; voidscreeninit(void){	scr.l = &slock;	qlock(scr.l);	proccreate(screenproc, nil, 256*1024);	while(osx.window == nil)		rsleep(&scr);	qunlock(scr.l);} static voidscreenproc(void *v){	qlock(scr.l);	_screeninit();	rwakeup(&scr);	qunlock(scr.l);	RunApplicationEventLoop();} static OSStatus kbdevent(EventRef);static OSStatus mouseevent(EventRef); static OSStatuscmdhandler(EventHandlerCallRef next, EventRef event, void *arg){	return eventhandler(next, event, arg);} static OSStatusquithandler(EventHandlerCallRef next, EventRef event, void *arg){	exit(0);	return 0;} static OSStatuseventhandler(EventHandlerCallRef next, EventRef event, void *arg){	OSStatus result; 	result = CallNextEventHandler(next, event); 	switch(GetEventClass(event)){ 	case 'P9PE':		if (GetEventKind(event) == P9PEventLabelUpdate) {			qlock(&osx.labellock);			setlabel(osx.label);			qunlock(&osx.labellock);			return noErr;		} else			return eventNotHandledErr; 	case kEventClassApplication:;		Rectangle r = Rect(0, 0, Dx(osx.screenr), Dy(osx.screenr));		_flushmemscreen(r);		return eventNotHandledErr; 	case kEventClassKeyboard:		return kbdevent(event);		case kEventClassMouse:		return mouseevent(event);		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;				case kEventWindowActivated:			activated(1);			return eventNotHandledErr;							case kEventWindowDeactivated:			activated(0);			return eventNotHandledErr; 		default:			return eventNotHandledErr;		}		break;	}		return result;} static ulongmsec(void){	return nsec()/1000000;} static OSStatusmouseevent(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); 		but = mouseswap(but);				// Apply keyboard modifiers and pretend it was a real mouse button.		// (Modifiers typed while holding the button go into kbuttons,		// but this one does not.)		if(but == 1){			if(mod & optionKey) {				// Take the ALT away from the keyboard handler.				if(osx.kalting) {					osx.kalting = 0;					keystroke(Kalt);				}				but = 2;			}			else if(mod & cmdKey)				but = 4;		}		osx.buttons = but;		break; 	case kEventMouseMoved:	case kEventMouseDragged:		break;		default:		return eventNotHandledErr;	} 	mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, 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 OSStatuskbdevent(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:		osx.kalting = 0;		if(mod == cmdKey){			if(ch == 'F' || ch == 'f'){				if(osx.isfullscreen && msec() - osx.fullscreentime > 500)					fullscreen(0);				return noErr;			}						// Pass most Cmd keys through as Kcmd + ch.			// OS X interprets a few no matter what we do,			// so it is useless to pass them through as keystrokes too.			switch(ch) {			case 'm':	// minimize window			case 'h':	// hide window			case 'H':	// hide others			case 'q':	// quit				return eventNotHandledErr;			}			if(' ' <= ch && ch <= '~') {				keystroke(Kcmd + ch);				return noErr;			}			return eventNotHandledErr;		}		k = ch;		if(code < nelem(keycvt) && keycvt[code])			k = keycvt[code];		if(k >= 0)			keystroke(k);		else{			UniChar uc;			OSStatus s; 			s = GetEventParameter(event, kEventParamKeyUnicodes,				typeUnicodeText, nil, sizeof uc, nil, &uc);			if(s == noErr)				keystroke(uc);		}		break; 

Lines 1–500 of 911.