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