merged 30707:31141 from trunk
[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_NDOFManagerCocoa.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                                         UInt32 deadKeyState=0;
375                                         UniCharCount actualStrLength=0;
376
377                                         UCKeyTranslate(uchrData, rawCode, keyAction, 0,
378                                                         LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar);
379                                 }
380 #else
381                                 /* Leopard and Snow Leopard 64bit compatible API*/
382                                 CFDataRef uchrHandle; /*the keyboard layout*/
383                                 TISInputSourceRef kbdTISHandle;
384
385                                 kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource();
386                                 uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData);
387                                 CFRelease(kbdTISHandle);
388
389                                 /*get actual character value of the "remappable" keys in int'l keyboards,
390                                  if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger),
391                                  then fallback on using the received charactersIgnoringModifiers */
392                                 if (uchrHandle) {
393                                         UInt32 deadKeyState=0;
394                                         UniCharCount actualStrLength=0;
395
396                                         UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0,
397                                                                    LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar);
398                                 }
399 #endif
400                                 switch (recvChar) {
401                                         case '-':       return GHOST_kKeyMinus;
402                                         case '=':       return GHOST_kKeyEqual;
403                                         case ',':       return GHOST_kKeyComma;
404                                         case '.':       return GHOST_kKeyPeriod;
405                                         case '/':       return GHOST_kKeySlash;
406                                         case ';':       return GHOST_kKeySemicolon;
407                                         case '\'':      return GHOST_kKeyQuote;
408                                         case '\\':      return GHOST_kKeyBackslash;
409                                         case '[':       return GHOST_kKeyLeftBracket;
410                                         case ']':       return GHOST_kKeyRightBracket;
411                                         case '`':       return GHOST_kKeyAccentGrave;
412                                         default:
413                                                 return GHOST_kKeyUnknown;
414                                 }
415                         }
416         }
417         return GHOST_kKeyUnknown;
418 }
419
420
421 #pragma mark defines for 10.6 api not documented in 10.5
422 #ifndef MAC_OS_X_VERSION_10_6
423 enum {
424         /* The following event types are available on some hardware on 10.5.2 and later */
425         NSEventTypeGesture          = 29,
426         NSEventTypeMagnify          = 30,
427         NSEventTypeSwipe            = 31,
428         NSEventTypeRotate           = 18,
429         NSEventTypeBeginGesture     = 19,
430         NSEventTypeEndGesture       = 20
431 };
432
433 @interface NSEvent(GestureEvents)
434 /* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */
435 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
436 - (float)magnification;       // change in magnification.
437 #else
438 - (CGFloat)magnification;       // change in magnification.
439 #endif
440 @end 
441
442 #endif
443
444
445 #pragma mark Utility functions
446
447 #define FIRSTFILEBUFLG 512
448 static bool g_hasFirstFile = false;
449 static char g_firstFileBuf[512];
450
451 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
452 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
453         if (g_hasFirstFile) {
454                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
455                 buf[FIRSTFILEBUFLG - 1] = '\0';
456                 return 1;
457         } else {
458                 return 0; 
459         }
460 }
461
462 #if defined(WITH_QUICKTIME) && !defined(USE_QTKIT)
463 //Need to place this quicktime function in an ObjC file
464 //It is used to avoid memory leak when raising the quicktime "compression settings" standard dialog
465 extern "C" {
466         struct bContext;
467         struct wmOperator;
468         extern int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op);
469
470
471 int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
472 {
473         int result;
474         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
475
476         result = fromcocoa_request_qtcodec_settings(C, op);
477
478         [pool drain];
479         return result;
480 }
481 };
482 #endif
483
484
485 #pragma mark Cocoa objects
486
487 /**
488  * CocoaAppDelegate
489  * ObjC object to capture applicationShouldTerminate, and send quit event
490  **/
491 @interface CocoaAppDelegate : NSObject {
492         GHOST_SystemCocoa *systemCocoa;
493 }
494 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
495 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
496 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
497 - (void)applicationWillTerminate:(NSNotification *)aNotification;
498 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
499 @end
500
501 @implementation CocoaAppDelegate : NSObject
502 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
503 {
504         systemCocoa = sysCocoa;
505 }
506
507 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
508 {
509         return systemCocoa->handleOpenDocumentRequest(filename);
510 }
511
512 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
513 {
514         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
515         //Note that Cmd+Q is already handled by keyhandler
516     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
517                 return NSTerminateCancel;//NSTerminateNow;
518         else
519                 return NSTerminateCancel;
520 }
521
522 // To avoid cancelling a log off process, we must use Cocoa termination process
523 // And this function is the only chance to perform clean up
524 // So WM_exit needs to be called directly, as the event loop will never run before termination
525 - (void)applicationWillTerminate:(NSNotification *)aNotification
526 {
527         /*G.afbreek = 0; //Let Cocoa perform the termination at the end
528         WM_exit(C);*/
529 }
530
531 - (void)applicationWillBecomeActive:(NSNotification *)aNotification
532 {
533         systemCocoa->handleApplicationBecomeActiveEvent();
534 }
535 @end
536
537
538
539 #pragma mark initialization/finalization
540
541
542 GHOST_SystemCocoa::GHOST_SystemCocoa()
543 {
544         int mib[2];
545         struct timeval boottime;
546         size_t len;
547         char *rstring = NULL;
548
549         m_modifierMask =0;
550         m_pressedMouseButtons =0;
551         m_isGestureInProgress = false;
552         m_cursorDelta_x=0;
553         m_cursorDelta_y=0;
554         m_tablet_mouse_id = TOOL_ID_NONE;
555         m_tablet_pen_id = TOOL_ID_NONE;
556         m_tablet_pen_mode = GHOST_kTabletModeNone;
557         m_outsideLoopEventProcessed = false;
558         m_needDelayedApplicationBecomeActiveEventProcessing = false;
559         m_displayManager = new GHOST_DisplayManagerCocoa ();
560         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
561         m_displayManager->initialize();
562
563         //NSEvent timeStamp is given in system uptime, state start date is boot time
564         mib[0] = CTL_KERN;
565         mib[1] = KERN_BOOTTIME;
566         len = sizeof(struct timeval);
567
568         sysctl(mib, 2, &boottime, &len, NULL, 0);
569         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
570
571         m_start_time_2 = CFAbsoluteTimeGetCurrent();
572
573         //Detect multitouch trackpad
574         mib[0] = CTL_HW;
575         mib[1] = HW_MODEL;
576         sysctl( mib, 2, NULL, &len, NULL, 0 );
577         rstring = (char*)malloc( len );
578         sysctl( mib, 2, rstring, &len, NULL, 0 );
579
580         //Hack on MacBook revision, as multitouch avail. function missing
581         m_hasMultiTouchTrackpad =
582                 (strstr(rstring,"MacBookAir") ||
583                 (strstr(rstring,"MacBook") && (rstring[strlen(rstring)-3]>='5') && (rstring[strlen(rstring)-3]<='9')));
584
585         free( rstring );
586         rstring = NULL;
587
588         m_ignoreWindowSizedMessages = false;
589
590         m_input_fidelity_hint = HI_FI; // just for testing...
591 }
592
593 GHOST_SystemCocoa::~GHOST_SystemCocoa()
594 {
595 }
596
597
598 GHOST_TSuccess GHOST_SystemCocoa::init()
599 {
600         GHOST_TSuccess success = GHOST_System::init();
601         if (success) {
602
603                 m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
604                 
605                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
606                 if (NSApp == nil) {
607                         [NSApplication sharedApplication];
608
609                         if ([NSApp mainMenu] == nil) {
610                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
611                                 NSMenuItem *menuItem;
612                                 NSMenu *windowMenu;
613                                 NSMenu *appMenu;
614
615                                 //Create the application menu
616                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
617
618                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
619                                 [appMenu addItem:[NSMenuItem separatorItem]];
620
621                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
622                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
623
624                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
625                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
626
627                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
628
629                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
630                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
631
632                                 menuItem = [[NSMenuItem alloc] init];
633                                 [menuItem setSubmenu:appMenu];
634
635                                 [mainMenubar addItem:menuItem];
636                                 [menuItem release];
637                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
638                                 [appMenu release];
639
640                                 //Create the window menu
641                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
642
643                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
644                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
645
646                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
647
648                                 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
649                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
650
651                                 menuItem = [[NSMenuItem alloc] init];
652                                 [menuItem setSubmenu:windowMenu];
653
654                                 [mainMenubar addItem:menuItem];
655                                 [menuItem release];
656
657                                 [NSApp setMainMenu:mainMenubar];
658                                 [NSApp setWindowsMenu:windowMenu];
659                                 [windowMenu release];
660                         }
661                 }
662                 if ([NSApp delegate] == nil) {
663                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
664                         [appDelegate setSystemCocoa:this];
665                         [NSApp setDelegate:appDelegate];
666                 }
667
668                 [NSApp finishLaunching];
669
670                 [pool drain];
671         }
672         return success;
673 }
674
675
676 #pragma mark window management
677
678 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
679 {
680 /*
681         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
682         struct timeval currentTime;
683
684         gettimeofday(&currentTime, NULL);
685
686         //Return timestamp of system uptime
687         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
688 */
689
690         double now = CFAbsoluteTimeGetCurrent();
691         return 1000 * (now - m_start_time_2);
692 }
693
694
695 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
696 {
697         //Note that OS X supports monitor hot plug
698         // We do not support multiple monitors at the moment
699         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
700
701         GHOST_TUns8 count = [[NSScreen screens] count];
702
703         [pool drain];
704         return count;
705 }
706
707
708 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
709 {
710         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
711         //Get visible frame, that is frame excluding dock and top menu bar
712         NSRect frame = [[NSScreen mainScreen] visibleFrame];
713
714         //Returns max window contents (excluding title bar...)
715         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
716                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
717
718         width = contentRect.size.width;
719         height = contentRect.size.height;
720
721         [pool drain];
722 }
723
724
725 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
726         const STR_String& title,
727         GHOST_TInt32 left,
728         GHOST_TInt32 top,
729         GHOST_TUns32 width,
730         GHOST_TUns32 height,
731         GHOST_TWindowState state,
732         GHOST_TDrawingContextType type,
733         bool stereoVisual,
734         const GHOST_TUns16 numOfAASamples,
735         const GHOST_TEmbedderWindowID parentWindow
736 )
737 {
738         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
739         GHOST_IWindow* window = 0;
740
741         //Get the available rect for including window contents
742         NSRect frame = [[NSScreen mainScreen] visibleFrame];
743         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
744                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
745
746         //Ensures window top left is inside this available rect
747         left = left > contentRect.origin.x ? left : contentRect.origin.x;
748         top = top > contentRect.origin.y ? top : contentRect.origin.y;
749
750         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
751
752         if (window) {
753                 if (window->getValid()) {
754                         // Store the pointer to the window 
755                         GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
756                         m_windowManager->addWindow(window);
757                         m_windowManager->setActiveWindow(window);
758                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
759                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
760                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
761                 }
762                 else {
763                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
764                 delete window;
765                 window = 0;
766                 }
767         }
768         else {
769                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
770         }
771         [pool drain];
772         return window;
773 }
774
775 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
776 {
777         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
778         *window = currentWindow;
779
780         if(!currentWindow) return GHOST_kFailure;
781
782         return currentWindow->setState(GHOST_kWindowStateFullScreen);
783 }
784
785 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
786 {
787         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
788         if(!currentWindow) return GHOST_kFailure;
789
790         return currentWindow->setState(GHOST_kWindowStateNormal);
791 }
792
793
794         
795 /**
796  * @note : returns coordinates in Cocoa screen coordinates
797  */
798 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
799 {
800         NSPoint mouseLoc = [NSEvent mouseLocation];
801
802         // Returns the mouse location in screen coordinates
803         x = (GHOST_TInt32)mouseLoc.x;
804         y = (GHOST_TInt32)mouseLoc.y;
805
806         return GHOST_kSuccess;
807 }
808
809 /**
810  * @note : expect Cocoa screen coordinates
811  */
812 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
813 {
814         GHOST_TInt32 wx,wy;
815         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
816         if (!window) return GHOST_kFailure;
817
818         //Cursor and mouse dissociation placed here not to interfere with continuous grab
819         // (in cont. grab setMouseCursorPosition is directly called)
820         CGAssociateMouseAndMouseCursorPosition(false);
821         setMouseCursorPosition(x, y);
822         CGAssociateMouseAndMouseCursorPosition(true);
823         
824         //Force mouse move event (not pushed by Cocoa)
825         window->screenToClient(x, y, wx, wy);
826         pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, wx,wy));
827         m_outsideLoopEventProcessed = true;
828
829         return GHOST_kSuccess;
830 }
831
832 GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(float xf, float yf)
833 {
834 //      float xf=(float)x, yf=(float)y;
835         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
836         if (!window) return GHOST_kFailure;
837
838         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
839         NSScreen *windowScreen = window->getScreen();
840         NSRect screenRect = [windowScreen frame];
841
842         //Set position relative to current screen
843         xf -= screenRect.origin.x;
844         yf -= screenRect.origin.y;
845
846         //Quartz Display Services uses the old coordinates (top left origin)
847         yf = screenRect.size.height -yf;
848         
849         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
850
851         [pool drain];
852         return GHOST_kSuccess;
853 }
854
855
856 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
857 {
858         keys.set(GHOST_kModifierKeyCommand, (m_modifierMask & NSCommandKeyMask) ? true : false);
859         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
860         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
861         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
862
863         return GHOST_kSuccess;
864 }
865
866 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
867 {
868         buttons.clear();
869         buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
870         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
871         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
872         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
873         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
874         return GHOST_kSuccess;
875 }
876
877
878
879 #pragma mark Event handlers
880
881 /**
882  * The event queue polling function
883  */
884 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
885 {
886         bool anyProcessed = false;
887         NSEvent *event;
888         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
889
890         //TODO : implement timer ??
891         
892         do {
893                 event = [NSApp nextEventMatchingMask:NSAnyEventMask
894                                                                         untilDate:[NSDate distantPast]
895                                                                           inMode:NSDefaultRunLoopMode
896                                                                          dequeue:YES];
897                 if (event==nil)
898                         break;
899                 
900                 anyProcessed = true;
901                 
902                 switch ([event type]) {
903                         case NSKeyDown:
904                                 if ([event isARepeat])
905                                         break;
906                                 // else fall through
907                         case NSKeyUp:
908                         case NSFlagsChanged:
909                                 handleKeyEvent(event);
910                                 // resend to ensure Mac-wide events are handled
911                                 [NSApp sendEvent:event];
912                                 break;
913                                 
914                         case NSLeftMouseDown:
915                         case NSLeftMouseUp:
916                         case NSLeftMouseDragged:
917                         case NSRightMouseDown:
918                         case NSRightMouseUp:
919                         case NSRightMouseDragged:
920                         case NSOtherMouseDown:
921                         case NSOtherMouseUp:
922                         case NSOtherMouseDragged:
923                         case NSMouseMoved:
924                                 switch ([event subtype])
925                                         {
926                                         case NSMouseEventSubtype:
927                                                 handleMouseEvent(event);
928                                                 break;
929                                         case NSTabletPointEventSubtype:
930                                                 if ([event deviceID] == m_tablet_mouse_id)
931                                                         handleMouseEvent(event);
932                                                 else
933                                                         handleTabletEvent(event);
934                                                 break;
935                                         case NSTabletProximityEventSubtype:
936                                                 // I think only LMB down/up sends this.
937                                                 // Always preceded by a real NSTabletProximity event, so it's redundant.
938                                                 // handleTabletProximity(event);
939                                                 break;
940
941                                         // Mac OS 10.6 introduces a Touch subtype
942                                         // that we ignore for now.
943                                         }
944                                 break;
945
946                         case NSScrollWheel:
947                                 handleMouseEvent(event);
948                                 break;
949                                 
950                         case NSTabletProximity:
951                                 handleTabletProximity(event);
952                                 break;
953
954                         case NSTabletPoint:
955                                 if ([event deviceID] == m_tablet_pen_id)
956                                         handleTabletEvent(event);
957                                 else {
958                                         // Treat tablet mouse like any other mouse.
959                                         // TODO: teach Windows and Linux the same trick
960
961                                         // It continues to send events even when still, to mimic the pen's
962                                         // ability to vary pressure without moving. Since the mouse is
963                                         // unable to vary its pressure, filter them out as noise!
964
965                                         bool didMove = [event deltaX] != 0 and [event deltaY] != 0;
966                                         if (didMove)
967                                                 handleMouseEvent(event);
968                                         // LMB Down gets sent for the initial point (and LMB Up for the final), so this is safe.
969                                 }
970                                 break;
971                                 
972                         case NSEventTypeMagnify:
973                         case NSEventTypeRotate:
974                         case NSEventTypeBeginGesture:
975                         case NSEventTypeEndGesture:
976                                 handleMouseEvent(event);
977                                 // break out into handleGestureEvent?
978                                 break;
979
980                         /* Trackpad features, fired only from OS X 10.5.2
981                                  case NSEventTypeGesture:
982                                  case NSEventTypeSwipe:
983                                  break; */
984
985                         default:
986                                 break;
987                         /* Unused events:
988                                 NSMouseEntered       = 8,
989                                 NSMouseExited        = 9,
990                                 NSAppKitDefined      = 13,
991                                 NSSystemDefined      = 14,
992                                 NSApplicationDefined = 15,
993                                 NSPeriodic           = 16,
994                                 NSCursorUpdate       = 17,*/
995                 }
996         } while (event != nil);         
997
998         [pool drain];
999
1000         if (m_needDelayedApplicationBecomeActiveEventProcessing)
1001                 handleApplicationBecomeActiveEvent();
1002
1003         if (m_outsideLoopEventProcessed) {
1004                 m_outsideLoopEventProcessed = false;
1005                 return true;
1006         }
1007
1008         return anyProcessed;
1009 }
1010
1011
1012 //Note: called from NSApplication delegate
1013 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
1014 {
1015         //Update the modifiers key mask, as its status may have changed when the application was not active
1016         //(that is when update events are sent to another application)
1017         unsigned int modifiers;
1018         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1019
1020         if (!window) {
1021                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
1022                 return GHOST_kFailure;
1023         }
1024         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
1025
1026         modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags];
1027
1028         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1029                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1030         }
1031         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1032                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1033         }
1034         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1035                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1036         }
1037         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1038                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1039         }
1040
1041         m_modifierMask = modifiers;
1042
1043         m_outsideLoopEventProcessed = true;
1044         return GHOST_kSuccess;
1045 }
1046
1047 void GHOST_SystemCocoa::notifyExternalEventProcessed()
1048 {
1049         m_outsideLoopEventProcessed = true;
1050 }
1051
1052 //Note: called from NSWindow delegate
1053 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
1054 {
1055         if (!validWindow(window)) {
1056                 return GHOST_kFailure;
1057         }
1058
1059         switch(eventType) {
1060                 case GHOST_kEventWindowClose:
1061                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
1062                         break;
1063                 case GHOST_kEventWindowActivate:
1064                         m_windowManager->setActiveWindow(window);
1065                         window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1066                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
1067                         break;
1068                 case GHOST_kEventWindowDeactivate:
1069                         m_windowManager->setWindowInactive(window);
1070                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
1071                         break;
1072                 case GHOST_kEventWindowUpdate:
1073                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
1074                         break;
1075                 case GHOST_kEventWindowMove:
1076                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
1077                         break;
1078                 case GHOST_kEventWindowSize:
1079                         if (!m_ignoreWindowSizedMessages) {
1080                                 window->updateDrawingContext();
1081                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1082                         }
1083                         break;
1084                 default:
1085                         return GHOST_kFailure;
1086                         break;
1087         }
1088         
1089         m_outsideLoopEventProcessed = true;
1090         return GHOST_kSuccess;
1091 }
1092
1093 //Note: called from NSWindow subclass
1094 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
1095                                                                    GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
1096 {
1097         if (!validWindow(window)) {
1098                 return GHOST_kFailure;
1099         }
1100
1101         switch(eventType) {
1102                 case GHOST_kEventDraggingEntered:
1103                 case GHOST_kEventDraggingUpdated:
1104                 case GHOST_kEventDraggingExited:
1105                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
1106                         break;
1107
1108                 case GHOST_kEventDraggingDropDone:
1109                 {
1110                         GHOST_TUns8 * temp_buff;
1111                         GHOST_TStringArray *strArray;
1112                         NSArray *droppedArray;
1113                         size_t pastedTextSize;
1114                         NSString *droppedStr;
1115                         GHOST_TEventDataPtr eventData;
1116                         int i;
1117
1118                         if (!data) return GHOST_kFailure;
1119
1120                         switch (draggedObjectType) {
1121                                 case GHOST_kDragnDropTypeFilenames:
1122                                         droppedArray = (NSArray*)data;
1123
1124                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
1125                                         if (!strArray) return GHOST_kFailure;
1126
1127                                         strArray->count = [droppedArray count];
1128                                         if (strArray->count == 0) return GHOST_kFailure;
1129
1130                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
1131
1132                                         for (i=0;i<strArray->count;i++)
1133                                         {
1134                                                 droppedStr = [droppedArray objectAtIndex:i];
1135
1136                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1137                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1);
1138
1139                                                 if (!temp_buff) {
1140                                                         strArray->count = i;
1141                                                         break;
1142                                                 }
1143
1144                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1145                                                 temp_buff[pastedTextSize] = '\0';
1146
1147                                                 strArray->strings[i] = temp_buff;
1148                                         }
1149
1150                                         eventData = (GHOST_TEventDataPtr) strArray;
1151                                         break;
1152
1153                                 case GHOST_kDragnDropTypeString:
1154                                         droppedStr = (NSString*)data;
1155                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1156
1157                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1158
1159                                         if (temp_buff == NULL) {
1160                                                 return GHOST_kFailure;
1161                                         }
1162
1163                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1164
1165                                         temp_buff[pastedTextSize] = '\0';
1166
1167                                         eventData = (GHOST_TEventDataPtr) temp_buff;
1168                                         break;
1169
1170                                 case GHOST_kDragnDropTypeBitmap:
1171                                 {
1172                                         NSImage *droppedImg = (NSImage*)data;
1173                                         NSSize imgSize = [droppedImg size];
1174                                         ImBuf *ibuf = NULL;
1175                                         GHOST_TUns8 *rasterRGB = NULL;
1176                                         GHOST_TUns8 *rasterRGBA = NULL;
1177                                         GHOST_TUns8 *toIBuf = NULL;
1178                                         int x, y, to_i, from_i;
1179                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
1180                                         NSEnumerator *enumerator;
1181                                         NSImageRep *representation;
1182
1183                                         ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect, 0);
1184                                         if (!ibuf) {
1185                                                 [droppedImg release];
1186                                                 return GHOST_kFailure;
1187                                         }
1188
1189                                         /*Get the bitmap of the image*/
1190                                         enumerator = [[droppedImg representations] objectEnumerator];
1191                                         while ((representation = [enumerator nextObject])) {
1192                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1193                                                         bitmapImage = (NSBitmapImageRep *)representation;
1194                                                         break;
1195                                                 }
1196                                         }
1197                                         if (bitmapImage == nil) return GHOST_kFailure;
1198
1199                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
1200                                                 && ![bitmapImage isPlanar]) {
1201                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
1202                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1203                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
1204                                                 for (y = 0; y < imgSize.height; y++) {
1205                                                         to_i = (imgSize.height-y-1)*imgSize.width;
1206                                                         from_i = y*imgSize.width;
1207                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
1208                                                 }
1209                                         }
1210                                         else {
1211                                                 /* Tell cocoa image resolution is same as current system one */
1212                                                 [bitmapImage setSize:imgSize];
1213
1214                                                 /* Convert the image in a RGBA 32bit format */
1215                                                 /* As Core Graphics does not support contexts with non premutliplied alpha,
1216                                                  we need to get alpha key values in a separate batch */
1217
1218                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
1219                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1220                                                                                                                                                                                  pixelsWide:imgSize.width 
1221                                                                                                                                                                                  pixelsHigh:imgSize.height
1222                                                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
1223                                                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
1224                                                                                                                                                                            bitmapFormat:(NSBitmapFormat)0
1225                                                                                                                                                                                 bytesPerRow:4*imgSize.width
1226                                                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
1227
1228                                                 [NSGraphicsContext saveGraphicsState];
1229                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1230                                                 [bitmapImage draw];
1231                                                 [NSGraphicsContext restoreGraphicsState];
1232
1233                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1234                                                 if (rasterRGB == NULL) {
1235                                                         [bitmapImage release];
1236                                                         [blBitmapFormatImageRGB release];
1237                                                         [droppedImg release];
1238                                                         return GHOST_kFailure;
1239                                                 }
1240
1241                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1242                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1243                                                                                                                                                                                   pixelsWide:imgSize.width
1244                                                                                                                                                                                   pixelsHigh:imgSize.height
1245                                                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1246                                                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
1247                                                                                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1248                                                                                                                                                                                  bytesPerRow:4*imgSize.width
1249                                                                                                                                                                                 bitsPerPixel:32/* RGBA */];
1250
1251                                                 [NSGraphicsContext saveGraphicsState];
1252                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1253                                                 [bitmapImage draw];
1254                                                 [NSGraphicsContext restoreGraphicsState];
1255
1256                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1257                                                 if (rasterRGBA == NULL) {
1258                                                         [bitmapImage release];
1259                                                         [blBitmapFormatImageRGB release];
1260                                                         [blBitmapFormatImageRGBA release];
1261                                                         [droppedImg release];
1262                                                         return GHOST_kFailure;
1263                                                 }
1264
1265                                                 /*Copy the image to ibuf, flipping it vertically*/
1266                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1267                                                 for (y = 0; y < imgSize.height; y++) {
1268                                                         for (x = 0; x < imgSize.width; x++) {
1269                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1270                                                                 from_i = y*imgSize.width + x;
1271
1272                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1273                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1274                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1275                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1276                                                         }
1277                                                 }
1278
1279                                                 [blBitmapFormatImageRGB release];
1280                                                 [blBitmapFormatImageRGBA release];
1281                                                 [droppedImg release];
1282                                         }
1283
1284                                         eventData = (GHOST_TEventDataPtr) ibuf;
1285                                         break;
1286                                 }
1287
1288                                 default:
1289                                         return GHOST_kFailure;
1290                                         break;
1291                         }
1292                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1293                         break;
1294                 }
1295                 default:
1296                         return GHOST_kFailure;
1297         }
1298         m_outsideLoopEventProcessed = true;
1299         return GHOST_kSuccess;
1300 }
1301
1302
1303 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1304 {
1305         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1306
1307         //Discard quit event if we are in cursor grab sequence
1308         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1309                 return GHOST_kExitCancel;
1310
1311         //Check open windows if some changes are not saved
1312         if (m_windowManager->getAnyModifiedState())
1313         {
1314                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
1315                                                                                  @"Cancel", @"Quit Anyway", nil);
1316                 if (shouldQuit == NSAlertAlternateReturn)
1317                 {
1318                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1319                         return GHOST_kExitNow;
1320                 } else {
1321                         //Give back focus to the blender window if user selected cancel quit
1322                         NSArray *windowsList = [NSApp orderedWindows];
1323                         if ([windowsList count]) {
1324                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1325                                 //Handle the modifiers keyes changed state issue
1326                                 //as recovering from the quit dialog is like application
1327                                 //gaining focus back.
1328                                 //Main issue fixed is Cmd modifier not being cleared
1329                                 handleApplicationBecomeActiveEvent();
1330                         }
1331                 }
1332
1333         }
1334         else {
1335                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1336                 m_outsideLoopEventProcessed = true;
1337                 return GHOST_kExitNow;
1338         }
1339
1340         return GHOST_kExitCancel;
1341 }
1342
1343 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1344 {
1345         NSString *filepath = (NSString*)filepathStr;
1346         int confirmOpen = NSAlertAlternateReturn;
1347         NSArray *windowsList;
1348         char * temp_buff;
1349         size_t filenameTextSize;        
1350         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1351
1352         if (!window) {
1353                 return NO;
1354         }       
1355
1356         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1357         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1358                 return GHOST_kExitCancel;
1359
1360         //Check open windows if some changes are not saved
1361         if (m_windowManager->getAnyModifiedState())
1362         {
1363                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1364                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1365                                                                                  @"Cancel", @"Open", nil);
1366         }
1367
1368         //Give back focus to the blender window
1369         windowsList = [NSApp orderedWindows];
1370         if ([windowsList count]) {
1371                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1372         }
1373
1374         if (confirmOpen == NSAlertAlternateReturn)
1375         {
1376                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1377
1378                 temp_buff = (char*) malloc(filenameTextSize+1); 
1379
1380                 if (temp_buff == NULL) {
1381                         return GHOST_kFailure;
1382                 }
1383
1384                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSISOLatin1StringEncoding], filenameTextSize);
1385
1386                 temp_buff[filenameTextSize] = '\0';
1387
1388                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1389
1390                 return YES;
1391         }
1392         else return NO;
1393 }
1394
1395 GHOST_TSuccess GHOST_SystemCocoa::handleTabletProximity(void *eventPtr)
1396 {
1397         printf("tablet prox: ");
1398         NSEvent *event = (NSEvent *)eventPtr;
1399         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)
1400                 m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1401
1402         if (!window) {
1403                 printf("\nW failure for event 0x%x",[event type]);
1404                 return GHOST_kFailure;
1405         }
1406
1407 // don't involve the window!
1408 //      GHOST_TabletData& ct = window->GetCocoaTabletData();
1409
1410         GHOST_TTabletMode active_tool;
1411         int* tool_id_ptr;
1412
1413         switch ([event pointingDeviceType])
1414                 {
1415                 case NSPenPointingDevice:
1416                         printf("pen ");
1417                         active_tool = GHOST_kTabletModeStylus;
1418                         tool_id_ptr = &m_tablet_pen_id;
1419                         break;
1420                 case NSEraserPointingDevice:
1421                         printf("eraser ");
1422                         active_tool = GHOST_kTabletModeEraser;
1423                         tool_id_ptr = &m_tablet_pen_id;
1424                         break;
1425                 case NSCursorPointingDevice:
1426                         printf("cursor ");
1427                         active_tool = GHOST_kTabletModeNone;
1428                         tool_id_ptr = &m_tablet_mouse_id;
1429                         break;
1430                 default:
1431                         printf("<!> unknown device %d\n", [event pointingDeviceType]);
1432                         return GHOST_kFailure; // fail on unknown device
1433                 }
1434
1435         if ([event isEnteringProximity]) {
1436                 printf("entering\n");
1437                 *tool_id_ptr = [event deviceID];
1438
1439                 m_tablet_pen_mode = active_tool;
1440
1441 //              ct.Active = active_tool;
1442 //              ct.Pressure = (active_tool == GHOST_kTabletModeNone) ? /*mouse*/ 1 : /*pen*/ 0;
1443 //              ct.Xtilt = 0;
1444 //              ct.Ytilt = 0;
1445
1446                 // this is a good place to remember the tool's capabilities
1447                 }
1448         else {
1449                 printf("leaving\n");
1450                 *tool_id_ptr = TOOL_ID_NONE;
1451
1452                 m_tablet_pen_mode = GHOST_kTabletModeNone;
1453
1454 //              ct.Active = GHOST_kTabletModeNone;
1455 //              ct.Pressure = 0;
1456 //              ct.Xtilt = 0;
1457 //              ct.Ytilt = 0;
1458                 }
1459
1460         return GHOST_kSuccess;
1461 }
1462
1463 void GHOST_SystemCocoa::fillTabletData(GHOST_TabletData& tablet, void* event_ptr)
1464         {
1465         NSEvent* event = (NSEvent*)event_ptr;
1466         NSPoint tilt = [event tilt];
1467
1468         tablet.Active = m_tablet_pen_mode;
1469         tablet.Pressure = [event pressure];
1470         tablet.Xtilt = tilt.x;
1471         tablet.Ytilt = tilt.y;
1472
1473         printf("> pressure = %.2f   tilt = %.2f %2f\n", tablet.Pressure, tablet.Xtilt, tablet.Ytilt);
1474         }
1475
1476 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
1477 {
1478         puts("tablet point");
1479         NSEvent *event = (NSEvent*)eventPtr;
1480         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)
1481                 m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1482
1483         if (!window) {
1484                 //printf("\nW failure for event 0x%x",[event type]);
1485                 return GHOST_kFailure;
1486         }
1487
1488 /*
1489         // don't involve the window!
1490         GHOST_TabletData& ct = window->GetCocoaTabletData();
1491
1492         ct.Pressure = [event pressure];
1493         NSPoint tilt = [event tilt];
1494         ct.Xtilt = tilt.x;
1495         ct.Ytilt = tilt.y;
1496 */
1497
1498         switch ([event type])
1499                 {
1500                 case NSLeftMouseDown:
1501                         {
1502                         if (m_input_fidelity_hint == HI_FI)
1503                                 {
1504                                 printf("hi-fi on\n");
1505                                 [NSEvent setMouseCoalescingEnabled:NO];
1506                                 }
1507                         GHOST_EventButton* e = new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber]));
1508                         GHOST_TEventButtonData* data = (GHOST_TEventButtonData*) e->getData();
1509                         fillTabletData(data->tablet, event);
1510                         pushEvent(e);
1511                         break;
1512                         }
1513                 case NSLeftMouseUp:
1514                         {
1515                         if (m_input_fidelity_hint == HI_FI)
1516                                 {
1517                                 printf("hi-fi off\n");
1518                                 [NSEvent setMouseCoalescingEnabled:YES];
1519                                 }
1520                         // no tablet data needed for 'pen up'
1521                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1522                         break;
1523                         }
1524                 default:
1525                         {
1526                         NSPoint pos = [event locationInWindow];
1527                         GHOST_EventCursor* e = new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, pos.x, pos.y);
1528                         GHOST_TEventCursorData* data = (GHOST_TEventCursorData*) e->getData();
1529                         fillTabletData(data->tablet, event);
1530                         pushEvent(e);
1531                         break;
1532                         }
1533                 }
1534
1535         return GHOST_kSuccess;
1536 }
1537
1538
1539 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1540 {
1541         printf("mouse ");
1542         NSEvent *event = (NSEvent *)eventPtr;
1543         GHOST_Window* window =
1544                 (GHOST_Window*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1545
1546         if (!window) {
1547                 printf("\nW failure for event 0x%x",[event type]);
1548                 return GHOST_kFailure;
1549         }
1550
1551         static float warp_dx = 0, warp_dy = 0; // need to reset these. e.g. each grab operation should get its own.
1552                 // ^^ not currently useful, try m_cursorDelta_* instead.
1553
1554         switch ([event type]) {
1555                 case NSLeftMouseDown:
1556                 case NSRightMouseDown:
1557                 case NSOtherMouseDown:
1558                         printf("button down\n");
1559                         if (m_input_fidelity_hint == HI_FI)
1560                                 {
1561                                 printf("hi-fi on\n");
1562                                 [NSEvent setMouseCoalescingEnabled:NO];
1563                                 }
1564                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1565                         break;
1566
1567                 case NSLeftMouseUp:
1568                 case NSRightMouseUp:
1569                 case NSOtherMouseUp:
1570                         printf("button up\n");
1571                         if (m_input_fidelity_hint == HI_FI)
1572                                 {
1573                                 printf("hi-fi off\n");
1574                                 [NSEvent setMouseCoalescingEnabled:YES];
1575                                 }
1576                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1577                         // cheap hack, should reset when grab ends.
1578                         warp_dx = warp_dy = 0;
1579                         break;
1580
1581                 case NSLeftMouseDragged:
1582                 case NSRightMouseDragged:
1583                 case NSOtherMouseDragged:
1584                 case NSMouseMoved:
1585                         {
1586                         NSPoint mousePos = [event locationInWindow];
1587                         float event_dx = [event deltaX];
1588                         float event_dy = [event deltaY];
1589
1590                         bool coalesced = [NSEvent isMouseCoalescingEnabled];
1591                         if (not coalesced)
1592                                 printf("[hi-fi] ");
1593                         printf("move <%.2f,%.2f> to (%.2f,%.2f)\n", event_dx, event_dy, mousePos.x, mousePos.y);
1594
1595                         event_dy = -event_dy; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1596
1597                                 switch (window->getCursorGrabMode()) {
1598                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1599                                                 {
1600                                                 printf(" - grab hide\n");
1601                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1602
1603                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1604
1605                                                 window->getCursorGrabAccum(x_accum, y_accum);
1606                                                 x_accum += event_dx;
1607                                                 y_accum += event_dy;
1608                                                 window->setCursorGrabAccum(x_accum, y_accum);
1609
1610                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1611                                                 break;
1612                                                 }
1613                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1614                                                 {
1615                                                 NSPoint mousePos = [event locationInWindow];
1616                                                 GHOST_TInt32 x_mouse= mousePos.x;
1617                                                 GHOST_TInt32 y_mouse= mousePos.y;
1618                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1619                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1620
1621                                                 /* fallback to window bounds */
1622                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1623                                                         window->getClientBounds(bounds);
1624
1625                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1626                                                 window->getClientBounds(windowBounds);
1627                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1628                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1629                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1630                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1631
1632                                                 //Update accumulation counts
1633                                                 window->getCursorGrabAccum(x_accum, y_accum);
1634                                                 x_accum += event_dx - m_cursorDelta_x;
1635                                                 y_accum += event_dy - m_cursorDelta_y;
1636                                                 window->setCursorGrabAccum(x_accum, y_accum);
1637
1638                                                 //Warp mouse cursor if needed
1639                                                 x_mouse += event_dx - m_cursorDelta_x;
1640                                                 y_mouse += event_dy - m_cursorDelta_y;
1641                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1642
1643                                                 //Compensate for mouse moved event taking cursor position set into account
1644                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1645                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1646
1647                                                 //Set new cursor position
1648                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1649                                                 setMouseCursorPosition(x_cur, y_cur); /* wrap */
1650
1651                                                 //Post event
1652                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1653                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1654
1655                                                 break;
1656                                                 }
1657                                         default:
1658                                                 //Normal cursor operation: send mouse position in window
1659                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1660                                                 m_cursorDelta_x=0;
1661                                                 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter
1662                                                 warp_dx = 0;
1663                                                 warp_dy = 0;
1664                                 } // END cursor grab mode
1665                         break;
1666                         } // END mouse moved
1667
1668                 case NSScrollWheel:
1669                         /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */
1670                         if (m_hasMultiTouchTrackpad and m_isGestureInProgress) {
1671                                 NSPoint mousePos = [event locationInWindow];
1672                                 double dx = [event deltaX];
1673                                 double dy = -[event deltaY];
1674
1675                                 const double deltaMax = 50.0;
1676
1677                                 if ((dx == 0) && (dy == 0)) break;
1678
1679                                 /* Quadratic acceleration */
1680                                 dx = dx*(fabs(dx)+0.5);
1681                                 if (dx<0.0) dx-=0.5; else dx+=0.5;
1682                                 if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax;
1683
1684                                 dy = dy*(fabs(dy)+0.5);
1685                                 if (dy<0.0) dy-=0.5; else dy+=0.5;
1686                                 if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax;
1687
1688                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, mousePos.x, mousePos.y, dx, dy));
1689                         }
1690                         else {
1691                                 GHOST_TInt32 delta;
1692
1693                                 double deltaF = [event deltaY];
1694                                 if (deltaF == 0.0) break; //discard trackpad delta=0 events
1695
1696                                 delta = deltaF > 0.0 ? 1 : -1;
1697                                 pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta));
1698                         }
1699                         break;
1700
1701                 case NSEventTypeMagnify:
1702                         {
1703                         NSPoint mousePos = [event locationInWindow];
1704                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, mousePos.x, mousePos.y, [event magnification]*250.0 + 0.1, 0));
1705                         break;
1706                         }
1707                 case NSEventTypeRotate:
1708                         {
1709                         NSPoint mousePos = [event locationInWindow];
1710                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, mousePos.x, mousePos.y, -[event rotation] * 5.0, 0));
1711                         break;
1712                         }
1713                 case NSEventTypeBeginGesture:
1714                         m_isGestureInProgress = true;
1715                         break;
1716                 case NSEventTypeEndGesture:
1717                         m_isGestureInProgress = false;
1718                         break;
1719                 default:
1720                         printf("<!> unknown event type %d\n", [event type]);
1721                         return GHOST_kFailure;
1722         }
1723
1724         return GHOST_kSuccess;
1725 }
1726
1727
1728 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1729 {
1730         NSEvent *event = (NSEvent *)eventPtr;
1731         GHOST_IWindow* window;
1732         unsigned int modifiers;
1733         NSString *characters;
1734         NSData *convertedCharacters;
1735         GHOST_TKey keyCode;
1736         unsigned char ascii;
1737         NSString* charsIgnoringModifiers;
1738
1739         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1740         if (!window) {
1741                 //printf("\nW failure for event 0x%x",[event type]);
1742                 return GHOST_kFailure;
1743         }
1744
1745         switch ([event type]) {
1746                 case NSKeyDown:
1747                 case NSKeyUp:
1748                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1749                         if ([charsIgnoringModifiers length]>0)
1750                                 keyCode = convertKey([event keyCode],
1751                                                                          [charsIgnoringModifiers characterAtIndex:0],
1752                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1753                         else
1754                                 keyCode = convertKey([event keyCode],0,
1755                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1756
1757
1758                         characters = [event characters];
1759                         if ([characters length]>0) { //Check for dead keys
1760                                 //Convert characters to iso latin 1 encoding
1761                                 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding];
1762                                 if ([convertedCharacters length]>0)
1763                                         ascii =((char*)[convertedCharacters bytes])[0];
1764                                 else
1765                                         ascii = 0; //Character not available in iso latin 1 encoding
1766                         }
1767                         else
1768                                 ascii= 0;
1769
1770                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1771                                 break; //Cmd-Q is directly handled by Cocoa
1772
1773                         if ([event type] == NSKeyDown) {
1774                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
1775                                 //printf("\nKey down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1776                         } else {
1777                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
1778                                 //printf("\nKey up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1779                         }
1780                         break;
1781
1782                 case NSFlagsChanged: 
1783                         modifiers = [event modifierFlags];
1784
1785                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1786                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1787                         }
1788                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1789                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1790                         }
1791                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1792                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1793                         }
1794                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1795                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1796                         }
1797
1798                         m_modifierMask = modifiers;
1799                         break;
1800
1801                 default:
1802                         return GHOST_kFailure;
1803                         break;
1804         }
1805
1806         return GHOST_kSuccess;
1807 }
1808
1809
1810 #pragma mark Clipboard get/set
1811
1812 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1813 {
1814         GHOST_TUns8 * temp_buff;
1815         size_t pastedTextSize;  
1816
1817         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1818
1819         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1820
1821         if (pasteBoard == nil) {
1822                 [pool drain];
1823                 return NULL;
1824         }
1825
1826         NSArray *supportedTypes =
1827                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1828
1829         NSString *bestType = [[NSPasteboard generalPasteboard]
1830                                                   availableTypeFromArray:supportedTypes];
1831
1832         if (bestType == nil) {
1833                 [pool drain];
1834                 return NULL;
1835         }
1836
1837         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1838
1839         if (textPasted == nil) {
1840                 [pool drain];
1841                 return NULL;
1842         }
1843
1844         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1845
1846         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1847
1848         if (temp_buff == NULL) {
1849                 [pool drain];
1850                 return NULL;
1851         }
1852
1853         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1854
1855         temp_buff[pastedTextSize] = '\0';
1856         
1857         [pool drain];
1858
1859         if(temp_buff) {
1860                 return temp_buff;
1861         } else {
1862                 return NULL;
1863         }
1864 }
1865
1866 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1867 {
1868         NSString *textToCopy;
1869
1870         if(selection) {return;} // for copying the selection, used on X11
1871
1872         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1873
1874         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1875
1876         if (pasteBoard == nil) {
1877                 [pool drain];
1878                 return;
1879         }
1880
1881         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1882
1883         [pasteBoard declareTypes:supportedTypes owner:nil];
1884
1885         textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding];
1886
1887         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1888
1889         [pool drain];
1890 }
1891
1892 #pragma mark Base directories retrieval
1893
1894 const GHOST_TUns8* GHOST_SystemCocoa::getSystemDir() const
1895 {
1896         static GHOST_TUns8 tempPath[512] = "";
1897         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1898         NSString *basePath;
1899         NSArray *paths;
1900         
1901         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask, YES);
1902         
1903         if ([paths count] > 0)
1904                 basePath = [paths objectAtIndex:0];
1905         else { 
1906                 [pool drain];
1907                 return NULL;
1908         }
1909         
1910         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1911         
1912         [pool drain];
1913         return tempPath;
1914 }
1915
1916 const GHOST_TUns8* GHOST_SystemCocoa::getUserDir() const
1917 {
1918         static GHOST_TUns8 tempPath[512] = "";
1919         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1920         NSString *basePath;
1921         NSArray *paths;
1922
1923         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
1924
1925         if ([paths count] > 0)
1926                 basePath = [paths objectAtIndex:0];
1927         else { 
1928                 [pool drain];
1929                 return NULL;
1930         }
1931
1932         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1933         
1934         [pool drain];
1935         return tempPath;
1936 }
1937
1938 const GHOST_TUns8* GHOST_SystemCocoa::getBinaryDir() const
1939 {
1940         static GHOST_TUns8 tempPath[512] = "";
1941         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1942         NSString *basePath;
1943         
1944         basePath = [[NSBundle mainBundle] bundlePath];
1945         
1946         if (basePath == nil) {
1947                 [pool drain];
1948                 return NULL;
1949         }
1950
1951         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1952
1953         [pool drain];
1954         return tempPath;
1955 }