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