Blender V2.61 - r43446
|
00001 /* 00002 * ***** BEGIN GPL LICENSE BLOCK ***** 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00019 * All rights reserved. 00020 * 00021 * The Original Code is: all of this file. 00022 * 00023 * Contributors: Maarten Gribnau 05/2001 00024 * Damien Plisson 09/2009 00025 * 00026 * ***** END GPL LICENSE BLOCK ***** 00027 */ 00028 00029 #import <Cocoa/Cocoa.h> 00030 00031 /*For the currently not ported to Cocoa keyboard layout functions (64bit & 10.6 compatible)*/ 00032 #include <Carbon/Carbon.h> 00033 00034 #include <sys/time.h> 00035 #include <sys/types.h> 00036 #include <sys/sysctl.h> 00037 00038 #include "GHOST_SystemCocoa.h" 00039 00040 #include "GHOST_DisplayManagerCocoa.h" 00041 #include "GHOST_EventKey.h" 00042 #include "GHOST_EventButton.h" 00043 #include "GHOST_EventCursor.h" 00044 #include "GHOST_EventWheel.h" 00045 #include "GHOST_EventTrackpad.h" 00046 #include "GHOST_EventDragnDrop.h" 00047 #include "GHOST_EventString.h" 00048 #include "GHOST_TimerManager.h" 00049 #include "GHOST_TimerTask.h" 00050 #include "GHOST_WindowManager.h" 00051 #include "GHOST_WindowCocoa.h" 00052 #ifdef WITH_INPUT_NDOF 00053 #include "GHOST_NDOFManagerCocoa.h" 00054 #endif 00055 00056 #include "AssertMacros.h" 00057 00058 #pragma mark KeyMap, mouse converters 00059 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00060 /* Keycodes not defined in Tiger */ 00061 /* 00062 * Summary: 00063 * Virtual keycodes 00064 * 00065 * Discussion: 00066 * These constants are the virtual keycodes defined originally in 00067 * Inside Mac Volume V, pg. V-191. They identify physical keys on a 00068 * keyboard. Those constants with "ANSI" in the name are labeled 00069 * according to the key position on an ANSI-standard US keyboard. 00070 * For example, kVK_ANSI_A indicates the virtual keycode for the key 00071 * with the letter 'A' in the US keyboard layout. Other keyboard 00072 * layouts may have the 'A' key label on a different physical key; 00073 * in this case, pressing 'A' will generate a different virtual 00074 * keycode. 00075 */ 00076 enum { 00077 kVK_ANSI_A = 0x00, 00078 kVK_ANSI_S = 0x01, 00079 kVK_ANSI_D = 0x02, 00080 kVK_ANSI_F = 0x03, 00081 kVK_ANSI_H = 0x04, 00082 kVK_ANSI_G = 0x05, 00083 kVK_ANSI_Z = 0x06, 00084 kVK_ANSI_X = 0x07, 00085 kVK_ANSI_C = 0x08, 00086 kVK_ANSI_V = 0x09, 00087 kVK_ANSI_B = 0x0B, 00088 kVK_ANSI_Q = 0x0C, 00089 kVK_ANSI_W = 0x0D, 00090 kVK_ANSI_E = 0x0E, 00091 kVK_ANSI_R = 0x0F, 00092 kVK_ANSI_Y = 0x10, 00093 kVK_ANSI_T = 0x11, 00094 kVK_ANSI_1 = 0x12, 00095 kVK_ANSI_2 = 0x13, 00096 kVK_ANSI_3 = 0x14, 00097 kVK_ANSI_4 = 0x15, 00098 kVK_ANSI_6 = 0x16, 00099 kVK_ANSI_5 = 0x17, 00100 kVK_ANSI_Equal = 0x18, 00101 kVK_ANSI_9 = 0x19, 00102 kVK_ANSI_7 = 0x1A, 00103 kVK_ANSI_Minus = 0x1B, 00104 kVK_ANSI_8 = 0x1C, 00105 kVK_ANSI_0 = 0x1D, 00106 kVK_ANSI_RightBracket = 0x1E, 00107 kVK_ANSI_O = 0x1F, 00108 kVK_ANSI_U = 0x20, 00109 kVK_ANSI_LeftBracket = 0x21, 00110 kVK_ANSI_I = 0x22, 00111 kVK_ANSI_P = 0x23, 00112 kVK_ANSI_L = 0x25, 00113 kVK_ANSI_J = 0x26, 00114 kVK_ANSI_Quote = 0x27, 00115 kVK_ANSI_K = 0x28, 00116 kVK_ANSI_Semicolon = 0x29, 00117 kVK_ANSI_Backslash = 0x2A, 00118 kVK_ANSI_Comma = 0x2B, 00119 kVK_ANSI_Slash = 0x2C, 00120 kVK_ANSI_N = 0x2D, 00121 kVK_ANSI_M = 0x2E, 00122 kVK_ANSI_Period = 0x2F, 00123 kVK_ANSI_Grave = 0x32, 00124 kVK_ANSI_KeypadDecimal = 0x41, 00125 kVK_ANSI_KeypadMultiply = 0x43, 00126 kVK_ANSI_KeypadPlus = 0x45, 00127 kVK_ANSI_KeypadClear = 0x47, 00128 kVK_ANSI_KeypadDivide = 0x4B, 00129 kVK_ANSI_KeypadEnter = 0x4C, 00130 kVK_ANSI_KeypadMinus = 0x4E, 00131 kVK_ANSI_KeypadEquals = 0x51, 00132 kVK_ANSI_Keypad0 = 0x52, 00133 kVK_ANSI_Keypad1 = 0x53, 00134 kVK_ANSI_Keypad2 = 0x54, 00135 kVK_ANSI_Keypad3 = 0x55, 00136 kVK_ANSI_Keypad4 = 0x56, 00137 kVK_ANSI_Keypad5 = 0x57, 00138 kVK_ANSI_Keypad6 = 0x58, 00139 kVK_ANSI_Keypad7 = 0x59, 00140 kVK_ANSI_Keypad8 = 0x5B, 00141 kVK_ANSI_Keypad9 = 0x5C 00142 }; 00143 00144 /* keycodes for keys that are independent of keyboard layout*/ 00145 enum { 00146 kVK_Return = 0x24, 00147 kVK_Tab = 0x30, 00148 kVK_Space = 0x31, 00149 kVK_Delete = 0x33, 00150 kVK_Escape = 0x35, 00151 kVK_Command = 0x37, 00152 kVK_Shift = 0x38, 00153 kVK_CapsLock = 0x39, 00154 kVK_Option = 0x3A, 00155 kVK_Control = 0x3B, 00156 kVK_RightShift = 0x3C, 00157 kVK_RightOption = 0x3D, 00158 kVK_RightControl = 0x3E, 00159 kVK_Function = 0x3F, 00160 kVK_F17 = 0x40, 00161 kVK_VolumeUp = 0x48, 00162 kVK_VolumeDown = 0x49, 00163 kVK_Mute = 0x4A, 00164 kVK_F18 = 0x4F, 00165 kVK_F19 = 0x50, 00166 kVK_F20 = 0x5A, 00167 kVK_F5 = 0x60, 00168 kVK_F6 = 0x61, 00169 kVK_F7 = 0x62, 00170 kVK_F3 = 0x63, 00171 kVK_F8 = 0x64, 00172 kVK_F9 = 0x65, 00173 kVK_F11 = 0x67, 00174 kVK_F13 = 0x69, 00175 kVK_F16 = 0x6A, 00176 kVK_F14 = 0x6B, 00177 kVK_F10 = 0x6D, 00178 kVK_F12 = 0x6F, 00179 kVK_F15 = 0x71, 00180 kVK_Help = 0x72, 00181 kVK_Home = 0x73, 00182 kVK_PageUp = 0x74, 00183 kVK_ForwardDelete = 0x75, 00184 kVK_F4 = 0x76, 00185 kVK_End = 0x77, 00186 kVK_F2 = 0x78, 00187 kVK_PageDown = 0x79, 00188 kVK_F1 = 0x7A, 00189 kVK_LeftArrow = 0x7B, 00190 kVK_RightArrow = 0x7C, 00191 kVK_DownArrow = 0x7D, 00192 kVK_UpArrow = 0x7E 00193 }; 00194 00195 /* ISO keyboards only*/ 00196 enum { 00197 kVK_ISO_Section = 0x0A 00198 }; 00199 00200 /* JIS keyboards only*/ 00201 enum { 00202 kVK_JIS_Yen = 0x5D, 00203 kVK_JIS_Underscore = 0x5E, 00204 kVK_JIS_KeypadComma = 0x5F, 00205 kVK_JIS_Eisu = 0x66, 00206 kVK_JIS_Kana = 0x68 00207 }; 00208 #endif 00209 00210 static GHOST_TButtonMask convertButton(int button) 00211 { 00212 switch (button) { 00213 case 0: 00214 return GHOST_kButtonMaskLeft; 00215 case 1: 00216 return GHOST_kButtonMaskRight; 00217 case 2: 00218 return GHOST_kButtonMaskMiddle; 00219 case 3: 00220 return GHOST_kButtonMaskButton4; 00221 case 4: 00222 return GHOST_kButtonMaskButton5; 00223 default: 00224 return GHOST_kButtonMaskLeft; 00225 } 00226 } 00227 00235 static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) 00236 { 00237 00238 //printf("\nrecvchar %c 0x%x",recvChar,recvChar); 00239 switch (rawCode) { 00240 /*Physical keycodes not used due to map changes in int'l keyboards 00241 case kVK_ANSI_A: return GHOST_kKeyA; 00242 case kVK_ANSI_B: return GHOST_kKeyB; 00243 case kVK_ANSI_C: return GHOST_kKeyC; 00244 case kVK_ANSI_D: return GHOST_kKeyD; 00245 case kVK_ANSI_E: return GHOST_kKeyE; 00246 case kVK_ANSI_F: return GHOST_kKeyF; 00247 case kVK_ANSI_G: return GHOST_kKeyG; 00248 case kVK_ANSI_H: return GHOST_kKeyH; 00249 case kVK_ANSI_I: return GHOST_kKeyI; 00250 case kVK_ANSI_J: return GHOST_kKeyJ; 00251 case kVK_ANSI_K: return GHOST_kKeyK; 00252 case kVK_ANSI_L: return GHOST_kKeyL; 00253 case kVK_ANSI_M: return GHOST_kKeyM; 00254 case kVK_ANSI_N: return GHOST_kKeyN; 00255 case kVK_ANSI_O: return GHOST_kKeyO; 00256 case kVK_ANSI_P: return GHOST_kKeyP; 00257 case kVK_ANSI_Q: return GHOST_kKeyQ; 00258 case kVK_ANSI_R: return GHOST_kKeyR; 00259 case kVK_ANSI_S: return GHOST_kKeyS; 00260 case kVK_ANSI_T: return GHOST_kKeyT; 00261 case kVK_ANSI_U: return GHOST_kKeyU; 00262 case kVK_ANSI_V: return GHOST_kKeyV; 00263 case kVK_ANSI_W: return GHOST_kKeyW; 00264 case kVK_ANSI_X: return GHOST_kKeyX; 00265 case kVK_ANSI_Y: return GHOST_kKeyY; 00266 case kVK_ANSI_Z: return GHOST_kKeyZ;*/ 00267 00268 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/ 00269 case kVK_ISO_Section: return GHOST_kKeyUnknown; 00270 case kVK_ANSI_1: return GHOST_kKey1; 00271 case kVK_ANSI_2: return GHOST_kKey2; 00272 case kVK_ANSI_3: return GHOST_kKey3; 00273 case kVK_ANSI_4: return GHOST_kKey4; 00274 case kVK_ANSI_5: return GHOST_kKey5; 00275 case kVK_ANSI_6: return GHOST_kKey6; 00276 case kVK_ANSI_7: return GHOST_kKey7; 00277 case kVK_ANSI_8: return GHOST_kKey8; 00278 case kVK_ANSI_9: return GHOST_kKey9; 00279 case kVK_ANSI_0: return GHOST_kKey0; 00280 00281 case kVK_ANSI_Keypad0: return GHOST_kKeyNumpad0; 00282 case kVK_ANSI_Keypad1: return GHOST_kKeyNumpad1; 00283 case kVK_ANSI_Keypad2: return GHOST_kKeyNumpad2; 00284 case kVK_ANSI_Keypad3: return GHOST_kKeyNumpad3; 00285 case kVK_ANSI_Keypad4: return GHOST_kKeyNumpad4; 00286 case kVK_ANSI_Keypad5: return GHOST_kKeyNumpad5; 00287 case kVK_ANSI_Keypad6: return GHOST_kKeyNumpad6; 00288 case kVK_ANSI_Keypad7: return GHOST_kKeyNumpad7; 00289 case kVK_ANSI_Keypad8: return GHOST_kKeyNumpad8; 00290 case kVK_ANSI_Keypad9: return GHOST_kKeyNumpad9; 00291 case kVK_ANSI_KeypadDecimal: return GHOST_kKeyNumpadPeriod; 00292 case kVK_ANSI_KeypadEnter: return GHOST_kKeyNumpadEnter; 00293 case kVK_ANSI_KeypadPlus: return GHOST_kKeyNumpadPlus; 00294 case kVK_ANSI_KeypadMinus: return GHOST_kKeyNumpadMinus; 00295 case kVK_ANSI_KeypadMultiply: return GHOST_kKeyNumpadAsterisk; 00296 case kVK_ANSI_KeypadDivide: return GHOST_kKeyNumpadSlash; 00297 case kVK_ANSI_KeypadClear: return GHOST_kKeyUnknown; 00298 00299 case kVK_F1: return GHOST_kKeyF1; 00300 case kVK_F2: return GHOST_kKeyF2; 00301 case kVK_F3: return GHOST_kKeyF3; 00302 case kVK_F4: return GHOST_kKeyF4; 00303 case kVK_F5: return GHOST_kKeyF5; 00304 case kVK_F6: return GHOST_kKeyF6; 00305 case kVK_F7: return GHOST_kKeyF7; 00306 case kVK_F8: return GHOST_kKeyF8; 00307 case kVK_F9: return GHOST_kKeyF9; 00308 case kVK_F10: return GHOST_kKeyF10; 00309 case kVK_F11: return GHOST_kKeyF11; 00310 case kVK_F12: return GHOST_kKeyF12; 00311 case kVK_F13: return GHOST_kKeyF13; 00312 case kVK_F14: return GHOST_kKeyF14; 00313 case kVK_F15: return GHOST_kKeyF15; 00314 case kVK_F16: return GHOST_kKeyF16; 00315 case kVK_F17: return GHOST_kKeyF17; 00316 case kVK_F18: return GHOST_kKeyF18; 00317 case kVK_F19: return GHOST_kKeyF19; 00318 case kVK_F20: return GHOST_kKeyF20; 00319 00320 case kVK_UpArrow: return GHOST_kKeyUpArrow; 00321 case kVK_DownArrow: return GHOST_kKeyDownArrow; 00322 case kVK_LeftArrow: return GHOST_kKeyLeftArrow; 00323 case kVK_RightArrow: return GHOST_kKeyRightArrow; 00324 00325 case kVK_Return: return GHOST_kKeyEnter; 00326 case kVK_Delete: return GHOST_kKeyBackSpace; 00327 case kVK_ForwardDelete: return GHOST_kKeyDelete; 00328 case kVK_Escape: return GHOST_kKeyEsc; 00329 case kVK_Tab: return GHOST_kKeyTab; 00330 case kVK_Space: return GHOST_kKeySpace; 00331 00332 case kVK_Home: return GHOST_kKeyHome; 00333 case kVK_End: return GHOST_kKeyEnd; 00334 case kVK_PageUp: return GHOST_kKeyUpPage; 00335 case kVK_PageDown: return GHOST_kKeyDownPage; 00336 00337 /*case kVK_ANSI_Minus: return GHOST_kKeyMinus; 00338 case kVK_ANSI_Equal: return GHOST_kKeyEqual; 00339 case kVK_ANSI_Comma: return GHOST_kKeyComma; 00340 case kVK_ANSI_Period: return GHOST_kKeyPeriod; 00341 case kVK_ANSI_Slash: return GHOST_kKeySlash; 00342 case kVK_ANSI_Semicolon: return GHOST_kKeySemicolon; 00343 case kVK_ANSI_Quote: return GHOST_kKeyQuote; 00344 case kVK_ANSI_Backslash: return GHOST_kKeyBackslash; 00345 case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket; 00346 case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket; 00347 case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave;*/ 00348 00349 case kVK_VolumeUp: 00350 case kVK_VolumeDown: 00351 case kVK_Mute: 00352 return GHOST_kKeyUnknown; 00353 00354 default: 00355 /* alphanumerical or punctuation key that is remappable in int'l keyboards */ 00356 if ((recvChar >= 'A') && (recvChar <= 'Z')) { 00357 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA); 00358 } else if ((recvChar >= 'a') && (recvChar <= 'z')) { 00359 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA); 00360 } else { 00361 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00362 KeyboardLayoutRef keyLayout; 00363 UCKeyboardLayout *uchrData; 00364 00365 KLGetCurrentKeyboardLayout(&keyLayout); 00366 KLGetKeyboardLayoutProperty(keyLayout, kKLuchrData, (const void **) 00367 &uchrData); 00368 /*get actual character value of the "remappable" keys in int'l keyboards, 00369 if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), 00370 then fallback on using the received charactersIgnoringModifiers */ 00371 if (uchrData) 00372 { 00373 UInt32 deadKeyState=0; 00374 UniCharCount actualStrLength=0; 00375 00376 UCKeyTranslate(uchrData, rawCode, keyAction, 0, 00377 LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); 00378 00379 } 00380 #else 00381 /* Leopard and Snow Leopard 64bit compatible API*/ 00382 CFDataRef uchrHandle; /*the keyboard layout*/ 00383 TISInputSourceRef kbdTISHandle; 00384 00385 kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource(); 00386 uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData); 00387 CFRelease(kbdTISHandle); 00388 00389 /*get actual character value of the "remappable" keys in int'l keyboards, 00390 if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), 00391 then fallback on using the received charactersIgnoringModifiers */ 00392 if (uchrHandle) 00393 { 00394 UInt32 deadKeyState=0; 00395 UniCharCount actualStrLength=0; 00396 00397 UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0, 00398 LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); 00399 00400 } 00401 #endif 00402 switch (recvChar) { 00403 case '-': return GHOST_kKeyMinus; 00404 case '=': return GHOST_kKeyEqual; 00405 case ',': return GHOST_kKeyComma; 00406 case '.': return GHOST_kKeyPeriod; 00407 case '/': return GHOST_kKeySlash; 00408 case ';': return GHOST_kKeySemicolon; 00409 case '\'': return GHOST_kKeyQuote; 00410 case '\\': return GHOST_kKeyBackslash; 00411 case '[': return GHOST_kKeyLeftBracket; 00412 case ']': return GHOST_kKeyRightBracket; 00413 case '`': return GHOST_kKeyAccentGrave; 00414 default: 00415 return GHOST_kKeyUnknown; 00416 } 00417 } 00418 } 00419 return GHOST_kKeyUnknown; 00420 } 00421 00422 00423 #pragma mark defines for 10.6 api not documented in 10.5 00424 #ifndef MAC_OS_X_VERSION_10_6 00425 enum { 00426 /* The following event types are available on some hardware on 10.5.2 and later */ 00427 NSEventTypeGesture = 29, 00428 NSEventTypeMagnify = 30, 00429 NSEventTypeSwipe = 31, 00430 NSEventTypeRotate = 18, 00431 NSEventTypeBeginGesture = 19, 00432 NSEventTypeEndGesture = 20 00433 }; 00434 00435 @interface NSEvent(GestureEvents) 00436 /* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */ 00437 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 00438 - (float)magnification; // change in magnification. 00439 #else 00440 - (CGFloat)magnification; // change in magnification. 00441 #endif 00442 @end 00443 00444 #endif 00445 00446 00447 #pragma mark Utility functions 00448 00449 #define FIRSTFILEBUFLG 512 00450 static bool g_hasFirstFile = false; 00451 static char g_firstFileBuf[512]; 00452 00453 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true 00454 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) 00455 { 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 00899 do { 00900 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 00901 event = [NSApp nextEventMatchingMask:NSAnyEventMask 00902 untilDate:[NSDate distantPast] 00903 inMode:NSDefaultRunLoopMode 00904 dequeue:YES]; 00905 if (event==nil) { 00906 [pool drain]; 00907 break; 00908 } 00909 00910 anyProcessed = true; 00911 00912 switch ([event type]) { 00913 case NSKeyDown: 00914 case NSKeyUp: 00915 case NSFlagsChanged: 00916 handleKeyEvent(event); 00917 00918 /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */ 00919 /* if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) { 00920 [NSApp sendEvent:event]; 00921 }*/ 00922 break; 00923 00924 case NSLeftMouseDown: 00925 case NSLeftMouseUp: 00926 case NSRightMouseDown: 00927 case NSRightMouseUp: 00928 case NSMouseMoved: 00929 case NSLeftMouseDragged: 00930 case NSRightMouseDragged: 00931 case NSScrollWheel: 00932 case NSOtherMouseDown: 00933 case NSOtherMouseUp: 00934 case NSOtherMouseDragged: 00935 case NSEventTypeMagnify: 00936 case NSEventTypeRotate: 00937 case NSEventTypeBeginGesture: 00938 case NSEventTypeEndGesture: 00939 handleMouseEvent(event); 00940 break; 00941 00942 case NSTabletPoint: 00943 case NSTabletProximity: 00944 handleTabletEvent(event,[event type]); 00945 break; 00946 00947 /* Trackpad features, fired only from OS X 10.5.2 00948 case NSEventTypeGesture: 00949 case NSEventTypeSwipe: 00950 break; */ 00951 00952 /*Unused events 00953 NSMouseEntered = 8, 00954 NSMouseExited = 9, 00955 NSAppKitDefined = 13, 00956 NSSystemDefined = 14, 00957 NSApplicationDefined = 15, 00958 NSPeriodic = 16, 00959 NSCursorUpdate = 17,*/ 00960 00961 default: 00962 break; 00963 } 00964 //Resend event to NSApp to ensure Mac wide events are handled 00965 [NSApp sendEvent:event]; 00966 [pool drain]; 00967 } while (event!= nil); 00968 //} while (waitForEvent && !anyProcessed); Needed only for timer implementation 00969 00970 if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent(); 00971 00972 if (m_outsideLoopEventProcessed) { 00973 m_outsideLoopEventProcessed = false; 00974 return true; 00975 } 00976 00977 m_ignoreWindowSizedMessages = false; 00978 00979 return anyProcessed; 00980 } 00981 00982 //Note: called from NSApplication delegate 00983 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent() 00984 { 00985 //Update the modifiers key mask, as its status may have changed when the application was not active 00986 //(that is when update events are sent to another application) 00987 unsigned int modifiers; 00988 GHOST_IWindow* window = m_windowManager->getActiveWindow(); 00989 00990 if (!window) { 00991 m_needDelayedApplicationBecomeActiveEventProcessing = true; 00992 return GHOST_kFailure; 00993 } 00994 else m_needDelayedApplicationBecomeActiveEventProcessing = false; 00995 00996 modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags]; 00997 00998 if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) { 00999 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) ); 01000 } 01001 if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) { 01002 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) ); 01003 } 01004 if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) { 01005 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) ); 01006 } 01007 if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) { 01008 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) ); 01009 } 01010 01011 m_modifierMask = modifiers; 01012 01013 m_outsideLoopEventProcessed = true; 01014 return GHOST_kSuccess; 01015 } 01016 01017 void GHOST_SystemCocoa::notifyExternalEventProcessed() 01018 { 01019 m_outsideLoopEventProcessed = true; 01020 } 01021 01022 //Note: called from NSWindow delegate 01023 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window) 01024 { 01025 if (!validWindow(window)) { 01026 return GHOST_kFailure; 01027 } 01028 switch(eventType) 01029 { 01030 case GHOST_kEventWindowClose: 01031 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) ); 01032 break; 01033 case GHOST_kEventWindowActivate: 01034 m_windowManager->setActiveWindow(window); 01035 window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); 01036 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) ); 01037 break; 01038 case GHOST_kEventWindowDeactivate: 01039 m_windowManager->setWindowInactive(window); 01040 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) ); 01041 break; 01042 case GHOST_kEventWindowUpdate: 01043 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) ); 01044 break; 01045 case GHOST_kEventWindowMove: 01046 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) ); 01047 break; 01048 case GHOST_kEventWindowSize: 01049 if (!m_ignoreWindowSizedMessages) 01050 { 01051 //Enforce only one resize message per event loop (coalescing all the live resize messages) 01052 window->updateDrawingContext(); 01053 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) ); 01054 //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager 01055 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0))); 01056 m_ignoreWindowSizedMessages = true; 01057 } 01058 break; 01059 default: 01060 return GHOST_kFailure; 01061 break; 01062 } 01063 01064 m_outsideLoopEventProcessed = true; 01065 return GHOST_kSuccess; 01066 } 01067 01068 //Note: called from NSWindow subclass 01069 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, 01070 GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data) 01071 { 01072 if (!validWindow(window)) { 01073 return GHOST_kFailure; 01074 } 01075 switch(eventType) 01076 { 01077 case GHOST_kEventDraggingEntered: 01078 case GHOST_kEventDraggingUpdated: 01079 case GHOST_kEventDraggingExited: 01080 pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL)); 01081 break; 01082 01083 case GHOST_kEventDraggingDropDone: 01084 { 01085 GHOST_TUns8 * temp_buff; 01086 GHOST_TStringArray *strArray; 01087 NSArray *droppedArray; 01088 size_t pastedTextSize; 01089 NSString *droppedStr; 01090 GHOST_TEventDataPtr eventData; 01091 int i; 01092 01093 if (!data) return GHOST_kFailure; 01094 01095 switch (draggedObjectType) { 01096 case GHOST_kDragnDropTypeFilenames: 01097 droppedArray = (NSArray*)data; 01098 01099 strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray)); 01100 if (!strArray) return GHOST_kFailure; 01101 01102 strArray->count = [droppedArray count]; 01103 if (strArray->count == 0) return GHOST_kFailure; 01104 01105 strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*)); 01106 01107 for (i=0;i<strArray->count;i++) 01108 { 01109 droppedStr = [droppedArray objectAtIndex:i]; 01110 01111 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01112 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01113 01114 if (!temp_buff) { 01115 strArray->count = i; 01116 break; 01117 } 01118 01119 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize); 01120 temp_buff[pastedTextSize] = '\0'; 01121 01122 strArray->strings[i] = temp_buff; 01123 } 01124 01125 eventData = (GHOST_TEventDataPtr) strArray; 01126 break; 01127 01128 case GHOST_kDragnDropTypeString: 01129 droppedStr = (NSString*)data; 01130 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01131 01132 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01133 01134 if (temp_buff == NULL) { 01135 return GHOST_kFailure; 01136 } 01137 01138 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize); 01139 01140 temp_buff[pastedTextSize] = '\0'; 01141 01142 eventData = (GHOST_TEventDataPtr) temp_buff; 01143 break; 01144 01145 case GHOST_kDragnDropTypeBitmap: 01146 { 01147 NSImage *droppedImg = (NSImage*)data; 01148 NSSize imgSize = [droppedImg size]; 01149 ImBuf *ibuf = NULL; 01150 GHOST_TUns8 *rasterRGB = NULL; 01151 GHOST_TUns8 *rasterRGBA = NULL; 01152 GHOST_TUns8 *toIBuf = NULL; 01153 int x, y, to_i, from_i; 01154 NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil; 01155 NSEnumerator *enumerator; 01156 NSImageRep *representation; 01157 01158 ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect); 01159 if (!ibuf) { 01160 [droppedImg release]; 01161 return GHOST_kFailure; 01162 } 01163 01164 /*Get the bitmap of the image*/ 01165 enumerator = [[droppedImg representations] objectEnumerator]; 01166 while ((representation = [enumerator nextObject])) { 01167 if ([representation isKindOfClass:[NSBitmapImageRep class]]) { 01168 bitmapImage = (NSBitmapImageRep *)representation; 01169 break; 01170 } 01171 } 01172 if (bitmapImage == nil) return GHOST_kFailure; 01173 01174 if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0) 01175 && ![bitmapImage isPlanar]) { 01176 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/ 01177 toIBuf = (GHOST_TUns8*)ibuf->rect; 01178 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData]; 01179 for (y = 0; y < imgSize.height; y++) { 01180 to_i = (imgSize.height-y-1)*imgSize.width; 01181 from_i = y*imgSize.width; 01182 memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width); 01183 } 01184 } 01185 else { 01186 /* Tell cocoa image resolution is same as current system one */ 01187 [bitmapImage setSize:imgSize]; 01188 01189 /* Convert the image in a RGBA 32bit format */ 01190 /* As Core Graphics does not support contextes with non premutliplied alpha, 01191 we need to get alpha key values in a separate batch */ 01192 01193 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */ 01194 blBitmapFormatImageRGB = /*RGB format padded to 32bits*/[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 01195 pixelsWide:imgSize.width 01196 pixelsHigh:imgSize.height 01197 bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO 01198 colorSpaceName:NSDeviceRGBColorSpace 01199 bitmapFormat:(NSBitmapFormat)0 01200 bytesPerRow:4*imgSize.width 01201 bitsPerPixel:32]; 01202 01203 [NSGraphicsContext saveGraphicsState]; 01204 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]]; 01205 [bitmapImage draw]; 01206 [NSGraphicsContext restoreGraphicsState]; 01207 01208 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData]; 01209 if (rasterRGB == NULL) { 01210 [bitmapImage release]; 01211 [blBitmapFormatImageRGB release]; 01212 [droppedImg release]; 01213 return GHOST_kFailure; 01214 } 01215 01216 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */ 01217 blBitmapFormatImageRGBA = /* RGBA */[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 01218 pixelsWide:imgSize.width 01219 pixelsHigh:imgSize.height 01220 bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO 01221 colorSpaceName:NSDeviceRGBColorSpace 01222 bitmapFormat:(NSBitmapFormat)0 01223 bytesPerRow:4*imgSize.width 01224 bitsPerPixel:32]; 01225 01226 [NSGraphicsContext saveGraphicsState]; 01227 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]]; 01228 [bitmapImage draw]; 01229 [NSGraphicsContext restoreGraphicsState]; 01230 01231 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData]; 01232 if (rasterRGBA == NULL) { 01233 [bitmapImage release]; 01234 [blBitmapFormatImageRGB release]; 01235 [blBitmapFormatImageRGBA release]; 01236 [droppedImg release]; 01237 return GHOST_kFailure; 01238 } 01239 01240 /*Copy the image to ibuf, flipping it vertically*/ 01241 toIBuf = (GHOST_TUns8*)ibuf->rect; 01242 for (y = 0; y < imgSize.height; y++) { 01243 for (x = 0; x < imgSize.width; x++) { 01244 to_i = (imgSize.height-y-1)*imgSize.width + x; 01245 from_i = y*imgSize.width + x; 01246 01247 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */ 01248 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */ 01249 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */ 01250 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */ 01251 } 01252 } 01253 01254 [blBitmapFormatImageRGB release]; 01255 [blBitmapFormatImageRGBA release]; 01256 [droppedImg release]; 01257 } 01258 01259 eventData = (GHOST_TEventDataPtr) ibuf; 01260 } 01261 break; 01262 01263 default: 01264 return GHOST_kFailure; 01265 break; 01266 } 01267 pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData)); 01268 } 01269 break; 01270 default: 01271 return GHOST_kFailure; 01272 } 01273 m_outsideLoopEventProcessed = true; 01274 return GHOST_kSuccess; 01275 } 01276 01277 01278 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest() 01279 { 01280 GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow(); 01281 01282 //Discard quit event if we are in cursor grab sequence 01283 if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal)) 01284 return GHOST_kExitCancel; 01285 01286 //Check open windows if some changes are not saved 01287 if (m_windowManager->getAnyModifiedState()) 01288 { 01289 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?", 01290 @"Cancel", @"Quit Anyway", nil); 01291 if (shouldQuit == NSAlertAlternateReturn) 01292 { 01293 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) ); 01294 return GHOST_kExitNow; 01295 } else { 01296 //Give back focus to the blender window if user selected cancel quit 01297 NSArray *windowsList = [NSApp orderedWindows]; 01298 if ([windowsList count]) { 01299 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil]; 01300 //Handle the modifiers keyes changed state issue 01301 //as recovering from the quit dialog is like application 01302 //gaining focus back. 01303 //Main issue fixed is Cmd modifier not being cleared 01304 handleApplicationBecomeActiveEvent(); 01305 } 01306 } 01307 01308 } 01309 else { 01310 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) ); 01311 m_outsideLoopEventProcessed = true; 01312 return GHOST_kExitNow; 01313 } 01314 01315 return GHOST_kExitCancel; 01316 } 01317 01318 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr) 01319 { 01320 NSString *filepath = (NSString*)filepathStr; 01321 int confirmOpen = NSAlertAlternateReturn; 01322 NSArray *windowsList; 01323 char * temp_buff; 01324 size_t filenameTextSize; 01325 GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow(); 01326 01327 if (!window) { 01328 return NO; 01329 } 01330 01331 //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised 01332 if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal)) 01333 return GHOST_kExitCancel; 01334 01335 //Check open windows if some changes are not saved 01336 if (m_windowManager->getAnyModifiedState()) 01337 { 01338 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]], 01339 @"Current document has not been saved.\nDo you really want to proceed?", 01340 @"Cancel", @"Open", nil); 01341 } 01342 01343 //Give back focus to the blender window 01344 windowsList = [NSApp orderedWindows]; 01345 if ([windowsList count]) { 01346 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil]; 01347 } 01348 01349 if (confirmOpen == NSAlertAlternateReturn) 01350 { 01351 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01352 01353 temp_buff = (char*) malloc(filenameTextSize+1); 01354 01355 if (temp_buff == NULL) { 01356 return GHOST_kFailure; 01357 } 01358 01359 strncpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize); 01360 01361 temp_buff[filenameTextSize] = '\0'; 01362 01363 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff)); 01364 01365 return YES; 01366 } 01367 else return NO; 01368 } 01369 01370 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType) 01371 { 01372 NSEvent *event = (NSEvent *)eventPtr; 01373 GHOST_IWindow* window; 01374 01375 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01376 if (!window) { 01377 //printf("\nW failure for event 0x%x",[event type]); 01378 return GHOST_kFailure; 01379 } 01380 01381 GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData(); 01382 01383 switch (eventType) { 01384 case NSTabletPoint: 01385 ct.Pressure = [event pressure]; 01386 ct.Xtilt = [event tilt].x; 01387 ct.Ytilt = [event tilt].y; 01388 break; 01389 01390 case NSTabletProximity: 01391 ct.Pressure = 0; 01392 ct.Xtilt = 0; 01393 ct.Ytilt = 0; 01394 if ([event isEnteringProximity]) 01395 { 01396 //pointer is entering tablet area proximity 01397 switch ([event pointingDeviceType]) { 01398 case NSPenPointingDevice: 01399 ct.Active = GHOST_kTabletModeStylus; 01400 break; 01401 case NSEraserPointingDevice: 01402 ct.Active = GHOST_kTabletModeEraser; 01403 break; 01404 case NSCursorPointingDevice: 01405 case NSUnknownPointingDevice: 01406 default: 01407 ct.Active = GHOST_kTabletModeNone; 01408 break; 01409 } 01410 } else { 01411 // pointer is leaving - return to mouse 01412 ct.Active = GHOST_kTabletModeNone; 01413 } 01414 break; 01415 01416 default: 01417 GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received"); 01418 return GHOST_kFailure; 01419 break; 01420 } 01421 return GHOST_kSuccess; 01422 } 01423 01424 01425 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) 01426 { 01427 NSEvent *event = (NSEvent *)eventPtr; 01428 GHOST_WindowCocoa* window; 01429 01430 window = (GHOST_WindowCocoa*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01431 if (!window) { 01432 //printf("\nW failure for event 0x%x",[event type]); 01433 return GHOST_kFailure; 01434 } 01435 01436 switch ([event type]) 01437 { 01438 case NSLeftMouseDown: 01439 case NSRightMouseDown: 01440 case NSOtherMouseDown: 01441 pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber]))); 01442 //Handle tablet events combined with mouse events 01443 switch ([event subtype]) { 01444 case NX_SUBTYPE_TABLET_POINT: 01445 handleTabletEvent(eventPtr, NSTabletPoint); 01446 break; 01447 case NX_SUBTYPE_TABLET_PROXIMITY: 01448 handleTabletEvent(eventPtr, NSTabletProximity); 01449 break; 01450 default: 01451 //No tablet event included : do nothing 01452 break; 01453 } 01454 break; 01455 01456 case NSLeftMouseUp: 01457 case NSRightMouseUp: 01458 case NSOtherMouseUp: 01459 pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber]))); 01460 //Handle tablet events combined with mouse events 01461 switch ([event subtype]) { 01462 case NX_SUBTYPE_TABLET_POINT: 01463 handleTabletEvent(eventPtr, NSTabletPoint); 01464 break; 01465 case NX_SUBTYPE_TABLET_PROXIMITY: 01466 handleTabletEvent(eventPtr, NSTabletProximity); 01467 break; 01468 default: 01469 //No tablet event included : do nothing 01470 break; 01471 } 01472 break; 01473 01474 case NSLeftMouseDragged: 01475 case NSRightMouseDragged: 01476 case NSOtherMouseDragged: 01477 //Handle tablet events combined with mouse events 01478 switch ([event subtype]) { 01479 case NX_SUBTYPE_TABLET_POINT: 01480 handleTabletEvent(eventPtr, NSTabletPoint); 01481 break; 01482 case NX_SUBTYPE_TABLET_PROXIMITY: 01483 handleTabletEvent(eventPtr, NSTabletProximity); 01484 break; 01485 default: 01486 //No tablet event included : do nothing 01487 break; 01488 } 01489 01490 case NSMouseMoved: 01491 switch (window->getCursorGrabMode()) { 01492 case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move 01493 { 01494 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum, x, y; 01495 01496 window->getCursorGrabInitPos(x_warp, y_warp); 01497 01498 window->getCursorGrabAccum(x_accum, y_accum); 01499 x_accum += [event deltaX]; 01500 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ... 01501 window->setCursorGrabAccum(x_accum, y_accum); 01502 01503 window->clientToScreenIntern(x_warp+x_accum, y_warp+y_accum, x, y); 01504 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01505 } 01506 break; 01507 case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries 01508 { 01509 NSPoint mousePos = [event locationInWindow]; 01510 GHOST_TInt32 x_mouse= mousePos.x; 01511 GHOST_TInt32 y_mouse= mousePos.y; 01512 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur, x, y; 01513 GHOST_Rect bounds, windowBounds, correctedBounds; 01514 01515 /* fallback to window bounds */ 01516 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure) 01517 window->getClientBounds(bounds); 01518 01519 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates 01520 window->getClientBounds(windowBounds); 01521 window->screenToClient(bounds.m_l, bounds.m_b, correctedBounds.m_l, correctedBounds.m_t); 01522 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b); 01523 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b; 01524 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t; 01525 01526 //Update accumulation counts 01527 window->getCursorGrabAccum(x_accum, y_accum); 01528 x_accum += [event deltaX]-m_cursorDelta_x; 01529 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ... 01530 window->setCursorGrabAccum(x_accum, y_accum); 01531 01532 01533 //Warp mouse cursor if needed 01534 x_mouse += [event deltaX]-m_cursorDelta_x; 01535 y_mouse += -[event deltaY]-m_cursorDelta_y; 01536 correctedBounds.wrapPoint(x_mouse, y_mouse, 2); 01537 01538 //Compensate for mouse moved event taking cursor position set into account 01539 m_cursorDelta_x = x_mouse-mousePos.x; 01540 m_cursorDelta_y = y_mouse-mousePos.y; 01541 01542 //Set new cursor position 01543 window->clientToScreenIntern(x_mouse, y_mouse, x_cur, y_cur); 01544 setMouseCursorPosition(x_cur, y_cur); /* wrap */ 01545 01546 //Post event 01547 window->getCursorGrabInitPos(x_cur, y_cur); 01548 window->clientToScreenIntern(x_cur + x_accum, y_cur + y_accum, x, y); 01549 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01550 } 01551 break; 01552 default: 01553 { 01554 //Normal cursor operation: send mouse position in window 01555 NSPoint mousePos = [event locationInWindow]; 01556 GHOST_TInt32 x, y; 01557 01558 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01559 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x, y)); 01560 01561 m_cursorDelta_x=0; 01562 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter 01563 } 01564 break; 01565 } 01566 break; 01567 01568 case NSScrollWheel: 01569 { 01570 /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */ 01571 if (!m_hasMultiTouchTrackpad || !m_isGestureInProgress) { 01572 GHOST_TInt32 delta; 01573 01574 double deltaF = [event deltaY]; 01575 01576 if (deltaF == 0.0) deltaF = [event deltaX]; // make blender decide if it's horizontal scroll 01577 if (deltaF == 0.0) break; //discard trackpad delta=0 events 01578 01579 delta = deltaF > 0.0 ? 1 : -1; 01580 pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta)); 01581 } 01582 else { 01583 NSPoint mousePos = [event locationInWindow]; 01584 GHOST_TInt32 x, y; 01585 double dx = [event deltaX]; 01586 double dy = -[event deltaY]; 01587 01588 const double deltaMax = 50.0; 01589 01590 if ((dx == 0) && (dy == 0)) break; 01591 01592 /* Quadratic acceleration */ 01593 dx = dx*(fabs(dx)+0.5); 01594 if (dx<0.0) dx-=0.5; else dx+=0.5; 01595 if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax; 01596 01597 dy = dy*(fabs(dy)+0.5); 01598 if (dy<0.0) dy-=0.5; else dy+=0.5; 01599 if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax; 01600 01601 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01602 dy = -dy; 01603 01604 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, x, y, dx, dy)); 01605 } 01606 } 01607 break; 01608 01609 case NSEventTypeMagnify: 01610 { 01611 NSPoint mousePos = [event locationInWindow]; 01612 GHOST_TInt32 x, y; 01613 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01614 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, x, y, 01615 [event magnification]*125.0 + 0.1, 0)); 01616 } 01617 break; 01618 01619 case NSEventTypeRotate: 01620 { 01621 NSPoint mousePos = [event locationInWindow]; 01622 GHOST_TInt32 x, y; 01623 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); 01624 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, x, y, 01625 -[event rotation] * 5.0, 0)); 01626 } 01627 case NSEventTypeBeginGesture: 01628 m_isGestureInProgress = true; 01629 break; 01630 case NSEventTypeEndGesture: 01631 m_isGestureInProgress = false; 01632 break; 01633 default: 01634 return GHOST_kFailure; 01635 break; 01636 } 01637 01638 return GHOST_kSuccess; 01639 } 01640 01641 01642 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) 01643 { 01644 NSEvent *event = (NSEvent *)eventPtr; 01645 GHOST_IWindow* window; 01646 unsigned int modifiers; 01647 NSString *characters; 01648 NSData *convertedCharacters; 01649 GHOST_TKey keyCode; 01650 unsigned char ascii; 01651 NSString* charsIgnoringModifiers; 01652 01653 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]); 01654 if (!window) { 01655 //printf("\nW failure for event 0x%x",[event type]); 01656 return GHOST_kFailure; 01657 } 01658 01659 char utf8_buf[6]= {'\0'}; 01660 ascii = 0; 01661 01662 switch ([event type]) { 01663 01664 case NSKeyDown: 01665 case NSKeyUp: 01666 charsIgnoringModifiers = [event charactersIgnoringModifiers]; 01667 if ([charsIgnoringModifiers length]>0) 01668 keyCode = convertKey([event keyCode], 01669 [charsIgnoringModifiers characterAtIndex:0], 01670 [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp); 01671 else 01672 keyCode = convertKey([event keyCode],0, 01673 [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp); 01674 01675 /* handling both unicode or ascii */ 01676 characters = [event characters]; 01677 if ([characters length]>0) { 01678 convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding]; 01679 01680 for (int x = 0; x < [convertedCharacters length]; x++) { 01681 utf8_buf[x] = ((char*)[convertedCharacters bytes])[x]; 01682 } 01683 01684 /* ascii is a subset of unicode */ 01685 if ([convertedCharacters length] == 1) { 01686 ascii = utf8_buf[0]; 01687 } 01688 } 01689 01690 /* arrow keys should not have utf8 */ 01691 if ((keyCode > 266) && (keyCode < 271)) 01692 utf8_buf[0] = '\0'; 01693 01694 if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask)) 01695 break; //Cmd-Q is directly handled by Cocoa 01696 01697 if ([event type] == NSKeyDown) { 01698 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf) ); 01699 //printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii, utf8_buf); 01700 } else { 01701 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, 0, '\0') ); 01702 //printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii, utf8_buf); 01703 } 01704 break; 01705 01706 case NSFlagsChanged: 01707 modifiers = [event modifierFlags]; 01708 01709 if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) { 01710 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) ); 01711 } 01712 if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) { 01713 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) ); 01714 } 01715 if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) { 01716 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) ); 01717 } 01718 if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) { 01719 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) ); 01720 } 01721 01722 m_modifierMask = modifiers; 01723 break; 01724 01725 default: 01726 return GHOST_kFailure; 01727 break; 01728 } 01729 01730 return GHOST_kSuccess; 01731 } 01732 01733 01734 01735 #pragma mark Clipboard get/set 01736 01737 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const 01738 { 01739 GHOST_TUns8 * temp_buff; 01740 size_t pastedTextSize; 01741 01742 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 01743 01744 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; 01745 01746 if (pasteBoard == nil) { 01747 [pool drain]; 01748 return NULL; 01749 } 01750 01751 NSArray *supportedTypes = 01752 [NSArray arrayWithObjects: NSStringPboardType, nil]; 01753 01754 NSString *bestType = [[NSPasteboard generalPasteboard] 01755 availableTypeFromArray:supportedTypes]; 01756 01757 if (bestType == nil) { 01758 [pool drain]; 01759 return NULL; 01760 } 01761 01762 NSString * textPasted = [pasteBoard stringForType:NSStringPboardType]; 01763 01764 if (textPasted == nil) { 01765 [pool drain]; 01766 return NULL; 01767 } 01768 01769 pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 01770 01771 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 01772 01773 if (temp_buff == NULL) { 01774 [pool drain]; 01775 return NULL; 01776 } 01777 01778 strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize); 01779 01780 temp_buff[pastedTextSize] = '\0'; 01781 01782 [pool drain]; 01783 01784 if(temp_buff) { 01785 return temp_buff; 01786 } else { 01787 return NULL; 01788 } 01789 } 01790 01791 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const 01792 { 01793 NSString *textToCopy; 01794 01795 if(selection) {return;} // for copying the selection, used on X11 01796 01797 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 01798 01799 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; 01800 01801 if (pasteBoard == nil) { 01802 [pool drain]; 01803 return; 01804 } 01805 01806 NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType]; 01807 01808 [pasteBoard declareTypes:supportedTypes owner:nil]; 01809 01810 textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding]; 01811 01812 [pasteBoard setString:textToCopy forType:NSStringPboardType]; 01813 01814 [pool drain]; 01815 } 01816