SpaceNav turntable and fit in 3D view. Tablet data rides with cursor/button events...
[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                         }
1326                 }
1327
1328         }
1329         else {
1330                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1331                 m_outsideLoopEventProcessed = true;
1332                 return GHOST_kExitNow;
1333         }
1334
1335         return GHOST_kExitCancel;
1336 }
1337
1338 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1339 {
1340         NSString *filepath = (NSString*)filepathStr;
1341         int confirmOpen = NSAlertAlternateReturn;
1342         NSArray *windowsList;
1343         char * temp_buff;
1344         size_t filenameTextSize;        
1345         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1346
1347         if (!window) {
1348                 return NO;
1349         }       
1350
1351         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1352         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1353                 return GHOST_kExitCancel;
1354
1355         //Check open windows if some changes are not saved
1356         if (m_windowManager->getAnyModifiedState())
1357         {
1358                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1359                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1360                                                                                  @"Cancel", @"Open", nil);
1361         }
1362
1363         //Give back focus to the blender window
1364         windowsList = [NSApp orderedWindows];
1365         if ([windowsList count]) {
1366                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1367         }
1368
1369         if (confirmOpen == NSAlertAlternateReturn)
1370         {
1371                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1372
1373                 temp_buff = (char*) malloc(filenameTextSize+1); 
1374
1375                 if (temp_buff == NULL) {
1376                         return GHOST_kFailure;
1377                 }
1378
1379                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSISOLatin1StringEncoding], filenameTextSize);
1380
1381                 temp_buff[filenameTextSize] = '\0';
1382
1383                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1384
1385                 return YES;
1386         }
1387         else return NO;
1388 }
1389
1390 GHOST_TSuccess GHOST_SystemCocoa::handleTabletProximity(void *eventPtr)
1391 {
1392         printf("tablet prox: ");
1393         NSEvent *event = (NSEvent *)eventPtr;
1394         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)
1395                 m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1396
1397         if (!window) {
1398                 printf("\nW failure for event 0x%x",[event type]);
1399                 return GHOST_kFailure;
1400         }
1401
1402 // don't involve the window!
1403 //      GHOST_TabletData& ct = window->GetCocoaTabletData();
1404
1405         GHOST_TTabletMode active_tool;
1406         int* tool_id_ptr;
1407
1408         switch ([event pointingDeviceType])
1409                 {
1410                 case NSPenPointingDevice:
1411                         printf("pen ");
1412                         active_tool = GHOST_kTabletModeStylus;
1413                         tool_id_ptr = &m_tablet_pen_id;
1414                         break;
1415                 case NSEraserPointingDevice:
1416                         printf("eraser ");
1417                         active_tool = GHOST_kTabletModeEraser;
1418                         tool_id_ptr = &m_tablet_pen_id;
1419                         break;
1420                 case NSCursorPointingDevice:
1421                         printf("cursor ");
1422                         active_tool = GHOST_kTabletModeNone;
1423                         tool_id_ptr = &m_tablet_mouse_id;
1424                         break;
1425                 default:
1426                         printf("<!> unknown device %d\n", [event pointingDeviceType]);
1427                         return GHOST_kFailure; // fail on unknown device
1428                 }
1429
1430         if ([event isEnteringProximity]) {
1431                 printf("entering\n");
1432                 *tool_id_ptr = [event deviceID];
1433
1434                 m_tablet_pen_mode = active_tool;
1435
1436 //              ct.Active = active_tool;
1437 //              ct.Pressure = (active_tool == GHOST_kTabletModeNone) ? /*mouse*/ 1 : /*pen*/ 0;
1438 //              ct.Xtilt = 0;
1439 //              ct.Ytilt = 0;
1440
1441                 // this is a good place to remember the tool's capabilities
1442                 }
1443         else {
1444                 printf("leaving\n");
1445                 *tool_id_ptr = TOOL_ID_NONE;
1446
1447                 m_tablet_pen_mode = GHOST_kTabletModeNone;
1448
1449 //              ct.Active = GHOST_kTabletModeNone;
1450 //              ct.Pressure = 0;
1451 //              ct.Xtilt = 0;
1452 //              ct.Ytilt = 0;
1453                 }
1454
1455         return GHOST_kSuccess;
1456 }
1457
1458 void GHOST_SystemCocoa::fillTabletData(GHOST_TabletData& tablet, void* event_ptr)
1459         {
1460         NSEvent* event = (NSEvent*)event_ptr;
1461         NSPoint tilt = [event tilt];
1462
1463         tablet.Active = m_tablet_pen_mode;
1464         tablet.Pressure = [event pressure];
1465         tablet.Xtilt = tilt.x;
1466         tablet.Ytilt = tilt.y;
1467
1468         printf("> pressure = %.2f   tilt = %.2f %2f\n", tablet.Pressure, tablet.Xtilt, tablet.Ytilt);
1469         }
1470
1471 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
1472 {
1473         puts("tablet point");
1474         NSEvent *event = (NSEvent*)eventPtr;
1475         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)
1476                 m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1477
1478         if (!window) {
1479                 //printf("\nW failure for event 0x%x",[event type]);
1480                 return GHOST_kFailure;
1481         }
1482
1483 /*
1484         // don't involve the window!
1485         GHOST_TabletData& ct = window->GetCocoaTabletData();
1486
1487         ct.Pressure = [event pressure];
1488         NSPoint tilt = [event tilt];
1489         ct.Xtilt = tilt.x;
1490         ct.Ytilt = tilt.y;
1491 */
1492
1493         switch ([event type])
1494                 {
1495                 case NSLeftMouseDown:
1496                         {
1497                         if (m_input_fidelity_hint == HI_FI)
1498                                 {
1499                                 printf("hi-fi on\n");
1500                                 [NSEvent setMouseCoalescingEnabled:NO];
1501                                 }
1502                         GHOST_EventButton* e = new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber]));
1503                         GHOST_TEventButtonData* data = (GHOST_TEventButtonData*) e->getData();
1504                         fillTabletData(data->tablet, event);
1505                         pushEvent(e);
1506                         break;
1507                         }
1508                 case NSLeftMouseUp:
1509                         {
1510                         if (m_input_fidelity_hint == HI_FI)
1511                                 {
1512                                 printf("hi-fi off\n");
1513                                 [NSEvent setMouseCoalescingEnabled:YES];
1514                                 }
1515                         // no tablet data needed for 'pen up'
1516                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1517                         break;
1518                         }
1519                 default:
1520                         {
1521                         NSPoint pos = [event locationInWindow];
1522                         GHOST_EventCursor* e = new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, pos.x, pos.y);
1523                         GHOST_TEventCursorData* data = (GHOST_TEventCursorData*) e->getData();
1524                         fillTabletData(data->tablet, event);
1525                         pushEvent(e);
1526                         break;
1527                         }
1528                 }
1529
1530         return GHOST_kSuccess;
1531 }
1532
1533
1534 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1535 {
1536         printf("mouse ");
1537         NSEvent *event = (NSEvent *)eventPtr;
1538         GHOST_Window* window =
1539                 (GHOST_Window*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1540
1541         if (!window) {
1542                 printf("\nW failure for event 0x%x",[event type]);
1543                 return GHOST_kFailure;
1544         }
1545
1546         static float warp_dx = 0, warp_dy = 0; // need to reset these. e.g. each grab operation should get its own.
1547                 // ^^ not currently useful, try m_cursorDelta_* instead.
1548
1549         switch ([event type]) {
1550                 case NSLeftMouseDown:
1551                 case NSRightMouseDown:
1552                 case NSOtherMouseDown:
1553                         printf("button down\n");
1554                         if (m_input_fidelity_hint == HI_FI)
1555                                 {
1556                                 printf("hi-fi on\n");
1557                                 [NSEvent setMouseCoalescingEnabled:NO];
1558                                 }
1559                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1560                         break;
1561
1562                 case NSLeftMouseUp:
1563                 case NSRightMouseUp:
1564                 case NSOtherMouseUp:
1565                         printf("button up\n");
1566                         if (m_input_fidelity_hint == HI_FI)
1567                                 {
1568                                 printf("hi-fi off\n");
1569                                 [NSEvent setMouseCoalescingEnabled:YES];
1570                                 }
1571                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1572                         // cheap hack, should reset when grab ends.
1573                         warp_dx = warp_dy = 0;
1574                         break;
1575
1576                 case NSLeftMouseDragged:
1577                 case NSRightMouseDragged:
1578                 case NSOtherMouseDragged:
1579                 case NSMouseMoved:
1580                         {
1581                         NSPoint mousePos = [event locationInWindow];
1582                         float event_dx = [event deltaX];
1583                         float event_dy = [event deltaY];
1584
1585                         bool coalesced = [NSEvent isMouseCoalescingEnabled];
1586                         if (not coalesced)
1587                                 printf("[hi-fi] ");
1588                         printf("move <%.2f,%.2f> to (%.2f,%.2f)\n", event_dx, event_dy, mousePos.x, mousePos.y);
1589
1590                         event_dy = -event_dy; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1591
1592                                 switch (window->getCursorGrabMode()) {
1593                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1594                                                 {
1595                                                 printf(" - grab hide\n");
1596                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1597
1598                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1599
1600                                                 window->getCursorGrabAccum(x_accum, y_accum);
1601                                                 x_accum += event_dx;
1602                                                 y_accum += event_dy;
1603                                                 window->setCursorGrabAccum(x_accum, y_accum);
1604
1605                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1606                                                 break;
1607                                                 }
1608                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1609                                                 {
1610                                                 NSPoint mousePos = [event locationInWindow];
1611                                                 GHOST_TInt32 x_mouse= mousePos.x;
1612                                                 GHOST_TInt32 y_mouse= mousePos.y;
1613                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1614                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1615
1616                                                 /* fallback to window bounds */
1617                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1618                                                         window->getClientBounds(bounds);
1619
1620                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1621                                                 window->getClientBounds(windowBounds);
1622                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1623                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1624                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1625                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1626
1627                                                 //Update accumulation counts
1628                                                 window->getCursorGrabAccum(x_accum, y_accum);
1629                                                 x_accum += event_dx - m_cursorDelta_x;
1630                                                 y_accum += event_dy - m_cursorDelta_y;
1631                                                 window->setCursorGrabAccum(x_accum, y_accum);
1632
1633                                                 //Warp mouse cursor if needed
1634                                                 x_mouse += event_dx - m_cursorDelta_x;
1635                                                 y_mouse += event_dy - m_cursorDelta_y;
1636                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1637
1638                                                 //Compensate for mouse moved event taking cursor position set into account
1639                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1640                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1641
1642                                                 //Set new cursor position
1643                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1644                                                 setMouseCursorPosition(x_cur, y_cur); /* wrap */
1645
1646                                                 //Post event
1647                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1648                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1649
1650                                                 break;
1651                                                 }
1652                                         default:
1653                                                 //Normal cursor operation: send mouse position in window
1654                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1655                                                 m_cursorDelta_x=0;
1656                                                 m_cursorDelta_y=0; //Mouse motion occurred between two cursor warps, so we can reset the delta counter
1657                                                 warp_dx = 0;
1658                                                 warp_dy = 0;
1659                                 } // END cursor grab mode
1660                         break;
1661                         } // END mouse moved
1662
1663                 case NSScrollWheel:
1664                         /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */
1665                         if (m_hasMultiTouchTrackpad and m_isGestureInProgress) {
1666                                 NSPoint mousePos = [event locationInWindow];
1667                                 double dx = [event deltaX];
1668                                 double dy = -[event deltaY];
1669
1670                                 const double deltaMax = 50.0;
1671
1672                                 if ((dx == 0) && (dy == 0)) break;
1673
1674                                 /* Quadratic acceleration */
1675                                 dx = dx*(fabs(dx)+0.5);
1676                                 if (dx<0.0) dx-=0.5; else dx+=0.5;
1677                                 if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax;
1678
1679                                 dy = dy*(fabs(dy)+0.5);
1680                                 if (dy<0.0) dy-=0.5; else dy+=0.5;
1681                                 if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax;
1682
1683                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, mousePos.x, mousePos.y, dx, dy));
1684                         }
1685                         else {
1686                                 GHOST_TInt32 delta;
1687
1688                                 double deltaF = [event deltaY];
1689                                 if (deltaF == 0.0) break; //discard trackpad delta=0 events
1690
1691                                 delta = deltaF > 0.0 ? 1 : -1;
1692                                 pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta));
1693                         }
1694                         break;
1695
1696                 case NSEventTypeMagnify:
1697                         {
1698                         NSPoint mousePos = [event locationInWindow];
1699                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, mousePos.x, mousePos.y, [event magnification]*250.0 + 0.1, 0));
1700                         break;
1701                         }
1702                 case NSEventTypeRotate:
1703                         {
1704                         NSPoint mousePos = [event locationInWindow];
1705                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, mousePos.x, mousePos.y, -[event rotation] * 5.0, 0));
1706                         break;
1707                         }
1708                 case NSEventTypeBeginGesture:
1709                         m_isGestureInProgress = true;
1710                         break;
1711                 case NSEventTypeEndGesture:
1712                         m_isGestureInProgress = false;
1713                         break;
1714                 default:
1715                         printf("<!> unknown event type %d\n", [event type]);
1716                         return GHOST_kFailure;
1717         }
1718
1719         return GHOST_kSuccess;
1720 }
1721
1722
1723 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1724 {
1725         NSEvent *event = (NSEvent *)eventPtr;
1726         GHOST_IWindow* window;
1727         unsigned int modifiers;
1728         NSString *characters;
1729         NSData *convertedCharacters;
1730         GHOST_TKey keyCode;
1731         unsigned char ascii;
1732         NSString* charsIgnoringModifiers;
1733
1734         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1735         if (!window) {
1736                 //printf("\nW failure for event 0x%x",[event type]);
1737                 return GHOST_kFailure;
1738         }
1739
1740         switch ([event type]) {
1741                 case NSKeyDown:
1742                 case NSKeyUp:
1743                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1744                         if ([charsIgnoringModifiers length]>0)
1745                                 keyCode = convertKey([event keyCode],
1746                                                                          [charsIgnoringModifiers characterAtIndex:0],
1747                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1748                         else
1749                                 keyCode = convertKey([event keyCode],0,
1750                                                                          [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1751
1752
1753                         characters = [event characters];
1754                         if ([characters length]>0) { //Check for dead keys
1755                                 //Convert characters to iso latin 1 encoding
1756                                 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding];
1757                                 if ([convertedCharacters length]>0)
1758                                         ascii =((char*)[convertedCharacters bytes])[0];
1759                                 else
1760                                         ascii = 0; //Character not available in iso latin 1 encoding
1761                         }
1762                         else
1763                                 ascii= 0;
1764
1765                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1766                                 break; //Cmd-Q is directly handled by Cocoa
1767
1768                         if ([event type] == NSKeyDown) {
1769                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
1770                                 //printf("\nKey down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1771                         } else {
1772                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
1773                                 //printf("\nKey up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c",[event keyCode],[charsIgnoringModifiers length]>0?[charsIgnoringModifiers characterAtIndex:0]:' ',keyCode,ascii,ascii);
1774                         }
1775                         break;
1776
1777                 case NSFlagsChanged: 
1778                         modifiers = [event modifierFlags];
1779
1780                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1781                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1782                         }
1783                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1784                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1785                         }
1786                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1787                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1788                         }
1789                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1790                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1791                         }
1792
1793                         m_modifierMask = modifiers;
1794                         break;
1795
1796                 default:
1797                         return GHOST_kFailure;
1798                         break;
1799         }
1800
1801         return GHOST_kSuccess;
1802 }
1803
1804
1805 #pragma mark Clipboard get/set
1806
1807 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1808 {
1809         GHOST_TUns8 * temp_buff;
1810         size_t pastedTextSize;  
1811
1812         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1813
1814         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1815
1816         if (pasteBoard == nil) {
1817                 [pool drain];
1818                 return NULL;
1819         }
1820
1821         NSArray *supportedTypes =
1822                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1823
1824         NSString *bestType = [[NSPasteboard generalPasteboard]
1825                                                   availableTypeFromArray:supportedTypes];
1826
1827         if (bestType == nil) {
1828                 [pool drain];
1829                 return NULL;
1830         }
1831
1832         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1833
1834         if (textPasted == nil) {
1835                 [pool drain];
1836                 return NULL;
1837         }
1838
1839         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1840
1841         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1842
1843         if (temp_buff == NULL) {
1844                 [pool drain];
1845                 return NULL;
1846         }
1847
1848         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1849
1850         temp_buff[pastedTextSize] = '\0';
1851         
1852         [pool drain];
1853
1854         if(temp_buff) {
1855                 return temp_buff;
1856         } else {
1857                 return NULL;
1858         }
1859 }
1860
1861 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1862 {
1863         NSString *textToCopy;
1864
1865         if(selection) {return;} // for copying the selection, used on X11
1866
1867         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1868
1869         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1870
1871         if (pasteBoard == nil) {
1872                 [pool drain];
1873                 return;
1874         }
1875
1876         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1877
1878         [pasteBoard declareTypes:supportedTypes owner:nil];
1879
1880         textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding];
1881
1882         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1883
1884         [pool drain];
1885 }
1886
1887 #pragma mark Base directories retrieval
1888
1889 const GHOST_TUns8* GHOST_SystemCocoa::getSystemDir() const
1890 {
1891         static GHOST_TUns8 tempPath[512] = "";
1892         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1893         NSString *basePath;
1894         NSArray *paths;
1895         
1896         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask, YES);
1897         
1898         if ([paths count] > 0)
1899                 basePath = [paths objectAtIndex:0];
1900         else { 
1901                 [pool drain];
1902                 return NULL;
1903         }
1904         
1905         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1906         
1907         [pool drain];
1908         return tempPath;
1909 }
1910
1911 const GHOST_TUns8* GHOST_SystemCocoa::getUserDir() const
1912 {
1913         static GHOST_TUns8 tempPath[512] = "";
1914         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1915         NSString *basePath;
1916         NSArray *paths;
1917
1918         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
1919
1920         if ([paths count] > 0)
1921                 basePath = [paths objectAtIndex:0];
1922         else { 
1923                 [pool drain];
1924                 return NULL;
1925         }
1926
1927         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1928         
1929         [pool drain];
1930         return tempPath;
1931 }
1932
1933 const GHOST_TUns8* GHOST_SystemCocoa::getBinaryDir() const
1934 {
1935         static GHOST_TUns8 tempPath[512] = "";
1936         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1937         NSString *basePath;
1938         
1939         basePath = [[NSBundle mainBundle] bundlePath];
1940         
1941         if (basePath == nil) {
1942                 [pool drain];
1943                 return NULL;
1944         }
1945
1946         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1947
1948         [pool drain];
1949         return tempPath;
1950 }