Fix build error on Windows 32 bit.
[blender-staging.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 @interface CocoaAppDelegate : NSObject <NSApplicationDelegate> {
287
288         GHOST_SystemCocoa *systemCocoa;
289 }
290 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
291 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
292 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
293 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
294 - (void)applicationWillTerminate:(NSNotification *)aNotification;
295 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
296 - (void)toggleFullScreen:(NSNotification *)notification;
297 @end
298
299 @implementation CocoaAppDelegate : NSObject
300 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
301 {
302         systemCocoa = sysCocoa;
303 }
304
305 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
306 {
307         // raise application to front, convenient when starting from the terminal
308         // and important for launching the animation player. we call this after the
309         // application finishes launching, as doing it earlier can make us end up
310         // with a frontmost window but an inactive application
311         [NSApp activateIgnoringOtherApps:YES];
312 }
313
314 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
315 {
316         return systemCocoa->handleOpenDocumentRequest(filename);
317 }
318
319 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
320 {
321         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be canceled
322         //Note that Cmd+Q is already handled by keyhandler
323         if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
324                 return NSTerminateCancel;//NSTerminateNow;
325         else
326                 return NSTerminateCancel;
327 }
328
329 // To avoid canceling a log off process, we must use Cocoa termination process
330 // And this function is the only chance to perform clean up
331 // So WM_exit needs to be called directly, as the event loop will never run before termination
332 - (void)applicationWillTerminate:(NSNotification *)aNotification
333 {
334         /*G.is_break = FALSE; //Let Cocoa perform the termination at the end
335         WM_exit(C);*/
336 }
337
338 - (void)applicationWillBecomeActive:(NSNotification *)aNotification
339 {
340         systemCocoa->handleApplicationBecomeActiveEvent();
341 }
342
343 - (void)toggleFullScreen:(NSNotification *)notification
344 {
345 }
346
347 @end
348
349
350 #pragma mark initialization/finalization
351
352 GHOST_SystemCocoa::GHOST_SystemCocoa()
353 {
354         int mib[2];
355         struct timeval boottime;
356         size_t len;
357         char *rstring = NULL;
358
359         m_modifierMask =0;
360         m_outsideLoopEventProcessed = false;
361         m_needDelayedApplicationBecomeActiveEventProcessing = false;
362         m_displayManager = new GHOST_DisplayManagerCocoa ();
363         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
364         m_displayManager->initialize();
365
366         //NSEvent timeStamp is given in system uptime, state start date is boot time
367         mib[0] = CTL_KERN;
368         mib[1] = KERN_BOOTTIME;
369         len = sizeof(struct timeval);
370
371         sysctl(mib, 2, &boottime, &len, NULL, 0);
372         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
373
374         //Detect multitouch trackpad
375         mib[0] = CTL_HW;
376         mib[1] = HW_MODEL;
377         sysctl( mib, 2, NULL, &len, NULL, 0 );
378         rstring = (char*)malloc( len );
379         sysctl( mib, 2, rstring, &len, NULL, 0 );
380
381         free( rstring );
382         rstring = NULL;
383
384         m_ignoreWindowSizedMessages = false;
385         m_ignoreMomentumScroll = false;
386         m_multiTouchScroll = false;
387 }
388
389 GHOST_SystemCocoa::~GHOST_SystemCocoa()
390 {
391 }
392
393
394 GHOST_TSuccess GHOST_SystemCocoa::init()
395 {
396         GHOST_TSuccess success = GHOST_System::init();
397         if (success) {
398
399 #ifdef WITH_INPUT_NDOF
400                 m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
401 #endif
402
403                 //ProcessSerialNumber psn;
404
405                 //Carbon stuff to move window & menu to foreground
406                 /*if (!GetCurrentProcess(&psn)) {
407                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
408                         SetFrontProcess(&psn);
409                 }*/
410
411                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
412                 [NSApplication sharedApplication]; // initializes       NSApp
413
414                 if ([NSApp mainMenu] == nil) {
415                         NSMenu *mainMenubar = [[NSMenu alloc] init];
416                         NSMenuItem *menuItem;
417                         NSMenu *windowMenu;
418                         NSMenu *appMenu;
419
420                         //Create the application menu
421                         appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
422
423                         [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
424                         [appMenu addItem:[NSMenuItem separatorItem]];
425
426                         menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
427                         [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
428
429                         menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
430                         [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
431
432                         [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
433
434                         menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
435                         [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
436
437                         menuItem = [[NSMenuItem alloc] init];
438                         [menuItem setSubmenu:appMenu];
439
440                         [mainMenubar addItem:menuItem];
441                         [menuItem release];
442                         [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
443                         [appMenu release];
444
445                         //Create the window menu
446                         windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
447
448                         menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
449                         [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
450
451                         [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
452
453                         menuItem = [windowMenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f" ];
454                         [menuItem setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
455
456                         menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
457                         [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
458
459                         menuItem = [[NSMenuItem alloc] init];
460                         [menuItem setSubmenu:windowMenu];
461
462                         [mainMenubar addItem:menuItem];
463                         [menuItem release];
464
465                         [NSApp setMainMenu:mainMenubar];
466                         [NSApp setWindowsMenu:windowMenu];
467                         [windowMenu release];
468                 }
469
470                 if ([NSApp delegate] == nil) {
471                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
472                         [appDelegate setSystemCocoa:this];
473                         [NSApp setDelegate:appDelegate];
474                 }
475
476                 [NSApp finishLaunching];
477                 
478                 [pool drain];
479         }
480         return success;
481 }
482
483
484 #pragma mark window management
485
486 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
487 {
488         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
489         struct timeval currentTime;
490
491         gettimeofday(&currentTime, NULL);
492
493         //Return timestamp of system uptime
494
495         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
496 }
497
498
499 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
500 {
501         //Note that OS X supports monitor hot plug
502         // We do not support multiple monitors at the moment
503         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
504
505         GHOST_TUns8 count = [[NSScreen screens] count];
506
507         [pool drain];
508         return count;
509 }
510
511
512 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
513 {
514         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
515         //Get visible frame, that is frame excluding dock and top menu bar
516         NSRect frame = [[NSScreen mainScreen] visibleFrame];
517
518         //Returns max window contents (excluding title bar...)
519         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
520                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
521
522         width = contentRect.size.width;
523         height = contentRect.size.height;
524
525         [pool drain];
526 }
527
528 void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
529 {
530         /* TODO! */
531         getMainDisplayDimensions(width, height);
532 }
533
534 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
535         const STR_String& title, 
536         GHOST_TInt32 left,
537         GHOST_TInt32 top,
538         GHOST_TUns32 width,
539         GHOST_TUns32 height,
540         GHOST_TWindowState state,
541         GHOST_TDrawingContextType type,
542         GHOST_GLSettings glSettings,
543         const bool exclusive,
544         const GHOST_TEmbedderWindowID parentWindow
545 )
546 {
547         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
548         GHOST_IWindow* window = NULL;
549
550         //Get the available rect for including window contents
551         NSRect frame = [[NSScreen mainScreen] visibleFrame];
552         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
553                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
554
555         GHOST_TInt32 bottom = (contentRect.size.height - 1) - height - top;
556
557         //Ensures window top left is inside this available rect
558         left = left > contentRect.origin.x ? left : contentRect.origin.x;
559         // Add contentRect.origin.y to respect docksize
560         bottom = bottom > contentRect.origin.y ? bottom + contentRect.origin.y : contentRect.origin.y;
561
562         window = new GHOST_WindowCocoa(this, title, left, bottom, width, height, state, type, glSettings.flags & GHOST_glStereoVisual, glSettings.numOfAASamples, glSettings.flags & GHOST_glDebugContext);
563
564         if (window->getValid()) {
565                 // Store the pointer to the window
566                 GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
567                 m_windowManager->addWindow(window);
568                 m_windowManager->setActiveWindow(window);
569                 //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
570                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
571                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
572         }
573         else {
574                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
575                 delete window;
576                 window = NULL;
577         }
578
579         [pool drain];
580         return window;
581 }
582
583 /**
584  * \note : returns coordinates in Cocoa screen coordinates
585  */
586 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
587 {
588         NSPoint mouseLoc = [NSEvent mouseLocation];
589
590         // Returns the mouse location in screen coordinates
591         x = (GHOST_TInt32)mouseLoc.x;
592         y = (GHOST_TInt32)mouseLoc.y;
593         return GHOST_kSuccess;
594 }
595
596 /**
597  * \note : expect Cocoa screen coordinates
598  */
599 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
600 {
601         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
602         if (!window) return GHOST_kFailure;
603
604         //Cursor and mouse dissociation placed here not to interfere with continuous grab
605         // (in cont. grab setMouseCursorPosition is directly called)
606         CGAssociateMouseAndMouseCursorPosition(false);
607         setMouseCursorPosition(x, y);
608         CGAssociateMouseAndMouseCursorPosition(true);
609
610         //Force mouse move event (not pushed by Cocoa)
611         pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x, y));
612         m_outsideLoopEventProcessed = true;
613
614         return GHOST_kSuccess;
615 }
616
617 GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
618 {
619         float xf=(float)x, yf=(float)y;
620         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
621         if (!window) return GHOST_kFailure;
622
623         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
624         NSScreen *windowScreen = window->getScreen();
625         NSRect screenRect = [windowScreen frame];
626
627         //Set position relative to current screen
628         xf -= screenRect.origin.x;
629         yf -= screenRect.origin.y;
630
631         //Quartz Display Services uses the old coordinates (top left origin)
632         yf = screenRect.size.height -yf;
633
634         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
635
636         // See https://stackoverflow.com/a/17559012. By default, hardware events
637         // will be suppressed for 500ms after a synthetic mouse event. For unknown
638         // reasons CGEventSourceSetLocalEventsSuppressionInterval does not work,
639         // however calling CGAssociateMouseAndMouseCursorPosition also removes the
640         // delay, even if this is undocumented.
641         CGAssociateMouseAndMouseCursorPosition(true);
642
643         [pool drain];
644         return GHOST_kSuccess;
645 }
646
647
648 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
649 {
650         keys.set(GHOST_kModifierKeyOS, (m_modifierMask & NSCommandKeyMask) ? true : false);
651         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
652         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
653         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
654
655         return GHOST_kSuccess;
656 }
657
658 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
659 {
660         UInt32 button_state = GetCurrentEventButtonState();
661
662         buttons.clear();
663         buttons.set(GHOST_kButtonMaskLeft, button_state & (1 << 0));
664         buttons.set(GHOST_kButtonMaskRight, button_state & (1 << 1));
665         buttons.set(GHOST_kButtonMaskMiddle, button_state & (1 << 2));
666         buttons.set(GHOST_kButtonMaskButton4, button_state & (1 << 3));
667         buttons.set(GHOST_kButtonMaskButton5, button_state & (1 << 4));
668         return GHOST_kSuccess;
669 }
670
671
672 #pragma mark Event handlers
673
674 /**
675  * The event queue polling function
676  */
677 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
678 {
679         bool anyProcessed = false;
680         NSEvent *event;
681
682         //      SetMouseCoalescingEnabled(false, NULL);
683         //TODO : implement timer ??
684 #if 0
685         do {
686                 GHOST_TimerManager* timerMgr = getTimerManager();
687
688                 if (waitForEvent) {
689                         GHOST_TUns64 next = timerMgr->nextFireTime();
690                         double timeOut;
691
692                         if (next == GHOST_kFireTimeNever) {
693                                 timeOut = kEventDurationForever;
694                         }
695                         else {
696                                 timeOut = (double)(next - getMilliSeconds())/1000.0;
697                                 if (timeOut < 0.0)
698                                         timeOut = 0.0;
699                         }
700
701                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
702                 }
703
704                 if (timerMgr->fireTimers(getMilliSeconds())) {
705                         anyProcessed = true;
706                 }
707 #endif
708                 do {
709                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
710                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
711                                                    untilDate:[NSDate distantPast]
712                                                       inMode:NSDefaultRunLoopMode
713                                                      dequeue:YES];
714                         if (event==nil) {
715                                 [pool drain];
716                                 break;
717                         }
718
719                         anyProcessed = true;
720
721                         // Send event to NSApp to ensure Mac wide events are handled,
722                         // this will send events to CocoaWindow which will call back
723                         // to handleKeyEvent, handleMouseEvent and handleTabletEvent
724
725                         // There is on special exception for ctrl+(shift)+tab. We do not
726                         // get keyDown events delivered to the view because they are
727                         // special hotkeys to switch between views, so override directly
728
729                         if ([event type] == NSKeyDown &&
730                            [event keyCode] == kVK_Tab &&
731                            ([event modifierFlags] & NSControlKeyMask)) {
732                                 handleKeyEvent(event);
733                         }
734                         else {
735                                 // For some reason NSApp is swallowing the key up events when modifier
736                                 // key is pressed, even if there seems to be no apparent reason to do
737                                 // so, as a workaround we always handle these up events.
738                                 if ([event type] == NSKeyUp && ([event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask)))
739                                         handleKeyEvent(event);
740
741                                 [NSApp sendEvent:event];
742                         }
743
744                         [pool drain];
745                 } while (event != nil);
746 #if 0
747         } while (waitForEvent && !anyProcessed); // Needed only for timer implementation
748 #endif
749
750         if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent();
751
752         if (m_outsideLoopEventProcessed) {
753                 m_outsideLoopEventProcessed = false;
754                 return true;
755         }
756
757         m_ignoreWindowSizedMessages = false;
758
759         return anyProcessed;
760 }
761
762 //Note: called from NSApplication delegate
763 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
764 {
765         //Update the modifiers key mask, as its status may have changed when the application was not active
766         //(that is when update events are sent to another application)
767         unsigned int modifiers;
768         GHOST_IWindow* window = m_windowManager->getActiveWindow();
769
770         if (!window) {
771                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
772                 return GHOST_kFailure;
773         }
774         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
775
776         modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags];
777
778         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
779                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift));
780         }
781         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
782                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl));
783         }
784         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
785                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt));
786         }
787         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
788                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyOS));
789         }
790
791         m_modifierMask = modifiers;
792
793         m_outsideLoopEventProcessed = true;
794         return GHOST_kSuccess;
795 }
796
797 void GHOST_SystemCocoa::notifyExternalEventProcessed()
798 {
799         m_outsideLoopEventProcessed = true;
800 }
801
802 //Note: called from NSWindow delegate
803 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
804 {
805         NSArray *windowsList;
806         windowsList = [NSApp orderedWindows];
807         if (!validWindow(window)) {
808                 return GHOST_kFailure;
809         }
810         switch (eventType) {
811                 case GHOST_kEventWindowClose:
812                         // check for index of mainwindow as it would quit blender without dialog and discard
813                         if ([windowsList count] > 1  && window->getCocoaWindow() != [windowsList objectAtIndex:[windowsList count] - 1]) {
814                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
815                         }
816                         else {
817                                 handleQuitRequest(); // -> quit dialog
818                         }
819                         break;
820                 case GHOST_kEventWindowActivate:
821                         m_windowManager->setActiveWindow(window);
822                         window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
823                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
824                         break;
825                 case GHOST_kEventWindowDeactivate:
826                         m_windowManager->setWindowInactive(window);
827                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
828                         break;
829                 case GHOST_kEventWindowUpdate:
830                         if (m_nativePixel) {
831                                 window->setNativePixelSize();
832                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) );
833                         }
834                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
835                         break;
836                 case GHOST_kEventWindowMove:
837                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
838                         break;
839                 case GHOST_kEventWindowSize:
840                         if (!m_ignoreWindowSizedMessages) {
841                                 //Enforce only one resize message per event loop (coalescing all the live resize messages)
842                                 window->updateDrawingContext();
843                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
844                                 //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager
845                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft));
846                                 //m_ignoreWindowSizedMessages = true;
847                         }
848                         break;
849                 case GHOST_kEventNativeResolutionChange:
850
851                         if (m_nativePixel) {
852                                 window->setNativePixelSize();
853                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) );
854                         }
855
856                 default:
857                         return GHOST_kFailure;
858                         break;
859         }
860
861         m_outsideLoopEventProcessed = true;
862         return GHOST_kSuccess;
863 }
864
865 //Note: called from NSWindow subclass
866 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
867                                                       GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
868 {
869         if (!validWindow(window)) {
870                 return GHOST_kFailure;
871         }
872         switch (eventType) {
873                 case GHOST_kEventDraggingEntered:
874                 case GHOST_kEventDraggingUpdated:
875                 case GHOST_kEventDraggingExited:
876                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
877                         break;
878
879                 case GHOST_kEventDraggingDropDone:
880                 {
881                         GHOST_TUns8 * temp_buff;
882                         GHOST_TStringArray *strArray;
883                         NSArray *droppedArray;
884                         size_t pastedTextSize;
885                         NSString *droppedStr;
886                         GHOST_TEventDataPtr eventData;
887                         int i;
888
889                         if (!data) return GHOST_kFailure;
890
891                         switch (draggedObjectType) {
892                                 case GHOST_kDragnDropTypeFilenames:
893                                         droppedArray = (NSArray*)data;
894
895                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
896                                         if (!strArray) return GHOST_kFailure;
897
898                                         strArray->count = [droppedArray count];
899                                         if (strArray->count == 0) {
900                                                 free(strArray);
901                                                 return GHOST_kFailure;
902                                         }
903
904                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
905
906                                         for (i=0;i<strArray->count;i++)
907                                         {
908                                                 droppedStr = [droppedArray objectAtIndex:i];
909
910                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
911                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
912
913                                                 if (!temp_buff) {
914                                                         strArray->count = i;
915                                                         break;
916                                                 }
917
918                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
919                                                 temp_buff[pastedTextSize] = '\0';
920
921                                                 strArray->strings[i] = temp_buff;
922                                         }
923
924                                         eventData = (GHOST_TEventDataPtr) strArray;
925                                         break;
926
927                                 case GHOST_kDragnDropTypeString:
928                                         droppedStr = (NSString*)data;
929                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
930
931                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
932
933                                         if (temp_buff == NULL) {
934                                                 return GHOST_kFailure;
935                                         }
936
937                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
938
939                                         temp_buff[pastedTextSize] = '\0';
940
941                                         eventData = (GHOST_TEventDataPtr) temp_buff;
942                                         break;
943
944                                 case GHOST_kDragnDropTypeBitmap:
945                                 {
946                                         NSImage *droppedImg = (NSImage*)data;
947                                         NSSize imgSize = [droppedImg size];
948                                         ImBuf *ibuf = NULL;
949                                         GHOST_TUns8 *rasterRGB = NULL;
950                                         GHOST_TUns8 *rasterRGBA = NULL;
951                                         GHOST_TUns8 *toIBuf = NULL;
952                                         int x, y, to_i, from_i;
953                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
954                                         NSEnumerator *enumerator;
955                                         NSImageRep *representation;
956
957                                         ibuf = IMB_allocImBuf (imgSize.width, imgSize.height, 32, IB_rect);
958                                         if (!ibuf) {
959                                                 [droppedImg release];
960                                                 return GHOST_kFailure;
961                                         }
962
963                                         /*Get the bitmap of the image*/
964                                         enumerator = [[droppedImg representations] objectEnumerator];
965                                         while ((representation = [enumerator nextObject])) {
966                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
967                                                         bitmapImage = (NSBitmapImageRep *)representation;
968                                                         break;
969                                                 }
970                                         }
971                                         if (bitmapImage == nil) return GHOST_kFailure;
972
973                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
974                                                 && ![bitmapImage isPlanar]) {
975                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
976                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
977                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
978                                                 for (y = 0; y < imgSize.height; y++) {
979                                                         to_i = (imgSize.height-y-1)*imgSize.width;
980                                                         from_i = y*imgSize.width;
981                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
982                                                 }
983                                         }
984                                         else {
985                                                 /* Tell cocoa image resolution is same as current system one */
986                                                 [bitmapImage setSize:imgSize];
987
988                                                 /* Convert the image in a RGBA 32bit format */
989                                                 /* As Core Graphics does not support contextes with non premutliplied alpha,
990                                                  we need to get alpha key values in a separate batch */
991
992                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
993                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
994                                                                                                                  pixelsWide:imgSize.width 
995                                                                                                                  pixelsHigh:imgSize.height
996                                                                                                               bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
997                                                                                                              colorSpaceName:NSDeviceRGBColorSpace 
998                                                                                                                bitmapFormat:(NSBitmapFormat)0
999                                                                                                                 bytesPerRow:4*imgSize.width
1000                                                                                                                bitsPerPixel:32/*RGB format padded to 32bits*/];
1001
1002                                                 [NSGraphicsContext saveGraphicsState];
1003                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1004                                                 [bitmapImage draw];
1005                                                 [NSGraphicsContext restoreGraphicsState];
1006
1007                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1008                                                 if (rasterRGB == NULL) {
1009                                                         [bitmapImage release];
1010                                                         [blBitmapFormatImageRGB release];
1011                                                         [droppedImg release];
1012                                                         return GHOST_kFailure;
1013                                                 }
1014
1015                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1016                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1017                                                                                                                   pixelsWide:imgSize.width
1018                                                                                                                   pixelsHigh:imgSize.height
1019                                                                                                                bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1020                                                                                                               colorSpaceName:NSDeviceRGBColorSpace
1021                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1022                                                                                                                  bytesPerRow:4*imgSize.width
1023                                                                                                                 bitsPerPixel:32/* RGBA */];
1024
1025                                                 [NSGraphicsContext saveGraphicsState];
1026                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1027                                                 [bitmapImage draw];
1028                                                 [NSGraphicsContext restoreGraphicsState];
1029
1030                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1031                                                 if (rasterRGBA == NULL) {
1032                                                         [bitmapImage release];
1033                                                         [blBitmapFormatImageRGB release];
1034                                                         [blBitmapFormatImageRGBA release];
1035                                                         [droppedImg release];
1036                                                         return GHOST_kFailure;
1037                                                 }
1038
1039                                                 /*Copy the image to ibuf, flipping it vertically*/
1040                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1041                                                 for (y = 0; y < imgSize.height; y++) {
1042                                                         for (x = 0; x < imgSize.width; x++) {
1043                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1044                                                                 from_i = y*imgSize.width + x;
1045
1046                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1047                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1048                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1049                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1050                                                         }
1051                                                 }
1052
1053                                                 [blBitmapFormatImageRGB release];
1054                                                 [blBitmapFormatImageRGBA release];
1055                                                 [droppedImg release];
1056                                         }
1057
1058                                         eventData = (GHOST_TEventDataPtr) ibuf;
1059
1060                                         break;
1061                                 }
1062                                 default:
1063                                         return GHOST_kFailure;
1064                                         break;
1065                         }
1066                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1067
1068                         break;
1069                 }
1070                 default:
1071                         return GHOST_kFailure;
1072         }
1073         m_outsideLoopEventProcessed = true;
1074         return GHOST_kSuccess;
1075 }
1076
1077
1078 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1079 {
1080         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1081
1082         //Discard quit event if we are in cursor grab sequence
1083         if (window && window->getCursorGrabModeIsWarp())
1084                 return GHOST_kExitCancel;
1085
1086         //Check open windows if some changes are not saved
1087         if (m_windowManager->getAnyModifiedState())
1088         {
1089                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit?",
1090                                                  @"Cancel", @"Quit Anyway", nil);
1091                 if (shouldQuit == NSAlertAlternateReturn)
1092                 {
1093                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1094                         return GHOST_kExitNow;
1095                 }
1096                 else {
1097                         //Give back focus to the blender window if user selected cancel quit
1098                         NSArray *windowsList = [NSApp orderedWindows];
1099                         if ([windowsList count]) {
1100                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1101                                 //Handle the modifiers keyes changed state issue
1102                                 //as recovering from the quit dialog is like application
1103                                 //gaining focus back.
1104                                 //Main issue fixed is Cmd modifier not being cleared
1105                                 handleApplicationBecomeActiveEvent();
1106                         }
1107                 }
1108         }
1109         else {
1110                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1111                 m_outsideLoopEventProcessed = true;
1112                 return GHOST_kExitNow;
1113         }
1114
1115         return GHOST_kExitCancel;
1116 }
1117
1118 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1119 {
1120         NSString *filepath = (NSString*)filepathStr;
1121         int confirmOpen = NSAlertAlternateReturn;
1122         NSArray *windowsList;
1123         char * temp_buff;
1124         size_t filenameTextSize;
1125         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1126
1127         if (!window) {
1128                 return NO;
1129         }       
1130
1131         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1132         if (window && window->getCursorGrabModeIsWarp())
1133                 return GHOST_kExitCancel;
1134
1135         //Check open windows if some changes are not saved
1136         if (m_windowManager->getAnyModifiedState())
1137         {
1138                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1139                                               @"Current document has not been saved.\nDo you really want to proceed?",
1140                                               @"Cancel", @"Open", nil);
1141         }
1142
1143         //Give back focus to the blender window
1144         windowsList = [NSApp orderedWindows];
1145         if ([windowsList count]) {
1146                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1147         }
1148
1149         if (confirmOpen == NSAlertAlternateReturn)
1150         {
1151                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1152
1153                 temp_buff = (char*) malloc(filenameTextSize+1); 
1154
1155                 if (temp_buff == NULL) {
1156                         return GHOST_kFailure;
1157                 }
1158
1159                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize);
1160
1161                 temp_buff[filenameTextSize] = '\0';
1162
1163                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1164
1165                 return YES;
1166         }
1167         else return NO;
1168 }
1169
1170 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1171 {
1172         NSEvent *event = (NSEvent *)eventPtr;
1173         GHOST_IWindow* window;
1174
1175         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1176         if (!window) {
1177                 //printf("\nW failure for event 0x%x",[event type]);
1178                 return GHOST_kFailure;
1179         }
1180
1181         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1182
1183         switch (eventType) {
1184                 case NSTabletPoint:
1185                         // workaround 2 cornercases:
1186                         // 1. if [event isEnteringProximity] was not triggered since program-start
1187                         // 2. device is not sending [event pointingDeviceType], due no eraser
1188                         if (ct.Active == GHOST_kTabletModeNone)
1189                                 ct.Active = GHOST_kTabletModeStylus;
1190
1191                         ct.Pressure = [event pressure];
1192                         ct.Xtilt = [event tilt].x;
1193                         ct.Ytilt = [event tilt].y;
1194                         break;
1195
1196                 case NSTabletProximity:
1197                         ct.Pressure = 0;
1198                         ct.Xtilt = 0;
1199                         ct.Ytilt = 0;
1200                         if ([event isEnteringProximity])
1201                         {
1202                                 //pointer is entering tablet area proximity
1203                                 switch ([event pointingDeviceType]) {
1204                                         case NSPenPointingDevice:
1205                                                 ct.Active = GHOST_kTabletModeStylus;
1206                                                 break;
1207                                         case NSEraserPointingDevice:
1208                                                 ct.Active = GHOST_kTabletModeEraser;
1209                                                 break;
1210                                         case NSCursorPointingDevice:
1211                                         case NSUnknownPointingDevice:
1212                                         default:
1213                                                 ct.Active = GHOST_kTabletModeNone;
1214                                                 break;
1215                                 }
1216                         }
1217                         else {
1218                                 // pointer is leaving - return to mouse
1219                                 ct.Active = GHOST_kTabletModeNone;
1220                         }
1221                         break;
1222                 
1223                 default:
1224                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1225                         return GHOST_kFailure;
1226                         break;
1227         }
1228         return GHOST_kSuccess;
1229 }
1230
1231 bool GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
1232 {
1233         NSEvent *event = (NSEvent *)eventPtr;
1234
1235         switch ([event subtype]) {
1236                 case NSTabletPointEventSubtype:
1237                         handleTabletEvent(eventPtr, NSTabletPoint);
1238                         return true;
1239                 case NSTabletProximityEventSubtype:
1240                         handleTabletEvent(eventPtr, NSTabletProximity);
1241                         return true;
1242                 default:
1243                         //No tablet event included : do nothing
1244                         return false;
1245         }
1246 }
1247
1248 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1249 {
1250         NSEvent *event = (NSEvent *)eventPtr;
1251         GHOST_WindowCocoa* window;
1252         CocoaWindow *cocoawindow;
1253
1254         /* [event window] returns other windows if mouse-over, that's OSX input standard
1255            however, if mouse exits window(s), the windows become inactive, until you click.
1256            We then fall back to the active window from ghost */
1257         window = (GHOST_WindowCocoa*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1258         if (!window) {
1259                 window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
1260                 if (!window) {
1261                         //printf("\nW failure for event 0x%x",[event type]);
1262                         return GHOST_kFailure;
1263                 }
1264         }
1265
1266         cocoawindow = (CocoaWindow *)window->getOSWindow();
1267
1268         switch ([event type]) {
1269                 case NSLeftMouseDown:
1270                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft));
1271                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1272                         break;
1273                 case NSRightMouseDown:
1274                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight));
1275                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1276                         break;
1277                 case NSOtherMouseDown:
1278                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1279                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1280                         break;
1281
1282                 case NSLeftMouseUp:
1283                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft));
1284                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1285                         break;
1286                 case NSRightMouseUp:
1287                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight));
1288                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1289                         break;
1290                 case NSOtherMouseUp:
1291                         pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1292                         handleTabletEvent(event); //Handle tablet events combined with mouse events
1293                         break;
1294
1295                 case NSLeftMouseDragged:
1296                 case NSRightMouseDragged:
1297                 case NSOtherMouseDragged:
1298                         //Handle tablet events combined with mouse events
1299                         handleTabletEvent(event);
1300
1301                 case NSMouseMoved: 
1302                         {
1303                                 GHOST_TGrabCursorMode grab_mode = window->getCursorGrabMode();
1304
1305                                 /* TODO: CHECK IF THIS IS A TABLET EVENT */
1306                                 bool is_tablet = false;
1307
1308                                 if (is_tablet && window->getCursorGrabModeIsWarp()) {
1309                                         grab_mode = GHOST_kGrabDisable;
1310                                 }
1311
1312                                 switch (grab_mode) {
1313                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1314                                         {
1315                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum, x, y;
1316
1317                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1318                                                 window->screenToClientIntern(x_warp, y_warp, x_warp, y_warp);
1319
1320                                                 window->getCursorGrabAccum(x_accum, y_accum);
1321                                                 x_accum += [event deltaX];
1322                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1323                                                 window->setCursorGrabAccum(x_accum, y_accum);
1324
1325                                                 window->clientToScreenIntern(x_warp+x_accum, y_warp+y_accum, x, y);
1326                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1327                                                 break;
1328                                         }
1329                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1330                                         {
1331                                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1332                                                 GHOST_TInt32 x_mouse = mousePos.x;
1333                                                 GHOST_TInt32 y_mouse = mousePos.y;
1334                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1335
1336                                                 /* fallback to window bounds */
1337                                                 if (window->getCursorGrabBounds(bounds) == GHOST_kFailure)
1338                                                         window->getClientBounds(bounds);
1339
1340                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1341                                                 window->getClientBounds(windowBounds);
1342                                                 window->screenToClient(bounds.m_l, bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1343                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1344                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1345                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1346
1347                                                 //Get accumulation from previous mouse warps
1348                                                 GHOST_TInt32 x_accum, y_accum;
1349                                                 window->getCursorGrabAccum(x_accum, y_accum);
1350
1351                                                 //Warp mouse cursor if needed
1352                                                 GHOST_TInt32 warped_x_mouse = x_mouse;
1353                                                 GHOST_TInt32 warped_y_mouse = y_mouse;
1354                                                 correctedBounds.wrapPoint(warped_x_mouse, warped_y_mouse, 4);
1355
1356                                                 //Set new cursor position
1357                                                 if (x_mouse != warped_x_mouse || y_mouse != warped_y_mouse) {
1358                                                         GHOST_TInt32 warped_x, warped_y;
1359                                                         window->clientToScreenIntern(warped_x_mouse, warped_y_mouse, warped_x, warped_y);
1360                                                         setMouseCursorPosition(warped_x, warped_y); /* wrap */
1361                                                         window->setCursorGrabAccum(x_accum + (x_mouse - warped_x_mouse), y_accum + (y_mouse - warped_y_mouse));
1362                                                 }
1363
1364                                                 //Generate event
1365                                                 GHOST_TInt32 x, y;
1366                                                 window->clientToScreenIntern(x_mouse + x_accum, y_mouse + y_accum, x, y);
1367                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1368                                                 break;
1369                                         }
1370                                         default:
1371                                         {
1372                                                 //Normal cursor operation: send mouse position in window
1373                                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1374                                                 GHOST_TInt32 x, y;
1375
1376                                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1377                                                 pushEvent(new GHOST_EventCursor([event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
1378                                                 break;
1379                                         }
1380                                 }
1381                         }
1382                         break;
1383
1384                 case NSScrollWheel:
1385                         {
1386                                 NSEventPhase momentumPhase = NSEventPhaseNone;
1387                                 NSEventPhase phase = NSEventPhaseNone;
1388
1389                                 if ([event respondsToSelector:@selector(momentumPhase)])
1390                                         momentumPhase = [event momentumPhase];
1391                                 if ([event respondsToSelector:@selector(phase)])
1392                                         phase = [event phase];
1393
1394                                 /* when pressing a key while momentum scrolling continues after
1395                                  * lifting fingers off the trackpad, the action can unexpectedly
1396                                  * change from e.g. scrolling to zooming. this works around the
1397                                  * issue by ignoring momentum scroll after a key press */
1398                                 if (momentumPhase) {
1399                                         if (m_ignoreMomentumScroll)
1400                                                 break;
1401                                 }
1402                                 else {
1403                                         m_ignoreMomentumScroll = false;
1404                                 }
1405
1406                                 /* we assume phases are only set for gestures from trackpad or magic
1407                                  * mouse events. note that using tablet at the same time may not work
1408                                  * since this is a static variable */
1409                                 if (phase == NSEventPhaseBegan)
1410                                         m_multiTouchScroll = true;
1411                                 else if (phase == NSEventPhaseEnded)
1412                                         m_multiTouchScroll = false;
1413
1414                                 /* standard scrollwheel case, if no swiping happened, and no momentum (kinetic scroll) works */
1415                                 if (!m_multiTouchScroll && momentumPhase == NSEventPhaseNone) {
1416                                         GHOST_TInt32 delta;
1417
1418                                         double deltaF = [event deltaY];
1419
1420                                         if (deltaF == 0.0) deltaF = [event deltaX]; // make blender decide if it's horizontal scroll
1421                                         if (deltaF == 0.0) break; //discard trackpad delta=0 events
1422
1423                                         delta = deltaF > 0.0 ? 1 : -1;
1424                                         pushEvent(new GHOST_EventWheel([event timestamp] * 1000, window, delta));
1425                                 }
1426                                 else {
1427                                         NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1428                                         GHOST_TInt32 x, y;
1429                                         double dx;
1430                                         double dy;
1431
1432                                         /* with 10.7 nice scrolling deltas are supported */
1433                                         dx = [event scrollingDeltaX];
1434                                         dy = [event scrollingDeltaY];
1435
1436                                         /* however, wacom tablet (intuos5) needs old deltas, it then has momentum and phase at zero */
1437                                         if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
1438                                                 dx = [event deltaX];
1439                                                 dy = [event deltaY];
1440                                         }
1441                                         window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1442
1443                                         pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventScroll, x, y, dx, dy));
1444                                 }
1445                         }
1446                         break;
1447
1448                 case NSEventTypeMagnify:
1449                         {
1450                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1451                                 GHOST_TInt32 x, y;
1452                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1453                                 pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventMagnify, x, y,
1454                                                                   [event magnification] * 125.0 + 0.1, 0));
1455                         }
1456                         break;
1457
1458                 case NSEventTypeRotate:
1459                         {
1460                                 NSPoint mousePos = [cocoawindow mouseLocationOutsideOfEventStream];
1461                                 GHOST_TInt32 x, y;
1462                                 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1463                                 pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventRotate, x, y,
1464                                                                   [event rotation] * -5.0, 0));
1465                         }
1466                 default:
1467                         return GHOST_kFailure;
1468                         break;
1469                 }
1470
1471         return GHOST_kSuccess;
1472 }
1473
1474
1475 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1476 {
1477         NSEvent *event = (NSEvent *)eventPtr;
1478         GHOST_IWindow* window;
1479         unsigned int modifiers;
1480         NSString *characters;
1481         NSData *convertedCharacters;
1482         GHOST_TKey keyCode;
1483         unsigned char ascii;
1484         NSString* charsIgnoringModifiers;
1485
1486         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1487         if (!window) {
1488                 //printf("\nW failure for event 0x%x",[event type]);
1489                 return GHOST_kFailure;
1490         }
1491
1492         char utf8_buf[6]= {'\0'};
1493         ascii = 0;
1494
1495         switch ([event type]) {
1496
1497                 case NSKeyDown:
1498                 case NSKeyUp:
1499                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1500                         if ([charsIgnoringModifiers length] > 0) {
1501                                 keyCode = convertKey([event keyCode],
1502                                                      [charsIgnoringModifiers characterAtIndex:0],
1503                                                      [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1504                         }
1505                         else {
1506                                 keyCode = convertKey([event keyCode],0,
1507                                                      [event type] == NSKeyDown?kUCKeyActionDown:kUCKeyActionUp);
1508                         }
1509
1510                         /* handling both unicode or ascii */
1511                         characters = [event characters];
1512                         if ([characters length] > 0) {
1513                                 convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding];
1514
1515                                 for (int x = 0; x < [convertedCharacters length]; x++) {
1516                                         utf8_buf[x] = ((char*)[convertedCharacters bytes])[x];
1517                                 }
1518                         }
1519
1520                         /* arrow keys should not have utf8 */
1521                         if ((keyCode > 266) && (keyCode < 271))
1522                                 utf8_buf[0] = '\0';
1523
1524                         /* F keys should not have utf8 */
1525                         if ((keyCode >= GHOST_kKeyF1) && (keyCode <= GHOST_kKeyF20))
1526                                 utf8_buf[0] = '\0';
1527
1528                         /* no text with command key pressed */
1529                         if (m_modifierMask & NSCommandKeyMask)
1530                                 utf8_buf[0] = '\0';
1531
1532                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1533                                 break; //Cmd-Q is directly handled by Cocoa
1534
1535                         /* ascii is a subset of unicode */
1536                         if (utf8_buf[0] && !utf8_buf[1]) {
1537                                 ascii = utf8_buf[0];
1538                         }
1539
1540                         if ([event type] == NSKeyDown) {
1541                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, ascii, utf8_buf));
1542                                 //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);
1543                         }
1544                         else {
1545                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL));
1546                                 //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);
1547                         }
1548                         m_ignoreMomentumScroll = true;
1549                         break;
1550
1551                 case NSFlagsChanged: 
1552                         modifiers = [event modifierFlags];
1553                         
1554                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1555                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSShiftKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift));
1556                         }
1557                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1558                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSControlKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl));
1559                         }
1560                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1561                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSAlternateKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt));
1562                         }
1563                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1564                                 pushEvent(new GHOST_EventKey([event timestamp] * 1000, (modifiers & NSCommandKeyMask) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyOS));
1565                         }
1566
1567                         m_modifierMask = modifiers;
1568                         m_ignoreMomentumScroll = true;
1569                         break;
1570
1571                 default:
1572                         return GHOST_kFailure;
1573                         break;
1574         }
1575
1576         return GHOST_kSuccess;
1577 }
1578
1579
1580 #pragma mark Clipboard get/set
1581
1582 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1583 {
1584         GHOST_TUns8 * temp_buff;
1585         size_t pastedTextSize;
1586
1587         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1588
1589         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1590
1591         if (pasteBoard == nil) {
1592                 [pool drain];
1593                 return NULL;
1594         }
1595
1596         NSArray *supportedTypes =
1597                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1598
1599         NSString *bestType = [[NSPasteboard generalPasteboard] availableTypeFromArray:supportedTypes];
1600
1601         if (bestType == nil) {
1602                 [pool drain];
1603                 return NULL;
1604         }
1605
1606         NSString *textPasted = [pasteBoard stringForType:NSStringPboardType];
1607
1608         if (textPasted == nil) {
1609                 [pool drain];
1610                 return NULL;
1611         }
1612
1613         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1614
1615         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1616
1617         if (temp_buff == NULL) {
1618                 [pool drain];
1619                 return NULL;
1620         }
1621
1622         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1623
1624         temp_buff[pastedTextSize] = '\0';
1625
1626         [pool drain];
1627
1628         if (temp_buff) {
1629                 return temp_buff;
1630         }
1631         else {
1632                 return NULL;
1633         }
1634 }
1635
1636 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1637 {
1638         NSString *textToCopy;
1639
1640         if (selection) return;  // for copying the selection, used on X11
1641
1642         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1643
1644         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1645
1646         if (pasteBoard == nil) {
1647                 [pool drain];
1648                 return;
1649         }
1650
1651         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1652
1653         [pasteBoard declareTypes:supportedTypes owner:nil];
1654
1655         textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
1656
1657         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1658
1659         [pool drain];
1660 }