Blender V2.61 - r43446

GHOST_SystemCocoa.mm

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