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