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