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