OSX: Get the current locale in objC-style
[blender.git] / intern / ghost / intern / GHOST_SystemCocoa.mm
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributors: Maarten Gribnau 05/2001
24  *               Damien Plisson 09/2009
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #import <Cocoa/Cocoa.h>
30
31 /*For the currently not ported to Cocoa keyboard layout functions (64bit & 10.6 compatible)*/
32 #include <Carbon/Carbon.h>
33
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/sysctl.h>
37
38 #include "GHOST_SystemCocoa.h"
39
40 #include "GHOST_DisplayManagerCocoa.h"
41 #include "GHOST_EventKey.h"
42 #include "GHOST_EventButton.h"
43 #include "GHOST_EventCursor.h"
44 #include "GHOST_EventWheel.h"
45 #include "GHOST_EventTrackpad.h"
46 #include "GHOST_EventDragnDrop.h"
47 #include "GHOST_EventString.h"
48 #include "GHOST_TimerManager.h"
49 #include "GHOST_TimerTask.h"
50 #include "GHOST_WindowManager.h"
51 #include "GHOST_WindowCocoa.h"
52 #ifdef WITH_INPUT_NDOF
53 #include "GHOST_NDOFManagerCocoa.h"
54 #endif
55
56 #include "AssertMacros.h"
57
58 #pragma mark KeyMap, mouse converters
59 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1040
60 /* Keycodes not defined in Tiger */
61 /*  
62  *  Summary:
63  *    Virtual keycodes
64  *  
65  *  Discussion:
66  *    These constants are the virtual keycodes defined originally in
67  *    Inside Mac Volume V, pg. V-191. They identify physical keys on a
68  *    keyboard. Those constants with "ANSI" in the name are labeled
69  *    according to the key position on an ANSI-standard US keyboard.
70  *    For example, kVK_ANSI_A indicates the virtual keycode for the key
71  *    with the letter 'A' in the US keyboard layout. Other keyboard
72  *    layouts may have the 'A' key label on a different physical key;
73  *    in this case, pressing 'A' will generate a different virtual
74  *    keycode.
75  */
76 enum {
77         kVK_ANSI_A                    = 0x00,
78         kVK_ANSI_S                    = 0x01,
79         kVK_ANSI_D                    = 0x02,
80         kVK_ANSI_F                    = 0x03,
81         kVK_ANSI_H                    = 0x04,
82         kVK_ANSI_G                    = 0x05,
83         kVK_ANSI_Z                    = 0x06,
84         kVK_ANSI_X                    = 0x07,
85         kVK_ANSI_C                    = 0x08,
86         kVK_ANSI_V                    = 0x09,
87         kVK_ANSI_B                    = 0x0B,
88         kVK_ANSI_Q                    = 0x0C,
89         kVK_ANSI_W                    = 0x0D,
90         kVK_ANSI_E                    = 0x0E,
91         kVK_ANSI_R                    = 0x0F,
92         kVK_ANSI_Y                    = 0x10,
93         kVK_ANSI_T                    = 0x11,
94         kVK_ANSI_1                    = 0x12,
95         kVK_ANSI_2                    = 0x13,
96         kVK_ANSI_3                    = 0x14,
97         kVK_ANSI_4                    = 0x15,
98         kVK_ANSI_6                    = 0x16,
99         kVK_ANSI_5                    = 0x17,
100         kVK_ANSI_Equal                = 0x18,
101         kVK_ANSI_9                    = 0x19,
102         kVK_ANSI_7                    = 0x1A,
103         kVK_ANSI_Minus                = 0x1B,
104         kVK_ANSI_8                    = 0x1C,
105         kVK_ANSI_0                    = 0x1D,
106         kVK_ANSI_RightBracket         = 0x1E,
107         kVK_ANSI_O                    = 0x1F,
108         kVK_ANSI_U                    = 0x20,
109         kVK_ANSI_LeftBracket          = 0x21,
110         kVK_ANSI_I                    = 0x22,
111         kVK_ANSI_P                    = 0x23,
112         kVK_ANSI_L                    = 0x25,
113         kVK_ANSI_J                    = 0x26,
114         kVK_ANSI_Quote                = 0x27,
115         kVK_ANSI_K                    = 0x28,
116         kVK_ANSI_Semicolon            = 0x29,
117         kVK_ANSI_Backslash            = 0x2A,
118         kVK_ANSI_Comma                = 0x2B,
119         kVK_ANSI_Slash                = 0x2C,
120         kVK_ANSI_N                    = 0x2D,
121         kVK_ANSI_M                    = 0x2E,
122         kVK_ANSI_Period               = 0x2F,
123         kVK_ANSI_Grave                = 0x32,
124         kVK_ANSI_KeypadDecimal        = 0x41,
125         kVK_ANSI_KeypadMultiply       = 0x43,
126         kVK_ANSI_KeypadPlus           = 0x45,
127         kVK_ANSI_KeypadClear          = 0x47,
128         kVK_ANSI_KeypadDivide         = 0x4B,
129         kVK_ANSI_KeypadEnter          = 0x4C,
130         kVK_ANSI_KeypadMinus          = 0x4E,
131         kVK_ANSI_KeypadEquals         = 0x51,
132         kVK_ANSI_Keypad0              = 0x52,
133         kVK_ANSI_Keypad1              = 0x53,
134         kVK_ANSI_Keypad2              = 0x54,
135         kVK_ANSI_Keypad3              = 0x55,
136         kVK_ANSI_Keypad4              = 0x56,
137         kVK_ANSI_Keypad5              = 0x57,
138         kVK_ANSI_Keypad6              = 0x58,
139         kVK_ANSI_Keypad7              = 0x59,
140         kVK_ANSI_Keypad8              = 0x5B,
141         kVK_ANSI_Keypad9              = 0x5C
142 };
143
144 /* keycodes for keys that are independent of keyboard layout*/
145 enum {
146         kVK_Return                    = 0x24,
147         kVK_Tab                       = 0x30,
148         kVK_Space                     = 0x31,
149         kVK_Delete                    = 0x33,
150         kVK_Escape                    = 0x35,
151         kVK_Command                   = 0x37,
152         kVK_Shift                     = 0x38,
153         kVK_CapsLock                  = 0x39,
154         kVK_Option                    = 0x3A,
155         kVK_Control                   = 0x3B,
156         kVK_RightShift                = 0x3C,
157         kVK_RightOption               = 0x3D,
158         kVK_RightControl              = 0x3E,
159         kVK_Function                  = 0x3F,
160         kVK_F17                       = 0x40,
161         kVK_VolumeUp                  = 0x48,
162         kVK_VolumeDown                = 0x49,
163         kVK_Mute                      = 0x4A,
164         kVK_F18                       = 0x4F,
165         kVK_F19                       = 0x50,
166         kVK_F20                       = 0x5A,
167         kVK_F5                        = 0x60,
168         kVK_F6                        = 0x61,
169         kVK_F7                        = 0x62,
170         kVK_F3                        = 0x63,
171         kVK_F8                        = 0x64,
172         kVK_F9                        = 0x65,
173         kVK_F11                       = 0x67,
174         kVK_F13                       = 0x69,
175         kVK_F16                       = 0x6A,
176         kVK_F14                       = 0x6B,
177         kVK_F10                       = 0x6D,
178         kVK_F12                       = 0x6F,
179         kVK_F15                       = 0x71,
180         kVK_Help                      = 0x72,
181         kVK_Home                      = 0x73,
182         kVK_PageUp                    = 0x74,
183         kVK_ForwardDelete             = 0x75,
184         kVK_F4                        = 0x76,
185         kVK_End                       = 0x77,
186         kVK_F2                        = 0x78,
187         kVK_PageDown                  = 0x79,
188         kVK_F1                        = 0x7A,
189         kVK_LeftArrow                 = 0x7B,
190         kVK_RightArrow                = 0x7C,
191         kVK_DownArrow                 = 0x7D,
192         kVK_UpArrow                   = 0x7E
193 };
194
195 /* ISO keyboards only*/
196 enum {
197         kVK_ISO_Section               = 0x0A
198 };
199
200 /* JIS keyboards only*/
201 enum {
202         kVK_JIS_Yen                   = 0x5D,
203         kVK_JIS_Underscore            = 0x5E,
204         kVK_JIS_KeypadComma           = 0x5F,
205         kVK_JIS_Eisu                  = 0x66,
206         kVK_JIS_Kana                  = 0x68
207 };
208 #endif
209
210 static GHOST_TButtonMask convertButton(int button)
211 {
212         switch (button) {
213                 case 0:
214                         return GHOST_kButtonMaskLeft;
215                 case 1:
216                         return GHOST_kButtonMaskRight;
217                 case 2:
218                         return GHOST_kButtonMaskMiddle;
219                 case 3:
220                         return GHOST_kButtonMaskButton4;
221                 case 4:
222                         return GHOST_kButtonMaskButton5;
223                 default:
224                         return GHOST_kButtonMaskLeft;
225         }
226 }
227
228 /**
229  * Converts Mac rawkey codes (same for Cocoa & Carbon)
230  * into GHOST key codes
231  * \param rawCode The raw physical key code
232  * \param recvChar the character ignoring modifiers (except for shift)
233  * \return Ghost key code
234  */
235 static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) 
236 {       
237         
238         //printf("\nrecvchar %c 0x%x",recvChar,recvChar);
239         switch (rawCode) {
240                 /*Physical keycodes not used due to map changes in int'l keyboards
241                 case kVK_ANSI_A:        return GHOST_kKeyA;
242                 case kVK_ANSI_B:        return GHOST_kKeyB;
243                 case kVK_ANSI_C:        return GHOST_kKeyC;
244                 case kVK_ANSI_D:        return GHOST_kKeyD;
245                 case kVK_ANSI_E:        return GHOST_kKeyE;
246                 case kVK_ANSI_F:        return GHOST_kKeyF;
247                 case kVK_ANSI_G:        return GHOST_kKeyG;
248                 case kVK_ANSI_H:        return GHOST_kKeyH;
249                 case kVK_ANSI_I:        return GHOST_kKeyI;
250                 case kVK_ANSI_J:        return GHOST_kKeyJ;
251                 case kVK_ANSI_K:        return GHOST_kKeyK;
252                 case kVK_ANSI_L:        return GHOST_kKeyL;
253                 case kVK_ANSI_M:        return GHOST_kKeyM;
254                 case kVK_ANSI_N:        return GHOST_kKeyN;
255                 case kVK_ANSI_O:        return GHOST_kKeyO;
256                 case kVK_ANSI_P:        return GHOST_kKeyP;
257                 case kVK_ANSI_Q:        return GHOST_kKeyQ;
258                 case kVK_ANSI_R:        return GHOST_kKeyR;
259                 case kVK_ANSI_S:        return GHOST_kKeyS;
260                 case kVK_ANSI_T:        return GHOST_kKeyT;
261                 case kVK_ANSI_U:        return GHOST_kKeyU;
262                 case kVK_ANSI_V:        return GHOST_kKeyV;
263                 case kVK_ANSI_W:        return GHOST_kKeyW;
264                 case kVK_ANSI_X:        return GHOST_kKeyX;
265                 case kVK_ANSI_Y:        return GHOST_kKeyY;
266                 case kVK_ANSI_Z:        return GHOST_kKeyZ;*/
267                 
268                 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/
269                 case kVK_ISO_Section: return    GHOST_kKeyUnknown;
270                 case kVK_ANSI_1:        return GHOST_kKey1;
271                 case kVK_ANSI_2:        return GHOST_kKey2;
272                 case kVK_ANSI_3:        return GHOST_kKey3;
273                 case kVK_ANSI_4:        return GHOST_kKey4;
274                 case kVK_ANSI_5:        return GHOST_kKey5;
275                 case kVK_ANSI_6:        return GHOST_kKey6;
276                 case kVK_ANSI_7:        return GHOST_kKey7;
277                 case kVK_ANSI_8:        return GHOST_kKey8;
278                 case kVK_ANSI_9:        return GHOST_kKey9;
279                 case kVK_ANSI_0:        return GHOST_kKey0;
280         
281                 case kVK_ANSI_Keypad0:                  return GHOST_kKeyNumpad0;
282                 case kVK_ANSI_Keypad1:                  return GHOST_kKeyNumpad1;
283                 case kVK_ANSI_Keypad2:                  return GHOST_kKeyNumpad2;
284                 case kVK_ANSI_Keypad3:                  return GHOST_kKeyNumpad3;
285                 case kVK_ANSI_Keypad4:                  return GHOST_kKeyNumpad4;
286                 case kVK_ANSI_Keypad5:                  return GHOST_kKeyNumpad5;
287                 case kVK_ANSI_Keypad6:                  return GHOST_kKeyNumpad6;
288                 case kVK_ANSI_Keypad7:                  return GHOST_kKeyNumpad7;
289                 case kVK_ANSI_Keypad8:                  return GHOST_kKeyNumpad8;
290                 case kVK_ANSI_Keypad9:                  return GHOST_kKeyNumpad9;
291                 case kVK_ANSI_KeypadDecimal:    return GHOST_kKeyNumpadPeriod;
292                 case kVK_ANSI_KeypadEnter:              return GHOST_kKeyNumpadEnter;
293                 case kVK_ANSI_KeypadPlus:               return GHOST_kKeyNumpadPlus;
294                 case kVK_ANSI_KeypadMinus:              return GHOST_kKeyNumpadMinus;
295                 case kVK_ANSI_KeypadMultiply:   return GHOST_kKeyNumpadAsterisk;
296                 case kVK_ANSI_KeypadDivide:     return GHOST_kKeyNumpadSlash;
297                 case kVK_ANSI_KeypadClear:              return GHOST_kKeyUnknown;
298
299                 case kVK_F1:                            return GHOST_kKeyF1;
300                 case kVK_F2:                            return GHOST_kKeyF2;
301                 case kVK_F3:                            return GHOST_kKeyF3;
302                 case kVK_F4:                            return GHOST_kKeyF4;
303                 case kVK_F5:                            return GHOST_kKeyF5;
304                 case kVK_F6:                            return GHOST_kKeyF6;
305                 case kVK_F7:                            return GHOST_kKeyF7;
306                 case kVK_F8:                            return GHOST_kKeyF8;
307                 case kVK_F9:                            return GHOST_kKeyF9;
308                 case kVK_F10:                           return GHOST_kKeyF10;
309                 case kVK_F11:                           return GHOST_kKeyF11;
310                 case kVK_F12:                           return GHOST_kKeyF12;
311                 case kVK_F13:                           return GHOST_kKeyF13;
312                 case kVK_F14:                           return GHOST_kKeyF14;
313                 case kVK_F15:                           return GHOST_kKeyF15;
314                 case kVK_F16:                           return GHOST_kKeyF16;
315                 case kVK_F17:                           return GHOST_kKeyF17;
316                 case kVK_F18:                           return GHOST_kKeyF18;
317                 case kVK_F19:                           return GHOST_kKeyF19;
318                 case kVK_F20:                           return GHOST_kKeyF20;
319                         
320                 case kVK_UpArrow:                       return GHOST_kKeyUpArrow;
321                 case kVK_DownArrow:                     return GHOST_kKeyDownArrow;
322                 case kVK_LeftArrow:                     return GHOST_kKeyLeftArrow;
323                 case kVK_RightArrow:            return GHOST_kKeyRightArrow;
324                         
325                 case kVK_Return:                        return GHOST_kKeyEnter;
326                 case kVK_Delete:                        return GHOST_kKeyBackSpace;
327                 case kVK_ForwardDelete:         return GHOST_kKeyDelete;
328                 case kVK_Escape:                        return GHOST_kKeyEsc;
329                 case kVK_Tab:                           return GHOST_kKeyTab;
330                 case kVK_Space:                         return GHOST_kKeySpace;
331                         
332                 case kVK_Home:                          return GHOST_kKeyHome;
333                 case kVK_End:                           return GHOST_kKeyEnd;
334                 case kVK_PageUp:                        return GHOST_kKeyUpPage;
335                 case kVK_PageDown:                      return GHOST_kKeyDownPage;
336                         
337                 /*case kVK_ANSI_Minus:          return GHOST_kKeyMinus;
338                 case kVK_ANSI_Equal:            return GHOST_kKeyEqual;
339                 case kVK_ANSI_Comma:            return GHOST_kKeyComma;
340                 case kVK_ANSI_Period:           return GHOST_kKeyPeriod;
341                 case kVK_ANSI_Slash:            return GHOST_kKeySlash;
342                 case kVK_ANSI_Semicolon:        return GHOST_kKeySemicolon;
343                 case kVK_ANSI_Quote:            return GHOST_kKeyQuote;
344                 case kVK_ANSI_Backslash:        return GHOST_kKeyBackslash;
345                 case kVK_ANSI_LeftBracket:      return GHOST_kKeyLeftBracket;
346                 case kVK_ANSI_RightBracket:     return GHOST_kKeyRightBracket;
347                 case kVK_ANSI_Grave:            return GHOST_kKeyAccentGrave;*/
348                         
349                 case kVK_VolumeUp:
350                 case kVK_VolumeDown:
351                 case kVK_Mute:
352                         return GHOST_kKeyUnknown;
353                         
354                 default:
355                         /* alphanumerical or punctuation key that is remappable in int'l keyboards */
356                         if ((recvChar >= 'A') && (recvChar <= 'Z')) {
357                                 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);
358                         }
359                         else if ((recvChar >= 'a') && (recvChar <= 'z')) {
360                                 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);
361                         }
362                         else {
363 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1040
364                                 KeyboardLayoutRef keyLayout;
365                                 UCKeyboardLayout *uchrData;
366                                 
367                                 KLGetCurrentKeyboardLayout(&keyLayout);
368                                 KLGetKeyboardLayoutProperty(keyLayout, kKLuchrData, (const void **)
369                                                             &uchrData);
370                                 /*get actual character value of the "remappable" keys in int'l keyboards,
371                                  if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger),
372                                  then fallback on using the received charactersIgnoringModifiers */
373                                 if (uchrData)
374                                 {
375                                         UInt32 deadKeyState=0;
376                                         UniCharCount actualStrLength=0;
377                                         
378                                         UCKeyTranslate(uchrData, rawCode, keyAction, 0,
379                                                                    LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar);
380                                         
381                                 }                               
382 #else
383                                 /* Leopard and Snow Leopard 64bit compatible API*/
384                                 CFDataRef uchrHandle; /*the keyboard layout*/
385                                 TISInputSourceRef kbdTISHandle;
386                                 
387                                 kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource();
388                                 uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData);
389                                 CFRelease(kbdTISHandle);
390                                 
391                                 /*get actual character value of the "remappable" keys in int'l keyboards,
392                                  if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger),
393                                  then fallback on using the received charactersIgnoringModifiers */
394                                 if (uchrHandle)
395                                 {
396                                         UInt32 deadKeyState=0;
397                                         UniCharCount actualStrLength=0;
398                                         
399                                         UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0,
400                                                                    LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar);
401                                         
402                                 }
403 #endif
404                                 switch (recvChar) {
405                                         case '-':       return GHOST_kKeyMinus;
406                                         case '=':       return GHOST_kKeyEqual;
407                                         case ',':       return GHOST_kKeyComma;
408                                         case '.':       return GHOST_kKeyPeriod;
409                                         case '/':       return GHOST_kKeySlash;
410                                         case ';':       return GHOST_kKeySemicolon;
411                                         case '\'':      return GHOST_kKeyQuote;
412                                         case '\\':      return GHOST_kKeyBackslash;
413                                         case '[':       return GHOST_kKeyLeftBracket;
414                                         case ']':       return GHOST_kKeyRightBracket;
415                                         case '`':       return GHOST_kKeyAccentGrave;
416                                         default:
417                                                 return GHOST_kKeyUnknown;
418                                 }
419                         }
420         }
421         return GHOST_kKeyUnknown;
422 }
423
424
425 #pragma mark defines for 10.6 api not documented in 10.5
426 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1040
427 enum {
428         /* The following event types are available on some hardware on 10.5.2 and later */
429         NSEventTypeGesture          = 29,
430         NSEventTypeMagnify          = 30,
431         NSEventTypeSwipe            = 31,
432         NSEventTypeRotate           = 18,
433         NSEventTypeBeginGesture     = 19,
434         NSEventTypeEndGesture       = 20
435 };
436
437 @interface NSEvent(GestureEvents)
438 - (float)magnification;       // change in magnification.
439 #else
440 @interface NSEvent(GestureEvents)
441 - (CGFloat)magnification;       // change in magnification on 10.5.2 or later.
442 @end 
443
444 #endif
445
446
447 #pragma mark Utility functions
448
449 #define FIRSTFILEBUFLG 512
450 static bool g_hasFirstFile = false;
451 static char g_firstFileBuf[512];
452
453 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
454 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
455 {
456         if (g_hasFirstFile) {
457                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
458                 buf[FIRSTFILEBUFLG - 1] = '\0';
459                 return 1;
460         }
461         else {
462                 return 0; 
463         }
464 }
465
466 #if defined(WITH_QUICKTIME) && !defined(USE_QTKIT)
467 //Need to place this quicktime function in an ObjC file
468 //It is used to avoid memory leak when raising the quicktime "compression settings" standard dialog
469 extern "C" {
470         struct bContext;
471         struct wmOperator;
472         extern int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op);
473
474
475 int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
476 {
477         int result;
478         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
479         
480         result = fromcocoa_request_qtcodec_settings(C, op);
481         
482         [pool drain];
483         return result;
484 }
485 };
486 #endif
487
488
489 #pragma mark Cocoa objects
490
491 /**
492  * CocoaAppDelegate
493  * ObjC object to capture applicationShouldTerminate, and send quit event
494  **/
495 @interface CocoaAppDelegate : NSObject {
496         GHOST_SystemCocoa *systemCocoa;
497 }
498 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
499 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
500 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
501 - (void)applicationWillTerminate:(NSNotification *)aNotification;
502 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
503 @end
504
505 @implementation CocoaAppDelegate : NSObject
506 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
507 {
508         systemCocoa = sysCocoa;
509 }
510
511 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
512 {
513         return systemCocoa->handleOpenDocumentRequest(filename);
514 }
515
516 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
517 {
518         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
519         //Note that Cmd+Q is already handled by keyhandler
520     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
521                 return NSTerminateCancel;//NSTerminateNow;
522         else
523                 return NSTerminateCancel;
524 }
525
526 // To avoid cancelling a log off process, we must use Cocoa termination process
527 // And this function is the only chance to perform clean up
528 // So WM_exit needs to be called directly, as the event loop will never run before termination
529 - (void)applicationWillTerminate:(NSNotification *)aNotification
530 {
531         /*G.is_break = FALSE; //Let Cocoa perform the termination at the end
532         WM_exit(C);*/
533 }
534
535 - (void)applicationWillBecomeActive:(NSNotification *)aNotification
536 {
537         systemCocoa->handleApplicationBecomeActiveEvent();
538 }
539 @end
540
541
542
543 #pragma mark initialization/finalization
544
545 const char *user_locale; // Global current user locale
546
547 GHOST_SystemCocoa::GHOST_SystemCocoa()
548 {
549         int mib[2];
550         struct timeval boottime;
551         size_t len;
552         char *rstring = NULL;
553         
554         m_modifierMask =0;
555         m_cursorDelta_x=0;
556         m_cursorDelta_y=0;
557         m_outsideLoopEventProcessed = false;
558         m_needDelayedApplicationBecomeActiveEventProcessing = false;
559         m_displayManager = new GHOST_DisplayManagerCocoa ();
560         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
561         m_displayManager->initialize();
562
563         //NSEvent timeStamp is given in system uptime, state start date is boot time
564         mib[0] = CTL_KERN;
565         mib[1] = KERN_BOOTTIME;
566         len = sizeof(struct timeval);
567         
568         sysctl(mib, 2, &boottime, &len, NULL, 0);
569         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
570         
571         //Detect multitouch trackpad
572         mib[0] = CTL_HW;
573         mib[1] = HW_MODEL;
574         sysctl( mib, 2, NULL, &len, NULL, 0 );
575         rstring = (char*)malloc( len );
576         sysctl( mib, 2, rstring, &len, NULL, 0 );
577         
578         m_hasMultiTouchTrackpad = false;
579         
580         free( rstring );
581         rstring = NULL;
582         
583         m_ignoreWindowSizedMessages = false;
584         
585         //Get current locale
586         CFLocaleRef myCFLocale = CFLocaleCopyCurrent();
587         NSLocale *myNSLocale = (NSLocale *)CFBridgingRelease(myCFLocale);
588         NSString *nsIdentifier = [myNSLocale localeIdentifier];
589         user_locale = [nsIdentifier UTF8String];        
590 }
591
592 GHOST_SystemCocoa::~GHOST_SystemCocoa()
593 {
594 }
595
596
597 GHOST_TSuccess GHOST_SystemCocoa::init()
598 {
599         GHOST_TSuccess success = GHOST_System::init();
600         if (success) {
601
602 #ifdef WITH_INPUT_NDOF
603                 m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
604 #endif
605
606                 //ProcessSerialNumber psn;
607                 
608                 //Carbon stuff to move window & menu to foreground
609                 /*if (!GetCurrentProcess(&psn)) {
610                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
611                         SetFrontProcess(&psn);
612                 }*/
613                 
614                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
615                 if (NSApp == nil) {
616                         [NSApplication sharedApplication];
617                         
618                         if ([NSApp mainMenu] == nil) {
619                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
620                                 NSMenuItem *menuItem;
621                                 NSMenu *windowMenu;
622                                 NSMenu *appMenu;
623                                 
624                                 //Create the application menu
625                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
626                                 
627                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
628                                 [appMenu addItem:[NSMenuItem separatorItem]];
629                                 
630                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
631                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
632                                  
633                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
634                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
635                                 
636                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
637                                 
638                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
639                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
640                                 
641                                 menuItem = [[NSMenuItem alloc] init];
642                                 [menuItem setSubmenu:appMenu];
643                                 
644                                 [mainMenubar addItem:menuItem];
645                                 [menuItem release];
646                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
647                                 [appMenu release];
648                                 
649                                 //Create the window menu
650                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
651                                 
652                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
653                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
654                                 
655                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
656                                 
657                                 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
658                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
659                                 
660                                 menuItem = [[NSMenuItem alloc] init];
661                                 [menuItem setSubmenu:windowMenu];
662                                 
663                                 [mainMenubar addItem:menuItem];
664                                 [menuItem release];
665                                 
666                                 [NSApp setMainMenu:mainMenubar];
667                                 [NSApp setWindowsMenu:windowMenu];
668                                 [windowMenu release];
669                         }
670                 }
671                 if ([NSApp delegate] == nil) {
672                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
673                         [appDelegate setSystemCocoa:this];
674                         [NSApp setDelegate:appDelegate];
675                 }
676                 
677                 [NSApp finishLaunching];
678                 
679                 [pool drain];
680         }
681         return success;
682 }
683
684
685 #pragma mark window management
686
687 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
688 {
689         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
690         struct timeval currentTime;
691         
692         gettimeofday(&currentTime, NULL);
693         
694         //Return timestamp of system uptime
695         
696         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
697 }
698
699
700 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
701 {
702         //Note that OS X supports monitor hot plug
703         // We do not support multiple monitors at the moment
704         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
705
706         GHOST_TUns8 count = [[NSScreen screens] count];
707
708         [pool drain];
709         return count;
710 }
711
712
713 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
714 {
715         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
716         //Get visible frame, that is frame excluding dock and top menu bar
717         NSRect frame = [[NSScreen mainScreen] visibleFrame];
718         
719         //Returns max window contents (excluding title bar...)
720         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
721                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
722         
723         width = contentRect.size.width;
724         height = contentRect.size.height;
725
726         [pool drain];
727 }
728
729 void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
730 {
731         /* TODO! */
732         getMainDisplayDimensions(width, height);
733 }
734
735 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
736         const STR_String& title, 
737         GHOST_TInt32 left,
738         GHOST_TInt32 top,
739         GHOST_TUns32 width,
740         GHOST_TUns32 height,
741         GHOST_TWindowState state,
742         GHOST_TDrawingContextType type,
743         bool stereoVisual,
744         const bool exclusive,
745         const GHOST_TUns16 numOfAASamples,
746         const GHOST_TEmbedderWindowID parentWindow
747 )
748 {
749         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
750         GHOST_IWindow* window = 0;
751         
752         //Get the available rect for including window contents
753         NSRect frame = [[NSScreen mainScreen] visibleFrame];
754         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
755                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
756         
757         GHOST_TInt32 bottom = (contentRect.size.height - 1) - height - top;
758
759         //Ensures window top left is inside this available rect
760         left = left > contentRect.origin.x ? left : contentRect.origin.x;
761         bottom = bottom > contentRect.origin.y ? bottom : contentRect.origin.y;
762
763         window = new GHOST_WindowCocoa (this, title, left, bottom, width, height, state, type, stereoVisual, numOfAASamples);
764
765         if (window) {
766                 if (window->getValid()) {
767                         // Store the pointer to the window
768                         GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
769                         m_windowManager->addWindow(window);
770                         m_windowManager->setActiveWindow(window);
771                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
772                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
773                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
774                 }
775                 else {
776                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
777                         delete window;
778                         window = 0;
779                 }
780         }
781         else {
782                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
783         }
784         [pool drain];
785         return window;
786 }
787
788 /**
789  * \note : returns coordinates in Cocoa screen coordinates
790  */
791 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
792 {
793         NSPoint mouseLoc = [NSEvent mouseLocation];
794         
795         // Returns the mouse location in screen coordinates
796         x = (GHOST_TInt32)mouseLoc.x;
797         y = (GHOST_TInt32)mouseLoc.y;
798         return GHOST_kSuccess;
799 }
800
801 /**
802  * \note : expect Cocoa screen coordinates
803  */
804 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
805 {
806         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
807         if (!window) return GHOST_kFailure;
808
809         //Cursor and mouse dissociation placed here not to interfere with continuous grab
810         // (in cont. grab setMouseCursorPosition is directly called)
811         CGAssociateMouseAndMouseCursorPosition(false);
812         setMouseCursorPosition(x, y);
813         CGAssociateMouseAndMouseCursorPosition(true);
814         
815         //Force mouse move event (not pushed by Cocoa)
816         pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x, y));
817         m_outsideLoopEventProcessed = true;
818         
819         return GHOST_kSuccess;
820 }
821
822 GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
823 {
824         float xf=(float)x, yf=(float)y;
825         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
826         if (!window) return GHOST_kFailure;
827
828         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
829         NSScreen *windowScreen = window->getScreen();
830         NSRect screenRect = [windowScreen frame];
831         
832         //Set position relative to current screen
833         xf -= screenRect.origin.x;
834         yf -= screenRect.origin.y;
835         
836         //Quartz Display Services uses the old coordinates (top left origin)
837         yf = screenRect.size.height -yf;
838
839         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
840
841         [pool drain];
842         return GHOST_kSuccess;
843 }
844
845
846 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
847 {
848         keys.set(GHOST_kModifierKeyOS, (m_modifierMask & NSCommandKeyMask) ? true : false);
849         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
850         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
851         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
852         
853         return GHOST_kSuccess;
854 }
855
856 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
857 {
858         UInt32 button_state = GetCurrentEventButtonState();
859
860         buttons.clear();
861         buttons.set(GHOST_kButtonMaskLeft, button_state & (1 << 0));
862         buttons.set(GHOST_kButtonMaskRight, button_state & (1 << 1));
863         buttons.set(GHOST_kButtonMaskMiddle, button_state & (1 << 2));
864         buttons.set(GHOST_kButtonMaskButton4, button_state & (1 << 3));
865         buttons.set(GHOST_kButtonMaskButton5, button_state & (1 << 4));
866         return GHOST_kSuccess;
867 }
868
869
870
871 #pragma mark Event handlers
872
873 /**
874  * The event queue polling function
875  */
876 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
877 {
878         bool anyProcessed = false;
879         NSEvent *event;
880         
881         //      SetMouseCoalescingEnabled(false, NULL);
882         //TODO : implement timer ??
883 #if 0
884         do {
885                 GHOST_TimerManager* timerMgr = getTimerManager();
886                 
887                 if (waitForEvent) {
888                         GHOST_TUns64 next = timerMgr->nextFireTime();
889                         double timeOut;
890
891                         if (next == GHOST_kFireTimeNever) {
892                                 timeOut = kEventDurationForever;
893                         }
894                         else {
895                                 timeOut = (double)(next - getMilliSeconds())/1000.0;
896                                 if (timeOut < 0.0)
897                                         timeOut = 0.0;
898                         }
899
900                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
901                 }
902
903                 if (timerMgr->fireTimers(getMilliSeconds())) {
904                         anyProcessed = true;
905                 }
906 #endif
907                 
908                 do {
909                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
910                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
911                                                                            untilDate:[NSDate distantPast]
912                                                                                   inMode:NSDefaultRunLoopMode
913                                                                                  dequeue:YES];
914                         if (event==nil) {
915                                 [pool drain];
916                                 break;
917                         }
918                         
919                         anyProcessed = true;
920                         
921                         switch ([event type]) {
922                                 case NSKeyDown:
923                                 case NSKeyUp:
924                                 case NSFlagsChanged:
925                                         handleKeyEvent(event);
926                                         
927                                         /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */
928                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
929                                          [NSApp sendEvent:event];
930                                          }*/
931                                         break;
932                                         
933                                 case NSLeftMouseDown:
934                                 case NSLeftMouseUp:
935                                 case NSRightMouseDown:
936                                 case NSRightMouseUp:
937                                 case NSMouseMoved:
938                                 case NSLeftMouseDragged:
939                                 case NSRightMouseDragged:
940                                 case NSScrollWheel:
941                                 case NSOtherMouseDown:
942                                 case NSOtherMouseUp:
943                                 case NSOtherMouseDragged:
944                                 case NSEventTypeMagnify:
945                                 case NSEventTypeRotate:
946                                 case NSEventTypeBeginGesture:
947                                 case NSEventTypeEndGesture:
948                                         handleMouseEvent(event);
949                                         break;
950                                         
951                                 case NSTabletPoint:
952                                 case NSTabletProximity:
953                                         handleTabletEvent(event,[event type]);
954                                         break;
955                                         
956                                         /* Trackpad features, fired only from OS X 10.5.2
957                                          case NSEventTypeGesture:
958                                          case NSEventTypeSwipe:
959                                          break; */
960                                         
961                                         /*Unused events
962                                          NSMouseEntered       = 8,
963                                          NSMouseExited        = 9,
964                                          NSAppKitDefined      = 13,
965                                          NSSystemDefined      = 14,
966                                          NSApplicationDefined = 15,
967                                          NSPeriodic           = 16,
968                                          NSCursorUpdate       = 17,*/
969                                         
970                                 default:
971                                         break;
972                         }
973                         //Resend event to NSApp to ensure Mac wide events are handled
974                         [NSApp sendEvent:event];
975                         [pool drain];
976                 } while (event != nil);
977 #if 0
978         } while (waitForEvent && !anyProcessed); // Needed only for timer implementation
979 #endif
980         
981         if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent();
982         
983         if (m_outsideLoopEventProcessed) {
984                 m_outsideLoopEventProcessed = false;
985                 return true;
986         }
987         
988         m_ignoreWindowSizedMessages = false;
989         
990         return anyProcessed;
991 }
992
993 //Note: called from NSApplication delegate
994 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
995 {
996         //Update the modifiers key mask, as its status may have changed when the application was not active
997         //(that is when update events are sent to another application)
998         unsigned int modifiers;
999         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1000         
1001         if (!window) {
1002                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
1003                 return GHOST_kFailure;
1004         }
1005         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
1006
1007         modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags];
1008         
1009         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1010                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift));
1011         }
1012         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1013                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl));
1014         }
1015         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1016                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt));
1017         }
1018         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1019                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyOS));
1020         }
1021         
1022         m_modifierMask = modifiers;
1023         
1024         m_outsideLoopEventProcessed = true;
1025         return GHOST_kSuccess;
1026 }
1027
1028 void GHOST_SystemCocoa::notifyExternalEventProcessed()
1029 {
1030         m_outsideLoopEventProcessed = true;
1031 }
1032
1033 //Note: called from NSWindow delegate
1034 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
1035 {
1036         if (!validWindow(window)) {
1037                 return GHOST_kFailure;
1038         }
1039                 switch (eventType) {
1040                         case GHOST_kEventWindowClose:
1041                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
1042                                 break;
1043                         case GHOST_kEventWindowActivate:
1044                                 m_windowManager->setActiveWindow(window);
1045                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1046                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
1047                                 break;
1048                         case GHOST_kEventWindowDeactivate:
1049                                 m_windowManager->setWindowInactive(window);
1050                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
1051                                 break;
1052                         case GHOST_kEventWindowUpdate:
1053                                 if (m_nativePixel) {
1054                                         window->setNativePixelSize();
1055                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) );
1056                                 }
1057                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
1058                                 break;
1059                         case GHOST_kEventWindowMove:
1060                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
1061                                 break;
1062                         case GHOST_kEventWindowSize:
1063                                 if (!m_ignoreWindowSizedMessages)
1064                                 {
1065                                         //Enforce only one resize message per event loop (coalescing all the live resize messages)
1066                                         window->updateDrawingContext();
1067                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1068                                         //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager
1069                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0)));
1070                                         //m_ignoreWindowSizedMessages = true;
1071                                 }
1072                                 break;
1073                         case GHOST_kEventNativeResolutionChange:
1074                                 
1075                                 if (m_nativePixel) {
1076                                         window->setNativePixelSize();
1077                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) );
1078                                 }
1079
1080                         default:
1081                                 return GHOST_kFailure;
1082                                 break;
1083                 }
1084         
1085         m_outsideLoopEventProcessed = true;
1086         return GHOST_kSuccess;
1087 }
1088
1089 //Note: called from NSWindow subclass
1090 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
1091                                                                    GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
1092 {
1093         if (!validWindow(window)) {
1094                 return GHOST_kFailure;
1095         }
1096         switch (eventType) {
1097                 case GHOST_kEventDraggingEntered:
1098                 case GHOST_kEventDraggingUpdated:
1099                 case GHOST_kEventDraggingExited:
1100                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
1101                         break;
1102                         
1103                 case GHOST_kEventDraggingDropDone:
1104                 {
1105                         GHOST_TUns8 * temp_buff;
1106                         GHOST_TStringArray *strArray;
1107                         NSArray *droppedArray;
1108                         size_t pastedTextSize;
1109                         NSString *droppedStr;
1110                         GHOST_TEventDataPtr eventData;
1111                         int i;
1112
1113                         if (!data) return GHOST_kFailure;
1114                         
1115                         switch (draggedObjectType) {
1116                                 case GHOST_kDragnDropTypeFilenames:
1117                                         droppedArray = (NSArray*)data;
1118                                         
1119                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
1120                                         if (!strArray) return GHOST_kFailure;
1121                                         
1122                                         strArray->count = [droppedArray count];
1123                                         if (strArray->count == 0) return GHOST_kFailure;
1124                                         
1125                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
1126                                         
1127                                         for (i=0;i<strArray->count;i++)
1128                                         {
1129                                                 droppedStr = [droppedArray objectAtIndex:i];
1130                                                 
1131                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1132                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1133                                         
1134                                                 if (!temp_buff) {
1135                                                         strArray->count = i;
1136                                                         break;
1137                                                 }
1138                                         
1139                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1140                                                 temp_buff[pastedTextSize] = '\0';
1141                                                 
1142                                                 strArray->strings[i] = temp_buff;
1143                                         }
1144
1145                                         eventData = (GHOST_TEventDataPtr) strArray;
1146                                         break;
1147                                         
1148                                 case GHOST_kDragnDropTypeString:
1149                                         droppedStr = (NSString*)data;
1150                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1151                                         
1152                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1153                                         
1154                                         if (temp_buff == NULL) {
1155                                                 return GHOST_kFailure;
1156                                         }
1157                                         
1158                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1159                                         
1160                                         temp_buff[pastedTextSize] = '\0';
1161                                         
1162                                         eventData = (GHOST_TEventDataPtr) temp_buff;
1163                                         break;
1164                                 
1165                                 case GHOST_kDragnDropTypeBitmap:
1166                                 {
1167                                         NSImage *droppedImg = (NSImage*)data;
1168                                         NSSize imgSize = [droppedImg size];
1169                                         ImBuf *ibuf = NULL;
1170                                         GHOST_TUns8 *rasterRGB = NULL;
1171                                         GHOST_TUns8 *rasterRGBA = NULL;
1172                                         GHOST_TUns8 *toIBuf = NULL;
1173                                         int x, y, to_i, from_i;
1174                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
1175                                         NSEnumerator *enumerator;
1176                                         NSImageRep *representation;
1177                                         
1178                                         ibuf = IMB_allocImBuf (imgSize.width, imgSize.height, 32, IB_rect);
1179                                         if (!ibuf) {
1180                                                 [droppedImg release];
1181                                                 return GHOST_kFailure;
1182                                         }
1183                                         
1184                                         /*Get the bitmap of the image*/
1185                                         enumerator = [[droppedImg representations] objectEnumerator];
1186                                         while ((representation = [enumerator nextObject])) {
1187                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1188                                                         bitmapImage = (NSBitmapImageRep *)representation;
1189                                                         break;
1190                                                 }
1191                                         }
1192                                         if (bitmapImage == nil) return GHOST_kFailure;
1193                                         
1194                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
1195                                                 && ![bitmapImage isPlanar]) {
1196                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
1197                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1198                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
1199                                                 for (y = 0; y < imgSize.height; y++) {
1200                                                         to_i = (imgSize.height-y-1)*imgSize.width;
1201                                                         from_i = y*imgSize.width;
1202                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
1203                                                 }
1204                                         }
1205                                         else {
1206                                                 /* Tell cocoa image resolution is same as current system one */
1207                                                 [bitmapImage setSize:imgSize];
1208                                                 
1209                                                 /* Convert the image in a RGBA 32bit format */
1210                                                 /* As Core Graphics does not support contextes with non premutliplied alpha,
1211                                                  we need to get alpha key values in a separate batch */
1212                                                 
1213                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
1214                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1215                                                                                                                                                                                  pixelsWide:imgSize.width 
1216                                                                                                                                                                                  pixelsHigh:imgSize.height
1217                                                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
1218                                                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
1219                                                                                                                                                                            bitmapFormat:(NSBitmapFormat)0
1220                                                                                                                                                                                 bytesPerRow:4*imgSize.width
1221                                                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
1222                                                 
1223                                                 [NSGraphicsContext saveGraphicsState];
1224                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1225                                                 [bitmapImage draw];
1226                                                 [NSGraphicsContext restoreGraphicsState];
1227                                                 
1228                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1229                                                 if (rasterRGB == NULL) {
1230                                                         [bitmapImage release];
1231                                                         [blBitmapFormatImageRGB release];
1232                                                         [droppedImg release];
1233                                                         return GHOST_kFailure;
1234                                                 }
1235                                                 
1236                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1237                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1238                                                                                                                                                                                   pixelsWide:imgSize.width
1239                                                                                                                                                                                   pixelsHigh:imgSize.height
1240                                                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1241                                                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
1242                                                                                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1243                                                                                                                                                                                  bytesPerRow:4*imgSize.width
1244                                                                                                                                                                                 bitsPerPixel:32/* RGBA */];
1245                                                 
1246                                                 [NSGraphicsContext saveGraphicsState];
1247                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1248                                                 [bitmapImage draw];
1249                                                 [NSGraphicsContext restoreGraphicsState];
1250                                                 
1251                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1252                                                 if (rasterRGBA == NULL) {
1253                                                         [bitmapImage release];
1254                                                         [blBitmapFormatImageRGB release];
1255                                                         [blBitmapFormatImageRGBA release];
1256                                                         [droppedImg release];
1257                                                         return GHOST_kFailure;
1258                                                 }
1259                                                 
1260                                                 /*Copy the image to ibuf, flipping it vertically*/
1261                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1262                                                 for (y = 0; y < imgSize.height; y++) {
1263                                                         for (x = 0; x < imgSize.width; x++) {
1264                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1265                                                                 from_i = y*imgSize.width + x;
1266                                                                 
1267                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1268                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1269                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1270                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1271                                                         }
1272                                                 }
1273                                                 
1274                                                 [blBitmapFormatImageRGB release];
1275                                                 [blBitmapFormatImageRGBA release];
1276                                                 [droppedImg release];
1277                                         }
1278                                         
1279                                         eventData = (GHOST_TEventDataPtr) ibuf;
1280                                 }
1281                                         break;
1282                                         
1283                                 default:
1284                                         return GHOST_kFailure;
1285                                         break;
1286                         }
1287                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1288                 }
1289                         break;
1290                 default:
1291                         return GHOST_kFailure;
1292         }
1293         m_outsideLoopEventProcessed = true;
1294         return GHOST_kSuccess;
1295 }
1296
1297
1298 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1299 {
1300         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1301         
1302         //Discard quit event if we are in cursor grab sequence
1303         if (window && window->getCursorGrabModeIsWarp())
1304                 return GHOST_kExitCancel;
1305         
1306         //Check open windows if some changes are not saved
1307         if (m_windowManager->getAnyModifiedState())
1308         {
1309                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
1310                                                  @"Cancel", @"Quit Anyway", nil);
1311                 if (shouldQuit == NSAlertAlternateReturn)
1312                 {
1313                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1314                         return GHOST_kExitNow;
1315                 }
1316                 else {
1317                         //Give back focus to the blender window if user selected cancel quit
1318                         NSArray *windowsList = [NSApp orderedWindows];
1319                         if ([windowsList count]) {
1320                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1321                                 //Handle the modifiers keyes changed state issue
1322                                 //as recovering from the quit dialog is like application
1323                                 //gaining focus back.
1324                                 //Main issue fixed is Cmd modifier not being cleared
1325                                 handleApplicationBecomeActiveEvent();
1326                         }
1327                 }
1328
1329         }
1330         else {
1331                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1332                 m_outsideLoopEventProcessed = true;
1333                 return GHOST_kExitNow;
1334         }
1335         
1336         return GHOST_kExitCancel;
1337 }
1338
1339 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1340 {
1341         NSString *filepath = (NSString*)filepathStr;
1342         int confirmOpen = NSAlertAlternateReturn;
1343         NSArray *windowsList;
1344         char * temp_buff;
1345         size_t filenameTextSize;
1346         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1347         
1348         if (!window) {
1349                 return NO;
1350         }       
1351         
1352         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1353         if (window && window->getCursorGrabModeIsWarp())
1354                 return GHOST_kExitCancel;
1355
1356         //Check open windows if some changes are not saved
1357         if (m_windowManager->getAnyModifiedState())
1358         {
1359                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1360                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1361                                                                                  @"Cancel", @"Open", nil);
1362         }
1363
1364         //Give back focus to the blender window
1365         windowsList = [NSApp orderedWindows];
1366         if ([windowsList count]) {
1367                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1368         }
1369
1370         if (confirmOpen == NSAlertAlternateReturn)
1371         {
1372                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1373                 
1374                 temp_buff = (char*) malloc(filenameTextSize+1); 
1375                 
1376                 if (temp_buff == NULL) {
1377                         return GHOST_kFailure;
1378                 }
1379                 
1380                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize);
1381                 
1382                 temp_buff[filenameTextSize] = '\0';
1383
1384                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1385
1386                 return YES;
1387         }
1388         else return NO;
1389 }
1390
1391 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1392 {
1393         NSEvent *event = (NSEvent *)eventPtr;
1394         GHOST_IWindow* window;
1395         
1396         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1397         if (!window) {
1398                 //printf("\nW failure for event 0x%x",[event type]);
1399                 return GHOST_kFailure;
1400         }
1401         
1402         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1403         
1404         switch (eventType) {
1405                 case NSTabletPoint:
1406                         ct.Pressure = [event pressure];
1407                         ct.Xtilt = [event tilt].x;
1408                         ct.Ytilt = [event tilt].y;
1409                         break;
1410                 
1411                 case NSTabletProximity:
1412                         ct.Pressure = 0;
1413                         ct.Xtilt = 0;
1414                         ct.Ytilt = 0;
1415                         if ([event isEnteringProximity])
1416                         {
1417                                 //pointer is entering tablet area proximity
1418                                 switch ([event pointingDeviceType]) {
1419                                         case NSPenPointingDevice:
1420                                                 ct.Active = GHOST_kTabletModeStylus;
1421                                                 break;
1422                                         case NSEraserPointingDevice:
1423                                                 ct.Active = GHOST_kTabletModeEraser;
1424                                                 break;
1425                                         case NSCursorPointingDevice:
1426                                         case NSUnknownPointingDevice:
1427                                         default:
1428                                                 ct.Active = GHOST_kTabletModeNone;
1429                                                 break;
1430                                 }
1431                         }
1432                         else {
1433                                 // pointer is leaving - return to mouse
1434                                 ct.Active = GHOST_kTabletModeNone;
1435                         }
1436                         break;
1437                 
1438                 default:
1439                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1440                         return GHOST_kFailure;
1441                         break;
1442         }
1443         return GHOST_kSuccess;
1444 }
1445
1446 bool GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
1447 {
1448         NSEvent *event = (NSEvent *)eventPtr;
1449
1450         switch ([event subtype]) {
1451                 case NX_SUBTYPE_TABLET_POINT:
1452                         handleTabletEvent(eventPtr, NSTabletPoint);
1453                         return true;
1454                 case NX_SUBTYPE_TABLET_PROXIMITY:
1455                         handleTabletEvent(eventPtr, NSTabletProximity);
1456                         return true;
1457                 default:
1458                         //No tablet event included : do nothing
1459                         return false;
1460         }
1461
1462 }
1463
1464 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
1465 enum {
1466     NSEventPhaseNone = 0,
1467     NSEventPhaseBegan = 0x1 << 0,
1468     NSEventPhaseStationary = 0x1 << 1,
1469     NSEventPhaseChanged = 0x1 << 2,
1470     NSEventPhaseEnded = 0x1 << 3,
1471     NSEventPhaseCancelled = 0x1 << 4,
1472 };
1473 typedef NSUInteger NSEventPhase;
1474
1475 @interface NSEvent (AvailableOn1070AndLater)
1476 - (BOOL)hasPreciseScrollingDeltas;
1477 - (CGFloat)scrollingDeltaX;
1478 - (CGFloat)scrollingDeltaY;
1479 - (NSEventPhase)momentumPhase;
1480 - (BOOL)isDirectionInvertedFromDevice;
1481 - (NSEventPhase)phase;
1482 @end
1483 #endif
1484
1485 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1486 {
1487         NSEvent *event = (NSEvent *)eventPtr;
1488         GHOST_WindowCocoa* window;
1489         CocoaWindow *cocoawindow;
1490         
1491         /* [event window] returns other windows if mouse-over, that's OSX input standard
1492            however, if mouse exits window(s), the windows become inactive, until you click.
1493            We then fall back to the active window from ghost */
1494         window = (GHOST_WindowCocoa*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1495         if (!window) {
1496                 window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
1497                 if (!window) {
1498                         //printf("\nW failure for event 0x%x",[event type]);
1499                         return GHOST_kFailure;
1500                 }
1501         }
1502
1503         cocoawindow = (CocoaWindow *)window->getOSWindow();
1504         
1505         switch ([event type]) {
1506                 case NSLeftMouseDown:
1507                 case NSRightMouseDown:
1508                 case NSOtherMouseDown:
1509                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1510                         //Handle tablet events combined with mouse events
1511                         handleTabletEvent(event);
1512                         break;
1513
1514                 case NSLeftMouseUp:
1515                 case NSRightMouseUp:
1516                 case NSOtherMouseUp:
1517                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1518                         //Handle tablet events combined with mouse events
1519                         handleTabletEvent(event);
1520                         break;
1521                         
1522                 case NSLeftMouseDragged:
1523                 case NSRightMouseDragged:
1524                 case NSOtherMouseDragged:
1525                         //Handle tablet events combined with mouse events
1526                         handleTabletEvent(event);
1527                         
1528                 case NSMouseMoved: 
1529                         {
1530                                 GHOST_TGrabCursorMode grab_mode = window->getCursorGrabMode();
1531
1532                                 /* TODO: CHECK IF THIS IS A TABLET EVENT */
1533                                 bool is_tablet = false;
1534
1535                                 if (is_tablet && window->getCursorGrabModeIsWarp()) {
1536                                         grab_mode = GHOST_kGrabDisable;
1537                                 }
1538
1539                                 switch (grab_mode) {
1540                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1541                                         {
1542                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum, x, y;
1543                                                 
1544                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1545                                                 
1546                                                 window->getCursorGrabAccum(x_accum, y_accum);
1547                                                 x_accum += [event deltaX];
1548                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1549                                                 window->setCursorGrabAccum(x_accum, y_accum);
1550                                                 
1551                                                 window->clientToScreenIntern(x_warp+x_accum, y_warp+y_accum, x, y);
1552                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1553                                         }
1554                                                 break;
1555                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1556                                         {
1557                                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1558                                                 GHOST_TInt32 x_mouse= mousePos.x;
1559                                                 GHOST_TInt32 y_mouse= mousePos.y;
1560                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur, x, y;
1561                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1562                                                 
1563                                                 /* fallback to window bounds */
1564                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1565                                                         window->getClientBounds(bounds);
1566                                                 
1567                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1568                                                 window->getClientBounds(windowBounds);
1569                                                 window->screenToClient(bounds.m_l, bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1570                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1571                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1572                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1573                                                 
1574                                                 //Update accumulation counts
1575                                                 window->getCursorGrabAccum(x_accum, y_accum);
1576                                                 x_accum += [event deltaX]-m_cursorDelta_x;
1577                                                 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1578                                                 window->setCursorGrabAccum(x_accum, y_accum);
1579                                                 
1580                                                 
1581                                                 //Warp mouse cursor if needed
1582                                                 x_mouse += [event deltaX]-m_cursorDelta_x;
1583                                                 y_mouse += -[event deltaY]-m_cursorDelta_y;
1584                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1585                                                 
1586                                                 //Compensate for mouse moved event taking cursor position set into account
1587                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1588                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1589                                                 
1590                                                 //Set new cursor position
1591                                                 window->clientToScreenIntern(x_mouse, y_mouse, x_cur, y_cur);
1592                                                 setMouseCursorPosition(x_cur, y_cur); /* wrap */
1593                                                 
1594                                                 //Post event
1595                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1596                                                 window->clientToScreenIntern(x_cur + x_accum, y_cur + y_accum, x, y);
1597                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1598                                         }
1599                                                 break;
1600                                         default:
1601                                         {
1602                                                 //Normal cursor operation: send mouse position in window
1603                                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1604                                                 GHOST_TInt32 x, y;
1605                                                 
1606                                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1607                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1608
1609                                                 m_cursorDelta_x=0;
1610                                                 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter
1611                                         }
1612                                                 break;
1613                                 }
1614                         }
1615                         break;
1616                         
1617                         /* these events only happen on swiping trackpads or tablets */
1618                         /* warning: using tablet + trackpad at same time frustrates this static variable */
1619                 case NSEventTypeBeginGesture:
1620                         m_hasMultiTouchTrackpad = 1;
1621                         break;
1622                 case NSEventTypeEndGesture:
1623                         m_hasMultiTouchTrackpad = 0;
1624                         break;
1625                         
1626                 case NSScrollWheel:
1627                         {
1628                                 int *momentum = NULL;
1629                                 
1630                                 if ([event respondsToSelector:@selector(momentumPhase)])
1631                                         momentum = (int *)[event momentumPhase];
1632
1633                                 /* standard scrollwheel case, if no swiping happened, and no momentum (kinetic scroll) works */
1634                                 if (!m_hasMultiTouchTrackpad && momentum == NULL) {
1635                                         GHOST_TInt32 delta;
1636                                         
1637                                         double deltaF = [event deltaY];
1638
1639                                         if (deltaF == 0.0) deltaF = [event deltaX]; // make blender decide if it's horizontal scroll
1640                                         if (deltaF == 0.0) break; //discard trackpad delta=0 events
1641                                         
1642                                         delta = deltaF > 0.0 ? 1 : -1;
1643                                         pushEvent(new GHOST_EventWheel([event timestamp] * 1000, window, delta));
1644                                 }
1645                                 else {
1646                                         NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1647                                         GHOST_TInt32 x, y;
1648                                         double dx;
1649                                         double dy;
1650                                         
1651 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
1652                                         int phase = [event phase];
1653                                         
1654                                         /* with 10.7 nice scrolling deltas are supported */
1655                                         dx = [event scrollingDeltaX];
1656                                         dy = [event scrollingDeltaY];
1657                                         
1658                                         /* however, wacom tablet (intuos5) needs old deltas, it then has momentum and phase at zero */
1659                                         if (phase == 0 && momentum==NULL) {
1660                                                 dx = [event deltaX];
1661                                                 dy = [event deltaY];
1662                                         }
1663
1664 #else
1665                                         /* trying to pretend you have nice scrolls... */
1666                                         dx = [event deltaX];
1667                                         dy = -[event deltaY];
1668                                         const double deltaMax = 50.0;
1669                                         
1670                                         if ((dx == 0) && (dy == 0)) break;
1671                                         
1672                                         /* Quadratic acceleration */
1673                                         dx = dx*(fabs(dx) + 0.5);
1674                                         if (dx < 0.0) dx -= 0.5;
1675                                         else          dx += 0.5;
1676                                         if      (dx < -deltaMax) dx = -deltaMax;
1677                                         else if (dx >  deltaMax) dx =  deltaMax;
1678                                         
1679                                         dy = dy*(fabs(dy) + 0.5);
1680                                         if (dy < 0.0) dy -= 0.5;
1681                                         else          dy += 0.5;
1682                                         if      (dy < -deltaMax) dy= -deltaMax;
1683                                         else if (dy >  deltaMax) dy=  deltaMax;
1684                                         
1685                                         dy = -dy;
1686 #endif
1687                                         window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1688
1689                                         pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventScroll, x, y, dx, dy));
1690                                 }
1691                         }
1692                         break;
1693                         
1694                 case NSEventTypeMagnify:
1695                         {
1696                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1697                                 GHOST_TInt32 x, y;
1698                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1699                                 pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventMagnify, x, y,
1700                                                                   [event magnification] * 125.0 + 0.1, 0));
1701                         }
1702                         break;
1703
1704                 case NSEventTypeRotate:
1705                         {
1706                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1707                                 GHOST_TInt32 x, y;
1708                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1709                                 pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventRotate, x, y,
1710                                                                   -[event rotation] * 5.0, 0));
1711                         }
1712                 default:
1713                         return GHOST_kFailure;
1714                         break;
1715                 }
1716         
1717         return GHOST_kSuccess;
1718 }
1719
1720
1721 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1722 {
1723         NSEvent *event = (NSEvent *)eventPtr;
1724         GHOST_IWindow* window;
1725         unsigned int modifiers;
1726         NSString *characters;
1727         NSData *convertedCharacters;
1728         GHOST_TKey keyCode;
1729         unsigned char ascii;
1730         NSString* charsIgnoringModifiers;
1731
1732         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1733         if (!window) {
1734                 //printf("\nW failure for event 0x%x",[event type]);
1735                 return GHOST_kFailure;
1736         }
1737
1738         char utf8_buf[6]= {'\0'};
1739         ascii = 0;
1740         
1741         switch ([event type]) {
1742
1743                 case NSKeyDown:
1744                 case NSKeyUp:
1745                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1746                         if ([charsIgnoringModifiers length] > 0) {
1747                                 keyCode = convertKey([event keyCode],
1748                                                                          [charsIgnoringModifiers characterAtIndex:0],
1749                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1750                         }
1751                         else {
1752                                 keyCode = convertKey([event keyCode],0,
1753                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1754                         }
1755
1756                         /* handling both unicode or ascii */
1757                         characters = [event characters];
1758                         if ([characters length] > 0) {
1759                                 convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding];
1760                                 
1761                                 for (int x = 0; x < [convertedCharacters length]; x++) {
1762                                         utf8_buf[x] = ((char*)[convertedCharacters bytes])[x];
1763                                 }
1764
1765                                 /* ascii is a subset of unicode */
1766                                 if ([convertedCharacters length] == 1) {
1767                                         ascii = utf8_buf[0];
1768                                 }
1769                         }
1770
1771                         /* arrow keys should not have utf8 */
1772                         if ((keyCode > 266) && (keyCode < 271))
1773                                 utf8_buf[0] = '\0';
1774
1775                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1776                                 break; //Cmd-Q is directly handled by Cocoa
1777
1778                         if ([event type] == NSKeyDown) {
1779                                 pushEvent( new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf) );
1780                                 //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);
1781                         }
1782                         else {
1783                                 pushEvent( new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL) );
1784                                 //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);
1785                         }
1786                         break;
1787         
1788                 case NSFlagsChanged: 
1789                         modifiers = [event modifierFlags];
1790                         
1791                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1792                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSShiftKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift));
1793                         }
1794                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1795                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSControlKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl));
1796                         }
1797                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1798                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSAlternateKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt));
1799                         }
1800                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1801                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSCommandKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyOS));
1802                         }
1803                         
1804                         m_modifierMask = modifiers;
1805                         break;
1806                         
1807                 default:
1808                         return GHOST_kFailure;
1809                         break;
1810         }
1811         
1812         return GHOST_kSuccess;
1813 }
1814
1815
1816
1817 #pragma mark Clipboard get/set
1818
1819 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1820 {
1821         GHOST_TUns8 * temp_buff;
1822         size_t pastedTextSize;
1823         
1824         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1825         
1826         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1827         
1828         if (pasteBoard == nil) {
1829                 [pool drain];
1830                 return NULL;
1831         }
1832         
1833         NSArray *supportedTypes =
1834                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1835         
1836         NSString *bestType = [[NSPasteboard generalPasteboard]
1837                                                   availableTypeFromArray:supportedTypes];
1838         
1839         if (bestType == nil) {
1840                 [pool drain];
1841                 return NULL;
1842         }
1843         
1844         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1845
1846         if (textPasted == nil) {
1847                 [pool drain];
1848                 return NULL;
1849         }
1850         
1851         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1852         
1853         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1854
1855         if (temp_buff == NULL) {
1856                 [pool drain];
1857                 return NULL;
1858         }
1859         
1860         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1861         
1862         temp_buff[pastedTextSize] = '\0';
1863         
1864         [pool drain];
1865
1866         if(temp_buff) {
1867                 return temp_buff;
1868         }
1869         else {
1870                 return NULL;
1871         }
1872 }
1873
1874 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1875 {
1876         NSString *textToCopy;
1877         
1878         if(selection) {return;} // for copying the selection, used on X11
1879
1880         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1881                 
1882         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1883         
1884         if (pasteBoard == nil) {
1885                 [pool drain];
1886                 return;
1887         }
1888         
1889         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1890         
1891         [pasteBoard declareTypes:supportedTypes owner:nil];
1892         
1893         textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
1894         
1895         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1896         
1897         [pool drain];
1898 }
1899