Commit Diff


commit - daea2c7d501c3e825bede80992ade6b241efdce1
commit + 113867b836eaa85215e4b2ece5ccf612f34c3e03
blob - 96f329accc1c37c0c4ed4eb72fbfafe52dec1ab2
blob + eda896d9a1a296e9316a265578948a034f09a59c
--- src/cmd/devdraw/mkfile
+++ src/cmd/devdraw/mkfile
@@ -7,7 +7,7 @@ WSYSOFILES=\
 	latin1.$O\
 	mouseswap.$O\
 	winsize.$O\
-	
+
 <|sh ./mkwsysrules.sh
 
 OFILES=$WSYSOFILES
@@ -32,3 +32,12 @@ latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd
 	./$O.mklatinkbd -r $PLAN9/lib/keyboard | sed 's/, }/ }/' >$target
 
 CLEANFILES=latin1.h $O.mklatinkbd
+
+# Still in progress: Cocoa/Objective C version of devdraw
+
+%-objc.$O: %.m
+	$CC $CFLAGS -o $target $stem.m
+
+devdraw-cocoa: devdraw.o latin1.o mouseswap.o winsize.o osx-screen-objc.o osx-draw.o osx-srv-objc.o osx-delegate-objc.o
+	$LD -o $target $prereq
+
blob - /dev/null
blob + 77dde759ed7db8759eb939c776f8ab8c727764b5 (mode 644)
--- /dev/null
+++ src/cmd/devdraw/osx-delegate.h
@@ -0,0 +1,15 @@
+#import <Foundation/NSObject.h>
+#import <AppKit/NSMenu.h>
+
+@class NSFileHandle;
+
+@interface DevdrawDelegate : NSObject
+{
+	NSFileHandle *readHandle;
+}
++(void)populateMainMenu;
++(void)populateApplicationMenu:(NSMenu *)aMenu;
++(void)populateViewMenu:(NSMenu *)aMenu;
++(void)populateWindowMenu:(NSMenu *)aMenu;
++(void)populateHelpMenu:(NSMenu *)aMenu;
+@end
blob - /dev/null
blob + c4a5f5bb5086b4bc6009fe0dfc6597f248516b03 (mode 644)
--- /dev/null
+++ src/cmd/devdraw/osx-delegate.m
@@ -0,0 +1,282 @@
+#define Point OSXPoint
+#define Rect OSXRect
+#define Cursor OSXCursor
+#import "osx-delegate.h"
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+#undef Cursor
+#undef Rect
+#undef Point
+
+#include <u.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <drawfcall.h>
+
+AUTOFRAMEWORK(Foundation)
+AUTOFRAMEWORK(AppKit)
+
+extern int trace;
+
+extern void fullscreen(int);
+extern void kbdevent(NSEvent *event);
+extern void mouseevent(NSEvent *event);
+extern void eresized(int);
+
+extern void runmsg(Wsysmsg *m);
+extern void seticon();
+
+@implementation DevdrawDelegate
++(void)populateMainMenu
+{
+	NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"];
+	NSMenuItem *menuItem;
+	NSMenu *submenu;
+
+	menuItem = [mainMenu addItemWithTitle:@"Apple" action:NULL keyEquivalent:@""];
+	submenu = [[NSMenu alloc] initWithTitle:@"Apple"];
+	[NSApp performSelector:@selector(setAppleMenu:) withObject:submenu];
+	[self populateApplicationMenu:submenu];
+	[mainMenu setSubmenu:submenu forItem:menuItem];
+
+	menuItem = [mainMenu addItemWithTitle:@"View" action:NULL keyEquivalent:@""];
+	submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"View", "@The View menu")];
+	[self populateViewMenu:submenu];
+	[mainMenu setSubmenu:submenu forItem:menuItem];
+
+	menuItem = [mainMenu addItemWithTitle:@"Window" action:NULL keyEquivalent:@""];
+	submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", @"The Window menu")];
+	[self populateWindowMenu:submenu];
+	[mainMenu setSubmenu:submenu forItem:menuItem];
+	[NSApp setWindowsMenu:submenu];
+
+	menuItem = [mainMenu addItemWithTitle:@"Help" action:NULL keyEquivalent:@""];
+	submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", @"The Help menu")];
+	[self populateHelpMenu:submenu];
+	[mainMenu setSubmenu:submenu forItem:menuItem];
+
+	[NSApp setMainMenu:mainMenu];
+}
+
++(void)populateApplicationMenu:(NSMenu *)aMenu
+{
+	NSString *applicationName = [[NSProcessInfo processInfo] processName];
+	NSMenuItem *menuItem;
+	
+	menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"About", nil), applicationName]
+								action:@selector(orderFrontStandardAboutPanel:)
+						 keyEquivalent:@""];
+	[menuItem setTarget:NSApp];
+	
+	[aMenu addItem:[NSMenuItem separatorItem]];
+	
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Preferences...", nil)
+								action:NULL
+						 keyEquivalent:@","];
+	
+	[aMenu addItem:[NSMenuItem separatorItem]];
+	
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Services", nil)
+								action:NULL
+						 keyEquivalent:@""];
+	NSMenu * servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
+	[aMenu setSubmenu:servicesMenu forItem:menuItem];
+	[NSApp setServicesMenu:servicesMenu];
+	
+	[aMenu addItem:[NSMenuItem separatorItem]];
+	
+	menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Hide", nil), applicationName]
+								action:@selector(hide:)
+						 keyEquivalent:@"h"];
+	[menuItem setTarget:NSApp];
+	
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Hide Others", nil)
+								action:@selector(hideOtherApplications:)
+						 keyEquivalent:@"h"];
+	[menuItem setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
+	[menuItem setTarget:NSApp];
+	
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Show All", nil)
+								action:@selector(unhideAllApplications:)
+						 keyEquivalent:@""];
+	[menuItem setTarget:NSApp];
+	
+	[aMenu addItem:[NSMenuItem separatorItem]];
+	
+	menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Quit", nil), applicationName]
+								action:@selector(terminate:)
+						 keyEquivalent:@"q"];
+	[menuItem setTarget:NSApp];
+}
+
++(void)populateViewMenu:(NSMenu *)aMenu
+{
+	NSMenuItem *menuItem;
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Full Screen", nil)
+				action:@selector(fullscreen:) keyEquivalent:@"F"];
+	[menuItem setTarget:NSApp];
+
+	menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Cmd-F exits full screen", nil)
+				action:NULL keyEquivalent:@""];
+}
+
++(void)populateWindowMenu:(NSMenu *)aMenu
+{
+}
+
++(void)populateHelpMenu:(NSMenu *)aMenu
+{
+}
+
+- (void)applicationWillFinishLaunching:(NSNotification *)notification
+{
+	seticon();
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notification
+{
+	[DevdrawDelegate populateMainMenu];
+
+//	[NSThread detachNewThreadSelector:@selector(devdrawMain)
+//		toTarget:self withObject:nil];
+//	[NSApplication detachDrawingThread:@selector(devdrawMain)
+//		toTarget:self withObject:nil];
+	[readHandle waitForDataInBackgroundAndNotify];
+}
+
+- (id)init
+{
+	if(self = [super init]){
+		readHandle = [[NSFileHandle alloc] initWithFileDescriptor:3 closeOnDealloc:YES];
+		[[NSNotificationCenter defaultCenter] addObserver:self
+			selector:@selector(devdrawMain:)
+			name:NSFileHandleDataAvailableNotification
+			object:readHandle];
+		[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
+			selector:@selector(receiveWake:)
+			name:NSWorkspaceDidWakeNotification
+			object:NULL];
+	}
+	return self;
+}
+
+- (void)dealloc
+{
+	[[NSNotificationCenter defaultCenter] removeObserver:self];
+	[readHandle release];
+	return [super dealloc];
+}
+
+- (void)devdrawMain:(NSNotification *)notification
+{
+	uchar buf[4], *mbuf;
+	int nmbuf, n, nn;
+	Wsysmsg m;
+	NSData *data;
+
+	mbuf = nil;
+	nmbuf = 0;
+
+	data = [readHandle readDataOfLength:4];
+	if([data length] == 4){
+		[data getBytes:buf length:4];
+		GET(buf, n);
+		if(n > nmbuf){
+			free(mbuf);
+			mbuf = malloc(4+n);
+			if(mbuf == nil)
+			sysfatal("malloc: %r");
+			nmbuf = n;
+		}
+		memmove(mbuf, buf, 4);
+		data = [readHandle readDataOfLength:(n-4)];
+		[data getBytes:(mbuf+4)];
+		nn = [data length];
+		if(nn != n-4)
+			sysfatal("eof during message");
+
+		/* pick off messages one by one */
+		if(convM2W(mbuf, nn+4, &m) <= 0)
+			sysfatal("cannot convert message");
+		if(trace) fprint(2, "<- %W\n", &m);
+		runmsg(&m);
+	} else {
+		[NSApp terminate:self];
+	}
+	[readHandle waitForDataInBackgroundAndNotify];
+
+return;
+
+	while((n = read(3, buf, 4)) == 4){
+		GET(buf, n);
+		if(n > nmbuf){
+			free(mbuf);
+			mbuf = malloc(4+n);
+			if(mbuf == nil)
+				sysfatal("malloc: %r");
+			nmbuf = n;
+		}
+		memmove(mbuf, buf, 4);
+		nn = readn(3, mbuf+4, n-4);
+		if(nn != n-4)
+			sysfatal("eof during message");
+
+		/* pick off messages one by one */
+		if(convM2W(mbuf, nn+4, &m) <= 0)
+			sysfatal("cannot convert message");
+		if(trace) fprint(2, "<- %W\n", &m);
+		runmsg(&m);
+	}
+}
+
+#pragma mark Notifications
+
+- (void)fullscreen:(NSNotification *)notification
+{
+	fullscreen(1);
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+//	if(osx.window == [notification object]){
+		[[NSNotificationCenter defaultCenter] removeObserver:self];
+		[NSApp terminate:self];
+//	}
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+//	if(osx.window == [notification object]) {
+		eresized(1);
+//	}
+}
+
+- (void)receiveWake:(NSNotification *)notification
+{
+	if(trace) NSLog(@"%s:%d %@", __FILE__, __LINE__, notification);
+	// redraw
+}
+
+- (void)mouseDown:(NSEvent *)anEvent
+{
+	mouseevent(anEvent);
+}
+
+- (void)mouseDragged:(NSEvent *)anEvent
+{
+	mouseevent(anEvent);
+}
+
+- (void)keydown:(NSEvent *)anEvent
+{
+	kbdevent(anEvent);
+}
+
+@end
blob - /dev/null
blob + 7b1da4f6802b940ddfa94fb6505aec423db2cff6 (mode 644)
--- /dev/null
+++ src/cmd/devdraw/osx-screen.m
@@ -0,0 +1,680 @@
+#define Point OSXPoint
+#define Rect OSXRect
+#define Cursor OSXCursor
+#import <Cocoa/Cocoa.h>
+#import <AppKit/AppKit.h>
+#undef Rect
+#undef Point
+#undef Cursor
+#undef offsetof
+#undef nil
+
+#include <u.h>
+#include <libc.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(Cocoa)
+
+#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;
+	NSWindow *window;
+	CGImageRef image;
+	CGContextRef windowctx;
+
+	int needflush;
+	QLock flushlock;
+	int active;
+	int infullscreen;
+	int kalting;		// last keystroke was Kalt
+} osx;
+
+enum
+{
+	WindowAttrs = NSClosableWindowMask |
+		NSTitledWindowMask |
+		NSMiniaturizableWindowMask |
+		NSResizableWindowMask
+};
+
+static void screenproc(void*);
+ void eresized(int);
+ void fullscreen(int);
+ void seticon(void);
+static void activated(int);
+
+enum
+{
+	CmdFullScreen = 1,
+};
+
+@interface P9View : NSView
+{}
+@end
+
+@implementation P9View
+- (BOOL)acceptsFirstResponder
+{
+	return YES;
+}
+@end
+
+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;
+	NSRect or;
+	Rectangle r;
+	int havemin;
+
+	memimageinit();
+
+	cgr = CGDisplayBounds(CGMainDisplayID());
+	osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
+	
+	// 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 = NSMakeRect(r.min.x, r.min.y, r.max.x, r.max.y);
+	osx.window = [[NSWindow alloc] initWithContentRect:or styleMask:WindowAttrs
+		 backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
+	[osx.window setDelegate:[NSApp delegate]];
+	[osx.window setAcceptsMouseMovedEvents:YES];
+
+	P9View *view = [[P9View alloc] initWithFrame:or];
+	[osx.window setContentView:view];
+	[view release];
+
+	setlabel(osx.label);
+	seticon();
+	
+	// Finally, put the window on the screen.
+	eresized(0);
+	[osx.window makeKeyAndOrderFront:nil];
+
+	[NSCursor unhide];
+}
+
+static Rendez scr;
+static QLock slock;
+
+void
+screeninit(void)
+{
+	scr.l = &slock;
+	qlock(scr.l);
+//	proccreate(screenproc, nil, 256*1024);
+	screenproc(NULL);
+	while(osx.window == nil)
+		rsleep(&scr);
+	qunlock(scr.l);
+}
+
+static void
+screenproc(void *v)
+{
+	qlock(scr.l);
+	_screeninit();
+	rwakeup(&scr);
+	qunlock(scr.l);
+}
+
+static ulong
+msec(void)
+{
+	return nsec()/1000000;
+}
+
+//static void
+void
+mouseevent(NSEvent *event)
+{
+	int wheel;
+	NSPoint op;
+	
+	op = [event locationInWindow];
+
+	osx.xy = subpt(Pt(op.x, op.y), osx.screenr.min);
+	wheel = 0;
+
+	switch([event type]){
+	case NSScrollWheel:;
+		CGFloat delta = [event deltaY];
+		if(delta > 0)
+			wheel = 8;
+		else
+			wheel = 16;
+		break;
+	
+	case NSLeftMouseDown:
+	case NSRightMouseDown:
+	case NSOtherMouseDown:
+	case NSLeftMouseUp:
+	case NSRightMouseUp:
+	case NSOtherMouseUp:;
+		NSInteger but;
+		NSUInteger  mod;
+		but = [event buttonNumber];
+		mod = [event modifierFlags];
+		
+		// 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 & NSAlternateKeyMask) {
+				// Take the ALT away from the keyboard handler.
+				if(osx.kalting) {
+					osx.kalting = 0;
+					keystroke(Kalt);
+				}
+				but = 2;
+			}
+			else if(mod & NSCommandKeyMask)
+				but = 4;
+		}
+		osx.buttons = but;
+		break;
+
+	case NSMouseMoved:
+	case NSLeftMouseDragged:
+	case NSRightMouseDragged:
+	case NSOtherMouseDragged:
+		break;
+	
+	default:
+		return;
+	}
+
+	mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
+}
+
+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
+void
+kbdevent(NSEvent *event)
+{
+	char ch;
+	UInt32 code;
+	UInt32 mod;
+	int k;
+
+	ch = [[event characters] characterAtIndex:0];
+	code = [event keyCode];
+	mod = [event modifierFlags];
+
+	switch([event type]){
+	case NSKeyDown:
+		osx.kalting = 0;
+		if(mod == NSCommandKeyMask){
+			if(ch == 'F' || ch == 'f'){
+				if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
+					fullscreen(0);
+				return;
+			}
+			
+			// 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;
+			}
+			if(' ' <= ch && ch <= '~') {
+				keystroke(Kcmd + ch);
+				return;
+			}
+			return;
+		}
+		k = ch;
+		if(code < nelem(keycvt) && keycvt[code])
+			k = keycvt[code];
+		if(k >= 0)
+			keystroke(k);
+		else{
+			keystroke(ch);
+		}
+		break;
+
+	case NSFlagsChanged:
+		if(!osx.buttons && !osx.kbuttons){
+			if(mod == NSAlternateKeyMask) {
+				osx.kalting = 1;
+				keystroke(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 & NSAlternateKeyMask)
+			osx.kbuttons |= 2;
+		if(mod & NSCommandKeyMask)
+			osx.kbuttons |= 4;
+		mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
+		break;
+	}
+	return;
+}
+
+//static void
+void
+eresized(int new)
+{
+	Memimage *m;
+	NSRect or;
+	ulong chan;
+	Rectangle r;
+	int bpl;
+	CGDataProviderRef provider;
+	CGImageRef image;
+	CGColorSpaceRef cspace;
+
+	or = [[osx.window contentView] bounds];
+	r = Rect(or.origin.x, or.origin.y, or.size.width, or.size.height);
+	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);
+	//cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+	cspace = CGColorSpaceCreateDeviceRGB();
+	image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
+		cspace,
+		kCGImageAlphaNoneSkipLast,
+		provider, 0, 0, kCGRenderingIntentDefault);
+	CGColorSpaceRelease(cspace);
+	CGDataProviderRelease(provider);	// CGImageCreate did incref
+	
+	mouserect = m->r;
+	if(new){
+		mouseresized = 1;
+		mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
+	}
+//	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
+flushproc(void *v)
+{
+	for(;;){
+		if(osx.needflush && osx.windowctx && canqlock(&osx.flushlock)){
+			if(osx.windowctx){
+				CGContextFlush(osx.windowctx);
+				osx.needflush = 0;
+			}
+			qunlock(&osx.flushlock);
+		}
+		usleep(33333);
+	}
+}
+
+void
+_flushmemscreen(Rectangle r)
+{
+	CGRect cgr;
+	CGImageRef subimg;
+
+	qlock(&osx.flushlock);
+	if(osx.windowctx == nil){
+		osx.windowctx = [[osx.window graphicsContext] graphicsPort];
+//		[osx.window flushWindow];
+//		proccreate(flushproc, nil, 256*1024);
+	}
+	
+	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);
+	cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense?
+	CGContextDrawImage(osx.windowctx, cgr, subimg);
+	osx.needflush = 1;
+	qunlock(&osx.flushlock);
+	CGImageRelease(subimg);
+}
+
+void
+activated(int active)
+{
+	osx.active = active;
+}
+
+void
+fullscreen(int wascmd)
+{
+	NSView *view = [osx.window contentView];
+
+	if(osx.isfullscreen){
+		[view exitFullScreenModeWithOptions:nil];
+		osx.isfullscreen = 0;
+	}else{
+		[view enterFullScreenMode:[osx.window screen] withOptions:nil];
+		osx.isfullscreen = 1;
+		osx.fullscreentime = msec();
+	}
+	eresized(1);
+}
+		
+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(Cursor *c)
+{
+	NSImage *image;
+	NSBitmapImageRep *bitmap;
+	NSCursor *nsc;
+	unsigned char *planes[5];
+	int i;
+
+	if(c == nil){
+		[NSCursor pop];
+		return;
+	}
+	
+	image = [[NSImage alloc] initWithSize:NSMakeSize(16.0, 16.0)];
+	bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
+		pixelsWide:16
+		pixelsHigh:16
+		bitsPerSample:1
+		samplesPerPixel:2
+		hasAlpha:YES
+		isPlanar:YES
+		colorSpaceName:NSCalibratedWhiteColorSpace
+		bytesPerRow:2
+		bitsPerPixel:1];
+
+	[bitmap getBitmapDataPlanes:planes];
+
+	for(i=0; i<16; i++){
+		planes[0][i] = ((ushort*)c->set)[i];
+		planes[1][i] = planes[0][i] | ((ushort*)c->clr)[i];
+	}
+
+	[image addRepresentation:bitmap];
+
+	nsc = [[NSCursor alloc] initWithImage:image
+		hotSpot:NSMakePoint(c->offset.x, c->offset.y)];
+	[nsc push];
+
+	[image release];
+	[bitmap release];
+	[nsc release];
+}
+
+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];
+	NSPasteboard *apple;
+} clip;
+
+char*
+getsnarf(void)
+{
+	char *s, *t;
+	NSArray *types;
+	NSString *string;
+	NSData * data;
+	NSUInteger ndata;
+
+/*	fprint(2, "applegetsnarf\n"); */
+	qlock(&clip.lk);
+
+	clip.apple = [NSPasteboard generalPasteboard];
+	types = [clip.apple types];
+
+	string = [clip.apple stringForType:NSStringPboardType];
+	if(string == nil){
+		fprint(2, "apple pasteboard get item type failed\n");
+		qunlock(&clip.lk);
+		return nil;
+	}
+
+	data = [string dataUsingEncoding:NSUnicodeStringEncoding];
+	if(data != nil){
+		ndata = [data length];
+		qunlock(&clip.lk);
+		s = smprint("%.*S", ndata/2, (Rune*)[data bytes]);
+		for(t=s; *t; t++)
+			if(*t == '\r')
+				*t = '\n';
+		return s;
+	}
+
+	qunlock(&clip.lk);
+	return nil;		
+}
+
+void
+putsnarf(char *s)
+{
+	NSArray *pboardTypes;
+	NSString *string;
+
+/*	fprint(2, "appleputsnarf\n"); */
+
+	if(strlen(s) >= SnarfSize)
+		return;
+	qlock(&clip.lk);
+	strcpy(clip.buf, s);
+	runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
+
+	pboardTypes = [NSArray arrayWithObject:NSStringPboardType];
+
+	clip.apple = [NSPasteboard generalPasteboard];
+	[clip.apple declareTypes:pboardTypes owner:nil];
+
+	assert(sizeof(clip.rbuf[0]) == 2);
+	string = [NSString stringWithCharacters:clip.rbuf length:runestrlen(clip.rbuf)*2];
+	if(string == nil){
+		fprint(2, "apple pasteboard data create failed\n");
+		qunlock(&clip.lk);
+		return;
+	}
+	if(![clip.apple setString:string forType:NSStringPboardType]){
+		fprint(2, "apple pasteboard putitem failed\n");
+		qunlock(&clip.lk);
+		return;
+	}
+	qunlock(&clip.lk);
+}
+
+void
+setlabel(char *label)
+{
+	CFStringRef cs;
+	cs = CFStringCreateWithBytes(nil, (uchar*)label, strlen(osx.label), kCFStringEncodingUTF8, false);
+	[osx.window setTitle:(NSString*)cs];
+	CFRelease(cs);
+}
+
+void
+kicklabel(char *label)
+{
+	char *p;
+
+	p = strdup(label);
+	if(p == nil)
+		return;
+	qlock(&osx.labellock);
+	free(osx.label);
+	osx.label = p;
+	qunlock(&osx.labellock);
+
+	setlabel(label);	
+}
+
+// static void
+void
+seticon(void)
+{
+	NSImage *im;
+	NSData *d;
+
+	d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)];
+	im = [[NSImage alloc] initWithData:d];
+	if(im){
+		NSLog(@"here");
+		[NSApp setApplicationIconImage:im];
+		[[NSApp dockTile] setShowsApplicationBadge:YES];
+		[[NSApp dockTile] display];
+	}
+	[d release];
+	[im release];
+}
blob - /dev/null
blob + 722378eeb72b9466c72a47ab9b602cf86cf0b022 (mode 644)
--- /dev/null
+++ src/cmd/devdraw/osx-srv.m
@@ -0,0 +1,452 @@
+#define Point OSXPoint
+#define Rect OSXRect
+#define Cursor OSXCursor
+#import <AppKit/AppKit.h>
+#undef Rect
+#undef Point
+#undef Cursor
+
+/*
+ * Window system protocol server.
+ */
+
+#include <u.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <drawfcall.h>
+#include "osx-screen.h"
+#include "devdraw.h"
+
+AUTOFRAMEWORK(AppKit)
+
+#import "osx-delegate.h"
+
+#undef time
+
+#define MouseMask (\
+	ButtonPressMask|\
+	ButtonReleaseMask|\
+	PointerMotionMask|\
+	Button1MotionMask|\
+	Button2MotionMask|\
+	Button3MotionMask)
+
+#define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask
+
+typedef struct Kbdbuf Kbdbuf;
+typedef struct Mousebuf Mousebuf;
+typedef struct Fdbuf Fdbuf;
+typedef struct Tagbuf Tagbuf;
+
+struct Kbdbuf
+{
+	Rune r[32];
+	int ri;
+	int wi;
+	int stall;
+};
+
+struct Mousebuf
+{
+	Mouse m[32];
+	Mouse last;
+	int ri;
+	int wi;
+	int stall;
+};
+
+struct Tagbuf
+{
+	int t[32];
+	int ri;
+	int wi;
+};
+
+Kbdbuf kbd;
+Mousebuf mouse;
+Tagbuf kbdtags;
+Tagbuf mousetags;
+
+void fdslide(Fdbuf*);
+void runmsg(Wsysmsg*);
+void replymsg(Wsysmsg*);
+void matchkbd(void);
+void matchmouse(void);
+int fdnoblock(int);
+Rectangle mouserect;
+int mouseresized;
+
+
+QLock lk;
+void
+zlock(void)
+{
+	qlock(&lk);
+}
+
+void
+zunlock(void)
+{
+	qunlock(&lk);
+}
+
+int chatty;
+int drawsleep;
+int trace;
+
+void
+usage(void)
+{
+	fprint(2, "usage: devdraw (don't run directly)\n");
+	exits("usage");
+}
+
+void
+bell(void *v, char *msg)
+{
+	if(strcmp(msg, "alarm") == 0)
+		drawsleep = drawsleep ? 0 : 1000;
+	noted(NCONT);
+}
+
+int
+main(int argc, char **argv)
+{
+	NSAutoreleasePool *pool = nil;
+	NSApplication *application = nil;
+
+	/*
+	 * Move the protocol off stdin/stdout so that
+	 * any inadvertent prints don't screw things up.
+	 */
+	dup(0, 3);
+	dup(1, 4);
+	close(0);
+	close(1);
+	open("/dev/null", OREAD);
+	open("/dev/null", OWRITE);
+
+	trace = 1;
+	fmtinstall('W', drawfcallfmt);
+
+	ARGBEGIN{
+	case 'D':
+		chatty++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	/*
+	 * Ignore arguments.  They're only for good ps -a listings.
+	 */
+	
+	notify(bell);
+
+	pool = [[NSAutoreleasePool alloc] init];
+	application = [NSApplication sharedApplication];
+	[application setDelegate:[[DevdrawDelegate alloc] init]];
+	[application run];
+	[application setDelegate:nil];
+	[pool release];
+	return 0;
+}
+
+void
+replyerror(Wsysmsg *m)
+{
+	char err[256];
+	
+	rerrstr(err, sizeof err);
+	m->type = Rerror;
+	m->error = err;
+	replymsg(m);
+}
+
+/* 
+ * Handle a single wsysmsg. 
+ * Might queue for later (kbd, mouse read)
+ */
+void
+runmsg(Wsysmsg *m)
+{
+	static uchar buf[65536];
+	int n;
+	Memimage *i;
+	
+	switch(m->type){
+	case Tinit:
+		memimageinit();
+		i = attachscreen(m->label, m->winsize);
+		_initdisplaymemimage(i);
+		replymsg(m);
+		break;
+
+	case Trdmouse:
+		// zlock();
+		mousetags.t[mousetags.wi++] = m->tag;
+		if(mousetags.wi == nelem(mousetags.t))
+			mousetags.wi = 0;
+		if(mousetags.wi == mousetags.ri)
+			sysfatal("too many queued mouse reads");
+		mouse.stall = 0;
+		matchmouse();
+		// zunlock();
+		break;
+
+	case Trdkbd:
+		zlock();
+		kbdtags.t[kbdtags.wi++] = m->tag;
+		if(kbdtags.wi == nelem(kbdtags.t))
+			kbdtags.wi = 0;
+		if(kbdtags.wi == kbdtags.ri)
+			sysfatal("too many queued keyboard reads");
+		kbd.stall = 0;
+		matchkbd();
+		zunlock();
+		break;
+
+	case Tmoveto:
+		setmouse(m->mouse.xy);
+		replymsg(m);
+		break;
+
+	case Tcursor:
+		if(m->arrowcursor)
+			setcursor(nil);
+		else
+			setcursor(&m->cursor);
+		replymsg(m);
+		break;
+			
+	case Tbouncemouse:
+	//	_xbouncemouse(&m->mouse);
+		replymsg(m);
+		break;
+
+	case Tlabel:
+		kicklabel(m->label);
+		replymsg(m);
+		break;
+
+	case Trdsnarf:
+		m->snarf = getsnarf();
+		replymsg(m);
+		free(m->snarf);
+		break;
+
+	case Twrsnarf:
+		putsnarf(m->snarf);
+		replymsg(m);
+		break;
+
+	case Trddraw:
+		n = m->count;
+		if(n > sizeof buf)
+			n = sizeof buf;
+		n = _drawmsgread(buf, n);
+		if(n < 0)
+			replyerror(m);
+		else{
+			m->count = n;
+			m->data = buf;
+			replymsg(m);
+		}
+		break;
+
+	case Twrdraw:
+		if(_drawmsgwrite(m->data, m->count) < 0)
+			replyerror(m);
+		else
+			replymsg(m);
+		break;
+	
+	case Ttop:
+	//	_xtopwindow();
+		replymsg(m);
+		break;
+	
+	case Tresize:
+	//	_xresizewindow(m->rect);
+		replymsg(m);
+		break;
+	}
+}
+
+/*
+ * Reply to m.
+ */
+QLock replylock;
+void
+replymsg(Wsysmsg *m)
+{
+	int n;
+	static uchar *mbuf;
+	static int nmbuf;
+
+	/* T -> R msg */
+	if(m->type%2 == 0)
+		m->type++;
+		
+	if(trace) fprint(2, "-> %W\n", m);
+	/* copy to output buffer */
+	n = sizeW2M(m);
+
+	qlock(&replylock);
+	if(n > nmbuf){
+		free(mbuf);
+		mbuf = malloc(n);
+		if(mbuf == nil)
+			sysfatal("out of memory");
+		nmbuf = n;
+	}
+	convW2M(m, mbuf, n);
+	if(write(4, mbuf, n) != n)
+		sysfatal("write: %r");
+	qunlock(&replylock);
+}
+
+/*
+ * Match queued kbd reads with queued kbd characters.
+ */
+void
+matchkbd(void)
+{
+	Wsysmsg m;
+	
+	if(kbd.stall)
+		return;
+	while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
+		m.type = Rrdkbd;
+		m.tag = kbdtags.t[kbdtags.ri++];
+		if(kbdtags.ri == nelem(kbdtags.t))
+			kbdtags.ri = 0;
+		m.rune = kbd.r[kbd.ri++];
+		if(kbd.ri == nelem(kbd.r))
+			kbd.ri = 0;
+		replymsg(&m);
+	}
+}
+
+/*
+ * Match queued mouse reads with queued mouse events.
+ */
+void
+matchmouse(void)
+{
+	Wsysmsg m;
+	
+	while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
+		m.type = Rrdmouse;
+		m.tag = mousetags.t[mousetags.ri++];
+		if(mousetags.ri == nelem(mousetags.t))
+			mousetags.ri = 0;
+		m.mouse = mouse.m[mouse.ri];
+		m.resized = mouseresized;
+		/*
+		if(m.resized)
+			fprint(2, "sending resize\n");
+		*/
+		mouseresized = 0;
+		mouse.ri++;
+		if(mouse.ri == nelem(mouse.m))
+			mouse.ri = 0;
+		replymsg(&m);
+	}
+}
+
+void
+mousetrack(int x, int y, int b, int ms)
+{
+	Mouse *m;
+	
+	if(x < mouserect.min.x)
+		x = mouserect.min.x;
+	if(x > mouserect.max.x)
+		x = mouserect.max.x;
+	if(y < mouserect.min.y)
+		y = mouserect.min.y;
+	if(y > mouserect.max.y)
+		y = mouserect.max.y;
+
+//	zlock();
+	// If reader has stopped reading, don't bother.
+	// If reader is completely caught up, definitely queue.
+	// Otherwise, queue only button change events.
+	if(!mouse.stall)
+	if(mouse.wi == mouse.ri || mouse.last.buttons != b){
+		m = &mouse.last;
+		m->xy.x = x;
+		m->xy.y = y;
+		m->buttons = b;
+		m->msec = ms;
+
+		mouse.m[mouse.wi] = *m;
+		if(++mouse.wi == nelem(mouse.m))
+			mouse.wi = 0;
+		if(mouse.wi == mouse.ri){
+			mouse.stall = 1;
+			mouse.ri = 0;
+			mouse.wi = 1;
+			mouse.m[0] = *m;
+		}
+		matchmouse();
+	}
+//	zunlock();
+}
+
+void
+kputc(int c)
+{
+	zlock();
+	kbd.r[kbd.wi++] = c;
+	if(kbd.wi == nelem(kbd.r))
+		kbd.wi = 0;
+	if(kbd.ri == kbd.wi)
+		kbd.stall = 1;
+	matchkbd();
+	zunlock();
+}
+
+void
+keystroke(int c)
+{
+	static Rune k[10];
+	static int alting, nk;
+	int i;
+
+	if(c == Kalt){
+		alting = !alting;
+		return;
+	}
+	if(!alting){
+		kputc(c);
+		return;
+	}
+	if(nk >= nelem(k))      // should not happen
+		nk = 0;
+	k[nk++] = c;
+	c = _latin1(k, nk);
+	if(c > 0){
+		alting = 0;
+		kputc(c);
+		nk = 0;
+		return;
+	}
+	if(c == -1){
+		alting = 0;
+		for(i=0; i<nk; i++)
+			kputc(k[i]);
+		nk = 0;
+		return;
+	}
+	// need more input
+	return;
+}