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