|
Blender
V2.59
|
00001 00030 #import <Cocoa/Cocoa.h> 00031 00032 /*For the currently not ported to Cocoa keyboard layout functions (64bit & 10.6 compatible)*/ 00033 #include <Carbon/Carbon.h> 00034 00035 #include <sys/time.h> 00036 #include <sys/types.h> 00037 #include <sys/sysctl.h> 00038 00039 #include "GHOST_SystemCocoa.h" 00040 00041 #include "GHOST_DisplayManagerCocoa.h" 00042 #include "GHOST_EventKey.h" 00043 #include "GHOST_EventButton.h" 00044 #include "GHOST_EventCursor.h" 00045 #include "GHOST_EventWheel.h" 00046 #include "GHOST_EventTrackpad.h" 00047 #include "GHOST_EventDragnDrop.h" 00048 #include "GHOST_EventString.h" 00049 #include "GHOST_TimerManager.h" 00050 #include "GHOST_TimerTask.h" 00051 #include "GHOST_WindowManager.h" 00052 #include "GHOST_WindowCocoa.h" 00053 #ifdef WITH_INPUT_NDOF 00054 #include "GHOST_NDOFManagerCocoa.h" 00055 #endif 00056 00057 #include "AssertMacros.h" 00058 00059 #pragma mark KeyMap, mouse converters 00060 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00061 /* Keycodes not defined in Tiger */ 00062 /* 00063 * Summary: 00064 * Virtual keycodes 00065 * 00066 * Discussion: 00067 * These constants are the virtual keycodes defined originally in 00068 * Inside Mac Volume V, pg. V-191. They identify physical keys on a 00069 * keyboard. Those constants with "ANSI" in the name are labeled 00070 * according to the key position on an ANSI-standard US keyboard. 00071 * For example, kVK_ANSI_A indicates the virtual keycode for the key 00072 * with the letter 'A' in the US keyboard layout. Other keyboard 00073 * layouts may have the 'A' key label on a different physical key; 00074 * in this case, pressing 'A' will generate a different virtual 00075 * keycode. 00076 */ 00077 enum { 00078 kVK_ANSI_A = 0x00, 00079 kVK_ANSI_S = 0x01, 00080 kVK_ANSI_D = 0x02, 00081 kVK_ANSI_F = 0x03, 00082 kVK_ANSI_H = 0x04, 00083 kVK_ANSI_G = 0x05, 00084 kVK_ANSI_Z = 0x06, 00085 kVK_ANSI_X = 0x07, 00086 kVK_ANSI_C = 0x08, 00087 kVK_ANSI_V = 0x09, 00088 kVK_ANSI_B = 0x0B, 00089 kVK_ANSI_Q = 0x0C, 00090 kVK_ANSI_W = 0x0D, 00091 kVK_ANSI_E = 0x0E, 00092 kVK_ANSI_R = 0x0F, 00093 kVK_ANSI_Y = 0x10, 00094 kVK_ANSI_T = 0x11, 00095 kVK_ANSI_1 = 0x12, 00096 kVK_ANSI_2 = 0x13, 00097 kVK_ANSI_3 = 0x14, 00098 kVK_ANSI_4 = 0x15, 00099 kVK_ANSI_6 = 0x16, 00100 kVK_ANSI_5 = 0x17, 00101 kVK_ANSI_Equal = 0x18, 00102 kVK_ANSI_9 = 0x19, 00103 kVK_ANSI_7 = 0x1A, 00104 kVK_ANSI_Minus = 0x1B, 00105 kVK_ANSI_8 = 0x1C, 00106 kVK_ANSI_0 = 0x1D, 00107 kVK_ANSI_RightBracket = 0x1E, 00108 kVK_ANSI_O = 0x1F, 00109 kVK_ANSI_U = 0x20, 00110 kVK_ANSI_LeftBracket = 0x21, 00111 kVK_ANSI_I = 0x22, 00112 kVK_ANSI_P = 0x23, 00113 kVK_ANSI_L = 0x25, 00114 kVK_ANSI_J = 0x26, 00115 kVK_ANSI_Quote = 0x27, 00116 kVK_ANSI_K = 0x28, 00117 kVK_ANSI_Semicolon = 0x29, 00118 kVK_ANSI_Backslash = 0x2A, 00119 kVK_ANSI_Comma = 0x2B, 00120 kVK_ANSI_Slash = 0x2C, 00121 kVK_ANSI_N = 0x2D, 00122 kVK_ANSI_M = 0x2E, 00123 kVK_ANSI_Period = 0x2F, 00124 kVK_ANSI_Grave = 0x32, 00125 kVK_ANSI_KeypadDecimal = 0x41, 00126 kVK_ANSI_KeypadMultiply = 0x43, 00127 kVK_ANSI_KeypadPlus = 0x45, 00128 kVK_ANSI_KeypadClear = 0x47, 00129 kVK_ANSI_KeypadDivide = 0x4B, 00130 kVK_ANSI_KeypadEnter = 0x4C, 00131 kVK_ANSI_KeypadMinus = 0x4E, 00132 kVK_ANSI_KeypadEquals = 0x51, 00133 kVK_ANSI_Keypad0 = 0x52, 00134 kVK_ANSI_Keypad1 = 0x53, 00135 kVK_ANSI_Keypad2 = 0x54, 00136 kVK_ANSI_Keypad3 = 0x55, 00137 kVK_ANSI_Keypad4 = 0x56, 00138 kVK_ANSI_Keypad5 = 0x57, 00139 kVK_ANSI_Keypad6 = 0x58, 00140 kVK_ANSI_Keypad7 = 0x59, 00141 kVK_ANSI_Keypad8 = 0x5B, 00142 kVK_ANSI_Keypad9 = 0x5C 00143 }; 00144 00145 /* keycodes for keys that are independent of keyboard layout*/ 00146 enum { 00147 kVK_Return = 0x24, 00148 kVK_Tab = 0x30, 00149 kVK_Space = 0x31, 00150 kVK_Delete = 0x33, 00151 kVK_Escape = 0x35, 00152 kVK_Command = 0x37, 00153 kVK_Shift = 0x38, 00154 kVK_CapsLock = 0x39, 00155 kVK_Option = 0x3A, 00156 kVK_Control = 0x3B, 00157 kVK_RightShift = 0x3C, 00158 kVK_RightOption = 0x3D, 00159 kVK_RightControl = 0x3E, 00160 kVK_Function = 0x3F, 00161 kVK_F17 = 0x40, 00162 kVK_VolumeUp = 0x48, 00163 kVK_VolumeDown = 0x49, 00164 kVK_Mute = 0x4A, 00165 kVK_F18 = 0x4F, 00166 kVK_F19 = 0x50, 00167 kVK_F20 = 0x5A, 00168 kVK_F5 = 0x60, 00169 kVK_F6 = 0x61, 00170 kVK_F7 = 0x62, 00171 kVK_F3 = 0x63, 00172 kVK_F8 = 0x64, 00173 kVK_F9 = 0x65, 00174 kVK_F11 = 0x67, 00175 kVK_F13 = 0x69, 00176 kVK_F16 = 0x6A, 00177 kVK_F14 = 0x6B, 00178 kVK_F10 = 0x6D, 00179 kVK_F12 = 0x6F, 00180 kVK_F15 = 0x71, 00181 kVK_Help = 0x72, 00182 kVK_Home = 0x73, 00183 kVK_PageUp = 0x74, 00184 kVK_ForwardDelete = 0x75, 00185 kVK_F4 = 0x76, 00186 kVK_End = 0x77, 00187 kVK_F2 = 0x78, 00188 kVK_PageDown = 0x79, 00189 kVK_F1 = 0x7A, 00190 kVK_LeftArrow = 0x7B, 00191 kVK_RightArrow = 0x7C, 00192 kVK_DownArrow = 0x7D, 00193 kVK_UpArrow = 0x7E 00194 }; 00195 00196 /* ISO keyboards only*/ 00197 enum { 00198 kVK_ISO_Section = 0x0A 00199 }; 00200 00201 /* JIS keyboards only*/ 00202 enum { 00203 kVK_JIS_Yen = 0x5D, 00204 kVK_JIS_Underscore = 0x5E, 00205 kVK_JIS_KeypadComma = 0x5F, 00206 kVK_JIS_Eisu = 0x66, 00207 kVK_JIS_Kana = 0x68 00208 }; 00209 #endif 00210 00211 static GHOST_TButtonMask convertButton(int button) 00212 { 00213 switch (button) { 00214 case 0: 00215 return GHOST_kButtonMaskLeft; 00216 case 1: 00217 return GHOST_kButtonMaskRight; 00218 case 2: 00219 return GHOST_kButtonMaskMiddle; 00220 case 3: 00221 return GHOST_kButtonMaskButton4; 00222 case 4: 00223 return GHOST_kButtonMaskButton5; 00224 default: 00225 return GHOST_kButtonMaskLeft; 00226 } 00227 } 00228 00236 static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) 00237 { 00238 00239 //printf("\nrecvchar %c 0x%x",recvChar,recvChar); 00240 switch (rawCode) { 00241 /*Physical keycodes not used due to map changes in int'l keyboards 00242 case kVK_ANSI_A: return GHOST_kKeyA; 00243 case kVK_ANSI_B: return GHOST_kKeyB; 00244 case kVK_ANSI_C: return GHOST_kKeyC; 00245 case kVK_ANSI_D: return GHOST_kKeyD; 00246 case kVK_ANSI_E: return GHOST_kKeyE; 00247 case kVK_ANSI_F: return GHOST_kKeyF; 00248 case kVK_ANSI_G: return GHOST_kKeyG; 00249 case kVK_ANSI_H: return GHOST_kKeyH; 00250 case kVK_ANSI_I: return GHOST_kKeyI; 00251 case kVK_ANSI_J: return GHOST_kKeyJ; 00252 case kVK_ANSI_K: return GHOST_kKeyK; 00253 case kVK_ANSI_L: return GHOST_kKeyL; 00254 case kVK_ANSI_M: return GHOST_kKeyM; 00255 case kVK_ANSI_N: return GHOST_kKeyN; 00256 case kVK_ANSI_O: return GHOST_kKeyO; 00257 case kVK_ANSI_P: return GHOST_kKeyP; 00258 case kVK_ANSI_Q: return GHOST_kKeyQ; 00259 case kVK_ANSI_R: return GHOST_kKeyR; 00260 case kVK_ANSI_S: return GHOST_kKeyS; 00261 case kVK_ANSI_T: return GHOST_kKeyT; 00262 case kVK_ANSI_U: return GHOST_kKeyU; 00263 case kVK_ANSI_V: return GHOST_kKeyV; 00264 case kVK_ANSI_W: return GHOST_kKeyW; 00265 case kVK_ANSI_X: return GHOST_kKeyX; 00266 case kVK_ANSI_Y: return GHOST_kKeyY; 00267 case kVK_ANSI_Z: return GHOST_kKeyZ;*/ 00268 00269 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/ 00270 case kVK_ISO_Section: return GHOST_kKeyUnknown; 00271 case kVK_ANSI_1: return GHOST_kKey1; 00272 case kVK_ANSI_2: return GHOST_kKey2; 00273 case kVK_ANSI_3: return GHOST_kKey3; 00274 case kVK_ANSI_4: return GHOST_kKey4; 00275 case kVK_ANSI_5: return GHOST_kKey5; 00276 case kVK_ANSI_6: return GHOST_kKey6; 00277 case kVK_ANSI_7: return GHOST_kKey7; 00278 case kVK_ANSI_8: return GHOST_kKey8; 00279 case kVK_ANSI_9: return GHOST_kKey9; 00280 case kVK_ANSI_0: return GHOST_kKey0; 00281 00282 case kVK_ANSI_Keypad0: return GHOST_kKeyNumpad0; 00283 case kVK_ANSI_Keypad1: return GHOST_kKeyNumpad1; 00284 case kVK_ANSI_Keypad2: return GHOST_kKeyNumpad2; 00285 case kVK_ANSI_Keypad3: return GHOST_kKeyNumpad3; 00286 case kVK_ANSI_Keypad4: return GHOST_kKeyNumpad4; 00287 case kVK_ANSI_Keypad5: return GHOST_kKeyNumpad5; 00288 case kVK_ANSI_Keypad6: return GHOST_kKeyNumpad6; 00289 case kVK_ANSI_Keypad7: return GHOST_kKeyNumpad7; 00290 case kVK_ANSI_Keypad8: return GHOST_kKeyNumpad8; 00291 case kVK_ANSI_Keypad9: return GHOST_kKeyNumpad9; 00292 case kVK_ANSI_KeypadDecimal: return GHOST_kKeyNumpadPeriod; 00293 case kVK_ANSI_KeypadEnter: return GHOST_kKeyNumpadEnter; 00294 case kVK_ANSI_KeypadPlus: return GHOST_kKeyNumpadPlus; 00295 case kVK_ANSI_KeypadMinus: return GHOST_kKeyNumpadMinus; 00296 case kVK_ANSI_KeypadMultiply: return GHOST_kKeyNumpadAsterisk; 00297 case kVK_ANSI_KeypadDivide: return GHOST_kKeyNumpadSlash; 00298 case kVK_ANSI_KeypadClear: return GHOST_kKeyUnknown; 00299 00300 case kVK_F1: return GHOST_kKeyF1; 00301 case kVK_F2: return GHOST_kKeyF2; 00302 case kVK_F3: return GHOST_kKeyF3; 00303 case kVK_F4: return GHOST_kKeyF4; 00304 case kVK_F5: return GHOST_kKeyF5; 00305 case kVK_F6: return GHOST_kKeyF6; 00306 case kVK_F7: return GHOST_kKeyF7; 00307 case kVK_F8: return GHOST_kKeyF8; 00308 case kVK_F9: return GHOST_kKeyF9; 00309 case kVK_F10: return GHOST_kKeyF10; 00310 case kVK_F11: return GHOST_kKeyF11; 00311 case kVK_F12: return GHOST_kKeyF12; 00312 case kVK_F13: return GHOST_kKeyF13; 00313 case kVK_F14: return GHOST_kKeyF14; 00314 case kVK_F15: return GHOST_kKeyF15; 00315 case kVK_F16: return GHOST_kKeyF16; 00316 case kVK_F17: return GHOST_kKeyF17; 00317 case kVK_F18: return GHOST_kKeyF18; 00318 case kVK_F19: return GHOST_kKeyF19; 00319 case kVK_F20: return GHOST_kKeyF20; 00320 00321 case kVK_UpArrow: return GHOST_kKeyUpArrow; 00322 case kVK_DownArrow: return GHOST_kKeyDownArrow; 00323 case kVK_LeftArrow: return GHOST_kKeyLeftArrow; 00324 case kVK_RightArrow: return GHOST_kKeyRightArrow; 00325 00326 case kVK_Return: return GHOST_kKeyEnter; 00327 case kVK_Delete: return GHOST_kKeyBackSpace; 00328 case kVK_ForwardDelete: return GHOST_kKeyDelete; 00329 case kVK_Escape: return GHOST_kKeyEsc; 00330 case kVK_Tab: return GHOST_kKeyTab; 00331 case kVK_Space: return GHOST_kKeySpace; 00332 00333 case kVK_Home: return GHOST_kKeyHome; 00334 case kVK_End: return GHOST_kKeyEnd; 00335 case kVK_PageUp: return GHOST_kKeyUpPage; 00336 case kVK_PageDown: return GHOST_kKeyDownPage; 00337 00338 /*case kVK_ANSI_Minus: return GHOST_kKeyMinus; 00339 case kVK_ANSI_Equal: return GHOST_kKeyEqual; 00340 case kVK_ANSI_Comma: return GHOST_kKeyComma; 00341 case kVK_ANSI_Period: return GHOST_kKeyPeriod; 00342 case kVK_ANSI_Slash: return GHOST_kKeySlash; 00343 case kVK_ANSI_Semicolon: return GHOST_kKeySemicolon; 00344 case kVK_ANSI_Quote: return GHOST_kKeyQuote; 00345 case kVK_ANSI_Backslash: return GHOST_kKeyBackslash; 00346 case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket; 00347 case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket; 00348 case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave;*/ 00349 00350 case kVK_VolumeUp: 00351 case kVK_VolumeDown: 00352 case kVK_Mute: 00353 return GHOST_kKeyUnknown; 00354 00355 default: 00356 /* alphanumerical or punctuation key that is remappable in int'l keyboards */ 00357 if ((recvChar >= 'A') && (recvChar <= 'Z')) { 00358 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA); 00359 } else if ((recvChar >= 'a') && (recvChar <= 'z')) { 00360 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA); 00361 } else { 00362 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00363 KeyboardLayoutRef keyLayout; 00364 UCKeyboardLayout *uchrData; 00365 00366 KLGetCurrentKeyboardLayout(&keyLayout); 00367 KLGetKeyboardLayoutProperty(keyLayout, kKLuchrData, (const void **) 00368 &uchrData); 00369 /*get actual character value of the "remappable" keys in int'l keyboards, 00370 if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), 00371 then fallback on using the received charactersIgnoringModifiers */ 00372 if (uchrData) 00373 { 00374 UInt32 deadKeyState=0; 00375 UniCharCount actualStrLength=0; 00376 00377 UCKeyTranslate(uchrData, rawCode, keyAction, 0, 00378 LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); 00379 00380 } 00381 #else 00382 /* Leopard and Snow Leopard 64bit compatible API*/ 00383 CFDataRef uchrHandle; /*the keyboard layout*/ 00384 TISInputSourceRef kbdTISHandle; 00385 00386 kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource(); 00387 uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData); 00388 CFRelease(kbdTISHandle); 00389 00390 /*get actual character value of the "remappable" keys in int'l keyboards, 00391 if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), 00392 then fallback on using the received charactersIgnoringModifiers */ 00393 if (uchrHandle) 00394 { 00395 UInt32 deadKeyState=0; 00396 UniCharCount actualStrLength=0; 00397 00398 UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0, 00399 LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); 00400 00401 } 00402 #endif 00403 switch (recvChar) { 00404 case '-': return GHOST_kKeyMinus; 00405 case '=': return GHOST_kKeyEqual; 00406 case ',': return GHOST_kKeyComma; 00407 case '.': return GHOST_kKeyPeriod; 00408 case '/': return GHOST_kKeySlash; 00409 case ';': return GHOST_kKeySemicolon; 00410 case '\'': return GHOST_kKeyQuote; 00411 case '\\': return GHOST_kKeyBackslash; 00412 case '[': return GHOST_kKeyLeftBracket; 00413 case ']': return GHOST_kKeyRightBracket; 00414 case '`': return GHOST_kKeyAccentGrave; 00415 default: 00416 return GHOST_kKeyUnknown; 00417 } 00418 } 00419 } 00420 return GHOST_kKeyUnknown; 00421 } 00422 00423 00424 #pragma mark defines for 10.6 api not documented in 10.5 00425 #ifndef MAC_OS_X_VERSION_10_6 00426 enum { 00427 /* The following event types are available on some hardware on 10.5.2 and later */ 00428 NSEventTypeGesture = 29, 00429 NSEventTypeMagnify = 30, 00430 NSEventTypeSwipe = 31, 00431 NSEventTypeRotate = 18, 00432 NSEventTypeBeginGesture = 19, 00433 NSEventTypeEndGesture = 20 00434 }; 00435 00436 @interface NSEvent(GestureEvents) 00437 /* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */ 00438 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00439 - (float)magnification; // change in magnification. 00440 #else 00441 - (CGFloat)magnification; // change in magnification. 00442 #endif 00443 @end 00444 00445 #endif 00446 00447 00448 #pragma mark Utility functions 00449 00450 #define FIRSTFILEBUFLG 512 00451 static bool g_hasFirstFile = false; 00452 static char g_firstFileBuf[512]; 00453 00454 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true 00455 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 00456 if (g_hasFirstFile) { 00457 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1); 00458 buf[FIRSTFILEBUFLG - 1] = '\0'; 00459 return 1; 00460 } else { 00461 return 0; 00462 } 00463 } 00464 00465 #if defined(WITH_QUICKTIME) && !defined(USE_QTKIT) 00466 //Need to place this quicktime function in an ObjC file 00467 //It is used to avoid memory leak when raising the quicktime "compression settings" standard dialog 00468 extern "C" { 00469 struct bContext; 00470 struct wmOperator; 00471 extern int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op); 00472 00473 00474 int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op) 00475 { 00476 int result; 00477 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00478 00479 result = fromcocoa_request_qtcodec_settings(C, op); 00480 00481 [pool drain]; 00482 return result; 00483 } 00484 }; 00485 #endif 00486 00487 00488 #pragma mark Cocoa objects 00489 00494 @interface CocoaAppDelegate : NSObject { 00495 GHOST_SystemCocoa *systemCocoa; 00496 } 00497 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa; 00498 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; 00499 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; 00500 - (void)applicationWillTerminate:(NSNotification *)aNotification; 00501 - (void)applicationWillBecomeActive:(NSNotification *)aNotification; 00502 @end 00503 00504 @implementation CocoaAppDelegate : NSObject 00505 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa 00506 { 00507 systemCocoa = sysCocoa; 00508 } 00509 00510 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename 00511 { 00512 return systemCocoa->handleOpenDocumentRequest(filename); 00513 } 00514 00515 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 00516 { 00517 //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled 00518 //Note that Cmd+Q is already handled by keyhandler 00519 if (systemCocoa->handleQuitRequest() == GHOST_kExitNow) 00520 return NSTerminateCancel;//NSTerminateNow; 00521 else 00522 return NSTerminateCancel; 00523 } 00524 00525 // To avoid cancelling a log off process, we must use Cocoa termination process 00526 // And this function is the only chance to perform clean up 00527 // So WM_exit needs to be called directly, as the event loop will never run before termination 00528 - (void)applicationWillTerminate:(NSNotification *)aNotification 00529 { 00530 /*G.afbreek = 0; //Let Cocoa perform the termination at the end 00531 WM_exit(C);*/ 00532 } 00533 00534 - (void)applicationWillBecomeActive:(NSNotification *)aNotification 00535 { 00536 systemCocoa->handleApplicationBecomeActiveEvent(); 00537 } 00538 @end 00539 00540 00541 00542 #pragma mark initialization/finalization 00543 00544 00545 GHOST_SystemCocoa::GHOST_SystemCocoa() 00546 { 00547 int mib[2]; 00548 struct timeval boottime; 00549 size_t len; 00550 char *rstring = NULL; 00551 00552 m_modifierMask =0; 00553 m_pressedMouseButtons =0; 00554 m_isGestureInProgress = false; 00555 m_cursorDelta_x=0; 00556 m_cursorDelta_y=0; 00557 m_outsideLoopEventProcessed = false; 00558 m_needDelayedApplicationBecomeActiveEventProcessing = false; 00559 m_displayManager = new GHOST_DisplayManagerCocoa (); 00560 GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n"); 00561 m_displayManager->initialize(); 00562 00563 //NSEvent timeStamp is given in system uptime, state start date is boot time 00564 mib[0] = CTL_KERN; 00565 mib[1] = KERN_BOOTTIME; 00566 len = sizeof(struct timeval); 00567 00568 sysctl(mib, 2, &boottime, &len, NULL, 0); 00569 m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000)); 00570 00571 //Detect multitouch trackpad 00572 mib[0] = CTL_HW; 00573 mib[1] = HW_MODEL; 00574 sysctl( mib, 2, NULL, &len, NULL, 0 ); 00575 rstring = (char*)malloc( len ); 00576 sysctl( mib, 2, rstring, &len, NULL, 0 ); 00577 00578 //Hack on MacBook revision, as multitouch avail. function missing 00579 if (strstr(rstring,"MacBookAir") || 00580 (strstr(rstring,"MacBook") && (rstring[strlen(rstring)-3]>='5') && (rstring[strlen(rstring)-3]<='9'))) 00581 m_hasMultiTouchTrackpad = true; 00582 else m_hasMultiTouchTrackpad = false; 00583 00584 free( rstring ); 00585 rstring = NULL; 00586 00587 m_ignoreWindowSizedMessages = false; 00588 } 00589 00590 GHOST_SystemCocoa::~GHOST_SystemCocoa() 00591 { 00592 } 00593 00594 00595 GHOST_TSuccess GHOST_SystemCocoa::init() 00596 { 00597 00598 GHOST_TSuccess success = GHOST_System::init(); 00599 if (success) { 00600 00601 #ifdef WITH_INPUT_NDOF 00602 m_ndofManager = new GHOST_NDOFManagerCocoa(*this); 00603 #endif 00604 00605 //ProcessSerialNumber psn; 00606 00607 //Carbon stuff to move window & menu to foreground 00608 /*if (!GetCurrentProcess(&psn)) { 00609 TransformProcessType(&psn, kProcessTransformToForegroundApplication); 00610 SetFrontProcess(&psn); 00611 }*/ 00612 00613 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00614 if (NSApp == nil) { 00615 [NSApplication sharedApplication]; 00616 00617 if ([NSApp mainMenu] == nil) { 00618 NSMenu *mainMenubar = [[NSMenu alloc] init]; 00619 NSMenuItem *menuItem; 00620 NSMenu *windowMenu; 00621 NSMenu *appMenu; 00622 00623 //Create the application menu 00624 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"]; 00625 00626 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; 00627 [appMenu addItem:[NSMenuItem separatorItem]]; 00628 00629 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"]; 00630 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; 00631 00632 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; 00633 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; 00634 00635 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; 00636 00637 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"]; 00638 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; 00639 00640 menuItem = [[NSMenuItem alloc] init]; 00641 [menuItem setSubmenu:appMenu]; 00642 00643 [mainMenubar addItem:menuItem]; 00644 [menuItem release]; 00645 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5 00646 [appMenu release]; 00647 00648 //Create the window menu 00649 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 00650 00651 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; 00652 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; 00653 00654 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; 00655 00656 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; 00657 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; 00658 00659 menuItem = [[NSMenuItem alloc] init]; 00660 [menuItem setSubmenu:windowMenu]; 00661 00662 [mainMenubar addItem:menuItem]; 00663 [menuItem release]; 00664 00665 [NSApp setMainMenu:mainMenubar]; 00666 [NSApp setWindowsMenu:windowMenu]; 00667 [windowMenu release]; 00668 } 00669 } 00670 if ([NSApp delegate] == nil) { 00671 CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init]; 00672 [appDelegate setSystemCocoa:this]; 00673 [NSApp setDelegate:appDelegate]; 00674 } 00675 00676 [NSApp finishLaunching]; 00677 00678 [pool drain]; 00679 } 00680 return success; 00681 } 00682 00683 00684 #pragma mark window management 00685 00686 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const 00687 { 00688 //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime]) 00689 struct timeval currentTime; 00690 00691 gettimeofday(¤tTime, NULL); 00692 00693 //Return timestamp of system uptime 00694 00695 return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time); 00696 } 00697 00698 00699 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const 00700 { 00701 //Note that OS X supports monitor hot plug 00702 // We do not support multiple monitors at the moment 00703 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00704 00705 GHOST_TUns8 count = [[NSScreen screens] count]; 00706 00707 [pool drain]; 00708 return count; 00709 } 00710 00711 00712 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const 00713 { 00714 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00715 //Get visible frame, that is frame excluding dock and top menu bar 00716 NSRect frame = [[NSScreen mainScreen] visibleFrame]; 00717 00718 //Returns max window contents (excluding title bar...) 00719 NSRect contentRect = [NSWindow contentRectForFrameRect:frame 00720 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)]; 00721 00722 width = contentRect.size.width; 00723 height = contentRect.size.height; 00724 00725 [pool drain]; 00726 } 00727 00728 00729 GHOST_IWindow* GHOST_SystemCocoa::createWindow( 00730 const STR_String& title, 00731 GHOST_TInt32 left, 00732 GHOST_TInt32 top, 00733 GHOST_TUns32 width, 00734 GHOST_TUns32 height, 00735 GHOST_TWindowState state, 00736 GHOST_TDrawingContextType type, 00737 bool stereoVisual, 00738 const GHOST_TUns16 numOfAASamples, 00739 const GHOST_TEmbedderWindowID parentWindow 00740 ) 00741 { 00742 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00743 GHOST_IWindow* window = 0; 00744 00745 //Get the available rect for including window contents 00746 NSRect frame = [[NSScreen mainScreen] visibleFrame]; 00747 NSRect contentRect = [NSWindow contentRectForFrameRect:frame 00748 styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)]; 00749 00750 GHOST_TInt32 bottom = (contentRect.size.height - 1) - height - top; 00751 00752 //Ensures window top left is inside this available rect 00753 left = left > contentRect.origin.x ? left : contentRect.origin.x; 00754 bottom = bottom > contentRect.origin.y ? bottom : contentRect.origin.y; 00755 00756 window = new GHOST_WindowCocoa (this, title, left, bottom, width, height, state, type, stereoVisual, numOfAASamples); 00757 00758 if (window) { 00759 if (window->getValid()) { 00760 // Store the pointer to the window 00761 GHOST_ASSERT(m_windowManager, "m_windowManager not initialized"); 00762 m_windowManager->addWindow(window); 00763 m_windowManager->setActiveWindow(window); 00764 //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation) 00765 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window)); 00766 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window)); 00767 00768 } 00769 else { 00770 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n"); 00771 delete window; 00772 window = 0; 00773 } 00774 } 00775 else { 00776 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n"); 00777 } 00778 [pool drain]; 00779 return window; 00780 } 00781 00785 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const 00786 { 00787 NSPoint mouseLoc = [NSEvent mouseLocation]; 00788 00789 // Returns the mouse location in screen coordinates 00790 x = (GHOST_TInt32)mouseLoc.x; 00791 y = (GHOST_TInt32)mouseLoc.y; 00792 return GHOST_kSuccess; 00793 } 00794 00798 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) 00799 { 00800 GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow(); 00801 if (!window) return GHOST_kFailure; 00802 00803 //Cursor and mouse dissociation placed here not to interfere with continuous grab 00804 // (in cont. grab setMouseCursorPosition is directly called) 00805 CGAssociateMouseAndMouseCursorPosition(false); 00806 setMouseCursorPosition(x, y); 00807 CGAssociateMouseAndMouseCursorPosition(true); 00808 00809 //Force mouse move event (not pushed by Cocoa) 00810 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x, y)); 00811 m_outsideLoopEventProcessed = true; 00812 00813 return GHOST_kSuccess; 00814 } 00815 00816 GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) 00817 { 00818 float xf=(float)x, yf=(float)y; 00819 GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow(); 00820 if (!window) return GHOST_kFailure; 00821 00822 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00823 NSScreen *windowScreen = window->getScreen(); 00824 NSRect screenRect = [windowScreen frame]; 00825 00826 //Set position relative to current screen 00827 xf -= screenRect.origin.x; 00828 yf -= screenRect.origin.y; 00829 00830 //Quartz Display Services uses the old coordinates (top left origin) 00831 yf = screenRect.size.height -yf; 00832 00833 CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf)); 00834 00835 [pool drain]; 00836 return GHOST_kSuccess; 00837 } 00838 00839 00840 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const 00841 { 00842 keys.set(GHOST_kModifierKeyOS, (m_modifierMask & NSCommandKeyMask) ? true : false); 00843 keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false); 00844 keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false); 00845 keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false); 00846 00847 return GHOST_kSuccess; 00848 } 00849 00850 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const 00851 { 00852 buttons.clear(); 00853 buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft); 00854 buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight); 00855 buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle); 00856 buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4); 00857 buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5); 00858 return GHOST_kSuccess; 00859 } 00860 00861 00862 00863 #pragma mark Event handlers 00864 00868 bool GHOST_SystemCocoa::processEvents(bool waitForEvent) 00869 { 00870 bool anyProcessed = false; 00871 NSEvent *event; 00872 00873 // SetMouseCoalescingEnabled(false, NULL); 00874 //TODO : implement timer ?? 00875 00876 /*do { 00877 GHOST_TimerManager* timerMgr = getTimerManager(); 00878 00879 if (waitForEvent) { 00880 GHOST_TUns64 next = timerMgr->nextFireTime(); 00881 double timeOut; 00882 00883 if (next == GHOST_kFireTimeNever) { 00884 timeOut = kEventDurationForever; 00885 } else { 00886 timeOut = (double)(next - getMilliSeconds())/1000.0; 00887 if (timeOut < 0.0) 00888 timeOut = 0.0; 00889 } 00890 00891 ::ReceiveNextEvent(0, NULL, timeOut, false, &event); 00892 } 00893 00894 if (timerMgr->fireTimers(getMilliSeconds())) { 00895 anyProcessed = true; 00896 }*/ 00897 00898 do { 00899 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 00900 event = [NSApp nextEventMatchingMask:NSAnyEventMask 00901 untilDate:[NSDate distantPast] 00902 inMode:NSDefaultRunLoopMode 00903 dequeue:YES]; 00904 if (event==nil) { 00905 [pool drain]; 00906 break; 00907 } 00908 00909 anyProcessed = true; 00910 00911 switch ([event type]) { 00912 case NSKeyDown: 00913 case NSKeyUp: 00914 case NSFlagsChanged: 00915 handleKeyEvent(event); 00916 00917 /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */ 00918 /* if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) { 00919 [NSApp sendEvent:event]; 00920 }*/ 00921 break; 00922 00923 case NSLeftMouseDown: 00924 case NSLeftMouseUp: 00925 case NSRightMouseDown: 00926 case NSRightMouseUp: 00927 case NSMouseMoved: 00928 case NSLeftMouseDragged: 00929 case NSRightMouseDragged: 00930 case NSScrollWheel: 00931 case NSOtherMouseDown: 00932 case NSOtherMouseUp: 00933 case NSOtherMouseDragged: 00934 case NSEventTypeMagnify: 00935 case NSEventTypeRotate: 00936 case NSEventTypeBeginGesture: 00937 case NSEventTypeEndGesture: 00938 handleMouseEvent(event); 00939 break; 00940 00941 case NSTabletPoint: 00942 case NSTabletProximity: 00943 handleTabletEvent(event,[event type]); 00944 break; 00945 00946 /* Trackpad features, fired only from OS X 10.5.2 00947 case NSEventTypeGesture: 00948 case NSEventTypeSwipe: 00949 break; */ 00950 00951 /*Unused events 00952 NSMouseEntered = 8, 00953 NSMouseExited = 9, 00954 NSAppKitDefined = 13, 00955 NSSystemDefined = 14, 00956 NSApplicationDefined = 15, 00957 NSPeriodic = 16, 00958 NSCursorUpdate = 17,*/ 00959 00960 default: 00961 break; 00962 } 00963 //Resend event to NSApp to ensure Mac wide events are handled 00964 [NSApp sendEvent:event]; 00965 [pool drain]; 00966 } while (event!= nil); 00967 //} while (waitForEvent && !anyProcessed); Needed only for timer implementation 00968 00969 if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent(); 00970 00971 if (m_outsideLoopEventProcessed) { 00972 m_outsideLoopEventProcessed = false; 00973 return true; 00974 } 00975 00976 m_ignoreWindowSizedMessages = false; 00977 00978 return anyProcessed; 00979 } 00980 00981 //Note: called from NSApplication delegate 00982 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent() 00983 { 00984 //Update the modifiers key mask, as its status may have changed when the application was not active 00985 //(that is when update events are sent to another application) 00986 unsigned int modifiers; 00987 GHOST_IWindow* window = m_windowManager->getActiveWindow(); 00988 00989 if (!window) { 00990 m_needDelayedApplicationBecomeActiveEventProcessing = true; 00991 return GHOST_kFailure; 00992 } 00993 else m_needDelayedApplicationBecomeActiveEventProcessing = false; 00994 00995 modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags]; 00996 00997 if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) { 00998 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) ); 00999 } 01000 if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) { 01001 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) ); 01002 } 01003 if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) { 01004 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) ); 01005 } 01006 if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) { 01007 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) ); 01008 } 01009 01010 m_modifierMask = modifiers; 01011 01012 m_outsideLoopEventProcessed = true; 01013 return GHOST_kSuccess; 01014 } 01015 01016 void GHOST_SystemCocoa::notifyExternalEventProcessed() 01017 { 01018 m_outsideLoopEventProcessed = true; 01019 } 01020 01021 //Note: called from NSWindow delegate 01022 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window) 01023 { 01024 if (!validWindow(window)) { 01025 return GHOST_kFailure; 01026 } 01027 switch(eventType) 01028 { 01029 case GHOST_kEventWindowClose: 01030 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) ); 01031 break; 01032 case GHOST_kEventWindowActivate: 01033 m_windowManager->setActiveWindow(window); 01034 window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); 01035 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) ); 01036 break; 01037 case GHOST_kEventWindowDeactivate: 01038 m_windowManager->setWindowInactive(window); 01039 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) ); 01040 break; 01041 case GHOST_kEventWindowUpdate: 01042 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) ); 01043 break; 01044 case GHOST_kEventWindowMove: 01045 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) ); 01046 break; 01047 case GHOST_kEventWindowSize: 01048 if (!m_ignoreWindowSizedMessages) 01049 { 01050 //Enforce only one resize message per event loop (coalescing all the live resize messages) 01051 window->updateDrawingContext(); 01052 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) ); 01053 //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager 01054 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0))); 01055 m_ignoreWindowSizedMessages = true; 01056 } 01057 break; 01058 default: 01059 return GHOST_kFailure; 01060 break; 01061 } 01062 01063 m_outsideLoopEventProcessed = true; 01064 return GHOST_kSuccess; 01065 } 01066 01067 //Note: called from NSWindow subclass 01068 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, 01069 GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data) 01070 { 01071 if (!validWindow(window)) { 01072 return GHOST_kFailure; 01073 } 01074 switch(eventType) 01075 { 01076 case GHOST_kEventDraggingEntered: 01077 case GHOST_kEventDraggingUpdated: 01078 case GHOST_kEventDraggingExited: 01079 pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL)); 01080 break; 01081 01082 case GHOST_kEventDraggingDropDone: 01083 { 01084 GHOST_TUns8 * temp_buff; 01085 GHOST_TStringArray *strArray; 01086 NSArray *droppedArray; 01087 size_t pastedTextSize; 01088 NSString *droppedStr; 01089 GHOST_TEventDataPtr eventData; 01090 int i; 01091 01092 if (!data) return GHOST_kFailure; 01093 01094 switch (draggedObjectType) { 01095 case GHOST_kDragnDropTypeFilenames: 01096 droppedArray = (NSArray*)data; 01097 01098 strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray)); 01099 if (!strArray) return GHOST_kFailure; 01100 01101 strArray->count = [droppedArray count]; 01102 if (strArray->count == 0) return GHOST_kFailure; 01103 01104 strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*)); 01105 01106 for (i=0;i<strArray->count;i++) 01107 { 01108 droppedStr = [droppedArray objectAtIndex:i]; 01109 01110 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01111 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01112 01113 if (!temp_buff) { 01114 strArray->count = i; 01115 break; 01116 } 01117 01118 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize); 01119 temp_buff[pastedTextSize] = '\0'; 01120 01121 strArray->strings[i] = temp_buff; 01122 } 01123 01124 eventData = (GHOST_TEventDataPtr) strArray; 01125 break; 01126 01127 case GHOST_kDragnDropTypeString: 01128 droppedStr = (NSString*)data; 01129 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01130 01131 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01132 01133 if (temp_buff == NULL) { 01134 return GHOST_kFailure; 01135 } 01136 01137 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize); 01138 01139 temp_buff[pastedTextSize] = '\0'; 01140 01141 eventData = (GHOST_TEventDataPtr) temp_buff; 01142 break; 01143 01144 case GHOST_kDragnDropTypeBitmap: 01145 { 01146 NSImage *droppedImg = (NSImage*)data; 01147 NSSize imgSize = [droppedImg size]; 01148 ImBuf *ibuf = NULL; 01149 GHOST_TUns8 *rasterRGB = NULL; 01150 GHOST_TUns8 *rasterRGBA = NULL; 01151 GHOST_TUns8 *toIBuf = NULL; 01152 int x, y, to_i, from_i; 01153 NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil; 01154 NSEnumerator *enumerator; 01155 NSImageRep *representation; 01156 01157 ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect); 01158 if (!ibuf) { 01159 [droppedImg release]; 01160 return GHOST_kFailure; 01161 } 01162 01163 /*Get the bitmap of the image*/ 01164 enumerator = [[droppedImg representations] objectEnumerator]; 01165 while ((representation = [enumerator nextObject])) { 01166 if ([representation isKindOfClass:[NSBitmapImageRep class]]) { 01167 bitmapImage = (NSBitmapImageRep *)representation; 01168 break; 01169 } 01170 } 01171 if (bitmapImage == nil) return GHOST_kFailure; 01172 01173 if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0) 01174 && ![bitmapImage isPlanar]) { 01175 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/ 01176 toIBuf = (GHOST_TUns8*)ibuf->rect; 01177 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData]; 01178 for (y = 0; y < imgSize.height; y++) { 01179 to_i = (imgSize.height-y-1)*imgSize.width; 01180 from_i = y*imgSize.width; 01181 memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width); 01182 } 01183 } 01184 else { 01185 /* Tell cocoa image resolution is same as current system one */ 01186 [bitmapImage setSize:imgSize]; 01187 01188 /* Convert the image in a RGBA 32bit format */ 01189 /* As Core Graphics does not support contextes with non premutliplied alpha, 01190 we need to get alpha key values in a separate batch */ 01191 01192 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */ 01193 blBitmapFormatImageRGB = /*RGB format padded to 32bits*/[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 01194 pixelsWide:imgSize.width 01195 pixelsHigh:imgSize.height 01196 bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO 01197 colorSpaceName:NSDeviceRGBColorSpace 01198 bitmapFormat:(NSBitmapFormat)0 01199 bytesPerRow:4*imgSize.width 01200 bitsPerPixel:32]; 01201 01202 [NSGraphicsContext saveGraphicsState]; 01203 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]]; 01204 [bitmapImage draw]; 01205 [NSGraphicsContext restoreGraphicsState]; 01206 01207 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData]; 01208 if (rasterRGB == NULL) { 01209 [bitmapImage release]; 01210 [blBitmapFormatImageRGB release]; 01211 [droppedImg release]; 01212 return GHOST_kFailure; 01213 } 01214 01215 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */ 01216 blBitmapFormatImageRGBA = /* RGBA */[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 01217 pixelsWide:imgSize.width 01218 pixelsHigh:imgSize.height 01219 bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO 01220 colorSpaceName:NSDeviceRGBColorSpace 01221 bitmapFormat:(NSBitmapFormat)0 01222 bytesPerRow:4*imgSize.width 01223 bitsPerPixel:32]; 01224 01225 [NSGraphicsContext saveGraphicsState]; 01226 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]]; 01227 [bitmapImage draw]; 01228 [NSGraphicsContext restoreGraphicsState]; 01229 01230 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData]; 01231 if (rasterRGBA == NULL) { 01232 [bitmapImage release]; 01233 [blBitmapFormatImageRGB release]; 01234 [blBitmapFormatImageRGBA release]; 01235 [droppedImg release]; 01236 return GHOST_kFailure; 01237 } 01238 01239 /*Copy the image to ibuf, flipping it vertically*/ 01240 toIBuf = (GHOST_TUns8*)ibuf->rect; 01241 for (y = 0; y < imgSize.height; y++) { 01242 for (x = 0; x < imgSize.width; x++) { 01243 to_i = (imgSize.height-y-1)*imgSize.width + x; 01244 from_i = y*imgSize.width + x; 01245 01246 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */ 01247 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */ 01248 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */ 01249 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */ 01250 } 01251 } 01252 01253 [blBitmapFormatImageRGB release]; 01254 [blBitmapFormatImageRGBA release]; 01255 [droppedImg release]; 01256 } 01257 01258 eventData = (GHOST_TEventDataPtr) ibuf; 01259 } 01260 break; 01261 01262 default: 01263 return GHOST_kFailure; 01264 break; 01265 } 01266 pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData)); 01267 } 01268 break; 01269 default: 01270 return GHOST_kFailure; 01271 } 01272 m_outsideLoopEventProcessed = true; 01273 return GHOST_kSuccess; 01274 } 01275 01276 01277 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest() 01278 { 01279 GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow(); 01280 01281 //Discard quit event if we are in cursor grab sequence 01282 if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal)) 01283 return GHOST_kExitCancel; 01284 01285 //Check open windows if some changes are not saved 01286 if (m_windowManager->getAnyModifiedState()) 01287 { 01288 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?", 01289 @"Cancel", @"Quit Anyway", nil); 01290 if (shouldQuit == NSAlertAlternateReturn) 01291 { 01292 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) ); 01293 return GHOST_kExitNow; 01294 } else { 01295 //Give back focus to the blender window if user selected cancel quit 01296 NSArray *windowsList = [NSApp orderedWindows]; 01297 if ([windowsList count]) { 01298 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil]; 01299 //Handle the modifiers keyes changed state issue 01300 //as recovering from the quit dialog is like application 01301 //gaining focus back. 01302 //Main issue fixed is Cmd modifier not being cleared 01303 handleApplicationBecomeActiveEvent(); 01304 } 01305 } 01306 01307 } 01308 else { 01309 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) ); 01310 m_outsideLoopEventProcessed = true; 01311 return GHOST_kExitNow; 01312 } 01313 01314 return GHOST_kExitCancel; 01315 } 01316 01317 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr) 01318 { 01319 NSString *filepath = (NSString*)filepathStr; 01320 int confirmOpen = NSAlertAlternateReturn; 01321 NSArray *windowsList; 01322 char * temp_buff; 01323 size_t filenameTextSize; 01324 GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow(); 01325 01326 if (!window) { 01327 return NO; 01328 } 01329 01330 //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised 01331 if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal)) 01332 return GHOST_kExitCancel; 01333 01334 //Check open windows if some changes are not saved 01335 if (m_windowManager->getAnyModifiedState()) 01336 { 01337 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]], 01338 @"Current document has not been saved.\nDo you really want to proceed?", 01339 @"Cancel", @"Open", nil); 01340 } 01341 01342 //Give back focus to the blender window 01343 windowsList = [NSApp orderedWindows]; 01344 if ([windowsList count]) { 01345 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil]; 01346 } 01347 01348 if (confirmOpen == NSAlertAlternateReturn) 01349 { 01350 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01351 01352 temp_buff = (char*) malloc(filenameTextSize+1); 01353 01354 if (temp_buff == NULL) { 01355 return GHOST_kFailure; 01356 } 01357 01358 strncpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize); 01359 01360 temp_buff[filenameTextSize] = '\0'; 01361 01362 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff)); 01363 01364 return YES; 01365 } 01366 else return NO; 01367 } 01368 01369 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType) 01370 { 01371 NSEvent *event = (NSEvent *)eventPtr; 01372 GHOST_IWindow* window; 01373 01374 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01375 if (!window) { 01376 //printf("\nW failure for event 0x%x",[event type]); 01377 return GHOST_kFailure; 01378 } 01379 01380 GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData(); 01381 01382 switch (eventType) { 01383 case NSTabletPoint: 01384 ct.Pressure = [event pressure]; 01385 ct.Xtilt = [event tilt].x; 01386 ct.Ytilt = [event tilt].y; 01387 break; 01388 01389 case NSTabletProximity: 01390 ct.Pressure = 0; 01391 ct.Xtilt = 0; 01392 ct.Ytilt = 0; 01393 if ([event isEnteringProximity]) 01394 { 01395 //pointer is entering tablet area proximity 01396 switch ([event pointingDeviceType]) { 01397 case NSPenPointingDevice: 01398 ct.Active = GHOST_kTabletModeStylus; 01399 break; 01400 case NSEraserPointingDevice: 01401 ct.Active = GHOST_kTabletModeEraser; 01402 break; 01403 case NSCursorPointingDevice: 01404 case NSUnknownPointingDevice: 01405 default: 01406 ct.Active = GHOST_kTabletModeNone; 01407 break; 01408 } 01409 } else { 01410 // pointer is leaving - return to mouse 01411 ct.Active = GHOST_kTabletModeNone; 01412 } 01413 break; 01414 01415 default: 01416 GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received"); 01417 return GHOST_kFailure; 01418 break; 01419 } 01420 return GHOST_kSuccess; 01421 } 01422 01423 01424 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) 01425 { 01426 NSEvent *event = (NSEvent *)eventPtr; 01427 GHOST_WindowCocoa* window; 01428 01429 window = (GHOST_WindowCocoa*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01430 if (!window) { 01431 //printf("\nW failure for event 0x%x",[event type]); 01432 return GHOST_kFailure; 01433 } 01434 01435 switch ([event type]) 01436 { 01437 case NSLeftMouseDown: 01438 case NSRightMouseDown: 01439 case NSOtherMouseDown: 01440 pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber]))); 01441 //Handle tablet events combined with mouse events 01442 switch ([event subtype]) { 01443 case NX_SUBTYPE_TABLET_POINT: 01444 handleTabletEvent(eventPtr, NSTabletPoint); 01445 break; 01446 case NX_SUBTYPE_TABLET_PROXIMITY: 01447 handleTabletEvent(eventPtr, NSTabletProximity); 01448 break; 01449 default: 01450 //No tablet event included : do nothing 01451 break; 01452 } 01453 break; 01454 01455 case NSLeftMouseUp: 01456 case NSRightMouseUp: 01457 case NSOtherMouseUp: 01458 pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber]))); 01459 //Handle tablet events combined with mouse events 01460 switch ([event subtype]) { 01461 case NX_SUBTYPE_TABLET_POINT: 01462 handleTabletEvent(eventPtr, NSTabletPoint); 01463 break; 01464 case NX_SUBTYPE_TABLET_PROXIMITY: 01465 handleTabletEvent(eventPtr, NSTabletProximity); 01466 break; 01467 default: 01468 //No tablet event included : do nothing 01469 break; 01470 } 01471 break; 01472 01473 case NSLeftMouseDragged: 01474 case NSRightMouseDragged: 01475 case NSOtherMouseDragged: 01476 //Handle tablet events combined with mouse events 01477 switch ([event subtype]) { 01478 case NX_SUBTYPE_TABLET_POINT: 01479 handleTabletEvent(eventPtr, NSTabletPoint); 01480 break; 01481 case NX_SUBTYPE_TABLET_PROXIMITY: 01482 handleTabletEvent(eventPtr, NSTabletProximity); 01483 break; 01484 default: 01485 //No tablet event included : do nothing 01486 break; 01487 } 01488 01489 case NSMouseMoved: 01490 switch (window->getCursorGrabMode()) { 01491 case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move 01492 { 01493 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum, x, y; 01494 01495 window->getCursorGrabInitPos(x_warp, y_warp); 01496 01497 window->getCursorGrabAccum(x_accum, y_accum); 01498 x_accum += [event deltaX]; 01499 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ... 01500 window->setCursorGrabAccum(x_accum, y_accum); 01501 01502 window->clientToScreenIntern(x_warp+x_accum, y_warp+y_accum, x, y); 01503 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01504 } 01505 break; 01506 case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries 01507 { 01508 NSPoint mousePos = [event locationInWindow]; 01509 GHOST_TInt32 x_mouse= mousePos.x; 01510 GHOST_TInt32 y_mouse= mousePos.y; 01511 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur, x, y; 01512 GHOST_Rect bounds, windowBounds, correctedBounds; 01513 01514 /* fallback to window bounds */ 01515 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure) 01516 window->getClientBounds(bounds); 01517 01518 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates 01519 window->getClientBounds(windowBounds); 01520 window->screenToClient(bounds.m_l, bounds.m_b, correctedBounds.m_l, correctedBounds.m_t); 01521 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b); 01522 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b; 01523 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t; 01524 01525 //Update accumulation counts 01526 window->getCursorGrabAccum(x_accum, y_accum); 01527 x_accum += [event deltaX]-m_cursorDelta_x; 01528 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ... 01529 window->setCursorGrabAccum(x_accum, y_accum); 01530 01531 01532 //Warp mouse cursor if needed 01533 x_mouse += [event deltaX]-m_cursorDelta_x; 01534 y_mouse += -[event deltaY]-m_cursorDelta_y; 01535 correctedBounds.wrapPoint(x_mouse, y_mouse, 2); 01536 01537 //Compensate for mouse moved event taking cursor position set into account 01538 m_cursorDelta_x = x_mouse-mousePos.x; 01539 m_cursorDelta_y = y_mouse-mousePos.y; 01540 01541 //Set new cursor position 01542 window->clientToScreenIntern(x_mouse, y_mouse, x_cur, y_cur); 01543 setMouseCursorPosition(x_cur, y_cur); /* wrap */ 01544 01545 //Post event 01546 window->getCursorGrabInitPos(x_cur, y_cur); 01547 window->clientToScreenIntern(x_cur + x_accum, y_cur + y_accum, x, y); 01548 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01549 } 01550 break; 01551 default: 01552 { 01553 //Normal cursor operation: send mouse position in window 01554 NSPoint mousePos = [event locationInWindow]; 01555 GHOST_TInt32 x, y; 01556 01557 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01558 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01559 01560 m_cursorDelta_x=0; 01561 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter 01562 } 01563 break; 01564 } 01565 break; 01566 01567 case NSScrollWheel: 01568 { 01569 /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */ 01570 if (!m_hasMultiTouchTrackpad || !m_isGestureInProgress) { 01571 GHOST_TInt32 delta; 01572 01573 double deltaF = [event deltaY]; 01574 01575 if (deltaF == 0.0) deltaF = [event deltaX]; // make blender decide if it's horizontal scroll 01576 if (deltaF == 0.0) break; //discard trackpad delta=0 events 01577 01578 delta = deltaF > 0.0 ? 1 : -1; 01579 pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta)); 01580 } 01581 else { 01582 NSPoint mousePos = [event locationInWindow]; 01583 GHOST_TInt32 x, y; 01584 double dx = [event deltaX]; 01585 double dy = -[event deltaY]; 01586 01587 const double deltaMax = 50.0; 01588 01589 if ((dx == 0) && (dy == 0)) break; 01590 01591 /* Quadratic acceleration */ 01592 dx = dx*(fabs(dx)+0.5); 01593 if (dx<0.0) dx-=0.5; else dx+=0.5; 01594 if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax; 01595 01596 dy = dy*(fabs(dy)+0.5); 01597 if (dy<0.0) dy-=0.5; else dy+=0.5; 01598 if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax; 01599 01600 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01601 dy = -dy; 01602 01603 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, x, y, dx, dy)); 01604 } 01605 } 01606 break; 01607 01608 case NSEventTypeMagnify: 01609 { 01610 NSPoint mousePos = [event locationInWindow]; 01611 GHOST_TInt32 x, y; 01612 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01613 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, x, y, 01614 [event magnification]*250.0 + 0.1, 0)); 01615 } 01616 break; 01617 01618 case NSEventTypeRotate: 01619 { 01620 NSPoint mousePos = [event locationInWindow]; 01621 GHOST_TInt32 x, y; 01622 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01623 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, x, y, 01624 -[event rotation] * 5.0, 0)); 01625 } 01626 case NSEventTypeBeginGesture: 01627 m_isGestureInProgress = true; 01628 break; 01629 case NSEventTypeEndGesture: 01630 m_isGestureInProgress = false; 01631 break; 01632 default: 01633 return GHOST_kFailure; 01634 break; 01635 } 01636 01637 return GHOST_kSuccess; 01638 } 01639 01640 01641 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) 01642 { 01643 NSEvent *event = (NSEvent *)eventPtr; 01644 GHOST_IWindow* window; 01645 unsigned int modifiers; 01646 NSString *characters; 01647 NSData *convertedCharacters; 01648 GHOST_TKey keyCode; 01649 unsigned char ascii; 01650 NSString* charsIgnoringModifiers; 01651 01652 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01653 if (!window) { 01654 //printf("\nW failure for event 0x%x",[event type]); 01655 return GHOST_kFailure; 01656 } 01657 01658 switch ([event type]) { 01659 case NSKeyDown: 01660 case NSKeyUp: 01661 charsIgnoringModifiers = [event charactersIgnoringModifiers]; 01662 if ([charsIgnoringModifiers length]>0) 01663 keyCode = convertKey([event keyCode], 01664 [charsIgnoringModifiers characterAtIndex:0], 01665 [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp); 01666 else 01667 keyCode = convertKey([event keyCode],0, 01668 [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp); 01669 01670 01671 characters = [event characters]; 01672 if ([characters length]>0) { //Check for dead keys 01673 //Convert characters to iso latin 1 encoding 01674 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding]; 01675 if ([convertedCharacters length]>0) 01676 ascii =((char*)[convertedCharacters bytes])[0]; 01677 else 01678 ascii = 0; //Character not available in iso latin 1 encoding 01679 } 01680 else 01681 ascii= 0; 01682 01683 if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask)) 01684 break; //Cmd-Q is directly handled by Cocoa 01685 01686 if ([event type] == NSKeyDown) { 01687 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) ); 01688 //printf("\nKey down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii); 01689 } else { 01690 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) ); 01691 //printf("\nKey up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii); 01692 } 01693 break; 01694 01695 case NSFlagsChanged: 01696 modifiers = [event modifierFlags]; 01697 01698 if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) { 01699 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) ); 01700 } 01701 if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) { 01702 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) ); 01703 } 01704 if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) { 01705 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) ); 01706 } 01707 if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) { 01708 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) ); 01709 } 01710 01711 m_modifierMask = modifiers; 01712 break; 01713 01714 default: 01715 return GHOST_kFailure; 01716 break; 01717 } 01718 01719 return GHOST_kSuccess; 01720 } 01721 01722 01723 01724 #pragma mark Clipboard get/set 01725 01726 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const 01727 { 01728 GHOST_TUns8 * temp_buff; 01729 size_t pastedTextSize; 01730 01731 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 01732 01733 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; 01734 01735 if (pasteBoard == nil) { 01736 [pool drain]; 01737 return NULL; 01738 } 01739 01740 NSArray *supportedTypes = 01741 [NSArray arrayWithObjects: NSStringPboardType, nil]; 01742 01743 NSString *bestType = [[NSPasteboard generalPasteboard] 01744 availableTypeFromArray:supportedTypes]; 01745 01746 if (bestType == nil) { 01747 [pool drain]; 01748 return NULL; 01749 } 01750 01751 NSString * textPasted = [pasteBoard stringForType:NSStringPboardType]; 01752 01753 if (textPasted == nil) { 01754 [pool drain]; 01755 return NULL; 01756 } 01757 01758 pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding]; 01759 01760 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01761 01762 if (temp_buff == NULL) { 01763 [pool drain]; 01764 return NULL; 01765 } 01766 01767 strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize); 01768 01769 temp_buff[pastedTextSize] = '\0'; 01770 01771 [pool drain]; 01772 01773 if(temp_buff) { 01774 return temp_buff; 01775 } else { 01776 return NULL; 01777 } 01778 } 01779 01780 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const 01781 { 01782 NSString *textToCopy; 01783 01784 if(selection) {return;} // for copying the selection, used on X11 01785 01786 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 01787 01788 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; 01789 01790 if (pasteBoard == nil) { 01791 [pool drain]; 01792 return; 01793 } 01794 01795 NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType]; 01796 01797 [pasteBoard declareTypes:supportedTypes owner:nil]; 01798 01799 textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding]; 01800 01801 [pasteBoard setString:textToCopy forType:NSStringPboardType]; 01802 01803 [pool drain]; 01804 } 01805