/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ /*********************************************************************************** SNES9X for Mac OS (c) Copyright John Stiles Snes9x for Mac OS X (c) Copyright 2001 - 2011 zones (c) Copyright 2002 - 2005 107 (c) Copyright 2002 PB1400c (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt ***********************************************************************************/ #include "port.h" #include "mac-prefix.h" #include "mac-dialog.h" #include "mac-gworld.h" #include "mac-os.h" #include "mac-keyboard.h" #define kmUpArrowKey 0x7E #define kmDownArrowKey 0x7D #define kmRightArrowKey 0x7C #define kmLeftArrowKey 0x7B #define kmReturnKey 0x24 #define kmTabKey 0x30 #define kmShiftKey 0x38 #define kmControlKey 0x3B #define kmOptionKey 0x3A #define kmCommandKey 0x37 #define kmXKey 0x07 #define kmZKey 0x06 #define kmKP2Key 0x54 #define kmKP4Key 0x56 #define kmKP5Key 0x57 #define kmKP6Key 0x58 #define kmKP8Key 0x5B #define kmKPEnterKey 0x4C #define kmKPPlusKey 0x45 #define kmKP0Key 0x52 #define kmKPPeriodKey 0x41 #define kmHomeKey 0x73 #define kmPageUpKey 0x74 #define kmEndKey 0x77 #define kmPageDownKey 0x79 #define kmBackslashKey 0x2A #define km1Key 0x12 #define km0Key 0x1D #define kmIKey 0x22 #define kmJKey 0x26 #define kmKKey 0x28 #define kmLKey 0x25 #define kmTildeKey 0x32 #define kmRKey 0x0F #define kmBKey 0x0B #define kmNKey 0x2D #define kmMKey 0x2E #define kmSpaceKey 0x31 #define kmSlashKey 0x2C #define kmPeriodKey 0x2F #define kmQKey 0x0C #define kmWKey 0x0D #define kmEscKey 0x35 #define kmCommaKey 0x2B #define kIconSize 16 #define kKeySize 24 #define KS kKeySize uint8 keyCode[kKeys] = { kmUpArrowKey, kmDownArrowKey, kmLeftArrowKey, kmRightArrowKey, kmShiftKey, kmOptionKey, kmControlKey, kmCommandKey, kmZKey, kmXKey, kmReturnKey, kmTabKey, kmKP8Key, kmKP2Key, kmKP4Key, kmKP6Key, kmPageDownKey, kmPageUpKey, kmEndKey, kmHomeKey, kmKP0Key, kmKPPeriodKey, kmKPEnterKey, kmKPPlusKey, kmBackslashKey, km1Key, km0Key, kmTildeKey, kmRKey, kmBKey, kmNKey, kmMKey, kmSpaceKey, kmSlashKey, kmPeriodKey, kmQKey, kmWKey, kmEscKey, kmCommaKey }; typedef struct { int keyWidth, keyHeight; uint8 scancode; const char *keyLabel; } KeyboardLayout; typedef struct { HIViewRef view; } CustomViewData; static CGImageRef iconTableImage; static CGImageRef keyLayoutImage; static CGImageRef iconPlaceImage; static Ptr iconTableCGWld; static Ptr keyLayoutWorld; static Ptr iconPlaceWorld; static CGRect keyRect[0x80][2]; static uint8 defaultKeys[kKeys]; static HIObjectClassRef theClass; static HIViewRef customView; static HIPoint mousePos; static float ofsx, ofsy; static int dragKey; static CGPoint dragKeyOfs; static CGRect dragKeyRect; static volatile Boolean keyInDrag; static const int kKeyLayoutWidth = kKeySize * 23 + 1, kKeyLayoutHeight = kKeySize * 7 + 1; static KeyboardLayout keys[] = { { KS, KS, 0x35, "esc" }, { KS, KS, 0x00, NULL }, { KS, KS, 0x7a, "F1" }, { KS, KS, 0x78, "F2" }, { KS, KS, 0x63, "F3" }, { KS, KS, 0x76, "F4" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x60, "F5" }, { KS, KS, 0x61, "F6" }, { KS, KS, 0x62, "F7" }, { KS, KS, 0x64, "F8" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x65, "F9" }, { KS, KS, 0x6d, "F10" }, { KS, KS, 0x67, "F11" }, { KS, KS, 0x6f, "F12" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x69, "F13" }, { KS, KS, 0x6b, "F14" }, { KS, KS, 0x71, "F15" }, { 0, 0, 0x00, NULL }, { 0, 0, 0x00, NULL }, { KS, KS, 0x32, "`" }, { KS, KS, 0x12, "1" }, { KS, KS, 0x13, "2" }, { KS, KS, 0x14, "3" }, { KS, KS, 0x15, "4" }, { KS, KS, 0x17, "5" }, { KS, KS, 0x16, "6" }, { KS, KS, 0x1a, "7" }, { KS, KS, 0x1c, "8" }, { KS, KS, 0x19, "9" }, { KS, KS, 0x1d, "0" }, { KS, KS, 0x1b, "-" }, { KS, KS, 0x18, "=" }, { KS * 2, KS, 0x33, "delete" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x72, "ins" }, { KS, KS, 0x73, "hom" }, { KS, KS, 0x74, "pgu" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x47, "clr" }, { KS, KS, 0x51, "=" }, { KS, KS, 0x4b, "/" }, { KS, KS, 0x43, "*" }, { 0, 0, 0x00, NULL }, { KS * 3 / 2, KS, 0x30, "tab" }, { KS, KS, 0x0c, "Q" }, { KS, KS, 0x0d, "W" }, { KS, KS, 0x0e, "E" }, { KS, KS, 0x0f, "R" }, { KS, KS, 0x11, "T" }, { KS, KS, 0x10, "Y" }, { KS, KS, 0x20, "U" }, { KS, KS, 0x22, "I" }, { KS, KS, 0x1f, "O" }, { KS, KS, 0x23, "P" }, { KS, KS, 0x21, "[" }, { KS, KS, 0x1e, "]" }, { KS * 3 / 2, KS, 0x2a, "\\" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x75, "del" }, { KS, KS, 0x77, "end" }, { KS, KS, 0x79, "pgd" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x59, "7" }, { KS, KS, 0x5b, "8" }, { KS, KS, 0x5c, "9" }, { KS, KS, 0x4e, "-" }, { 0, 0, 0x00, NULL }, { KS * 2, KS, 0x39, "caps" }, { KS, KS, 0x00, "A" }, { KS, KS, 0x01, "S" }, { KS, KS, 0x02, "D" }, { KS, KS, 0x03, "F" }, { KS, KS, 0x05, "G" }, { KS, KS, 0x04, "H" }, { KS, KS, 0x26, "J" }, { KS, KS, 0x28, "K" }, { KS, KS, 0x25, "L" }, { KS, KS, 0x29, ";" }, { KS, KS, 0x27, "\xd3" }, { KS * 2, KS, 0x24, "return" }, { KS * 4, KS, 0x00, NULL }, { KS, KS, 0x56, "4" }, { KS, KS, 0x57, "5" }, { KS, KS, 0x58, "6" }, { KS, KS, 0x45, "+" }, { 0, 0, 0x00, NULL }, { KS * 5 / 2, KS, 0x38, "shift" }, { KS, KS, 0x06, "Z" }, { KS, KS, 0x07, "X" }, { KS, KS, 0x08, "C" }, { KS, KS, 0x09, "V" }, { KS, KS, 0x0b, "B" }, { KS, KS, 0x2d, "N" }, { KS, KS, 0x2e, "M" }, { KS, KS, 0x2b, "," }, { KS, KS, 0x2f, "." }, { KS, KS, 0x2c, "/" }, { KS * 5 / 2, KS, 0x38, "shift" }, { KS * 3 / 2, KS, 0x00, NULL }, { KS, KS, 0x7e, "up" }, { KS * 3 / 2, KS, 0x00, NULL }, { KS, KS, 0x53, "1" }, { KS, KS, 0x54, "2" }, { KS, KS, 0x55, "3" }, { KS, KS * 2, 0x4c, "ent" }, { 0, 0, 0x00, NULL }, { KS * 3 / 2, KS, 0x3b, "ctrl" }, { KS * 3 / 2, KS, 0x3a, "opt" }, { KS * 3 / 2, KS, 0x37, "cmd" }, { KS * 6, KS, 0x31, " " }, { KS * 3 / 2, KS, 0x37, "cmd" }, { KS * 3 / 2, KS, 0x3a, "opt" }, { KS * 3 / 2, KS, 0x3b, "ctrl" }, { KS / 2, KS, 0x00, NULL }, { KS, KS, 0x7b, "lt" }, { KS, KS, 0x7d, "dn" }, { KS, KS, 0x7c, "rt" }, { KS / 2, KS, 0x00, NULL }, { KS * 2, KS, 0x52, "0" }, { KS, KS, 0x41, "." }, { 0, 0, 0x00, NULL } }; static void CreateIconTableImage (void); static void ReleaseIconTableImage (void); static void CreateKeyLayoutImage (void); static void ReleaseKeyLayoutImage (void); static void CreateIconPlaceImage (void); static void UpdateIconPlaceImage (void); static void ReleaseIconPlaceImage (void); static void DrawPlacedIcon (CGContextRef, int); static void DrawDraggedIcon (CGContextRef, int, CGPoint *); static Boolean KeyCodeInUse (int); static int FindHitKey (HIPoint, CGRect *, CGPoint *); static pascal OSStatus KeyWindowEventHandler (EventHandlerCallRef, EventRef, void *); static pascal OSStatus KeyLegendEventHandler (EventHandlerCallRef, EventRef, void *); static pascal OSStatus KeyLayoutEventHandler (EventHandlerCallRef, EventRef, void *); #define kCustomLayoutViewClassID CFSTR("com.snes9x.macos.snes9x.keylayout") void ConfigureKeyboard (void) { OSStatus err; IBNibRef nibRef; err = CreateNibReference(kMacS9XCFString, &nibRef); if (err == noErr) { WindowRef tWindowRef; err = CreateWindowFromNib(nibRef, CFSTR("Keyboard"), &tWindowRef); if (err == noErr) { EventHandlerRef wref, iref1, iref2; EventHandlerUPP wUPP, iUPP; EventTypeSpec wEvents[] = { { kEventClassWindow, kEventWindowClose }, { kEventClassCommand, kEventCommandProcess }, { kEventClassCommand, kEventCommandUpdateStatus } }, cEvents[] = { { kEventClassHIObject, kEventHIObjectConstruct }, { kEventClassHIObject, kEventHIObjectDestruct }, { kEventClassHIObject, kEventHIObjectInitialize }, { kEventClassControl, kEventControlDraw }, { kEventClassControl, kEventControlHitTest }, { kEventClassControl, kEventControlTrack } }, iEvents[] = { { kEventClassControl, kEventControlDraw } }; HIObjectRef hiObject; HIViewRef contentView, image1, image2; HIViewID cid; HIRect frame; Rect winBounds; UpdateIconPlaceImage(); keyInDrag = false; dragKey = -1; dragKeyOfs = CGPointMake(0.0f, 0.0f); dragKeyRect = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); mousePos = CGPointMake(0.0f, 0.0f); err = noErr; if (theClass == NULL) err = HIObjectRegisterSubclass(kCustomLayoutViewClassID, kHIViewClassID, 0, KeyLayoutEventHandler, GetEventTypeCount(cEvents), cEvents, NULL, &theClass); if (err == noErr) { err = HIObjectCreate(kCustomLayoutViewClassID, NULL, &hiObject); if (err == noErr) { GetWindowBounds(tWindowRef, kWindowContentRgn, &winBounds); frame.origin.x = 2.0f; frame.origin.y = 2.0f; frame.size.width = (float) (winBounds.right - winBounds.left) - 4.0f; frame.size.height = (float) kKeyLayoutHeight + 36.0f; ofsx = (float) (((int) frame.size.width - kKeyLayoutWidth ) >> 1) + 1.0f; ofsy = (float) (((int) frame.size.height - kKeyLayoutHeight) >> 1) + 1.0f; customView = (HIViewRef) hiObject; HIViewFindByID(HIViewGetRoot(tWindowRef), kHIViewWindowContentID, &contentView); HIViewAddSubview(contentView, customView); HIViewSetFrame(customView, &frame); HIViewSetVisible(customView, true); cid.signature = 'Lgnd'; cid.id = 0; HIViewFindByID(contentView, cid, &image1); cid.id = 1; HIViewFindByID(contentView, cid, &image2); iUPP = NewEventHandlerUPP(KeyLegendEventHandler); err = InstallControlEventHandler(image1, iUPP, GetEventTypeCount(iEvents), iEvents, (void *) image1, &iref1); err = InstallControlEventHandler(image2, iUPP, GetEventTypeCount(iEvents), iEvents, (void *) image2, &iref2); wUPP = NewEventHandlerUPP(KeyWindowEventHandler); err = InstallWindowEventHandler(tWindowRef, wUPP, GetEventTypeCount(wEvents), wEvents, (void *) tWindowRef, &wref); MoveWindowPosition(tWindowRef, kWindowKeyConfig, false); ShowWindow(tWindowRef); err = RunAppModalLoopForWindow(tWindowRef); HideWindow(tWindowRef); SaveWindowPosition(tWindowRef, kWindowKeyConfig); err = RemoveEventHandler(iref2); err = RemoveEventHandler(iref1); DisposeEventHandlerUPP(iUPP); err = RemoveEventHandler(wref); DisposeEventHandlerUPP(wUPP); } } CFRelease(tWindowRef); } DisposeNibReference(nibRef); } } static void CreateIconTableImage (void) { CGContextRef ctx; CGDataProviderRef prov; CGColorSpaceRef color; CGRect rct; rct = CGRectMake(0.0f, 0.0f, (float) kIconSize, (float) kIconSize); iconTableCGWld = (Ptr) malloc(kIconSize * kKeys * (kIconSize + 1) * 4); if (!iconTableCGWld) QuitWithFatalError(0, "keyboard 08"); ctx = NULL; color = CGColorSpaceCreateDeviceRGB(); if (color) { ctx = CGBitmapContextCreate(iconTableCGWld, kIconSize * kKeys, kIconSize, 8, kIconSize * kKeys * 4, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0)); CGColorSpaceRelease(color); } if (!ctx) QuitWithFatalError(0, "keyboard 09"); CGContextTranslateCTM(ctx, 0.0f, (float) kIconSize); CGContextScaleCTM(ctx, 1.0f, -1.0f); // SNES pads for (int i = macPadIconIndex; i < macPadIconIndex + 12 * 2; i++) { if (systemVersion >= 0x1040) CGContextDrawImage(ctx, rct, macIconImage[i]); #ifdef MAC_PANTHER_SUPPORT else PlotIconRefInContext(ctx, &rct, kAlignNone, kTransformNone, NULL, kPlotIconRefNormalFlags, macIconRef[i]); #endif rct = CGRectOffset(rct, kIconSize, 0); } // Function buttons for (int i = macFunctionIconIndex; i < macFunctionIconIndex + 17; i++) { if (systemVersion >= 0x1040) CGContextDrawImage(ctx, rct, macIconImage[i]); #ifdef MAC_PANTHER_SUPPORT else PlotIconRefInContext(ctx, &rct, kAlignNone, kTransformNone, NULL, kPlotIconRefNormalFlags, macIconRef[i]); #endif rct = CGRectOffset(rct, kIconSize, 0); } CGContextRelease(ctx); iconTableImage = NULL; prov = CGDataProviderCreateWithData(NULL, iconTableCGWld, kIconSize * kKeys * kIconSize * 4, NULL); if (prov) { color = CGColorSpaceCreateDeviceRGB(); if (color) { iconTableImage = CGImageCreate(kIconSize * kKeys, kIconSize, 8, 32, kIconSize * kKeys * 4, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0), prov, NULL, 0, kCGRenderingIntentDefault); CGColorSpaceRelease(color); } CGDataProviderRelease(prov); } if (!iconTableImage) QuitWithFatalError(0, "keyboard 10"); } static void ReleaseIconTableImage (void) { CGImageRelease(iconTableImage); free(iconTableCGWld); } static void CreateKeyLayoutImage (void) { CGContextRef ctx; CGDataProviderRef prov; CGColorSpaceRef color; CGAffineTransform flipMatrix; CGRect rct, r; int index, scancode; rct = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < 0x80; i++) keyRect[i][0] = keyRect[i][1] = rct; keyLayoutWorld = (Ptr) malloc(kKeyLayoutWidth * (kKeyLayoutHeight + 1) * 4); if (!keyLayoutWorld) QuitWithFatalError(0, "keyboard 02"); ctx = NULL; color = CGColorSpaceCreateDeviceRGB(); if (color) { ctx = CGBitmapContextCreate(keyLayoutWorld, kKeyLayoutWidth, kKeyLayoutHeight, 8, kKeyLayoutWidth * 4, color, kCGImageAlphaPremultipliedFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0)); CGColorSpaceRelease(color); } if (!ctx) QuitWithFatalError(0, "keyboard 04"); CGContextSetLineJoin(ctx, kCGLineJoinMiter); flipMatrix = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); CGContextSelectFont(ctx, "Helvetica", 10.0f, kCGEncodingMacRoman); CGContextSetTextDrawingMode(ctx, kCGTextFill); CGContextSetTextMatrix(ctx, flipMatrix); rct = CGRectMake(0.0f, 0.0f, (float) kKeyLayoutWidth, (float) kKeyLayoutHeight); CGContextClearRect(ctx, rct); index = 0; rct = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < 7; i++) { while (keys[index].keyWidth) { rct.size.width = (float) keys[index].keyWidth; if (keys[index].keyLabel) { rct.size.height = (float) keys[index].keyHeight; scancode = keys[index].scancode; if (keyRect[scancode][0].size.height < 1.0) keyRect[scancode][0] = rct; else keyRect[scancode][1] = rct; r = rct; r.origin.x += 1.0f; r.origin.y += 1.0f; r.size.width -= 1.0f; r.size.height -= 1.0f; CGContextSetRGBStrokeColor(ctx, 0.1f, 0.1f, 0.1f, 1.0f); CGContextStrokeRect(ctx, r); float h, p; CGRectInset(r, 2.0f, 2.0f); h = r.size.height; for (float f = h; f >= 1.0f; f -= 1.0f) { p = (155.0f + (h - f)) / 180.0f; CGContextSetRGBFillColor(ctx, p, p, p, 1.0f); CGContextFillRect(ctx, r); r.size.height -= 1.0f; } CGContextSetRGBFillColor(ctx, 0.1f, 0.1f, 0.1f, 1.0f); CGContextShowTextAtPoint(ctx, rct.origin.x + 3.0f, rct.origin.y + rct.size.height - 3.0f, keys[index].keyLabel, strlen(keys[index].keyLabel)); } rct.origin.x += rct.size.width; index++; } rct.origin.y += kKeySize; rct.origin.x = rct.size.width = 0; index++; } CGContextRelease(ctx); keyLayoutImage = NULL; prov = CGDataProviderCreateWithData(NULL, keyLayoutWorld, kKeyLayoutWidth * kKeyLayoutHeight * 4, NULL); if (prov) { color = CGColorSpaceCreateDeviceRGB(); if (color) { keyLayoutImage = CGImageCreate(kKeyLayoutWidth, kKeyLayoutHeight, 8, 32, kKeyLayoutWidth * 4, color, kCGImageAlphaPremultipliedFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0), prov, NULL, 0, kCGRenderingIntentDefault); CGColorSpaceRelease(color); } CGDataProviderRelease(prov); } if (!keyLayoutImage) QuitWithFatalError(0, "keyboard 05"); } static void ReleaseKeyLayoutImage (void) { CGImageRelease(keyLayoutImage); free(keyLayoutWorld); } static void CreateIconPlaceImage (void) { iconPlaceWorld = (Ptr) malloc(kKeyLayoutWidth * (kKeyLayoutHeight + 1) * 4); if (!iconPlaceWorld) QuitWithFatalError(0, "keyboard 06"); iconPlaceImage = NULL; UpdateIconPlaceImage(); } static void UpdateIconPlaceImage (void) { CGContextRef ctx; CGDataProviderRef prov; CGColorSpaceRef color; CGRect rct; if (iconPlaceImage) CGImageRelease(iconPlaceImage); iconPlaceImage = NULL; color = CGColorSpaceCreateDeviceRGB(); if (color) { ctx = CGBitmapContextCreate(iconPlaceWorld, kKeyLayoutWidth, kKeyLayoutHeight, 8, kKeyLayoutWidth * 4, color, kCGImageAlphaPremultipliedFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0)); if (ctx) { rct = CGRectMake(0.0f, 0.0f, (float) kKeyLayoutWidth, (float) kKeyLayoutHeight); CGContextDrawImage(ctx, rct, keyLayoutImage); for (int i = 0; i < kKeys; i++) DrawPlacedIcon(ctx, i); CGContextRelease(ctx); } prov = CGDataProviderCreateWithData(NULL, iconPlaceWorld, kKeyLayoutWidth * kKeyLayoutHeight * 4, NULL); if (prov) { iconPlaceImage = CGImageCreate(kKeyLayoutWidth, kKeyLayoutHeight, 8, 32, kKeyLayoutWidth * 4, color, kCGImageAlphaPremultipliedFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrderDefault : 0), prov, NULL, 0, kCGRenderingIntentDefault); CGDataProviderRelease(prov); } CGColorSpaceRelease(color); } if (!iconPlaceImage) QuitWithFatalError(0, "keyboard 07"); } static void ReleaseIconPlaceImage (void) { CGImageRelease(iconPlaceImage); free(iconPlaceWorld); } void InitKeyboard (void) { theClass = NULL; memcpy(defaultKeys, keyCode, sizeof(keyCode)); CreateIconTableImage(); CreateKeyLayoutImage(); CreateIconPlaceImage(); } void DeinitKeyboard (void) { ReleaseIconPlaceImage(); ReleaseKeyLayoutImage(); ReleaseIconTableImage(); } static void DrawPlacedIcon (CGContextRef ctx, int which) { CGRect keyBounds, srcRect, dstRect; CGContextSaveGState(ctx); CGContextSetRGBFillColor(ctx, 0.40f, 0.40f, 0.65f, 0.5f); for (int each = 0; each <= 1; each++) { keyBounds = keyRect[keyCode[which]][each]; if (keyBounds.size.height > 1.0f) { keyBounds.origin.x += 1.0f; keyBounds.origin.y += 1.0f; keyBounds.size.width -= 1.0f; keyBounds.size.height -= 1.0f; CGContextFillRect(ctx, keyBounds); keyBounds.origin.x -= 1.0f; keyBounds.origin.y -= 1.0f; keyBounds.size.width += 1.0f; keyBounds.size.height += 1.0f; srcRect.origin.x = (float) (which * kIconSize); srcRect.origin.y = 0.0f; srcRect.size.width = (float) kIconSize; srcRect.size.height = (float) kIconSize; dstRect.origin.x = keyBounds.origin.x + (keyBounds.size.width - kIconSize) / 2.0f; dstRect.origin.y = keyBounds.origin.y + (keyBounds.size.height - kIconSize) / 2.0f; dstRect.size.width = (float) kIconSize; dstRect.size.height = (float) kIconSize; DrawSubCGImage(ctx, iconTableImage, srcRect, dstRect); } } CGContextRestoreGState(ctx); } static void DrawDraggedIcon (CGContextRef ctx, int which, CGPoint *offset) { CGRect srcRect, dstRect; CGContextSaveGState(ctx); srcRect.origin.x = (float) (which * kIconSize); srcRect.origin.y = 0.0f; srcRect.size.width = (float) kIconSize; srcRect.size.height = (float) kIconSize; dstRect.origin.x = mousePos.x + offset->x; dstRect.origin.y = mousePos.y + offset->y; dstRect.size.width = (float) kIconSize; dstRect.size.height = (float) kIconSize; CGContextSetAlpha(ctx, 0.5f); DrawSubCGImage(ctx, iconTableImage, srcRect, dstRect); CGContextRestoreGState(ctx); } static Boolean KeyCodeInUse (int code) { for (int i = 0; i < kKeys; i++) if (keyCode[i] == code) return (true); return (false); } static int FindHitKey (HIPoint where, CGRect *keybounds, CGPoint *offset) { int hit; hit = -1; *offset = CGPointMake(0.0f, 0.0f); *keybounds = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); for (int which = 0; which < kKeys; which++) { for (int each = 0; each <= 1; each++) { if (CGRectContainsPoint(keyRect[keyCode[which]][each], where)) { hit = which; *keybounds = keyRect[keyCode[which]][each]; offset->x = keybounds->origin.x + (keybounds->size.width - kIconSize) / 2.0f - where.x + 18.0f; offset->y = keybounds->origin.y + (keybounds->size.height - kIconSize) / 2.0f - where.y + 18.0f; } } } return (hit); } static pascal OSStatus KeyWindowEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; WindowRef tWindowRef = (WindowRef) inUserData; switch (GetEventClass(inEvent)) { case kEventClassWindow: switch (GetEventKind(inEvent)) { case kEventWindowClose: QuitAppModalLoopForWindow(tWindowRef); result = noErr; } break; case kEventClassCommand: switch (GetEventKind(inEvent)) { HICommand tHICommand; case kEventCommandUpdateStatus: err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr && tHICommand.commandID == 'clos') { UpdateMenuCommandStatus(true); result = noErr; } break; case kEventCommandProcess: err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); if (err == noErr) { if (tHICommand.commandID == 'DFLT') { memcpy(keyCode, defaultKeys, sizeof(keyCode)); UpdateIconPlaceImage(); HIViewSetNeedsDisplay(customView, true); result = noErr; } } } } return (result); } static pascal OSStatus KeyLegendEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; HIViewRef view = (HIViewRef) inUserData; switch (GetEventClass(inEvent)) { case kEventClassControl: switch (GetEventKind(inEvent)) { case kEventControlDraw: CGContextRef ctx; err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(CGContextRef), NULL, &ctx); if (err == noErr) { HIViewID cid; HIRect bounds; GetControlID(view, &cid); HIViewGetBounds(view, &bounds); CGContextTranslateCTM(ctx, 0, bounds.size.height); CGContextScaleCTM(ctx, 1.0f, -1.0f); CGContextDrawImage(ctx, CGRectMake(0, 0, kIconSize, kIconSize), macIconImage[macLegendIconIndex + cid.id]); result = noErr; } } } return (result); } static pascal OSStatus KeyLayoutEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) { OSStatus err, result = eventNotHandledErr; CustomViewData *data = (CustomViewData *) inUserData; switch (GetEventClass(inEvent)) { case kEventClassHIObject: switch (GetEventKind(inEvent)) { case kEventHIObjectConstruct: data = (CustomViewData *) calloc(1, sizeof(CustomViewData)); if (data) { HIViewRef epView; err = GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(epView), NULL, &epView); if (err == noErr) { data->view = epView; result = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(data), &data); } } break; case kEventHIObjectDestruct: if (data) free(data); result = noErr; break; case kEventHIObjectInitialize: result = CallNextEventHandler(inHandlerRef, inEvent); } break; case kEventClassControl: switch (GetEventKind(inEvent)) { case kEventControlDraw: CGContextRef ctx; err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(ctx), NULL, &ctx); if (err == noErr) { HIRect bounds, srcRect, dstRect; HIViewGetBounds(customView, &bounds); srcRect = CGRectMake(0, 0, kKeyLayoutWidth, kKeyLayoutHeight); dstRect.origin.x = (float) (((int) bounds.size.width - kKeyLayoutWidth ) >> 1); dstRect.origin.y = (float) (((int) bounds.size.height - kKeyLayoutHeight) >> 1); dstRect.size.width = (float) kKeyLayoutWidth; dstRect.size.height = (float) kKeyLayoutHeight; DrawSubCGImage(ctx, iconPlaceImage, srcRect, dstRect); if (keyInDrag && (dragKey != -1)) DrawDraggedIcon(ctx, dragKey, &dragKeyOfs); } result = noErr; break; case kEventControlHitTest: ControlPartCode part; part = kControlButtonPart; result = SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part); break; case kEventControlTrack: MouseTrackingResult trackResult; WindowRef window; HIViewRef contentView; HIPoint hipt; dragKey = -1; dragKeyOfs = CGPointMake(0.0f, 0.0f); dragKeyRect = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); mousePos = CGPointMake(0.0f, 0.0f); trackResult = kMouseTrackingMouseDown; window = GetControlOwner(customView); HIViewFindByID(HIViewGetRoot(window), kHIViewWindowContentID, &contentView); #ifdef MAC_TIGER_PANTHER_SUPPORT CGrafPtr oldPort; Point qdpt; Boolean portChanged = false; if (systemVersion < 0x1050) portChanged = QDSwapPort(GetWindowPort(window), &oldPort); #endif err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(hipt), NULL, &hipt); if (err == noErr) { hipt.x -= ofsx; hipt.y -= ofsy; dragKey = FindHitKey(hipt, &dragKeyRect, &dragKeyOfs); if (dragKey != -1) { keyInDrag = true; while (trackResult != kMouseTrackingMouseUp) { if (CGPointEqualToPoint(mousePos, hipt) == 0) { mousePos = hipt; HIViewSetNeedsDisplay(customView, true); } if (systemVersion >= 0x1050) { err = HIViewTrackMouseLocation(customView, 0, kEventDurationForever, 0, NULL, &hipt, NULL, NULL, &trackResult); hipt.x -= ofsx; hipt.y -= ofsy; } #ifdef MAC_TIGER_PANTHER_SUPPORT else { TrackMouseLocation(NULL, &qdpt, &trackResult); hipt.x = qdpt.h - ofsx; hipt.y = qdpt.v - ofsy; HIViewConvertPoint(&hipt, contentView, customView); } #endif } keyInDrag = false; for (int code = 0; code < 0x80; code++) { for (int each = 0; each <= 1; each++) { if (CGRectContainsPoint(keyRect[code][each], mousePos)) { if (!KeyCodeInUse(code)) { keyCode[dragKey] = code; UpdateIconPlaceImage(); } } } } HIViewSetNeedsDisplay(customView, true); } } #ifdef MAC_TIGER_PANTHER_SUPPORT if (systemVersion < 0x1050) { if (portChanged) QDSwapPort(oldPort, NULL); } #endif result = noErr; } } return (result); }