xref: /inferno-os/emu/MacOSX/win.c (revision 9dbf735d35c339c90deaed43fc0ae17f16c122f7)
1 // in this file, _Rect is os x Rect,
2 // _Point is os x Point
3 #define Point _Point
4 #define Rect _Rect
5 
6 #include <Carbon/Carbon.h>
7 #include <QuickTime/QuickTime.h> // for full screen
8 
9 #undef Rect
10 #undef Point
11 
12 #undef nil
13 
14 #include "dat.h"
15 #include "fns.h"
16 #undef log2
17 #include <draw.h>
18 #include <memdraw.h>
19 #include "cursor.h"
20 #include "keyboard.h"
21 #include "keycodes.h"
22 
23 #define	Kup	Up
24 #define	Kleft	Left
25 #define	Kdown	Down
26 #define	Kright	Right
27 #define	Kalt	LAlt
28 #define	Kctl	LCtrl
29 #define	Kshift	LShift
30 #define	Kpgup	Pgup
31 #define	Kpgdown	Pgdown
32 #define	Khome	Home
33 #define	Kins	Ins
34 #define	Kend	End
35 
36 #define rWindowResource  128
37 
38 extern	void		flushmemscreen(Rectangle);
39 
40 Memimage	*gscreen;
41 
42 static int readybit;
43 static Rendez	rend;
44 static int triedscreen;
45 
46 ///
47 // menu
48 //
49 static MenuRef windMenu;
50 static MenuRef viewMenu;
51 
52 enum {
53 	kQuitCmd = 1,
54 	kFullScreenCmd = 2,
55 };
56 
57 static WindowGroupRef winGroup = NULL;
58 static WindowRef theWindow = NULL;
59 static CGContextRef context;
60 static CGDataProviderRef dataProviderRef;
61 static CGImageRef fullScreenImage;
62 static CGRect devRect;
63 static CGRect bounds;
64 static PasteboardRef appleclip;
65 static _Rect winRect;
66 
67 static Boolean altPressed = false;
68 static Boolean button2 = false;
69 static Boolean button3 = false;
70 
71 static Boolean needflush = false;
72 
73 
74 static int
75 isready(void*a)
76 {
77 	return readybit;
78 }
79 
80 CGContextRef QuartzContext;
81 
82 static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
83 static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
84 
85 static void winproc(void *a);
86 static void flushproc(void *a);
87 
88 void
89 screeninit(void)
90 {
91 	int fmt;
92 	int dx, dy;
93 	ProcessSerialNumber psn = { 0, kCurrentProcess };
94 	TransformProcessType(&psn, kProcessTransformToForegroundApplication);
95 	SetFrontProcess(&psn);
96 
97 	fmt = XBGR32; //XRGB32;
98 
99 	devRect = CGDisplayBounds(CGMainDisplayID());
100 //	devRect.origin.x = 0;
101 //	devRect.origin.y = 0;
102 //	devRect.size.width = 1024;
103 //	devRect.size.height = 768;
104 	dx = devRect.size.width;
105 	dy = devRect.size.height;
106 
107 	if(1){	/* TO DO: new dev draw for changing screen size */
108 		dx = Xsize;
109 		dy = Ysize;
110 	}
111 
112 	gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
113 	dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata,
114 						dx * dy * 4, 0);
115 	fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4,
116 				CGColorSpaceCreateDeviceRGB(),
117 				kCGImageAlphaNoneSkipLast,
118 				dataProviderRef, 0, 0, kCGRenderingIntentDefault);
119 
120 	kproc("osxscreen", winproc, nil, 0);
121 	kproc("osxflush", flushproc, nil, 0);
122 	Sleep(&rend, isready, nil);
123 }
124 
125 void
126 window_resized(void)
127 {
128 	GetWindowBounds(theWindow, kWindowContentRgn, &winRect);
129 
130 	bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top);
131 }
132 
133 static void
134 flushproc(void *a)
135 {
136 	for(;;) {
137 		if(needflush) {
138 			drawqlock();
139 			QDBeginCGContext(GetWindowPort(theWindow), &context);
140 			CGContextFlush(context);
141 			QDEndCGContext(GetWindowPort(theWindow), &context);
142 			needflush = false;
143  			drawqunlock();
144 		}
145 		usleep(33333);
146 	}
147 }
148 
149 static void
150 winproc(void *a)
151 {
152 	MenuItemIndex index;
153 	int dx, dy;
154 
155 	winRect.left = 30;
156 	winRect.top = 60;
157 	dx = devRect.size.width*0.75;	/* devRect is full screen; take only most of it */
158 	dy = devRect.size.height*0.75;
159 	if(1){	/* TO DO */
160 		dx = Xsize;
161 		dy = Ysize;
162 	}
163 	winRect.bottom = winRect.top + dy;
164 	winRect.right = winRect.left + dx;
165 
166 	ClearMenuBar();
167 	InitCursor();
168 
169 	CreateStandardWindowMenu(0, &windMenu);
170 	InsertMenu(windMenu, 0);
171 
172 	CreateNewMenu(1004, 0, &viewMenu);
173 	SetMenuTitleWithCFString(viewMenu, CFSTR("View"));
174 	AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0,
175 			kFullScreenCmd, &index);
176 	SetMenuItemCommandKey(viewMenu, index, 0, 'F');
177 	AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"),
178 			kMenuItemAttrDisabled,
179 			kFullScreenCmd, &index);
180 	InsertMenu(viewMenu, GetMenuID(windMenu));
181 
182 	DrawMenuBar();
183 	uint32_t windowAttrs = 0
184 				| kWindowCloseBoxAttribute
185 				| kWindowCollapseBoxAttribute
186 //				| kWindowResizableAttribute		// TO DO
187 				| kWindowStandardHandlerAttribute
188 //				| kWindowFullZoomAttribute		// TO DO
189 		;
190 
191 	CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
192 	CreateWindowGroup(0, &winGroup);
193 	SetWindowGroup(theWindow, winGroup);
194 
195 	SetWindowTitleWithCFString(theWindow, CFSTR("Inferno"));
196 
197 	if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
198 		sysfatal("pasteboard create failed");
199 
200 	const EventTypeSpec commands[] = {
201 		{ kEventClassWindow, kEventWindowClosed },
202 		{ kEventClassWindow, kEventWindowBoundsChanged },
203 		{ kEventClassCommand, kEventCommandProcess }
204 	};
205 	const EventTypeSpec events[] = {
206 		{ kEventClassKeyboard, kEventRawKeyDown },
207 		{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
208 		{ kEventClassKeyboard, kEventRawKeyRepeat },
209 		{ kEventClassMouse, kEventMouseDown },
210 		{ kEventClassMouse, kEventMouseUp },
211 		{ kEventClassMouse, kEventMouseMoved },
212 		{ kEventClassMouse, kEventMouseDragged },
213 		{ kEventClassMouse, kEventMouseWheelMoved },
214 	};
215 
216  	InstallApplicationEventHandler (
217  								NewEventHandlerUPP (MainWindowEventHandler),
218 								GetEventTypeCount(events),
219 								events,
220 								NULL,
221 								NULL);
222 	InstallWindowEventHandler (
223 								theWindow,
224 								NewEventHandlerUPP (MainWindowCommandHandler),
225 								GetEventTypeCount(commands),
226 								commands,
227 								theWindow,
228 								NULL);
229 
230 	ShowWindow(theWindow);
231 	ShowMenuBar();
232 	window_resized();
233 	SelectWindow(theWindow);
234 	// Run the event loop
235 	readybit = 1;
236 	Wakeup(&rend);
237 	RunApplicationEventLoop();
238 
239 }
240 
241 static int
242 convert_key(UInt32 key, UInt32 charcode)
243 {
244 	switch(key) {
245 	case QZ_IBOOK_ENTER:
246 	case QZ_RETURN: return '\n';
247 	case QZ_ESCAPE: return 27;
248 	case QZ_BACKSPACE: return '\b';
249 	case QZ_LALT: return Kalt;
250 	case QZ_LCTRL: return Kctl;
251 	case QZ_LSHIFT: return Kshift;
252 	case QZ_F1: return KF+1;
253 	case QZ_F2: return KF+2;
254 	case QZ_F3: return KF+3;
255 	case QZ_F4: return KF+4;
256 	case QZ_F5: return KF+5;
257 	case QZ_F6: return KF+6;
258 	case QZ_F7: return KF+7;
259 	case QZ_F8: return KF+8;
260 	case QZ_F9: return KF+9;
261 	case QZ_F10: return KF+10;
262 	case QZ_F11: return KF+11;
263 	case QZ_F12: return KF+12;
264 	case QZ_INSERT: return Kins;
265 	case QZ_DELETE: return 0x7F;
266 	case QZ_HOME: return Khome;
267 	case QZ_END: return Kend;
268 	case QZ_KP_PLUS: return '+';
269 	case QZ_KP_MINUS: return '-';
270 	case QZ_TAB: return '\t';
271 	case QZ_PAGEUP: return Kpgup;
272 	case QZ_PAGEDOWN: return Kpgdown;
273 	case QZ_UP: return Kup;
274 	case QZ_DOWN: return Kdown;
275 	case QZ_LEFT: return Kleft;
276 	case QZ_RIGHT: return Kright;
277 	case QZ_KP_MULTIPLY: return '*';
278 	case QZ_KP_DIVIDE: return '/';
279 	case QZ_KP_ENTER: return '\n';
280 	case QZ_KP_PERIOD: return '.';
281 	case QZ_KP0: return '0';
282 	case QZ_KP1: return '1';
283 	case QZ_KP2: return '2';
284 	case QZ_KP3: return '3';
285 	case QZ_KP4: return '4';
286 	case QZ_KP5: return '5';
287 	case QZ_KP6: return '6';
288 	case QZ_KP7: return '7';
289 	case QZ_KP8: return '8';
290 	case QZ_KP9: return '9';
291 	default: return charcode;
292 	}
293 }
294 
295 void
296 sendbuttons(int b, int x, int y)
297 {
298 	mousetrack(b, x, y, 0);
299 }
300 
301 static Ptr fullScreenRestore;
302 static int amFullScreen = 0;
303 static WindowRef oldWindow = NULL;
304 
305 static void
306 leave_full_screen(void)
307 {
308 	if(amFullScreen){
309 		EndFullScreen(fullScreenRestore, 0);
310 		theWindow = oldWindow;
311 		ShowWindow(theWindow);
312 		amFullScreen = 0;
313 		window_resized();
314 		Rectangle rect =  { { 0, 0 }, { bounds.size.width, bounds.size.height} };
315 		drawqlock();
316  		flushmemscreen(rect);
317  		drawqunlock();
318 	}
319 }
320 
321 static void
322 full_screen(void)
323 {
324 	if(!amFullScreen){
325 		oldWindow = theWindow;
326 		HideWindow(theWindow);
327 		BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0);
328 		amFullScreen = 1;
329 		window_resized();
330 		Rectangle rect =  { { 0, 0 },
331  							{ bounds.size.width,
332  							  bounds.size.height} };
333 		drawqlock();
334  		flushmemscreen(rect);
335  		drawqunlock();
336 	}
337 }
338 
339 static OSStatus
340 MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
341 {
342 	OSStatus result = noErr;
343 	result = CallNextEventHandler(nextHandler, event);
344 	UInt32 class = GetEventClass (event);
345 	UInt32 kind = GetEventKind (event);
346 	static uint32_t mousebuttons = 0; // bitmask of buttons currently down
347 	static uint32_t mouseX = 0;
348 	static uint32_t mouseY = 0;
349 
350 	if(class == kEventClassKeyboard) {
351 		char macCharCodes;
352 		UInt32 macKeyCode;
353 		UInt32 macKeyModifiers;
354 
355 		GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,
356 							NULL, sizeof(macCharCodes), NULL, &macCharCodes);
357 		GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
358 							sizeof(macKeyCode), NULL, &macKeyCode);
359 		GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
360 							sizeof(macKeyModifiers), NULL, &macKeyModifiers);
361         switch(kind) {
362 		case kEventRawKeyModifiersChanged:
363 			if (macKeyModifiers == (controlKey | optionKey)) leave_full_screen();
364 
365 			switch(macKeyModifiers & (optionKey | cmdKey)) {
366 			case (optionKey | cmdKey):
367 				/* due to chording we need to handle the case when both
368 				 * modifier keys are pressed at the same time.
369 				 * currently it's only 2-3 snarf and the 3-2 noop
370 				 */
371 				altPressed = true;
372 				if(mousebuttons & 1 || mousebuttons & 2 || mousebuttons & 4) {
373 					mousebuttons |= 2;	/* set button 2 */
374 					mousebuttons |= 4;	/* set button 3 */
375 					button2 = true;
376 					button3 = true;
377 					sendbuttons(mousebuttons, mouseX, mouseY);
378 				}
379 				break;
380 			case optionKey:
381 				altPressed = true;
382 				if(mousebuttons & 1 || mousebuttons & 4) {
383 					mousebuttons |= 2;	/* set button 2 */
384 					button2 = true;
385 					sendbuttons(mousebuttons, mouseX, mouseY);
386 				}
387 				break;
388 			case cmdKey:
389 				if(mousebuttons & 1 || mousebuttons & 2) {
390 					mousebuttons |= 4;	/* set button 3 */
391 					button3 = true;
392 					sendbuttons(mousebuttons, mouseX, mouseY);
393 				}else
394 					gkbdputc(gkbdq, Latin);
395 				break;
396 			case 0:
397 			default:
398 				if(button2 || button3) {
399 					if(button2) {
400 						mousebuttons &= ~2;	/* clear button 2 */
401 						button2 = false;
402 						altPressed = false;
403 					}
404 					if(button3) {
405 						mousebuttons &= ~4;	/* clear button 3 */
406 						button3 = false;
407 					}
408 					sendbuttons(mousebuttons, mouseX, mouseY);
409 				}
410 				if(altPressed) {
411 					gkbdputc(gkbdq, Kalt);
412 					altPressed = false;
413 				}
414 				break;
415 			}
416 			break;
417 		case kEventRawKeyDown:
418 		case kEventRawKeyRepeat:
419 			if(macKeyModifiers != cmdKey) {
420 				int key;
421 				key = convert_key(macKeyCode, macCharCodes);
422 				if(key != -1)
423 					gkbdputc(gkbdq, key);
424 			}else
425 				result = eventNotHandledErr;
426 			break;
427 		default:
428 			break;
429 		}
430 	}
431 	else if(class == kEventClassMouse) {
432 		_Point mousePos;
433 
434 		GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
435 							0, sizeof mousePos, 0, &mousePos);
436 
437 		switch(kind) {
438 		case kEventMouseWheelMoved:
439 		{
440 		    int32_t wheeldelta;
441 			GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32,
442 								0,sizeof(wheeldelta), 0, &wheeldelta);
443 			mouseX = mousePos.h - winRect.left;
444 			mouseY = mousePos.v - winRect.top;
445 			sendbuttons(wheeldelta>0 ? 8 : 16, mouseX, mouseY);
446 			break;
447 		}
448 		case kEventMouseUp:
449 		case kEventMouseDown:
450 		{
451 			uint32_t buttons;
452 			uint32_t modifiers;
453 			uint32_t clkcnt;
454 
455 			GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
456 								0, sizeof(modifiers), 0, &modifiers);
457 			GetEventParameter(event, kEventParamMouseChord, typeUInt32,
458 								0, sizeof buttons, 0, &buttons);
459 			GetEventParameter(event, kEventParamClickCount, typeUInt32,
460 								0, sizeof(clkcnt), 0, &clkcnt);
461 
462 			/* simulate other buttons via alt/apple key. like x11 */
463 			if(modifiers & optionKey) {
464 				mousebuttons = ((buttons & 1) ? 2 : 0);
465 				altPressed = false;
466 			} else if(modifiers & cmdKey)
467 				mousebuttons = ((buttons & 1) ? 4 : 0);
468 			else
469 				mousebuttons = (buttons & 1);
470 
471 			mousebuttons |= ((buttons & 2)<<1);
472 			mousebuttons |= ((buttons & 4)>>1);
473 			if(clkcnt > 1)
474 				mousebuttons |= 1<<8;
475 
476 		} /* Fallthrough */
477 		case kEventMouseMoved:
478 		case kEventMouseDragged:
479 			mouseX = mousePos.h - winRect.left;
480 			mouseY = mousePos.v - winRect.top;
481 			sendbuttons(mousebuttons, mouseX, mouseY);
482 			break;
483 		default:
484 			result = eventNotHandledErr;
485 			break;
486 		}
487 	}
488 	return result;
489 }
490 
491 
492 //default window command handler (from menus)
493 static OSStatus
494 MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
495 {
496 	OSStatus result = noErr;
497 	UInt32 class = GetEventClass (event);
498 	UInt32 kind = GetEventKind (event);
499 
500 	result = CallNextEventHandler(nextHandler, event);
501 
502 	if(class == kEventClassCommand) {
503 		HICommand theHICommand;
504 		GetEventParameter(event, kEventParamDirectObject, typeHICommand,
505 							NULL, sizeof(HICommand), NULL, &theHICommand);
506 
507 		switch(theHICommand.commandID) {
508 		case kHICommandQuit:
509 			cleanexit(0);
510 			break;
511 
512 		case kFullScreenCmd:
513 			full_screen();
514 			break;
515 
516 		default:
517 			result = eventNotHandledErr;
518 			break;
519 		}
520 	} else if(class == kEventClassWindow) {
521 		WindowRef     window;
522 		_Rect          rectPort = {0,0,0,0};
523 
524 		GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
525 							NULL, sizeof(WindowRef), NULL, &window);
526 
527 		if(window)
528 			GetPortBounds(GetWindowPort(window), &rectPort);
529 
530 		switch(kind) {
531 		case kEventWindowClosed:
532 			theWindow = NULL;
533 			cleanexit(0); // only one window
534 			break;
535 
536 		//resize window
537 		case kEventWindowBoundsChanged:
538 			window_resized();
539 			Rectangle rect =  { { 0, 0 },
540  									{ bounds.size.width,
541  									  bounds.size.height} };
542 			drawqlock();
543  			flushmemscreen(rect);
544  			drawqunlock();
545 			break;
546 
547 		default:
548 			result = eventNotHandledErr;
549 			break;
550 		}
551 	}
552 
553 	return result;
554 }
555 
556 void
557 flushmemscreen(Rectangle r)
558 {
559 	CGRect rbounds;
560 
561 	// sanity check.  Trips from the initial "terminal"
562 	if (r.max.x < r.min.x || r.max.y < r.min.y)
563 		return;
564 
565 	rbounds.size.width = r.max.x - r.min.x;
566 	rbounds.size.height = r.max.y - r.min.y;
567 	rbounds.origin.x = r.min.x;
568 	rbounds.origin.y = r.min.y;
569 
570 	if(rbounds.size.width <= 0 || rbounds.size.height <= 0)
571 		return;
572 
573 	QDBeginCGContext(GetWindowPort(theWindow), &context);
574 
575 	// The sub-image is relative to our whole screen image.
576 	CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds);
577 
578 	// Drawing the sub-image is relative to the window.
579 	rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height;
580 	CGContextDrawImage(context, rbounds, subimg);
581 	CGImageRelease(subimg);
582 	QDEndCGContext(GetWindowPort(theWindow), &context);
583 
584 	needflush = true;
585 }
586 
587 uchar*
588 attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
589 {
590 	if(!triedscreen) {
591 		triedscreen = 1;
592 		screeninit();	/* TO DO: call this elsewhere? */
593 	}
594 	*r = gscreen->r;
595 	*chan = gscreen->chan;
596 	*depth = gscreen->depth;
597 	*width = gscreen->width;
598 	*softscreen = 1;
599 
600 	return gscreen->data->bdata;
601 }
602 
603 // PAL - no palette handling.  Don't intend to either.
604 void
605 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
606 {
607 
608 // PAL: Certainly wrong to return a grayscale.
609 	 *r = i;
610 	 *g = i;
611 	 *b = i;
612 }
613 
614 void
615 setcolor(ulong index, ulong r, ulong g, ulong b)
616 {
617 	USED(index); USED(r); USED(g); USED(b);
618 }
619 
620 enum{
621 	SnarfSize=	100*1024
622 };
623 
624 static char snarf[3*SnarfSize+1];
625 static Rune rsnarf[SnarfSize+1];
626 
627 char*
628 clipread(void)
629 {
630 	CFDataRef cfdata;
631 	OSStatus err = noErr;
632 	ItemCount nitems;
633 	int i;
634 	char *s;
635 
636 	if(appleclip == NULL)
637 		return nil;
638 	// Wow.  This is ridiculously complicated.
639 	PasteboardSynchronize(appleclip);
640 	if((err = PasteboardGetItemCount(appleclip, &nitems)) != noErr) {
641 		fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err);
642 		return 0;
643 	}
644 
645 	// Yes, based at 1.  Silly API.
646 	for(i = 1; i <= nitems; i++) {
647 		PasteboardItemID itemID;
648 		CFArrayRef flavorTypeArray;
649 		CFIndex flavorCount;
650 
651 		if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){
652 			fprint(2, "Can't get pasteboard item identifier: %d\n", err);
653 			return 0;
654 		}
655 
656 		if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){
657 			fprint(2, "Can't copy pasteboard item flavors: %d\n", err);
658 			return 0;
659 		}
660 
661 		flavorCount = CFArrayGetCount(flavorTypeArray);
662 		CFIndex flavorIndex;
663 		for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){
664 			CFStringRef flavorType;
665 			flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
666 			if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){
667 				if((err = PasteboardCopyItemFlavorData(appleclip, itemID,
668 					CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){
669 					fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err);
670 					return 0;
671 				}
672 				CFIndex length = CFDataGetLength(cfdata);
673 				if (length > sizeof rsnarf) length = sizeof rsnarf;
674 				CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf);
675 				snprint(snarf, sizeof snarf, "%.*S", length/sizeof(Rune), rsnarf);
676 				for(s = snarf; *s; s++)
677 					if(*s == '\r')
678 						*s = '\n';
679 				CFRelease(cfdata);
680 				return strdup(snarf);
681 			}
682 		}
683 	}
684 	return 0;
685 }
686 
687 int
688 clipwrite(char *snarf)
689 {
690 	CFDataRef cfdata;
691 	PasteboardSyncFlags flags;
692 
693 	if(appleclip == NULL)
694 		return 0;
695 	runeseprint(rsnarf, rsnarf+nelem(rsnarf), "%s", snarf);
696 	if(PasteboardClear(appleclip) != noErr){
697 		fprint(2, "apple pasteboard clear failed\n");
698 		return 0;
699 	}
700 	flags = PasteboardSynchronize(appleclip);
701 	if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
702 		fprint(2, "apple pasteboard cannot assert ownership\n");
703 		return 0;
704 	}
705 	cfdata = CFDataCreate(kCFAllocatorDefault, (uchar*)rsnarf, runestrlen(rsnarf)*2);
706 	if(cfdata == nil){
707 		fprint(2, "apple pasteboard cfdatacreate failed\n");
708 		return 0;
709 	}
710 	if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
711 		CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
712 		fprint(2, "apple pasteboard putitem failed\n");
713 		CFRelease(cfdata);
714 		return 0;
715 	}
716 	CFRelease(cfdata);
717 	return 1;
718 }
719 
720 void
721 setpointer(int x, int y)
722 {
723 	CGPoint pnt;
724 
725 	pnt.x = x + winRect.left;
726 	pnt.y = y + winRect.top;
727 	CGWarpMouseCursorPosition(pnt);
728 }
729 
730 void
731 drawcursor(Drawcursor* c)
732 {
733 	USED(c);
734 	/* removed, pending extensive change for newer MacOS X */
735 }
736