migrated NDOF code from soc-2010-merwin, SpaceNavigator now works on Mac blender
[blender.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_NDOFManagerCocoa.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
600                 m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
601
602                 //ProcessSerialNumber psn;
603                 
604                 //Carbon stuff to move window & menu to foreground
605                 /*if (!GetCurrentProcess(&psn)) {
606                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
607                         SetFrontProcess(&psn);
608                 }*/
609                 
610                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
611                 if (NSApp == nil) {
612                         [NSApplication sharedApplication];
613                         
614                         if ([NSApp mainMenu] == nil) {
615                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
616                                 NSMenuItem *menuItem;
617                                 NSMenu *windowMenu;
618                                 NSMenu *appMenu;
619                                 
620                                 //Create the application menu
621                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
622                                 
623                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
624                                 [appMenu addItem:[NSMenuItem separatorItem]];
625                                 
626                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
627                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
628                                  
629                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
630                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
631                                 
632                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
633                                 
634                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
635                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
636                                 
637                                 menuItem = [[NSMenuItem alloc] init];
638                                 [menuItem setSubmenu:appMenu];
639                                 
640                                 [mainMenubar addItem:menuItem];
641                                 [menuItem release];
642                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
643                                 [appMenu release];
644                                 
645                                 //Create the window menu
646                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
647                                 
648                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
649                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
650                                 
651                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
652                                 
653                                 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
654                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
655                                 
656                                 menuItem = [[NSMenuItem alloc] init];
657                                 [menuItem setSubmenu:windowMenu];
658                                 
659                                 [mainMenubar addItem:menuItem];
660                                 [menuItem release];
661                                 
662                                 [NSApp setMainMenu:mainMenubar];
663                                 [NSApp setWindowsMenu:windowMenu];
664                                 [windowMenu release];
665                         }
666                 }
667                 if ([NSApp delegate] == nil) {
668                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
669                         [appDelegate setSystemCocoa:this];
670                         [NSApp setDelegate:appDelegate];
671                 }
672                 
673                 [NSApp finishLaunching];
674                 
675                 [pool drain];
676     }
677     return success;
678 }
679
680
681 #pragma mark window management
682
683 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
684 {
685         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
686         struct timeval currentTime;
687         
688         gettimeofday(&currentTime, NULL);
689         
690         //Return timestamp of system uptime
691         
692         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
693 }
694
695
696 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
697 {
698         //Note that OS X supports monitor hot plug
699         // We do not support multiple monitors at the moment
700         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
701
702         GHOST_TUns8 count = [[NSScreen screens] count];
703
704         [pool drain];
705         return count;
706 }
707
708
709 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
710 {
711         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
712         //Get visible frame, that is frame excluding dock and top menu bar
713         NSRect frame = [[NSScreen mainScreen] visibleFrame];
714         
715         //Returns max window contents (excluding title bar...)
716         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
717                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
718         
719         width = contentRect.size.width;
720         height = contentRect.size.height;
721
722         [pool drain];
723 }
724
725
726 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
727         const STR_String& title, 
728         GHOST_TInt32 left,
729         GHOST_TInt32 top,
730         GHOST_TUns32 width,
731         GHOST_TUns32 height,
732         GHOST_TWindowState state,
733         GHOST_TDrawingContextType type,
734         bool stereoVisual,
735         const GHOST_TUns16 numOfAASamples,
736         const GHOST_TEmbedderWindowID parentWindow
737 )
738 {
739     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
740         GHOST_IWindow* window = 0;
741         
742         //Get the available rect for including window contents
743         NSRect frame = [[NSScreen mainScreen] visibleFrame];
744         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
745                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
746         
747         //Ensures window top left is inside this available rect
748         left = left > contentRect.origin.x ? left : contentRect.origin.x;
749         top = top > contentRect.origin.y ? top : contentRect.origin.y;
750         
751         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
752
753     if (window) {
754         if (window->getValid()) {
755             // Store the pointer to the window 
756             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
757             m_windowManager->addWindow(window);
758             m_windowManager->setActiveWindow(window);
759                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
760             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
761                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
762
763         }
764         else {
765                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
766             delete window;
767             window = 0;
768         }
769     }
770         else {
771                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
772         }
773         [pool drain];
774     return window;
775 }
776
777 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
778 {       
779         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
780         *window = currentWindow;
781         
782         if(!currentWindow) return GHOST_kFailure;
783         
784         return currentWindow->setState(GHOST_kWindowStateFullScreen);
785 }
786
787 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
788 {       
789         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
790         if(!currentWindow) return GHOST_kFailure;
791         
792         return currentWindow->setState(GHOST_kWindowStateNormal);
793 }
794
795
796         
797 /**
798  * @note : returns coordinates in Cocoa screen coordinates
799  */
800 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
801 {
802     NSPoint mouseLoc = [NSEvent mouseLocation];
803         
804     // Returns the mouse location in screen coordinates
805     x = (GHOST_TInt32)mouseLoc.x;
806     y = (GHOST_TInt32)mouseLoc.y;
807     return GHOST_kSuccess;
808 }
809
810 /**
811  * @note : expect Cocoa screen coordinates
812  */
813 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
814 {
815         GHOST_TInt32 wx,wy;
816         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
817         if (!window) return GHOST_kFailure;
818
819         //Cursor and mouse dissociation placed here not to interfere with continuous grab
820         // (in cont. grab setMouseCursorPosition is directly called)
821         CGAssociateMouseAndMouseCursorPosition(false);
822         setMouseCursorPosition(x, y);
823         CGAssociateMouseAndMouseCursorPosition(true);
824         
825         //Force mouse move event (not pushed by Cocoa)
826         window->screenToClient(x, y, wx, wy);
827         pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, wx,wy));
828         m_outsideLoopEventProcessed = true;
829         
830         return GHOST_kSuccess;
831 }
832
833 GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
834 {
835         float xf=(float)x, yf=(float)y;
836         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
837         if (!window) return GHOST_kFailure;
838
839         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
840         NSScreen *windowScreen = window->getScreen();
841         NSRect screenRect = [windowScreen frame];
842         
843         //Set position relative to current screen
844         xf -= screenRect.origin.x;
845         yf -= screenRect.origin.y;
846         
847         //Quartz Display Services uses the old coordinates (top left origin)
848         yf = screenRect.size.height -yf;
849
850         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
851
852         [pool drain];
853     return GHOST_kSuccess;
854 }
855
856
857 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
858 {
859         keys.set(GHOST_kModifierKeyOS, (m_modifierMask & NSCommandKeyMask) ? true : false);
860         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
861         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
862         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
863         
864     return GHOST_kSuccess;
865 }
866
867 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
868 {
869         buttons.clear();
870     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
871         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
872         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
873         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
874         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
875     return GHOST_kSuccess;
876 }
877
878
879
880 #pragma mark Event handlers
881
882 /**
883  * The event queue polling function
884  */
885 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
886 {
887         bool anyProcessed = false;
888         NSEvent *event;
889         
890         //      SetMouseCoalescingEnabled(false, NULL);
891         //TODO : implement timer ??
892         
893         /*do {
894                 GHOST_TimerManager* timerMgr = getTimerManager();
895                 
896                  if (waitForEvent) {
897                  GHOST_TUns64 next = timerMgr->nextFireTime();
898                  double timeOut;
899                  
900                  if (next == GHOST_kFireTimeNever) {
901                  timeOut = kEventDurationForever;
902                  } else {
903                  timeOut = (double)(next - getMilliSeconds())/1000.0;
904                  if (timeOut < 0.0)
905                  timeOut = 0.0;
906                  }
907                  
908                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
909                  }
910                  
911                  if (timerMgr->fireTimers(getMilliSeconds())) {
912                  anyProcessed = true;
913                  }*/
914                 
915                 do {
916                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
917                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
918                                                                            untilDate:[NSDate distantPast]
919                                                                                   inMode:NSDefaultRunLoopMode
920                                                                                  dequeue:YES];
921                         if (event==nil) {
922                                 [pool drain];
923                                 break;
924                         }
925                         
926                         anyProcessed = true;
927                         
928                         switch ([event type]) {
929                                 case NSKeyDown:
930                                 case NSKeyUp:
931                                 case NSFlagsChanged:
932                                         handleKeyEvent(event);
933                                         
934                                         /* Support system-wide keyboard shortcuts, like Expos√©, ...) =>included in always NSApp sendEvent */
935                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
936                                          [NSApp sendEvent:event];
937                                          }*/
938                                         break;
939                                         
940                                 case NSLeftMouseDown:
941                                 case NSLeftMouseUp:
942                                 case NSRightMouseDown:
943                                 case NSRightMouseUp:
944                                 case NSMouseMoved:
945                                 case NSLeftMouseDragged:
946                                 case NSRightMouseDragged:
947                                 case NSScrollWheel:
948                                 case NSOtherMouseDown:
949                                 case NSOtherMouseUp:
950                                 case NSOtherMouseDragged:
951                                 case NSEventTypeMagnify:
952                                 case NSEventTypeRotate:
953                                 case NSEventTypeBeginGesture:
954                                 case NSEventTypeEndGesture:
955                                         handleMouseEvent(event);
956                                         break;
957                                         
958                                 case NSTabletPoint:
959                                 case NSTabletProximity:
960                                         handleTabletEvent(event,[event type]);
961                                         break;
962                                         
963                                         /* Trackpad features, fired only from OS X 10.5.2
964                                          case NSEventTypeGesture:
965                                          case NSEventTypeSwipe:
966                                          break; */
967                                         
968                                         /*Unused events
969                                          NSMouseEntered       = 8,
970                                          NSMouseExited        = 9,
971                                          NSAppKitDefined      = 13,
972                                          NSSystemDefined      = 14,
973                                          NSApplicationDefined = 15,
974                                          NSPeriodic           = 16,
975                                          NSCursorUpdate       = 17,*/
976                                         
977                                 default:
978                                         break;
979                         }
980                         //Resend event to NSApp to ensure Mac wide events are handled
981                         [NSApp sendEvent:event];
982                         [pool drain];
983                 } while (event!= nil);          
984         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
985         
986         if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent();
987         
988         if (m_outsideLoopEventProcessed) {
989                 m_outsideLoopEventProcessed = false;
990                 return true;
991         }
992         
993         m_ignoreWindowSizedMessages = false;
994         
995     return anyProcessed;
996 }
997
998 //Note: called from NSApplication delegate
999 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
1000 {
1001         //Update the modifiers key mask, as its status may have changed when the application was not active
1002         //(that is when update events are sent to another application)
1003         unsigned int modifiers;
1004         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1005         
1006         if (!window) {
1007                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
1008                 return GHOST_kFailure;
1009         }
1010         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
1011
1012         modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags];
1013         
1014         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1015                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1016         }
1017         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1018                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1019         }
1020         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1021                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1022         }
1023         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1024                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) );
1025         }
1026         
1027         m_modifierMask = modifiers;
1028         
1029         m_outsideLoopEventProcessed = true;
1030         return GHOST_kSuccess;
1031 }
1032
1033 void GHOST_SystemCocoa::notifyExternalEventProcessed()
1034 {
1035         m_outsideLoopEventProcessed = true;
1036 }
1037
1038 //Note: called from NSWindow delegate
1039 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
1040 {
1041         if (!validWindow(window)) {
1042                 return GHOST_kFailure;
1043         }
1044                 switch(eventType) 
1045                 {
1046                         case GHOST_kEventWindowClose:
1047                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
1048                                 break;
1049                         case GHOST_kEventWindowActivate:
1050                                 m_windowManager->setActiveWindow(window);
1051                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1052                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
1053                                 break;
1054                         case GHOST_kEventWindowDeactivate:
1055                                 m_windowManager->setWindowInactive(window);
1056                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
1057                                 break;
1058                         case GHOST_kEventWindowUpdate:
1059                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
1060                                 break;
1061                         case GHOST_kEventWindowMove:
1062                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
1063                                 break;
1064                         case GHOST_kEventWindowSize:
1065                                 if (!m_ignoreWindowSizedMessages)
1066                                 {
1067                                         //Enforce only one resize message per event loop (coalescing all the live resize messages)                                      
1068                                         window->updateDrawingContext();
1069                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1070                                         //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager
1071                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0)));
1072                                         m_ignoreWindowSizedMessages = true;
1073                                 }
1074                                 break;
1075                         default:
1076                                 return GHOST_kFailure;
1077                                 break;
1078                 }
1079         
1080         m_outsideLoopEventProcessed = true;
1081         return GHOST_kSuccess;
1082 }
1083
1084 //Note: called from NSWindow subclass
1085 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
1086                                                                    GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
1087 {
1088         if (!validWindow(window)) {
1089                 return GHOST_kFailure;
1090         }
1091         switch(eventType) 
1092         {
1093                 case GHOST_kEventDraggingEntered:
1094                 case GHOST_kEventDraggingUpdated:
1095                 case GHOST_kEventDraggingExited:
1096                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
1097                         break;
1098                         
1099                 case GHOST_kEventDraggingDropDone:
1100                 {
1101                         GHOST_TUns8 * temp_buff;
1102                         GHOST_TStringArray *strArray;
1103                         NSArray *droppedArray;
1104                         size_t pastedTextSize;  
1105                         NSString *droppedStr;
1106                         GHOST_TEventDataPtr eventData;
1107                         int i;
1108
1109                         if (!data) return GHOST_kFailure;
1110                         
1111                         switch (draggedObjectType) {
1112                                 case GHOST_kDragnDropTypeFilenames:
1113                                         droppedArray = (NSArray*)data;
1114                                         
1115                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
1116                                         if (!strArray) return GHOST_kFailure;
1117                                         
1118                                         strArray->count = [droppedArray count];
1119                                         if (strArray->count == 0) return GHOST_kFailure;
1120                                         
1121                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
1122                                         
1123                                         for (i=0;i<strArray->count;i++)
1124                                         {
1125                                                 droppedStr = [droppedArray objectAtIndex:i];
1126                                                 
1127                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1128                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1129                                         
1130                                                 if (!temp_buff) {
1131                                                         strArray->count = i;
1132                                                         break;
1133                                                 }
1134                                         
1135                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1136                                                 temp_buff[pastedTextSize] = '\0';
1137                                                 
1138                                                 strArray->strings[i] = temp_buff;
1139                                         }
1140
1141                                         eventData = (GHOST_TEventDataPtr) strArray;     
1142                                         break;
1143                                         
1144                                 case GHOST_kDragnDropTypeString:
1145                                         droppedStr = (NSString*)data;
1146                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1147                                         
1148                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1149                                         
1150                                         if (temp_buff == NULL) {
1151                                                 return GHOST_kFailure;
1152                                         }
1153                                         
1154                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1155                                         
1156                                         temp_buff[pastedTextSize] = '\0';
1157                                         
1158                                         eventData = (GHOST_TEventDataPtr) temp_buff;
1159                                         break;
1160                                 
1161                                 case GHOST_kDragnDropTypeBitmap:
1162                                 {
1163                                         NSImage *droppedImg = (NSImage*)data;
1164                                         NSSize imgSize = [droppedImg size];
1165                                         ImBuf *ibuf = NULL;
1166                                         GHOST_TUns8 *rasterRGB = NULL;
1167                                         GHOST_TUns8 *rasterRGBA = NULL;
1168                                         GHOST_TUns8 *toIBuf = NULL;
1169                                         int x, y, to_i, from_i;
1170                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
1171                                         NSEnumerator *enumerator;
1172                                         NSImageRep *representation;
1173                                         
1174                                         ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect);
1175                                         if (!ibuf) {
1176                                                 [droppedImg release];
1177                                                 return GHOST_kFailure;
1178                                         }
1179                                         
1180                                         /*Get the bitmap of the image*/
1181                                         enumerator = [[droppedImg representations] objectEnumerator];
1182                                         while ((representation = [enumerator nextObject])) {
1183                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1184                                                         bitmapImage = (NSBitmapImageRep *)representation;
1185                                                         break;
1186                                                 }
1187                                         }
1188                                         if (bitmapImage == nil) return GHOST_kFailure;
1189                                         
1190                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
1191                                                 && ![bitmapImage isPlanar]) {
1192                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
1193                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1194                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
1195                                                 for (y = 0; y < imgSize.height; y++) {
1196                                                         to_i = (imgSize.height-y-1)*imgSize.width;
1197                                                         from_i = y*imgSize.width;
1198                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
1199                                                 }
1200                                         }
1201                                         else {
1202                                                 /* Tell cocoa image resolution is same as current system one */
1203                                                 [bitmapImage setSize:imgSize];
1204                                                 
1205                                                 /* Convert the image in a RGBA 32bit format */
1206                                                 /* As Core Graphics does not support contextes with non premutliplied alpha,
1207                                                  we need to get alpha key values in a separate batch */
1208                                                 
1209                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
1210                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1211                                                                                                                                                                                  pixelsWide:imgSize.width 
1212                                                                                                                                                                                  pixelsHigh:imgSize.height
1213                                                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
1214                                                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
1215                                                                                                                                                                            bitmapFormat:(NSBitmapFormat)0
1216                                                                                                                                                                                 bytesPerRow:4*imgSize.width
1217                                                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
1218                                                 
1219                                                 [NSGraphicsContext saveGraphicsState];
1220                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1221                                                 [bitmapImage draw];
1222                                                 [NSGraphicsContext restoreGraphicsState];
1223                                                 
1224                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1225                                                 if (rasterRGB == NULL) {
1226                                                         [bitmapImage release];
1227                                                         [blBitmapFormatImageRGB release];
1228                                                         [droppedImg release];
1229                                                         return GHOST_kFailure;
1230                                                 }
1231                                                 
1232                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1233                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1234                                                                                                                                                                                   pixelsWide:imgSize.width
1235                                                                                                                                                                                   pixelsHigh:imgSize.height
1236                                                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1237                                                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
1238                                                                                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1239                                                                                                                                                                                  bytesPerRow:4*imgSize.width
1240                                                                                                                                                                                 bitsPerPixel:32/* RGBA */];
1241                                                 
1242                                                 [NSGraphicsContext saveGraphicsState];
1243                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1244                                                 [bitmapImage draw];
1245                                                 [NSGraphicsContext restoreGraphicsState];
1246                                                 
1247                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1248                                                 if (rasterRGBA == NULL) {
1249                                                         [bitmapImage release];
1250                                                         [blBitmapFormatImageRGB release];
1251                                                         [blBitmapFormatImageRGBA release];
1252                                                         [droppedImg release];
1253                                                         return GHOST_kFailure;
1254                                                 }
1255                                                 
1256                                                 /*Copy the image to ibuf, flipping it vertically*/
1257                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1258                                                 for (y = 0; y < imgSize.height; y++) {
1259                                                         for (x = 0; x < imgSize.width; x++) {
1260                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1261                                                                 from_i = y*imgSize.width + x;
1262                                                                 
1263                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1264                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1265                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1266                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1267                                                         }
1268                                                 }
1269                                                 
1270                                                 [blBitmapFormatImageRGB release];
1271                                                 [blBitmapFormatImageRGBA release];
1272                                                 [droppedImg release];
1273                                         }
1274                                         
1275                                         eventData = (GHOST_TEventDataPtr) ibuf;
1276                                 }
1277                                         break;
1278                                         
1279                                 default:
1280                                         return GHOST_kFailure;
1281                                         break;
1282                         }
1283                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1284                 }
1285                         break;
1286                 default:
1287                         return GHOST_kFailure;
1288         }
1289         m_outsideLoopEventProcessed = true;
1290         return GHOST_kSuccess;
1291 }
1292
1293
1294 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1295 {
1296         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1297         
1298         //Discard quit event if we are in cursor grab sequence
1299         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1300                 return GHOST_kExitCancel;
1301         
1302         //Check open windows if some changes are not saved
1303         if (m_windowManager->getAnyModifiedState())
1304         {
1305                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
1306                                                                                  @"Cancel", @"Quit Anyway", nil);
1307                 if (shouldQuit == NSAlertAlternateReturn)
1308                 {
1309                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1310                         return GHOST_kExitNow;
1311                 } else {
1312                         //Give back focus to the blender window if user selected cancel quit
1313                         NSArray *windowsList = [NSApp orderedWindows];
1314                         if ([windowsList count]) {
1315                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1316                                 //Handle the modifiers keyes changed state issue
1317                                 //as recovering from the quit dialog is like application
1318                                 //gaining focus back.
1319                                 //Main issue fixed is Cmd modifier not being cleared
1320                                 handleApplicationBecomeActiveEvent();
1321                         }
1322                 }
1323
1324         }
1325         else {
1326                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1327                 m_outsideLoopEventProcessed = true;
1328                 return GHOST_kExitNow;
1329         }
1330         
1331         return GHOST_kExitCancel;
1332 }
1333
1334 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1335 {
1336         NSString *filepath = (NSString*)filepathStr;
1337         int confirmOpen = NSAlertAlternateReturn;
1338         NSArray *windowsList;
1339         char * temp_buff;
1340         size_t filenameTextSize;        
1341         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1342         
1343         if (!window) {
1344                 return NO;
1345         }       
1346         
1347         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1348         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1349                 return GHOST_kExitCancel;
1350
1351         //Check open windows if some changes are not saved
1352         if (m_windowManager->getAnyModifiedState())
1353         {
1354                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1355                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1356                                                                                  @"Cancel", @"Open", nil);
1357         }
1358
1359         //Give back focus to the blender window
1360         windowsList = [NSApp orderedWindows];
1361         if ([windowsList count]) {
1362                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1363         }
1364
1365         if (confirmOpen == NSAlertAlternateReturn)
1366         {
1367                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1368                 
1369                 temp_buff = (char*) malloc(filenameTextSize+1); 
1370                 
1371                 if (temp_buff == NULL) {
1372                         return GHOST_kFailure;
1373                 }
1374                 
1375                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize);
1376                 
1377                 temp_buff[filenameTextSize] = '\0';
1378
1379                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1380
1381                 return YES;
1382         }
1383         else return NO;
1384 }
1385
1386 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1387 {
1388         NSEvent *event = (NSEvent *)eventPtr;
1389         GHOST_IWindow* window;
1390         
1391         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1392         if (!window) {
1393                 //printf("\nW failure for event 0x%x",[event type]);
1394                 return GHOST_kFailure;
1395         }
1396         
1397         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1398         
1399         switch (eventType) {
1400                 case NSTabletPoint:
1401                         ct.Pressure = [event pressure];
1402                         ct.Xtilt = [event tilt].x;
1403                         ct.Ytilt = [event tilt].y;
1404                         break;
1405                 
1406                 case NSTabletProximity:
1407                         ct.Pressure = 0;
1408                         ct.Xtilt = 0;
1409                         ct.Ytilt = 0;
1410                         if ([event isEnteringProximity])
1411                         {
1412                                 //pointer is entering tablet area proximity
1413                                 switch ([event pointingDeviceType]) {
1414                                         case NSPenPointingDevice:
1415                                                 ct.Active = GHOST_kTabletModeStylus;
1416                                                 break;
1417                                         case NSEraserPointingDevice:
1418                                                 ct.Active = GHOST_kTabletModeEraser;
1419                                                 break;
1420                                         case NSCursorPointingDevice:
1421                                         case NSUnknownPointingDevice:
1422                                         default:
1423                                                 ct.Active = GHOST_kTabletModeNone;
1424                                                 break;
1425                                 }
1426                         } else {
1427                                 // pointer is leaving - return to mouse
1428                                 ct.Active = GHOST_kTabletModeNone;
1429                         }
1430                         break;
1431                 
1432                 default:
1433                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1434                         return GHOST_kFailure;
1435                         break;
1436         }
1437         return GHOST_kSuccess;
1438 }
1439
1440
1441 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1442 {
1443         NSEvent *event = (NSEvent *)eventPtr;
1444     GHOST_Window* window;
1445         
1446         window = (GHOST_Window*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1447         if (!window) {
1448                 //printf("\nW failure for event 0x%x",[event type]);
1449                 return GHOST_kFailure;
1450         }
1451         
1452         switch ([event type])
1453     {
1454                 case NSLeftMouseDown:
1455                 case NSRightMouseDown:
1456                 case NSOtherMouseDown:
1457                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1458                         //Handle tablet events combined with mouse events
1459                         switch ([event subtype]) {
1460                                 case NX_SUBTYPE_TABLET_POINT:
1461                                         handleTabletEvent(eventPtr, NSTabletPoint);
1462                                         break;
1463                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1464                                         handleTabletEvent(eventPtr, NSTabletProximity);
1465                                         break;
1466                                 default:
1467                                         //No tablet event included : do nothing
1468                                         break;
1469                         }
1470                         break;
1471                                                 
1472                 case NSLeftMouseUp:
1473                 case NSRightMouseUp:
1474                 case NSOtherMouseUp:
1475                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1476                         //Handle tablet events combined with mouse events
1477                         switch ([event subtype]) {
1478                                 case NX_SUBTYPE_TABLET_POINT:
1479                                         handleTabletEvent(eventPtr, NSTabletPoint);
1480                                         break;
1481                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1482                                         handleTabletEvent(eventPtr, NSTabletProximity);
1483                                         break;
1484                                 default:
1485                                         //No tablet event included : do nothing
1486                                         break;
1487                         }
1488                         break;
1489                         
1490                 case NSLeftMouseDragged:
1491                 case NSRightMouseDragged:
1492                 case NSOtherMouseDragged:                               
1493                         //Handle tablet events combined with mouse events
1494                         switch ([event subtype]) {
1495                                 case NX_SUBTYPE_TABLET_POINT:
1496                                         handleTabletEvent(eventPtr, NSTabletPoint);
1497                                         break;
1498                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1499                                         handleTabletEvent(eventPtr, NSTabletProximity);
1500                                         break;
1501                                 default:
1502                                         //No tablet event included : do nothing
1503                                         break;
1504                         }
1505                         
1506                 case NSMouseMoved:
1507                                 switch (window->getCursorGrabMode()) {
1508                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1509                                         {
1510                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1511                                                 
1512                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1513                                                 
1514                                                 window->getCursorGrabAccum(x_accum, y_accum);
1515                                                 x_accum += [event deltaX];
1516                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1517                                                 window->setCursorGrabAccum(x_accum, y_accum);
1518                                                 
1519                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1520                                         }
1521                                                 break;
1522                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1523                                         {
1524                                                 NSPoint mousePos = [event locationInWindow];
1525                                                 GHOST_TInt32 x_mouse= mousePos.x;
1526                                                 GHOST_TInt32 y_mouse= mousePos.y;
1527                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1528                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1529                                                 
1530                                                 /* fallback to window bounds */
1531                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1532                                                         window->getClientBounds(bounds);
1533                                                 
1534                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1535                                                 window->getClientBounds(windowBounds);
1536                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1537                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1538                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1539                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1540                                                 
1541                                                 //Update accumulation counts
1542                                                 window->getCursorGrabAccum(x_accum, y_accum);
1543                                                 x_accum += [event deltaX]-m_cursorDelta_x;
1544                                                 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1545                                                 window->setCursorGrabAccum(x_accum, y_accum);
1546                                                 
1547                                                 
1548                                                 //Warp mouse cursor if needed
1549                                                 x_mouse += [event deltaX]-m_cursorDelta_x;
1550                                                 y_mouse += -[event deltaY]-m_cursorDelta_y;
1551                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1552                                                 
1553                                                 //Compensate for mouse moved event taking cursor position set into account
1554                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1555                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1556                                                 
1557                                                 //Set new cursor position
1558                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1559                                                 setMouseCursorPosition(x_cur, y_cur); /* wrap */
1560                                                 
1561                                                 //Post event
1562                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1563                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1564                                         }
1565                                                 break;
1566                                         default:
1567                                         {
1568                                                 //Normal cursor operation: send mouse position in window
1569                                                 NSPoint mousePos = [event locationInWindow];
1570                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1571                                                 m_cursorDelta_x=0;
1572                                                 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter
1573                                         }
1574                                                 break;
1575                                 }
1576                                 break;
1577                         
1578                 case NSScrollWheel:
1579                         {
1580                                 /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */
1581                                 if (!m_hasMultiTouchTrackpad || !m_isGestureInProgress) {
1582                                         GHOST_TInt32 delta;
1583                                         
1584                                         double deltaF = [event deltaY];
1585                                         if (deltaF == 0.0) break; //discard trackpad delta=0 events
1586                                         
1587                                         delta = deltaF > 0.0 ? 1 : -1;
1588                                         pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta));
1589                                 }
1590                                 else {
1591                                         NSPoint mousePos = [event locationInWindow];
1592                                         double dx = [event deltaX];
1593                                         double dy = -[event deltaY];
1594                                         
1595                                         const double deltaMax = 50.0;
1596                                         
1597                                         if ((dx == 0) && (dy == 0)) break;
1598                                         
1599                                         /* Quadratic acceleration */
1600                                         dx = dx*(fabs(dx)+0.5);
1601                                         if (dx<0.0) dx-=0.5; else dx+=0.5;
1602                                         if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax;
1603                                         
1604                                         dy = dy*(fabs(dy)+0.5);
1605                                         if (dy<0.0) dy-=0.5; else dy+=0.5;
1606                                         if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax;
1607
1608                                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, mousePos.x, mousePos.y, dx, dy));
1609                                 }
1610                         }
1611                         break;
1612                         
1613                 case NSEventTypeMagnify:
1614                         {
1615                                 NSPoint mousePos = [event locationInWindow];
1616                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, mousePos.x, mousePos.y,
1617                                                                                                   [event magnification]*250.0 + 0.1, 0));
1618                         }
1619                         break;
1620
1621                 case NSEventTypeRotate:
1622                         {
1623                                 NSPoint mousePos = [event locationInWindow];
1624                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, mousePos.x, mousePos.y,
1625                                                                                                   -[event rotation] * 5.0, 0));
1626                         }
1627                 case NSEventTypeBeginGesture:
1628                         m_isGestureInProgress = true;
1629                         break;
1630                 case NSEventTypeEndGesture:
1631                         m_isGestureInProgress = false;
1632                         break;
1633                 default:
1634                         return GHOST_kFailure;
1635                         break;
1636                 }
1637         
1638         return GHOST_kSuccess;
1639 }
1640
1641
1642 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1643 {
1644         NSEvent *event = (NSEvent *)eventPtr;
1645         GHOST_IWindow* window;
1646         unsigned int modifiers;
1647         NSString *characters;
1648         NSData *convertedCharacters;
1649         GHOST_TKey keyCode;
1650         unsigned char ascii;
1651         NSString* charsIgnoringModifiers;
1652
1653         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1654         if (!window) {
1655                 //printf("\nW failure for event 0x%x",[event type]);
1656                 return GHOST_kFailure;
1657         }
1658         
1659         switch ([event type]) {
1660                 case NSKeyDown:
1661                 case NSKeyUp:
1662                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1663                         if ([charsIgnoringModifiers length]>0)
1664                                 keyCode = convertKey([event keyCode],
1665                                                                          [charsIgnoringModifiers characterAtIndex:0],
1666                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1667                         else
1668                                 keyCode = convertKey([event keyCode],0,
1669                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1670
1671                                 
1672                         characters = [event characters];
1673                         if ([characters length]>0) { //Check for dead keys
1674                                 //Convert characters to iso latin 1 encoding
1675                                 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding];
1676                                 if ([convertedCharacters length]>0)
1677                                         ascii =((char*)[convertedCharacters bytes])[0];
1678                                 else
1679                                         ascii = 0; //Character not available in iso latin 1 encoding
1680                         }
1681                         else
1682                                 ascii= 0;
1683                         
1684                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1685                                 break; //Cmd-Q is directly handled by Cocoa
1686
1687                         if ([event type] == NSKeyDown) {
1688                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
1689                                 //printf("\nKey down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1690                         } else {
1691                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
1692                                 //printf("\nKey up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1693                         }
1694                         break;
1695         
1696                 case NSFlagsChanged: 
1697                         modifiers = [event modifierFlags];
1698                         
1699                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1700                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1701                         }
1702                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1703                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1704                         }
1705                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1706                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1707                         }
1708                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1709                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) );
1710                         }
1711                         
1712                         m_modifierMask = modifiers;
1713                         break;
1714                         
1715                 default:
1716                         return GHOST_kFailure;
1717                         break;
1718         }
1719         
1720         return GHOST_kSuccess;
1721 }
1722
1723
1724
1725 #pragma mark Clipboard get/set
1726
1727 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1728 {
1729         GHOST_TUns8 * temp_buff;
1730         size_t pastedTextSize;  
1731         
1732         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1733         
1734         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1735         
1736         if (pasteBoard == nil) {
1737                 [pool drain];
1738                 return NULL;
1739         }
1740         
1741         NSArray *supportedTypes =
1742                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1743         
1744         NSString *bestType = [[NSPasteboard generalPasteboard]
1745                                                   availableTypeFromArray:supportedTypes];
1746         
1747         if (bestType == nil) {
1748                 [pool drain];
1749                 return NULL;
1750         }
1751         
1752         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1753
1754         if (textPasted == nil) {
1755                 [pool drain];
1756                 return NULL;
1757         }
1758         
1759         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1760         
1761         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1762
1763         if (temp_buff == NULL) {
1764                 [pool drain];
1765                 return NULL;
1766         }
1767         
1768         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1769         
1770         temp_buff[pastedTextSize] = '\0';
1771         
1772         [pool drain];
1773
1774         if(temp_buff) {
1775                 return temp_buff;
1776         } else {
1777                 return NULL;
1778         }
1779 }
1780
1781 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1782 {
1783         NSString *textToCopy;
1784         
1785         if(selection) {return;} // for copying the selection, used on X11
1786
1787         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1788                 
1789         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1790         
1791         if (pasteBoard == nil) {
1792                 [pool drain];
1793                 return;
1794         }
1795         
1796         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1797         
1798         [pasteBoard declareTypes:supportedTypes owner:nil];
1799         
1800         textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding];
1801         
1802         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1803         
1804         [pool drain];
1805 }
1806