vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit e769e79cbe9454c7a317f53db28954df0c6d6f9b
parent c7c041c8ea44d3c1cc93a9b882963caabf26b4a1
Author: Russ Cox <rsc@swtch.com>
Date:   Sat, 28 Jun 2008 15:08:36 -0400

9vx/OSX: attempt at native GUI code

Diffstat:
src/9vx/Makefrag | 15+++++++++++++--
src/9vx/main.c | 4++++
src/9vx/osx/draw.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/osx/keycodes.h | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/osx/screen.c | 632+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/stub.c | 5+++++
6 files changed, 897 insertions(+), 2 deletions(-)

diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag @@ -10,7 +10,8 @@ endif ifeq ($(OS),darwin) PLAN9VX=1 - # TODO: carbon +# TODO: osx works except that Carbon gets confused +# that we can survive an EXC_BAD_ACCESS PLAN9GUI=x11 PLAN9AUDIO=none endif @@ -124,9 +125,16 @@ PLAN9_x11_OBJS = \ x11-kernel.o \ x11-keysym2rune.o \ ) - PLAN9_x11_LIBS = -L/usr/X11R6/lib -L/usr/local/lib -lX11 +PLAN9_osx_OBJS =\ + $(addprefix 9vx/osx/, \ + screen.o \ + draw.o \ + ) +PLAN9_osx_LIBS = -ggdb -framework Carbon -framework QuickTime + + PLAN9_GUI_OBJS = $(PLAN9_$(PLAN9GUI)_OBJS) PLAN9_GUI_LIBS = $(PLAN9_$(PLAN9GUI)_LIBS) @@ -149,6 +157,9 @@ PLAN9_DEPS = \ 9vx/x11/%.o: 9vx/x11/%.c $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -I/usr/X11R6/include -I/usr/local/include -Wall -Wno-missing-braces -c -o $@ $< +9vx/osx/%.o: 9vx/osx/%.c + $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -Wall -Wno-missing-braces -c -o $@ $< + 9vx/%.o: 9vx/%.c $(HOST_CC) $(HOST_CFLAGS) -I. -I9vx -I9vx/a -Wall -Wno-missing-braces -c -o $@ $< diff --git a/src/9vx/main.c b/src/9vx/main.c @@ -4,6 +4,10 @@ #define WANT_M +#ifdef __APPLE__ +#define __DARWIN_UNIX03 0 +#endif + #include "u.h" #include "libvx32/vx32.h" #include <sys/mman.h> diff --git a/src/9vx/osx/draw.c b/src/9vx/osx/draw.c @@ -0,0 +1,54 @@ +#include "u.h" +#include "lib.h" +#include "draw.h" +#include "memdraw.h" + +Memimage* +allocmemimage(Rectangle r, ulong chan) +{ + return _allocmemimage(r, chan); +} + +void +freememimage(Memimage *i) +{ + _freememimage(i); +} + +void +memfillcolor(Memimage *i, ulong val) +{ + _memfillcolor(i, val); +} + + +int +cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + return _cloadmemimage(i, r, data, ndata); +} + +void +memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op) +{ + _memimagedraw(_memimagedrawsetup(dst, r, src, sp, mask, mp, op)); +} + +ulong +pixelbits(Memimage *m, Point p) +{ + return _pixelbits(m, p); +} + +int +loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + return _loadmemimage(i, r, data, ndata); +} + +int +unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + return _unloadmemimage(i, r, data, ndata); +} + diff --git a/src/9vx/osx/keycodes.h b/src/9vx/osx/keycodes.h @@ -0,0 +1,189 @@ +/* These are the Macintosh key scancode constants -- from Inside Macintosh */ +#define QZ_ESCAPE 0x35 +#define QZ_F1 0x7A +#define QZ_F2 0x78 +#define QZ_F3 0x63 +#define QZ_F4 0x76 +#define QZ_F5 0x60 +#define QZ_F6 0x61 +#define QZ_F7 0x62 +#define QZ_F8 0x64 +#define QZ_F9 0x65 +#define QZ_F10 0x6D +#define QZ_F11 0x67 +#define QZ_F12 0x6F +#define QZ_PRINT 0x69 +#define QZ_SCROLLOCK 0x6B +#define QZ_PAUSE 0x71 +#define QZ_POWER 0x7F +#define QZ_BACKQUOTE 0x32 +#define QZ_1 0x12 +#define QZ_2 0x13 +#define QZ_3 0x14 +#define QZ_4 0x15 +#define QZ_5 0x17 +#define QZ_6 0x16 +#define QZ_7 0x1A +#define QZ_8 0x1C +#define QZ_9 0x19 +#define QZ_0 0x1D +#define QZ_MINUS 0x1B +#define QZ_EQUALS 0x18 +#define QZ_BACKSPACE 0x33 +#define QZ_INSERT 0x72 +#define QZ_HOME 0x73 +#define QZ_PAGEUP 0x74 +#define QZ_NUMLOCK 0x47 +#define QZ_KP_EQUALS 0x51 +#define QZ_KP_DIVIDE 0x4B +#define QZ_KP_MULTIPLY 0x43 +#define QZ_TAB 0x30 +#define QZ_q 0x0C +#define QZ_w 0x0D +#define QZ_e 0x0E +#define QZ_r 0x0F +#define QZ_t 0x11 +#define QZ_y 0x10 +#define QZ_u 0x20 +#define QZ_i 0x22 +#define QZ_o 0x1F +#define QZ_p 0x23 +#define QZ_LEFTBRACKET 0x21 +#define QZ_RIGHTBRACKET 0x1E +#define QZ_BACKSLASH 0x2A +#define QZ_DELETE 0x75 +#define QZ_END 0x77 +#define QZ_PAGEDOWN 0x79 +#define QZ_KP7 0x59 +#define QZ_KP8 0x5B +#define QZ_KP9 0x5C +#define QZ_KP_MINUS 0x4E +#define QZ_CAPSLOCK 0x39 +#define QZ_a 0x00 +#define QZ_s 0x01 +#define QZ_d 0x02 +#define QZ_f 0x03 +#define QZ_g 0x05 +#define QZ_h 0x04 +#define QZ_j 0x26 +#define QZ_k 0x28 +#define QZ_l 0x25 +#define QZ_SEMICOLON 0x29 +#define QZ_QUOTE 0x27 +#define QZ_RETURN 0x24 +#define QZ_KP4 0x56 +#define QZ_KP5 0x57 +#define QZ_KP6 0x58 +#define QZ_KP_PLUS 0x45 +#define QZ_LSHIFT 0x38 +#define QZ_z 0x06 +#define QZ_x 0x07 +#define QZ_c 0x08 +#define QZ_v 0x09 +#define QZ_b 0x0B +#define QZ_n 0x2D +#define QZ_m 0x2E +#define QZ_COMMA 0x2B +#define QZ_PERIOD 0x2F +#define QZ_SLASH 0x2C +/* These are the same as the left versions - use left by default */ +#if 0 +#define QZ_RSHIFT 0x38 +#endif +#define QZ_UP 0x7E +#define QZ_KP1 0x53 +#define QZ_KP2 0x54 +#define QZ_KP3 0x55 +#define QZ_KP_ENTER 0x4C +#define QZ_LCTRL 0x3B +#define QZ_LALT 0x3A +#define QZ_LMETA 0x37 +#define QZ_SPACE 0x31 +/* These are the same as the left versions - use left by default */ +#if 0 +#define QZ_RMETA 0x37 +#define QZ_RALT 0x3A +#define QZ_RCTRL 0x3B +#endif +#define QZ_LEFT 0x7B +#define QZ_DOWN 0x7D +#define QZ_RIGHT 0x7C +#define QZ_KP0 0x52 +#define QZ_KP_PERIOD 0x41 + +/* Wierd, these keys are on my iBook under MacOS X */ +#define QZ_IBOOK_ENTER 0x34 +#define QZ_IBOOK_LEFT 0x3B +#define QZ_IBOOK_RIGHT 0x3C +#define QZ_IBOOK_DOWN 0x3D +#define QZ_IBOOK_UP 0x3E +#define KEY_ENTER 13 +#define KEY_TAB 9 + +#define KEY_BASE 0x100 + +/* Function keys */ +#define KEY_F (KEY_BASE+64) + +/* Control keys */ +#define KEY_CTRL (KEY_BASE) +#define KEY_BACKSPACE (KEY_CTRL+0) +#define KEY_DELETE (KEY_CTRL+1) +#define KEY_INSERT (KEY_CTRL+2) +#define KEY_HOME (KEY_CTRL+3) +#define KEY_END (KEY_CTRL+4) +#define KEY_PAGE_UP (KEY_CTRL+5) +#define KEY_PAGE_DOWN (KEY_CTRL+6) +#define KEY_ESC (KEY_CTRL+7) + +/* Control keys short name */ +#define KEY_BS KEY_BACKSPACE +#define KEY_DEL KEY_DELETE +#define KEY_INS KEY_INSERT +#define KEY_PGUP KEY_PAGE_UP +#define KEY_PGDOWN KEY_PAGE_DOWN +#define KEY_PGDWN KEY_PAGE_DOWN + +/* Cursor movement */ +#define KEY_CRSR (KEY_BASE+16) +#define KEY_RIGHT (KEY_CRSR+0) +#define KEY_LEFT (KEY_CRSR+1) +#define KEY_DOWN (KEY_CRSR+2) +#define KEY_UP (KEY_CRSR+3) + +/* Multimedia keyboard/remote keys */ +#define KEY_MM_BASE (0x100+384) +#define KEY_POWER (KEY_MM_BASE+0) +#define KEY_MENU (KEY_MM_BASE+1) +#define KEY_PLAY (KEY_MM_BASE+2) +#define KEY_PAUSE (KEY_MM_BASE+3) +#define KEY_PLAYPAUSE (KEY_MM_BASE+4) +#define KEY_STOP (KEY_MM_BASE+5) +#define KEY_FORWARD (KEY_MM_BASE+6) +#define KEY_REWIND (KEY_MM_BASE+7) +#define KEY_NEXT (KEY_MM_BASE+8) +#define KEY_PREV (KEY_MM_BASE+9) +#define KEY_VOLUME_UP (KEY_MM_BASE+10) +#define KEY_VOLUME_DOWN (KEY_MM_BASE+11) +#define KEY_MUTE (KEY_MM_BASE+12) + +/* Keypad keys */ +#define KEY_KEYPAD (KEY_BASE+32) +#define KEY_KP0 (KEY_KEYPAD+0) +#define KEY_KP1 (KEY_KEYPAD+1) +#define KEY_KP2 (KEY_KEYPAD+2) +#define KEY_KP3 (KEY_KEYPAD+3) +#define KEY_KP4 (KEY_KEYPAD+4) +#define KEY_KP5 (KEY_KEYPAD+5) +#define KEY_KP6 (KEY_KEYPAD+6) +#define KEY_KP7 (KEY_KEYPAD+7) +#define KEY_KP8 (KEY_KEYPAD+8) +#define KEY_KP9 (KEY_KEYPAD+9) +#define KEY_KPDEC (KEY_KEYPAD+10) +#define KEY_KPINS (KEY_KEYPAD+11) +#define KEY_KPDEL (KEY_KEYPAD+12) +#define KEY_KPENTER (KEY_KEYPAD+13) + +/* Special keys */ +#define KEY_INTERN (0x1000) +#define KEY_CLOSE_WIN (KEY_INTERN+0) diff --git a/src/9vx/osx/screen.c b/src/9vx/osx/screen.c @@ -0,0 +1,632 @@ +#define Point OSXPoint +#define Rect OSXRect +#define Cursor OSXCursor +#include <Carbon/Carbon.h> +#include <QuickTime/QuickTime.h> // for full screen +#undef Rect +#undef Point +#undef Cursor +#undef offsetof +#undef nil + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#define Image IMAGE /* kernel has its own Image */ +#include <draw.h> +#include <memdraw.h> +#include <keyboard.h> +#include <cursor.h> +#include "screen.h" +#include "mouse.h" +#include "keycodes.h" + +struct { + Rectangle fullscreenr; + Rectangle screenr; + Memimage *screenimage; + int isfullscreen; + Rectangle nonfullscreenr; + + Point xy; + int buttons; + int kbuttons; + + CGDataProviderRef provider; + MenuRef wmenu; + MenuRef vmenu; + WindowRef window; + CGImageRef image; + WindowGroupRef wingroup; + PasteboardRef snarf; +} osx; + +enum +{ + WindowAttrs = + kWindowCloseBoxAttribute | + kWindowCollapseBoxAttribute | + kWindowResizableAttribute | + kWindowStandardHandlerAttribute | + kWindowFullZoomAttribute +}; + +static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*); +static void screenproc(void*); +static void eresized(int); +static void fullscreen(int); + +enum +{ + CmdFullScreen = 1, +}; + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *depth, + int *width, int *softscreen, void **X) +{ + Memimage *m; + + if(osx.screenimage == nil){ + screeninit(); + if(osx.screenimage == nil) + panic("cannot create OS X screen"); + } + m = osx.screenimage; + *r = m->r; + *chan = m->chan; + *depth = m->depth; + *width = m->width; + *X = nil; + *softscreen = 1; + return m->data->bdata; +} + +void +screeninit(void) +{ + CGRect cgr; + OSXRect or; + + _memimageinit(); + + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + + cgr = CGDisplayBounds(CGMainDisplayID()); + osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height); + + InitCursor(); + + // Create minimal menu with full-screen option. + ClearMenuBar(); + CreateStandardWindowMenu(0, &osx.wmenu); + InsertMenu(osx.wmenu, 0); + MenuItemIndex ix; + CreateNewMenu(1004, 0, &osx.vmenu); // XXX 1004? + SetMenuTitleWithCFString(osx.vmenu, CFSTR("View")); + AppendMenuItemTextWithCFString(osx.vmenu, + CFSTR("Full Screen"), 0, CmdFullScreen, &ix); + SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F'); + AppendMenuItemTextWithCFString(osx.vmenu, + CFSTR("Ctl-Opt exits full screen"), + kMenuItemAttrDisabled, CmdFullScreen, &ix); + InsertMenu(osx.vmenu, GetMenuID(osx.wmenu)); + DrawMenuBar(); + + // Create the window. + or.left = 0; + or.top = 30; + or.bottom = Dy(osx.fullscreenr) - 100; + or.right = Dx(osx.fullscreenr); + CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window); + CreateWindowGroup(0, &osx.wingroup); + SetWindowGroup(osx.window, osx.wingroup); + SetWindowTitleWithCFString(osx.window, CFSTR("Plan 9 VX")); + + // Set up the clip board. + if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr) + panic("pasteboard create"); + + // Explain in great detail which events we want to handle. + // Why can't we just have one handler? + const EventTypeSpec quits[] = { + { kEventClassApplication, kEventAppQuit } + }; + const EventTypeSpec commands[] = { + { kEventClassWindow, kEventWindowClosed }, + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassCommand, kEventCommandProcess } + }; + const EventTypeSpec events[] = { + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseWheelMoved }, + }; + InstallApplicationEventHandler( + NewEventHandlerUPP(eventhandler), + nelem(quits), quits, nil, nil); + InstallApplicationEventHandler( + NewEventHandlerUPP(eventhandler), + nelem(events), events, osx.window, nil); + InstallWindowEventHandler(osx.window, + NewEventHandlerUPP(eventhandler), + nelem(commands), commands, osx.window, nil); + + // Finally, put the window on the screen. + ShowWindow(osx.window); + ShowMenuBar(); + eresized(0); + SelectWindow(osx.window); + + InitCursor(); + + kproc("*screen*", screenproc, nil); +} + +static void +screenproc(void *v) +{ + RunApplicationEventLoop(); +} + +static OSStatus kbdevent(EventRef); +static OSStatus mouseevent(EventRef); + +static OSStatus +eventhandler(EventHandlerCallRef next, EventRef event, void *arg) +{ + OSStatus result; + + result = CallNextEventHandler(next, event); + + switch(GetEventClass(event)){ + case kEventClassKeyboard: + return kbdevent(event); + break; + + case kEventClassMouse: + return mouseevent(event); + break; + + case kEventClassCommand:; + HICommand cmd; + GetEventParameter(event, kEventParamDirectObject, + typeHICommand, nil, sizeof cmd, nil, &cmd); + switch(cmd.commandID){ + case kHICommandQuit: + exit(0); + + case CmdFullScreen: + fullscreen(1); + break; + + default: + return eventNotHandledErr; + } + break; + + case kEventClassWindow:; + switch(GetEventKind(event)){ + case kEventWindowClosed: + exit(0); + + case kEventWindowBoundsChanged: + eresized(1); + break; + + default: + return eventNotHandledErr; + } + break; + } + + return result; +} + +static OSStatus +mouseevent(EventRef event) +{ + int wheel; + OSXPoint op; + + GetEventParameter(event, kEventParamMouseLocation, + typeQDPoint, 0, sizeof op, 0, &op); + osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min); + wheel = 0; + + switch(GetEventKind(event)){ + case kEventMouseWheelMoved:; + SInt32 delta; + GetEventParameter(event, kEventParamMouseWheelDelta, + typeSInt32, 0, sizeof delta, 0, &delta); + if(delta > 0) + wheel = 8; + else + wheel = 16; + break; + + case kEventMouseDown: + case kEventMouseUp:; + UInt32 but, mod; + GetEventParameter(event, kEventParamMouseChord, + typeUInt32, 0, sizeof but, 0, &but); + GetEventParameter(event, kEventParamKeyModifiers, + typeUInt32, 0, sizeof mod, 0, &mod); + + // OS X swaps button 2 and 3 + but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1); + + // 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 & optionKey) + but = 2; + else if(mod & cmdKey) + but = 4; + } + osx.buttons = but; + break; + + case kEventMouseMoved: + break; + + default: + return eventNotHandledErr; + } + + mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec()); + return noErr; +} + +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 +kputc(int c) +{ + kbdputc(kbdq, c); +} + +static OSStatus +kbdevent(EventRef event) +{ + char ch; + UInt32 code; + UInt32 mod; + int k; + + GetEventParameter(event, kEventParamKeyMacCharCodes, + typeChar, nil, sizeof ch, nil, &ch); + GetEventParameter(event, kEventParamKeyCode, + typeUInt32, nil, sizeof code, nil, &code); + GetEventParameter(event, kEventParamKeyModifiers, + typeUInt32, nil, sizeof mod, nil, &mod); + + switch(GetEventKind(event)){ + case kEventRawKeyDown: + case kEventRawKeyRepeat: + if(mod == cmdKey){ + if(ch == 'F' && osx.isfullscreen){ + fullscreen(0); + break; + } + return eventNotHandledErr; + } + k = ch; + if(code < nelem(keycvt) && keycvt[code]) + k = keycvt[code]; + if(k >= 0) + latin1putc(k, kputc); + break; + + case kEventRawKeyModifiersChanged: + if(!osx.buttons && !osx.kbuttons){ + if(mod == optionKey) + kbdputc(kbdq, 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 & optionKey) + osx.kbuttons |= 2; + if(mod & cmdKey) + osx.kbuttons |= 4; + mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec()); + break; + } + return noErr; +} + +static void +eresized(int new) +{ + Memimage *m; + OSXRect or; + ulong chan; + Rectangle r; + int bpl; + CGDataProviderRef provider; + CGImageRef image; + + GetWindowBounds(osx.window, kWindowContentRgn, &or); + r = Rect(or.left, or.top, or.right, or.bottom); + 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); + image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl, + CGColorSpaceCreateDeviceRGB(), + kCGImageAlphaNoneSkipLast, + provider, 0, 0, kCGRenderingIntentDefault); + CGDataProviderRelease(provider); // CGImageCreate did incref + + 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 +flushmemscreen(Rectangle r) +{ + CGRect cgr; + CGContextRef context; + CGImageRef subimg; + + QDBeginCGContext(GetWindowPort(osx.window), &context); + + 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); + CGContextDrawImage(context, cgr, subimg); + CGContextFlush(context); + CGImageRelease(subimg); + + QDEndCGContext(GetWindowPort(osx.window), &context); +} + +void +fullscreen(int x) +{ +} + +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(struct Cursor *c) +{ + OSXCursor oc; + int i; + + // SetCursor is deprecated, but what replaces it? + for(i=0; i<16; i++){ + oc.data[i] = ((ushort*)c->set)[i]; + oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i]; + } + oc.hotSpot.h = - c->offset.x; + oc.hotSpot.v = - c->offset.y; + SetCursor(&oc); +} + +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]; + PasteboardRef apple; +} clip; + +char* +getsnarf(void) +{ + char *s, *t; + CFArrayRef flavors; + CFDataRef data; + CFIndex nflavor, ndata, j; + CFStringRef type; + ItemCount nitem; + PasteboardItemID id; + PasteboardSyncFlags flags; + UInt32 i; + +/* fprint(2, "applegetsnarf\n"); */ + qlock(&clip.lk); + clip.apple = osx.snarf; + if(clip.apple == nil){ + if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ + fprint(2, "apple pasteboard create failed\n"); + qunlock(&clip.lk); + return nil; + } + } + flags = PasteboardSynchronize(clip.apple); + if(flags&kPasteboardClientIsOwner){ + s = strdup(clip.buf); + qunlock(&clip.lk); + return s; + } + if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ + fprint(2, "apple pasteboard get item count failed\n"); + qunlock(&clip.lk); + return nil; + } + for(i=1; i<=nitem; i++){ + if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) + continue; + if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) + continue; + nflavor = CFArrayGetCount(flavors); + for(j=0; j<nflavor; j++){ + type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); + if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) + continue; + if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) + continue; + ndata = CFDataGetLength(data); + qunlock(&clip.lk); + s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data)); + CFRelease(flavors); + CFRelease(data); + for(t=s; *t; t++) + if(*t == '\r') + *t = '\n'; + return s; + } + CFRelease(flavors); + } + qunlock(&clip.lk); + return nil; +} + +void +putsnarf(char *s) +{ + CFDataRef cfdata; + PasteboardSyncFlags flags; + +/* fprint(2, "appleputsnarf\n"); */ + + if(strlen(s) >= SnarfSize) + return; + qlock(&clip.lk); + strcpy(clip.buf, s); + runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); + clip.apple = osx.snarf; + if(PasteboardClear(clip.apple) != noErr){ + fprint(2, "apple pasteboard clear failed\n"); + qunlock(&clip.lk); + return; + } + flags = PasteboardSynchronize(clip.apple); + if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ + fprint(2, "apple pasteboard cannot assert ownership\n"); + qunlock(&clip.lk); + return; + } + cfdata = CFDataCreate(kCFAllocatorDefault, + (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2); + if(cfdata == nil){ + fprint(2, "apple pasteboard cfdatacreate failed\n"); + qunlock(&clip.lk); + return; + } + if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, + CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ + fprint(2, "apple pasteboard putitem failed\n"); + CFRelease(cfdata); + qunlock(&clip.lk); + return; + } + /* CFRelease(cfdata); ??? */ + qunlock(&clip.lk); +} + diff --git a/src/9vx/stub.c b/src/9vx/stub.c @@ -1,5 +1,10 @@ + #define WANT_M +#ifdef __APPLE__ +#define __DARWIN_UNIX03 0 +#endif + #include "u.h" #include <sched.h> #include <signal.h>