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