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