#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]; }