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