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