commit 113867b836eaa85215e4b2ece5ccf612f34c3e03 from: Jeff Sickel date: Tue Sep 29 16:35:23 2009 UTC devdraw: draft of new Cocoa-based devdraw Can test with cd $PLAN9/src/cmd/devdraw mk devdraw-cocoa DEVDRAW=devdraw-cocoa colors 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 +#import + +@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 +#import +#undef Cursor +#undef Rect +#undef Point + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#import +#undef Rect +#undef Point +#undef Cursor +#undef offsetof +#undef nil + +#include +#include +#include +#include +#include +#include +#include +#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 +#undef Rect +#undef Point +#undef Cursor + +/* + * Window system protocol server. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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