aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/devdraw/mkfile11
-rw-r--r--src/cmd/devdraw/osx-delegate.h15
-rw-r--r--src/cmd/devdraw/osx-delegate.m282
-rw-r--r--src/cmd/devdraw/osx-screen.m680
-rw-r--r--src/cmd/devdraw/osx-srv.m452
5 files changed, 1439 insertions, 1 deletions
diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile
index 96f329ac..eda896d9 100644
--- a/src/cmd/devdraw/mkfile
+++ b/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
+
diff --git a/src/cmd/devdraw/osx-delegate.h b/src/cmd/devdraw/osx-delegate.h
new file mode 100644
index 00000000..77dde759
--- /dev/null
+++ b/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
diff --git a/src/cmd/devdraw/osx-delegate.m b/src/cmd/devdraw/osx-delegate.m
new file mode 100644
index 00000000..c4a5f5bb
--- /dev/null
+++ b/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
diff --git a/src/cmd/devdraw/osx-screen.m b/src/cmd/devdraw/osx-screen.m
new file mode 100644
index 00000000..7b1da4f6
--- /dev/null
+++ b/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];
+}
diff --git a/src/cmd/devdraw/osx-srv.m b/src/cmd/devdraw/osx-srv.m
new file mode 100644
index 00000000..722378ee
--- /dev/null
+++ b/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;
+}