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