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