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