summaryrefslogtreecommitdiffstats
path: root/src/cmd/devdraw/osx-screen.m
diff options
context:
space:
mode:
authorJeff Sickel <jas@corpus-callosum.com>2009-09-29 09:35:23 -0700
committerJeff Sickel <jas@corpus-callosum.com>2009-09-29 09:35:23 -0700
commit113867b836eaa85215e4b2ece5ccf612f34c3e03 (patch)
tree375cc7e2a45d14a5ae4694d55e803cea3b433cca /src/cmd/devdraw/osx-screen.m
parentdaea2c7d501c3e825bede80992ade6b241efdce1 (diff)
downloadplan9port-113867b836eaa85215e4b2ece5ccf612f34c3e03.tar.gz
plan9port-113867b836eaa85215e4b2ece5ccf612f34c3e03.zip
devdraw: draft of new Cocoa-based devdraw
Can test with cd $PLAN9/src/cmd/devdraw mk devdraw-cocoa DEVDRAW=devdraw-cocoa colors
Diffstat (limited to 'src/cmd/devdraw/osx-screen.m')
-rw-r--r--src/cmd/devdraw/osx-screen.m680
1 files changed, 680 insertions, 0 deletions
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];
+}