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