Blender  V2.59
GHOST_SystemCocoa.mm
Go to the documentation of this file.
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(&currentTime, 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