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