vx32

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

screen.c (15169B)


      1 #define Point OSXPoint
      2 #define Rect OSXRect
      3 #define Cursor OSXCursor
      4 #include <Carbon/Carbon.h>
      5 #include <QuickTime/QuickTime.h> // for full screen
      6 #undef Rect
      7 #undef Point
      8 #undef Cursor
      9 #undef offsetof
     10 #undef nil
     11 
     12 #include "u.h"
     13 #include "lib.h"
     14 #include "mem.h"
     15 #include "dat.h"
     16 #include "fns.h"
     17 #include "error.h"
     18 #define Image IMAGE	/* kernel has its own Image */
     19 #include <draw.h>
     20 #include <memdraw.h>
     21 #include <keyboard.h>
     22 #include <cursor.h>
     23 #include "screen.h"
     24 #include "mouse.h"
     25 #include "keycodes.h"
     26 #include "nineball.h"
     27 
     28 struct {
     29 	Rectangle fullscreenr;
     30 	Rectangle screenr;
     31 	Memimage *screenimage;
     32 	int isfullscreen;
     33 	ulong fullscreentime;
     34 	
     35 	Point xy;
     36 	int buttons;
     37 	int kbuttons;
     38 
     39 	CGDataProviderRef provider;
     40 	MenuRef wmenu;
     41 	MenuRef vmenu;
     42 	WindowRef window;
     43 	CGImageRef image;
     44 	PasteboardRef snarf;
     45 } osx;
     46 
     47 enum
     48 {
     49 	WindowAttrs =
     50 		kWindowCloseBoxAttribute |
     51 		kWindowCollapseBoxAttribute |
     52 		kWindowResizableAttribute |
     53 		kWindowStandardHandlerAttribute |
     54 		kWindowFullZoomAttribute
     55 };
     56 
     57 static void screenproc(void*);
     58 static void eresized(int force);
     59 static void fullscreen(void);
     60 static void seticon(void);
     61 
     62 static OSStatus quithandler(EventHandlerCallRef, EventRef, void*);
     63 static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
     64 static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*);
     65 
     66 enum
     67 {
     68 	CmdFullScreen = 1,
     69 };
     70 
     71 uchar*
     72 attachscreen(Rectangle *r, ulong *chan, int *depth,
     73 	int *width, int *softscreen, void **X)
     74 {
     75 	Memimage *m;
     76 
     77 	if(osx.screenimage == nil){
     78 		screeninit();
     79 		if(osx.screenimage == nil)
     80 			panic("cannot create OS X screen");
     81 	}
     82 	m = osx.screenimage;
     83 	*r = m->r;
     84 	*chan = m->chan;
     85 	*depth = m->depth;
     86 	*width = m->width;
     87 	*X = nil;
     88 	*softscreen = 1;
     89 	return m->data->bdata;
     90 }
     91 
     92 void
     93 _screeninit(void)
     94 {
     95 	CGRect cgr;
     96 	OSXRect or;
     97 
     98 	_memimageinit();
     99 
    100 	ProcessSerialNumber psn = { 0, kCurrentProcess };
    101 	TransformProcessType(&psn, kProcessTransformToForegroundApplication);
    102 	SetFrontProcess(&psn);
    103 
    104 	cgr = CGDisplayBounds(CGMainDisplayID());
    105 	osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
    106 	
    107 	InitCursor();
    108 	
    109 	// Create minimal menu with full-screen option.
    110 	ClearMenuBar();
    111 	CreateStandardWindowMenu(0, &osx.wmenu);
    112 	InsertMenu(osx.wmenu, 0);
    113 	MenuItemIndex ix;
    114 	CreateNewMenu(1004, 0, &osx.vmenu);	// XXX 1004?
    115 	SetMenuTitleWithCFString(osx.vmenu, CFSTR("View"));
    116 	AppendMenuItemTextWithCFString(osx.vmenu,
    117 		CFSTR("Full Screen"), 0, CmdFullScreen, &ix);
    118 	SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F');
    119 	InsertMenu(osx.vmenu, GetMenuID(osx.wmenu));
    120 	DrawMenuBar();
    121 
    122 	// Create the window.
    123 	or.left = 0;
    124 	or.top = 50;
    125 	or.bottom = Dy(osx.fullscreenr) - 200;
    126 	or.right = Dx(osx.fullscreenr);
    127 	CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window);
    128 	SetWindowTitleWithCFString(osx.window, CFSTR("Plan 9 VX"));
    129 	seticon();
    130 
    131 	// Set up the clip board.
    132 	if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr)
    133 		panic("pasteboard create");
    134 
    135 	// Explain in great detail which events we want to handle.
    136 	// Why can't we just have one handler?
    137 	const EventTypeSpec quits[] = {
    138 		{ kEventClassApplication, kEventAppQuit }
    139 	};
    140 	const EventTypeSpec cmds[] = {
    141 		{ kEventClassWindow, kEventWindowClosed },
    142 		{ kEventClassWindow, kEventWindowBoundsChanged },
    143 		{ kEventClassCommand, kEventCommandProcess }
    144 	};
    145 	const EventTypeSpec events[] = {
    146 		{ kEventClassKeyboard, kEventRawKeyDown },
    147 		{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
    148 		{ kEventClassKeyboard, kEventRawKeyRepeat },
    149 		{ kEventClassMouse, kEventMouseDown },
    150 		{ kEventClassMouse, kEventMouseUp },
    151 		{ kEventClassMouse, kEventMouseMoved },
    152 		{ kEventClassMouse, kEventMouseDragged },
    153 		{ kEventClassMouse, kEventMouseWheelMoved },
    154 	};
    155 
    156 	InstallApplicationEventHandler(
    157 		NewEventHandlerUPP(quithandler),
    158 		nelem(quits), quits, nil, nil);
    159 
    160  	InstallApplicationEventHandler(
    161  		NewEventHandlerUPP(eventhandler),
    162 		nelem(events), events, nil, nil);
    163 
    164 	InstallWindowEventHandler(osx.window,
    165 		NewEventHandlerUPP(cmdhandler),
    166 		nelem(cmds), cmds, osx.window, nil);
    167 
    168 	// Finally, put the window on the screen.
    169 	ShowWindow(osx.window);
    170 	ShowMenuBar();
    171 	eresized(1);
    172 	SelectWindow(osx.window);
    173 	
    174 	InitCursor();
    175 }
    176 
    177 static Psleep scr;
    178 
    179 void
    180 screeninit(void)
    181 {
    182 	plock(&scr);
    183 	kproc("*screen*", screenproc, nil);
    184 	while(osx.window == nil)
    185 		psleep(&scr);
    186 	punlock(&scr);
    187 }
    188 
    189 static void
    190 screenproc(void *v)
    191 {
    192 	plock(&scr);
    193 	_screeninit();
    194 	pwakeup(&scr);
    195 	punlock(&scr);
    196 	RunApplicationEventLoop();
    197 	iprint("screenproc exited!\n");
    198 }
    199 
    200 static OSStatus kbdevent(EventRef);
    201 static OSStatus mouseevent(EventRef);
    202 
    203 static OSStatus
    204 cmdhandler(EventHandlerCallRef next, EventRef event, void *arg)
    205 {
    206 	return eventhandler(next, event, arg);
    207 }
    208 
    209 static OSStatus
    210 quithandler(EventHandlerCallRef next, EventRef event, void *arg)
    211 {
    212 	restoretty();	// XXX: should we?
    213 	exit(0);
    214 	return 0;
    215 }
    216 
    217 static OSStatus
    218 eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
    219 {
    220 	OSStatus result;
    221 
    222 	result = CallNextEventHandler(next, event);
    223 
    224 	switch(GetEventClass(event)){
    225 	case kEventClassKeyboard:
    226 		return kbdevent(event);
    227 	
    228 	case kEventClassMouse:
    229 		return mouseevent(event);
    230 	
    231 	case kEventClassCommand:;
    232 		HICommand cmd;
    233 		GetEventParameter(event, kEventParamDirectObject,
    234 			typeHICommand, nil, sizeof cmd, nil, &cmd);
    235 		switch(cmd.commandID){
    236 		case kHICommandQuit:
    237 			restoretty();	// XXX: should we?
    238 			exit(0);
    239 		
    240 		case CmdFullScreen:
    241 			fullscreen();
    242 			break;
    243 		
    244 		default:
    245 			return eventNotHandledErr;
    246 		}
    247 		break;
    248 	
    249 	case kEventClassWindow:;
    250 		switch(GetEventKind(event)){
    251 		case kEventWindowClosed:
    252 			restoretty();	// XXX: should we?
    253 			exit(0);
    254 		
    255 		case kEventWindowBoundsChanged:
    256 			eresized(0);
    257 			break;
    258 		
    259 		default:
    260 			return eventNotHandledErr;
    261 		}
    262 		break;
    263 	}
    264 	
    265 	return result;
    266 }
    267 
    268 static OSStatus
    269 mouseevent(EventRef event)
    270 {
    271 	int wheel;
    272 	OSXPoint op;
    273 	
    274 	GetEventParameter(event, kEventParamMouseLocation,
    275 		typeQDPoint, 0, sizeof op, 0, &op);
    276 
    277 	osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min);
    278 	wheel = 0;
    279 
    280 	switch(GetEventKind(event)){
    281 	case kEventMouseWheelMoved:;
    282 		SInt32 delta;
    283 		GetEventParameter(event, kEventParamMouseWheelDelta,
    284 			typeSInt32, 0, sizeof delta, 0, &delta);
    285 		if(delta > 0)
    286 			wheel = 8;
    287 		else
    288 			wheel = 16;
    289 		break;
    290 	
    291 	case kEventMouseDown:
    292 	case kEventMouseUp:;
    293 		UInt32 but, mod;
    294 		GetEventParameter(event, kEventParamMouseChord,
    295 			typeUInt32, 0, sizeof but, 0, &but);
    296 		GetEventParameter(event, kEventParamKeyModifiers,
    297 			typeUInt32, 0, sizeof mod, 0, &mod);
    298 		
    299 		// OS X swaps button 2 and 3
    300 		but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
    301 		
    302 		// Apply keyboard modifiers and pretend it was a real mouse button.
    303 		// (Modifiers typed while holding the button go into kbuttons,
    304 		// but this one does not.)
    305 		if(but == 1){
    306 			if(mod & optionKey)
    307 				but = 2;
    308 			else if(mod & cmdKey)
    309 				but = 4;
    310 		}
    311 		osx.buttons = but;
    312 		break;
    313 
    314 	case kEventMouseMoved:
    315 	case kEventMouseDragged:
    316 		break;
    317 	
    318 	default:
    319 		return eventNotHandledErr;
    320 	}
    321 
    322 	mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
    323 	return noErr;	
    324 }
    325 
    326 static int keycvt[] =
    327 {
    328 	[QZ_IBOOK_ENTER] '\n',
    329 	[QZ_RETURN] '\n',
    330 	[QZ_ESCAPE] 27,
    331 	[QZ_BACKSPACE] '\b',
    332 	[QZ_LALT] Kalt,
    333 	[QZ_LCTRL] Kctl,
    334 	[QZ_LSHIFT] Kshift,
    335 	[QZ_F1] KF+1,
    336 	[QZ_F2] KF+2,
    337 	[QZ_F3] KF+3,
    338 	[QZ_F4] KF+4,
    339 	[QZ_F5] KF+5,
    340 	[QZ_F6] KF+6,
    341 	[QZ_F7] KF+7,
    342 	[QZ_F8] KF+8,
    343 	[QZ_F9] KF+9,
    344 	[QZ_F10] KF+10,
    345 	[QZ_F11] KF+11,
    346 	[QZ_F12] KF+12,
    347 	[QZ_INSERT] Kins,
    348 	[QZ_DELETE] 0x7F,
    349 	[QZ_HOME] Khome,
    350 	[QZ_END] Kend,
    351 	[QZ_KP_PLUS] '+',
    352 	[QZ_KP_MINUS] '-',
    353 	[QZ_TAB] '\t',
    354 	[QZ_PAGEUP] Kpgup,
    355 	[QZ_PAGEDOWN] Kpgdown,
    356 	[QZ_UP] Kup,
    357 	[QZ_DOWN] Kdown,
    358 	[QZ_LEFT] Kleft,
    359 	[QZ_RIGHT] Kright,
    360 	[QZ_KP_MULTIPLY] '*',
    361 	[QZ_KP_DIVIDE] '/',
    362 	[QZ_KP_ENTER] '\n',
    363 	[QZ_KP_PERIOD] '.',
    364 	[QZ_KP0] '0',
    365 	[QZ_KP1] '1',
    366 	[QZ_KP2] '2',
    367 	[QZ_KP3] '3',
    368 	[QZ_KP4] '4',
    369 	[QZ_KP5] '5',
    370 	[QZ_KP6] '6',
    371 	[QZ_KP7] '7',
    372 	[QZ_KP8] '8',
    373 	[QZ_KP9] '9',
    374 };
    375 
    376 static void
    377 kputc(int c)
    378 {
    379 	kbdputc(kbdq, c);
    380 }
    381 
    382 static OSStatus
    383 kbdevent(EventRef event)
    384 {
    385 	char ch;
    386 	UInt32 code;
    387 	UInt32 mod;
    388 	int k;
    389 
    390 	GetEventParameter(event, kEventParamKeyMacCharCodes,
    391 		typeChar, nil, sizeof ch, nil, &ch);
    392 	GetEventParameter(event, kEventParamKeyCode,
    393 		typeUInt32, nil, sizeof code, nil, &code);
    394 	GetEventParameter(event, kEventParamKeyModifiers,
    395 		typeUInt32, nil, sizeof mod, nil, &mod);
    396 
    397 	switch(GetEventKind(event)){
    398 	case kEventRawKeyDown:
    399 	case kEventRawKeyRepeat:
    400 		if(mod == cmdKey){
    401 			if(ch == 'F' || ch == 'f'){
    402 				if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
    403 					fullscreen();
    404 				return noErr;
    405 			}
    406 			return eventNotHandledErr;
    407 		}
    408 		k = ch;
    409 		if(code < nelem(keycvt) && keycvt[code])
    410 			k = keycvt[code];
    411 		if(k >= 0)
    412 			latin1putc(k, kputc);
    413 		else{
    414 			UniChar uc;
    415 			OSStatus s;
    416 			
    417 			s = GetEventParameter(event, kEventParamKeyUnicodes,
    418 				typeUnicodeText, nil, sizeof uc, nil, &uc);
    419 			if(s == noErr)
    420 				kputc(uc);
    421 		}
    422 		break;
    423 
    424 	case kEventRawKeyModifiersChanged:
    425 		if(!osx.buttons && !osx.kbuttons){
    426 			if(mod == optionKey)
    427 				latin1putc(Kalt, kputc);
    428 			break;
    429 		}
    430 		
    431 		// If the mouse button is being held down, treat 
    432 		// changes in the keyboard modifiers as changes
    433 		// in the mouse buttons.
    434 		osx.kbuttons = 0;
    435 		if(mod & optionKey)
    436 			osx.kbuttons |= 2;
    437 		if(mod & cmdKey)
    438 			osx.kbuttons |= 4;
    439 		mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
    440 		break;
    441 	}
    442 	return noErr;
    443 }
    444 
    445 static void
    446 eresized(int force)
    447 {
    448 	Memimage *m;
    449 	OSXRect or;
    450 	ulong chan;
    451 	Rectangle r;
    452 	int bpl;
    453 	CGDataProviderRef provider;
    454 	CGImageRef image;
    455 	
    456 	GetWindowBounds(osx.window, kWindowContentRgn, &or);
    457 	r = Rect(or.left, or.top, or.right, or.bottom);
    458 	if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr) && !force){
    459 		// No need to make new image.
    460 		osx.screenr = r;
    461 		return;
    462 	}
    463 
    464 	chan = XBGR32;
    465 	m = allocmemimage(Rect(0, 0, Dx(r), Dy(r)), chan);
    466 	if(m == nil)
    467 		panic("allocmemimage: %r");
    468 	if(m->data == nil)
    469 		panic("m->data == nil");
    470 	bpl = bytesperline(r, 32);
    471 	provider = CGDataProviderCreateWithData(0,
    472 		m->data->bdata, Dy(r)*bpl, 0);
    473 	image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
    474 		CGColorSpaceCreateDeviceRGB(),
    475 		kCGImageAlphaNoneSkipLast,
    476 		provider, 0, 0, kCGRenderingIntentDefault);
    477 	CGDataProviderRelease(provider);	// CGImageCreate did incref
    478 
    479 	mouserect = m->r;
    480 	if(osx.image)
    481 		CGImageRelease(osx.image);
    482 	osx.image = image;
    483 	osx.screenr = r;
    484 	osx.screenimage = m;
    485 	termreplacescreenimage(m);
    486 	drawreplacescreenimage(m);	// frees old osx.screenimage if any
    487 }
    488 
    489 void
    490 flushmemscreen(Rectangle r)
    491 {
    492 	CGRect cgr;
    493 	CGContextRef context;
    494 	CGImageRef subimg;
    495 
    496 	QDBeginCGContext(GetWindowPort(osx.window), &context);
    497 	
    498 	cgr.origin.x = r.min.x;
    499 	cgr.origin.y = r.min.y;
    500 	cgr.size.width = Dx(r);
    501 	cgr.size.height = Dy(r);
    502 	subimg = CGImageCreateWithImageInRect(osx.image, cgr);
    503 	cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense?
    504 	CGContextDrawImage(context, cgr, subimg);
    505 	CGContextFlush(context);
    506 	CGImageRelease(subimg);
    507 
    508 	QDEndCGContext(GetWindowPort(osx.window), &context);
    509 }
    510 
    511 void
    512 fullscreen(void)
    513 {
    514 	static Ptr restore;
    515 	static WindowRef oldwindow;
    516 	GDHandle device;
    517 
    518 	if(osx.isfullscreen){
    519 		EndFullScreen(restore, 0);
    520 		osx.window = oldwindow;
    521 		ShowWindow(osx.window);
    522 		osx.isfullscreen = 0;
    523 	}else{
    524 		HideWindow(osx.window);
    525 		oldwindow = osx.window;
    526 		GetWindowGreatestAreaDevice(osx.window, kWindowTitleBarRgn, &device, nil);
    527 		BeginFullScreen(&restore, device, 0, 0, &osx.window, 0, 0);
    528 		osx.isfullscreen = 1;
    529 		osx.fullscreentime = msec();
    530 	}
    531 	eresized(1);
    532 }
    533 
    534 void
    535 setmouse(Point p)
    536 {
    537 	CGPoint cgp;
    538 	
    539 	cgp.x = p.x + osx.screenr.min.x;
    540 	cgp.y = p.y + osx.screenr.min.y;
    541 	CGWarpMouseCursorPosition(cgp);
    542 }
    543 
    544 void
    545 setcursor(struct Cursor *c)
    546 {
    547 	OSXCursor oc;
    548 	int i;
    549 
    550 	// SetCursor is deprecated, but what replaces it?
    551 	for(i=0; i<16; i++){
    552 		oc.data[i] = ((ushort*)c->set)[i];
    553 		oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i];
    554 	}
    555 	oc.hotSpot.h = - c->offset.x;
    556 	oc.hotSpot.v = - c->offset.y;
    557 	SetCursor(&oc);
    558 }
    559 
    560 void
    561 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
    562 {
    563 	ulong v;
    564 	
    565 	v = 0;
    566 	*r = (v>>16)&0xFF;
    567 	*g = (v>>8)&0xFF;
    568 	*b = v&0xFF;
    569 }
    570 
    571 int
    572 setcolor(ulong i, ulong r, ulong g, ulong b)
    573 {
    574 	/* no-op */
    575 	return 0;
    576 }
    577 
    578 
    579 int
    580 hwdraw(Memdrawparam *p)
    581 {
    582 	return 0;
    583 }
    584 
    585 struct {
    586 	QLock lk;
    587 	char buf[SnarfSize];
    588 	Rune rbuf[SnarfSize];
    589 	PasteboardRef apple;
    590 } clip;
    591 
    592 char*
    593 getsnarf(void)
    594 {
    595 	char *s, *t;
    596 	CFArrayRef flavors;
    597 	CFDataRef data;
    598 	CFIndex nflavor, ndata, j;
    599 	CFStringRef type;
    600 	ItemCount nitem;
    601 	PasteboardItemID id;
    602 	PasteboardSyncFlags flags;
    603 	UInt32 i;
    604 
    605 /*	fprint(2, "applegetsnarf\n"); */
    606 	qlock(&clip.lk);
    607 	clip.apple = osx.snarf;
    608 	if(clip.apple == nil){
    609 		if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
    610 			fprint(2, "apple pasteboard create failed\n");
    611 			qunlock(&clip.lk);
    612 			return nil;
    613 		}
    614 	}
    615 	flags = PasteboardSynchronize(clip.apple);
    616 	if(flags&kPasteboardClientIsOwner){
    617 		s = strdup(clip.buf);
    618 		qunlock(&clip.lk);
    619 		return s;
    620 	}
    621 	if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
    622 		fprint(2, "apple pasteboard get item count failed\n");
    623 		qunlock(&clip.lk);
    624 		return nil;
    625 	}
    626 	for(i=1; i<=nitem; i++){
    627 		if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
    628 			continue;
    629 		if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
    630 			continue;
    631 		nflavor = CFArrayGetCount(flavors);
    632 		for(j=0; j<nflavor; j++){
    633 			type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
    634 			if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
    635 				continue;
    636 			if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
    637 				continue;
    638 			ndata = CFDataGetLength(data);
    639 			qunlock(&clip.lk);
    640 			s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data));
    641 			CFRelease(flavors);
    642 			CFRelease(data);
    643 			for(t=s; *t; t++)
    644 				if(*t == '\r')
    645 					*t = '\n';
    646 			return s;
    647 		}
    648 		CFRelease(flavors);
    649 	}
    650 	qunlock(&clip.lk);
    651 	return nil;		
    652 }
    653 
    654 void
    655 putsnarf(char *s)
    656 {
    657 	CFDataRef cfdata;
    658 	PasteboardSyncFlags flags;
    659 
    660 /*	fprint(2, "appleputsnarf\n"); */
    661 
    662 	if(strlen(s) >= SnarfSize)
    663 		return;
    664 	qlock(&clip.lk);
    665 	strcpy(clip.buf, s);
    666 	runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
    667 	clip.apple = osx.snarf;
    668 	if(PasteboardClear(clip.apple) != noErr){
    669 		fprint(2, "apple pasteboard clear failed\n");
    670 		qunlock(&clip.lk);
    671 		return;
    672 	}
    673 	flags = PasteboardSynchronize(clip.apple);
    674 	if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
    675 		fprint(2, "apple pasteboard cannot assert ownership\n");
    676 		qunlock(&clip.lk);
    677 		return;
    678 	}
    679 	cfdata = CFDataCreate(kCFAllocatorDefault, 
    680 		(uchar*)clip.rbuf, runestrlen(clip.rbuf)*2);
    681 	if(cfdata == nil){
    682 		fprint(2, "apple pasteboard cfdatacreate failed\n");
    683 		qunlock(&clip.lk);
    684 		return;
    685 	}
    686 	if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
    687 		CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
    688 		fprint(2, "apple pasteboard putitem failed\n");
    689 		CFRelease(cfdata);
    690 		qunlock(&clip.lk);
    691 		return;
    692 	}
    693 	/* CFRelease(cfdata); ??? */
    694 	qunlock(&clip.lk);
    695 }
    696 
    697 static void
    698 seticon(void)
    699 {
    700 	CGImageRef im;
    701 	CGDataProviderRef d;
    702 
    703 	d = CGDataProviderCreateWithData(nil, nineball_png, sizeof nineball_png, nil);
    704 	im = CGImageCreateWithPNGDataProvider(d, nil, true, kCGRenderingIntentDefault);
    705 	if(im)
    706 		SetApplicationDockTileImage(im);
    707 	CGImageRelease(im);
    708 	CGDataProviderRelease(d);
    709 }
    710