Cocoa port start:
[blender.git] / intern / ghost / intern / GHOST_SystemCocoa.mm
1 /**
2  * $Id: GHOST_SystemCocoa.cpp 23296 2009-09-16 22:27:27Z broken $
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s):      Maarten Gribnau 05/2001
25  *                                      Damien Plisson 09/2009
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #import <Cocoa/Cocoa.h>
31
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35
36 #include "GHOST_SystemCocoa.h"
37
38 #include "GHOST_DisplayManagerCocoa.h"
39 #include "GHOST_EventKey.h"
40 #include "GHOST_EventButton.h"
41 #include "GHOST_EventCursor.h"
42 #include "GHOST_EventWheel.h"
43 #include "GHOST_EventNDOF.h"
44
45 #include "GHOST_TimerManager.h"
46 #include "GHOST_TimerTask.h"
47 #include "GHOST_WindowManager.h"
48 #include "GHOST_WindowCocoa.h"
49 #include "GHOST_NDOFManager.h"
50 #include "AssertMacros.h"
51
52 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
53
54 /* blender class and types events */
55 enum {
56   kEventClassBlender              = 'blnd'
57 };
58
59 enum {
60         kEventBlenderNdofAxis                   = 1,
61         kEventBlenderNdofButtons                = 2
62 };
63
64 #pragma mark KeyMap, mouse converters
65
66 const EventTypeSpec     kEvents[] =
67 {
68         { kEventClassAppleEvent, kEventAppleEvent },
69 /*
70         { kEventClassApplication, kEventAppActivated },
71         { kEventClassApplication, kEventAppDeactivated },
72 */      
73         { kEventClassKeyboard, kEventRawKeyDown },
74         { kEventClassKeyboard, kEventRawKeyRepeat },
75         { kEventClassKeyboard, kEventRawKeyUp },
76         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
77         
78         { kEventClassMouse, kEventMouseDown },
79         { kEventClassMouse, kEventMouseUp },
80         { kEventClassMouse, kEventMouseMoved },
81         { kEventClassMouse, kEventMouseDragged },
82         { kEventClassMouse, kEventMouseWheelMoved },
83         
84         { kEventClassWindow, kEventWindowClickZoomRgn } ,  /* for new zoom behaviour */ 
85         { kEventClassWindow, kEventWindowZoom },  /* for new zoom behaviour */ 
86         { kEventClassWindow, kEventWindowExpand } ,  /* for new zoom behaviour */ 
87         { kEventClassWindow, kEventWindowExpandAll },  /* for new zoom behaviour */ 
88
89         { kEventClassWindow, kEventWindowClose },
90         { kEventClassWindow, kEventWindowActivated },
91         { kEventClassWindow, kEventWindowDeactivated },
92         { kEventClassWindow, kEventWindowUpdate },
93         { kEventClassWindow, kEventWindowBoundsChanged },
94         
95         { kEventClassBlender, kEventBlenderNdofAxis },
96         { kEventClassBlender, kEventBlenderNdofButtons }
97         
98         
99         
100 };
101
102 static GHOST_TButtonMask convertButton(EventMouseButton button)
103 {
104         switch (button) {
105                 case 0:
106                         return GHOST_kButtonMaskLeft;
107                 case 1:
108                         return GHOST_kButtonMaskRight;
109                 case 2:
110                         return GHOST_kButtonMaskMiddle;
111                 case 3:
112                         return GHOST_kButtonMaskButton4;
113                 case 4:
114                         return GHOST_kButtonMaskButton5;
115                 default:
116                         return GHOST_kButtonMaskLeft;
117         }
118 }
119
120 static GHOST_TKey convertKey(int rawCode, unsigned char asciiCharacter) 
121 {       
122                 /* This bit of magic converts the rawCode into a virtual
123                  * Mac key based on the current keyboard mapping, but
124                  * without regard to the modifiers (so we don't get 'a' 
125                  * and 'A' for example.
126                  */
127                 /* Map numpad based on rawcodes first, otherwise they
128                  * look like non-numpad events.
129                  * Added too: mapping the number keys, for french keyboards etc (ton)
130                  */
131         // printf("GHOST: vk: %d %c raw: %d\n", asciiCharacter, asciiCharacter, rawCode);
132         //FIXME : check rawcodes         
133         switch (rawCode) {
134         case 18:        return GHOST_kKey1;
135         case 19:        return GHOST_kKey2;
136         case 20:        return GHOST_kKey3;
137         case 21:        return GHOST_kKey4;
138         case 23:        return GHOST_kKey5;
139         case 22:        return GHOST_kKey6;
140         case 26:        return GHOST_kKey7;
141         case 28:        return GHOST_kKey8;
142         case 25:        return GHOST_kKey9;
143         case 29:        return GHOST_kKey0;
144         
145         case 82:        return GHOST_kKeyNumpad0;
146         case 83:        return GHOST_kKeyNumpad1;
147         case 84:        return GHOST_kKeyNumpad2;
148         case 85:        return GHOST_kKeyNumpad3;
149         case 86:        return GHOST_kKeyNumpad4;
150         case 87:        return GHOST_kKeyNumpad5;
151         case 88:        return GHOST_kKeyNumpad6;
152         case 89:        return GHOST_kKeyNumpad7;
153         case 91:        return GHOST_kKeyNumpad8;
154         case 92:        return GHOST_kKeyNumpad9;
155         case 65:        return GHOST_kKeyNumpadPeriod;
156         case 76:        return GHOST_kKeyNumpadEnter;
157         case 69:        return GHOST_kKeyNumpadPlus;
158         case 78:        return GHOST_kKeyNumpadMinus;
159         case 67:        return GHOST_kKeyNumpadAsterisk;
160         case 75:        return GHOST_kKeyNumpadSlash;
161         }
162         
163         if ((asciiCharacter >= 'a') && (asciiCharacter <= 'z')) {
164                 return (GHOST_TKey) (asciiCharacter - 'a' + GHOST_kKeyA);
165         } else if ((asciiCharacter >= '0') && (asciiCharacter <= '9')) {
166                 return (GHOST_TKey) (asciiCharacter - '0' + GHOST_kKey0);
167         } else if (asciiCharacter==16) {
168                 switch (rawCode) {
169                 case 122:       return GHOST_kKeyF1;
170                 case 120:       return GHOST_kKeyF2;
171                 case 99:        return GHOST_kKeyF3;
172                 case 118:       return GHOST_kKeyF4;
173                 case 96:        return GHOST_kKeyF5;
174                 case 97:        return GHOST_kKeyF6;
175                 case 98:        return GHOST_kKeyF7;
176                 case 100:       return GHOST_kKeyF8;
177                 case 101:       return GHOST_kKeyF9;
178                 case 109:       return GHOST_kKeyF10;
179                 case 103:       return GHOST_kKeyF11;
180                 case 111:       return GHOST_kKeyF12;  // FIXME : Never get, is used for ejecting the CD! 
181                 }
182         } else {
183                 switch (asciiCharacter) {
184                 case kUpArrowCharCode:          return GHOST_kKeyUpArrow;
185                 case kDownArrowCharCode:        return GHOST_kKeyDownArrow;
186                 case kLeftArrowCharCode:        return GHOST_kKeyLeftArrow;
187                 case kRightArrowCharCode:       return GHOST_kKeyRightArrow;
188
189                 case kReturnCharCode:           return GHOST_kKeyEnter;
190                 case kBackspaceCharCode:        return GHOST_kKeyBackSpace;
191                 case kDeleteCharCode:           return GHOST_kKeyDelete;
192                 case kEscapeCharCode:           return GHOST_kKeyEsc;
193                 case kTabCharCode:                      return GHOST_kKeyTab;
194                 case kSpaceCharCode:            return GHOST_kKeySpace;
195
196                 case kHomeCharCode:             return GHOST_kKeyHome;
197                 case kEndCharCode:                      return GHOST_kKeyEnd;
198                 case kPageUpCharCode:           return GHOST_kKeyUpPage;
199                 case kPageDownCharCode:         return GHOST_kKeyDownPage;
200
201                 case '-':       return GHOST_kKeyMinus;
202                 case '=':       return GHOST_kKeyEqual;
203                 case ',':       return GHOST_kKeyComma;
204                 case '.':       return GHOST_kKeyPeriod;
205                 case '/':       return GHOST_kKeySlash;
206                 case ';':       return GHOST_kKeySemicolon;
207                 case '\'':      return GHOST_kKeyQuote;
208                 case '\\':      return GHOST_kKeyBackslash;
209                 case '[':       return GHOST_kKeyLeftBracket;
210                 case ']':       return GHOST_kKeyRightBracket;
211                 case '`':       return GHOST_kKeyAccentGrave;
212                 }
213         }
214         
215         // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
216         
217         return GHOST_kKeyUnknown;
218 }
219
220 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
221  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
222  * I am not sure how international this works...
223  * For cross-platform convention, we'll use the Latin ascii set instead.
224  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
225  * 
226  */
227 static unsigned char convertRomanToLatin(unsigned char ascii)
228 {
229
230         if(ascii<128) return ascii;
231         
232         switch(ascii) {
233         case 128:       return 142;
234         case 129:       return 143;
235         case 130:       return 128;
236         case 131:       return 201;
237         case 132:       return 209;
238         case 133:       return 214;
239         case 134:       return 220;
240         case 135:       return 225;
241         case 136:       return 224;
242         case 137:       return 226;
243         case 138:       return 228;
244         case 139:       return 227;
245         case 140:       return 229;
246         case 141:       return 231;
247         case 142:       return 233;
248         case 143:       return 232;
249         case 144:       return 234;
250         case 145:       return 235;
251         case 146:       return 237;
252         case 147:       return 236;
253         case 148:       return 238;
254         case 149:       return 239;
255         case 150:       return 241;
256         case 151:       return 243;
257         case 152:       return 242;
258         case 153:       return 244;
259         case 154:       return 246;
260         case 155:       return 245;
261         case 156:       return 250;
262         case 157:       return 249;
263         case 158:       return 251;
264         case 159:       return 252;
265         case 160:       return 0;
266         case 161:       return 176;
267         case 162:       return 162;
268         case 163:       return 163;
269         case 164:       return 167;
270         case 165:       return 183;
271         case 166:       return 182;
272         case 167:       return 223;
273         case 168:       return 174;
274         case 169:       return 169;
275         case 170:       return 174;
276         case 171:       return 180;
277         case 172:       return 168;
278         case 173:       return 0;
279         case 174:       return 198;
280         case 175:       return 216;
281         case 176:       return 0;
282         case 177:       return 177;
283         case 178:       return 0;
284         case 179:       return 0;
285         case 180:       return 165;
286         case 181:       return 181;
287         case 182:       return 0;
288         case 183:       return 0;
289         case 184:       return 215;
290         case 185:       return 0;
291         case 186:       return 0;
292         case 187:       return 170;
293         case 188:       return 186;
294         case 189:       return 0;
295         case 190:       return 230;
296         case 191:       return 248;
297         case 192:       return 191;
298         case 193:       return 161;
299         case 194:       return 172;
300         case 195:       return 0;
301         case 196:       return 0;
302         case 197:       return 0;
303         case 198:       return 0;
304         case 199:       return 171;
305         case 200:       return 187;
306         case 201:       return 201;
307         case 202:       return 0;
308         case 203:       return 192;
309         case 204:       return 195;
310         case 205:       return 213;
311         case 206:       return 0;
312         case 207:       return 0;
313         case 208:       return 0;
314         case 209:       return 0;
315         case 210:       return 0;
316         
317         case 214:       return 247;
318
319         case 229:       return 194;
320         case 230:       return 202;
321         case 231:       return 193;
322         case 232:       return 203;
323         case 233:       return 200;
324         case 234:       return 205;
325         case 235:       return 206;
326         case 236:       return 207;
327         case 237:       return 204;
328         case 238:       return 211;
329         case 239:       return 212;
330         case 240:       return 0;
331         case 241:       return 210;
332         case 242:       return 218;
333         case 243:       return 219;
334         case 244:       return 217;
335         case 245:       return 0;
336         case 246:       return 0;
337         case 247:       return 0;
338         case 248:       return 0;
339         case 249:       return 0;
340         case 250:       return 0;
341
342         
343                 default: return 0;
344         }
345
346 }
347
348 #define FIRSTFILEBUFLG 512
349 static bool g_hasFirstFile = false;
350 static char g_firstFileBuf[512];
351
352 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
353         if (g_hasFirstFile) {
354                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
355                 buf[FIRSTFILEBUFLG - 1] = '\0';
356                 return 1;
357         } else {
358                 return 0; 
359         }
360 }
361
362
363 #pragma mark Cocoa objects
364
365 /**
366  * CocoaAppDelegate
367  * ObjC object to capture applicationShouldTerminate, and send quit event
368  **/
369 @interface CocoaAppDelegate : NSObject
370 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
371 @end
372
373 @implementation CocoaAppDelegate : NSObject
374 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
375 {
376         //Note that Cmd+Q is already handled by keyhandler
377     //FIXME: Cocoa_SendQuit();
378     return NSTerminateCancel;
379 }
380 @end
381
382
383
384 #pragma mark initialization/finalization
385
386 /***/
387
388 GHOST_SystemCocoa::GHOST_SystemCocoa()
389 {
390         m_modifierMask =0;
391         m_pressedMouseButtons =0;
392         m_displayManager = new GHOST_DisplayManagerCocoa ();
393         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
394         m_displayManager->initialize();
395
396         //NSEvent timeStamp is given in system uptime, state start date is boot time
397         //FIXME : replace by Cocoa equivalent
398         int mib[2];
399         struct timeval boottime;
400         size_t len;
401         
402         mib[0] = CTL_KERN;
403         mib[1] = KERN_BOOTTIME;
404         len = sizeof(struct timeval);
405         
406         sysctl(mib, 2, &boottime, &len, NULL, 0);
407         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
408         
409         m_ignoreWindowSizedMessages = false;
410 }
411
412 GHOST_SystemCocoa::~GHOST_SystemCocoa()
413 {
414 }
415
416
417 GHOST_TSuccess GHOST_SystemCocoa::init()
418 {
419         
420     GHOST_TSuccess success = GHOST_System::init();
421     if (success) {
422                 //ProcessSerialNumber psn;
423                 
424                 //FIXME: Carbon stuff to move window & menu to foreground
425                 /*if (!GetCurrentProcess(&psn)) {
426                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
427                         SetFrontProcess(&psn);
428                 }*/
429                 
430                 autoReleasePool = [[NSAutoreleasePool alloc] init];
431                 if (NSApp == nil) {
432                         [NSApplication sharedApplication];
433                         
434                         if ([NSApp mainMenu] == nil) {
435                                 //FIXME: CreateApplicationMenus();
436                                 printf("Creating main menu");
437                         }
438                         [NSApp finishLaunching];
439                 }
440                 if ([NSApp delegate] == nil) {
441                         [NSApp setDelegate:[[CocoaAppDelegate alloc] init]];
442                 }
443                 
444                 
445                 /*
446          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
447          * This initializes the cursor's visibility counter to 0.
448          */
449         /*::InitCursor();
450                 
451                 MenuRef windMenu;
452                 ::CreateStandardWindowMenu(0, &windMenu);
453                 ::InsertMenu(windMenu, 0);
454                 ::DrawMenuBar();
455                 
456         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
457                 
458                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
459                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
460                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
461                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
462                 */
463     }
464     return success;
465 }
466
467
468 GHOST_TSuccess GHOST_SystemCocoa::exit()
469 {
470         NSAutoreleasePool* pool = (NSAutoreleasePool *)autoReleasePool;
471         [pool release];
472     return GHOST_System::exit();
473 }
474
475 #pragma mark window management
476
477 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
478 {
479         //FIXME : replace by Cocoa equivalent
480         int mib[2];
481         struct timeval boottime;
482         size_t len;
483         
484         mib[0] = CTL_KERN;
485         mib[1] = KERN_BOOTTIME;
486         len = sizeof(struct timeval);
487         
488         sysctl(mib, 2, &boottime, &len, NULL, 0);
489
490         return ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
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         return [[NSScreen screens] count];
499 }
500
501
502 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
503 {
504         //TODO: Provide visible frame or total frame, check for consistency with rest of code
505         NSRect frame = [[NSScreen mainScreen] visibleFrame];
506         
507         width = frame.size.width;
508         height = frame.size.height;
509 }
510
511
512 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
513         const STR_String& title, 
514         GHOST_TInt32 left,
515         GHOST_TInt32 top,
516         GHOST_TUns32 width,
517         GHOST_TUns32 height,
518         GHOST_TWindowState state,
519         GHOST_TDrawingContextType type,
520         bool stereoVisual,
521         const GHOST_TEmbedderWindowID parentWindow
522 )
523 {
524     GHOST_IWindow* window = 0;
525
526         window = new GHOST_WindowCocoa (title, left, top, width, height, state, type);
527
528     if (window) {
529         if (window->getValid()) {
530             // Store the pointer to the window 
531             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
532             m_windowManager->addWindow(window);
533             m_windowManager->setActiveWindow(window);
534             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
535         }
536         else {
537                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
538             delete window;
539             window = 0;
540         }
541     }
542         else {
543                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
544         }
545     return window;
546 }
547
548 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
549 {       
550         GHOST_TSuccess success = GHOST_kFailure;
551
552         //TODO: update this method
553         // need yo make this Carbon all on 10.5 for fullscreen to work correctly
554         CGCaptureAllDisplays();
555         
556         success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
557         
558         if( success != GHOST_kSuccess ) {
559                         // fullscreen failed for other reasons, release
560                         CGReleaseAllDisplays(); 
561         }
562
563         return success;
564 }
565
566 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
567 {       
568         //TODO: update this method
569         CGReleaseAllDisplays();
570         return GHOST_System::endFullScreen();
571 }
572
573
574         
575
576 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
577 {
578     NSPoint mouseLoc = [NSEvent mouseLocation];
579         
580     // Convert the coordinates to screen coordinates
581     x = (GHOST_TInt32)mouseLoc.x;
582     y = (GHOST_TInt32)mouseLoc.y;
583     return GHOST_kSuccess;
584 }
585
586
587 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
588 {
589         float xf=(float)x, yf=(float)y;
590         //TODO:cocoatize this
591         CGAssociateMouseAndMouseCursorPosition(false);
592         CGSetLocalEventsSuppressionInterval(0);
593         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
594         CGAssociateMouseAndMouseCursorPosition(true);
595
596 //this doesn't work properly, see game engine mouse-look scripts
597 //      CGWarpMouseCursorPosition(CGPointMake(xf, yf));
598         // this call below sends event, but empties other events (like shift)
599         // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
600
601     return GHOST_kSuccess;
602 }
603
604
605 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
606 {
607     NSUInteger modifiers = [[NSApp currentEvent] modifierFlags];
608         //Direct query to modifierFlags can be used in 10.6
609
610     keys.set(GHOST_kModifierKeyCommand, (modifiers & NSCommandKeyMask) ? true : false);
611     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & NSAlternateKeyMask) ? true : false);
612     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & NSShiftKeyMask) ? true : false);
613     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & NSControlKeyMask) ? true : false);
614         
615     return GHOST_kSuccess;
616 }
617
618 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
619 {
620         buttons.clear();
621     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
622         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
623         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
624         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
625         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
626     return GHOST_kSuccess;
627 }
628
629
630
631 #pragma mark Event handlers
632
633 /**
634  * The event queue polling function
635  */
636 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
637 {
638         bool anyProcessed = false;
639         NSEvent *event;
640         
641         //      SetMouseCoalescingEnabled(false, NULL);
642         //TODO : implement timer ??
643         
644         do {
645                 //GHOST_TimerManager* timerMgr = getTimerManager();
646                 /*
647                  if (waitForEvent) {
648                  GHOST_TUns64 next = timerMgr->nextFireTime();
649                  double timeOut;
650                  
651                  if (next == GHOST_kFireTimeNever) {
652                  timeOut = kEventDurationForever;
653                  } else {
654                  timeOut = (double)(next - getMilliSeconds())/1000.0;
655                  if (timeOut < 0.0)
656                  timeOut = 0.0;
657                  }
658                  
659                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
660                  }
661                  
662                  if (timerMgr->fireTimers(getMilliSeconds())) {
663                  anyProcessed = true;
664                  }
665                  
666                  //TODO: check fullscreen redrawing issues
667                  if (getFullScreen()) {
668                  // Check if the full-screen window is dirty
669                  GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
670                  if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
671                  pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
672                  anyProcessed = true;
673                  }
674                  }*/
675                 
676                 do {
677                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
678                                                                            untilDate:[NSDate distantPast]
679                                                                                   inMode:NSDefaultRunLoopMode
680                                                                                  dequeue:YES];
681                         if (event==nil)
682                                 break;
683                         
684                         anyProcessed = true;
685                         
686                         switch ([event type]) {
687                                 case NSKeyDown:
688                                 case NSKeyUp:
689                                 case NSFlagsChanged:
690                                         handleKeyEvent(event);
691                                         
692                                         /* Support system-wide keyboard shortcuts, like Exposé, ...) */
693                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
694                                          [NSApp sendEvent:event];
695                                          }*/
696                                         break;
697                                         
698                                 case NSLeftMouseDown:
699                                 case NSLeftMouseUp:
700                                 case NSRightMouseDown:
701                                 case NSRightMouseUp:
702                                 case NSMouseMoved:
703                                 case NSLeftMouseDragged:
704                                 case NSRightMouseDragged:
705                                 case NSScrollWheel:
706                                 case NSOtherMouseDown:
707                                 case NSOtherMouseUp:
708                                 case NSOtherMouseDragged:                               
709                                         handleMouseEvent(event);
710                                         break;
711                                         
712                                 case NSTabletPoint:
713                                 case NSTabletProximity:
714                                         handleTabletEvent(event);
715                                         break;
716                                         
717                                         /* Trackpad features, will need OS X 10.6 for implementation
718                                          case NSEventTypeGesture:
719                                          case NSEventTypeMagnify:
720                                          case NSEventTypeSwipe:
721                                          case NSEventTypeRotate:
722                                          case NSEventTypeBeginGesture:
723                                          case NSEventTypeEndGesture:
724                                          break; */
725                                         
726                                         /*Unused events
727                                          NSMouseEntered       = 8,
728                                          NSMouseExited        = 9,
729                                          NSAppKitDefined      = 13,
730                                          NSSystemDefined      = 14,
731                                          NSApplicationDefined = 15,
732                                          NSPeriodic           = 16,
733                                          NSCursorUpdate       = 17,*/
734                                         
735                                 default:
736                                         break;
737                         }
738                         //Resend event to NSApp to ensure Mac wide events are handled
739                         [NSApp sendEvent:event];
740                 } while (event!= nil);          
741         } while (waitForEvent && !anyProcessed);
742         
743     return anyProcessed;
744 }
745
746 //TODO: To be called from NSWindow delegate
747 int GHOST_SystemCocoa::handleWindowEvent(void *eventPtr)
748 {
749         int err = eventNotHandledErr;
750         /*WindowRef windowRef;
751         GHOST_WindowCocoa *window;
752         
753         // Check if the event was send to a GHOST window
754         ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
755         window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
756         if (!validWindow(window)) {
757                 return err;
758         }
759
760         //if (!getFullScreen()) {
761                 err = noErr;
762                 switch([event ]) 
763                 {
764                         case kEventWindowClose:
765                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
766                                 break;
767                         case kEventWindowActivated:
768                                 m_windowManager->setActiveWindow(window);
769                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
770                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
771                                 break;
772                         case kEventWindowDeactivated:
773                                 m_windowManager->setWindowInactive(window);
774                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
775                                 break;
776                         case kEventWindowUpdate:
777                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
778                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
779                                 break;
780                         case kEventWindowBoundsChanged:
781                                 if (!m_ignoreWindowSizedMessages)
782                                 {
783                                         window->updateDrawingContext();
784                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
785                                 }
786                                 break;
787                         default:
788                                 err = eventNotHandledErr;
789                                 break;
790                 }
791 //      }
792         //else {
793                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
794                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
795                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
796         //}
797         */
798         return err;
799 }
800
801 int GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
802 {
803         NSEvent *event = (NSEvent *)eventPtr;
804         GHOST_IWindow* window = m_windowManager->getActiveWindow();
805         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
806         
807         ct.Pressure = 0;
808         ct.Xtilt = 0;
809         ct.Ytilt = 0;
810         
811         
812         switch ([event type]) {
813                 case NSTabletPoint:
814                         ct.Pressure = [event tangentialPressure];
815                         ct.Xtilt = [event tilt].x;
816                         ct.Ytilt = [event tilt].y;
817                         break;
818                 
819                 case NSTabletProximity:
820                         if ([event isEnteringProximity])
821                         {
822                                 //pointer is entering tablet area proximity
823                                 switch ([event pointingDeviceType]) {
824                                         case NSPenPointingDevice:
825                                                 ct.Active = GHOST_kTabletModeStylus;
826                                                 break;
827                                         case NSEraserPointingDevice:
828                                                 ct.Active = GHOST_kTabletModeEraser;
829                                                 break;
830                                         case NSCursorPointingDevice:
831                                         case NSUnknownPointingDevice:
832                                         default:
833                                                 ct.Active = GHOST_kTabletModeNone;
834                                                 break;
835                                 }
836                         } else {
837                                 // pointer is leaving - return to mouse
838                                 ct.Active = GHOST_kTabletModeNone;
839                         }
840                         break;
841                 
842                 default:
843                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
844                         break;
845         }
846         return noErr;
847 }
848
849
850 int GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
851 {
852         NSEvent *event = (NSEvent *)eventPtr;
853     GHOST_IWindow* window = m_windowManager->getActiveWindow();
854                         
855         switch ([event type])
856     {
857                         //TODO: check for tablet subtype events
858                 case NSLeftMouseDown:
859                 case NSRightMouseDown:
860                 case NSOtherMouseDown:
861                         if (m_windowManager->getActiveWindow()) {
862                                 pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
863                         }
864                         break;
865                                                 
866                 case NSLeftMouseUp:
867                 case NSRightMouseUp:
868                 case NSOtherMouseUp:
869                         if (m_windowManager->getActiveWindow()) {
870                                 pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
871                         }
872                         break;
873                         
874                 case NSMouseMoved:
875                 case NSLeftMouseDragged:
876                 case NSRightMouseDragged:
877                 case NSOtherMouseDragged:                               
878                         {
879                                 NSPoint mousePos = [event locationInWindow];
880                                 pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
881                                 break;
882                         }
883                         
884                 case NSScrollWheel:
885                         {
886                                 GHOST_TInt32 delta;
887                                 delta = [event deltaY] > 0 ? 1 : -1;
888                                 pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
889
890                         }
891                         break;
892                 }
893         
894         return noErr;
895 }
896
897
898 int GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
899 {
900         NSEvent *event = (NSEvent *)eventPtr;
901         OSStatus err = eventNotHandledErr;
902         GHOST_IWindow* window = m_windowManager->getActiveWindow();
903         NSUInteger modifiers;
904         unsigned short rawCode;
905         GHOST_TKey key;
906         unsigned char ascii;
907
908         /* Can happen, very rarely - seems to only be when command-H makes
909          * the window go away and we still get an HKey up. 
910          */
911         if (!window) {
912                 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
913                 //key = convertKey(rawCode);
914                 return err;
915         }
916         
917         err = noErr;
918         switch ([event type]) {
919                 case NSKeyDown:
920                 case NSKeyUp:
921                         rawCode = [event keyCode];
922                         ascii = [[event characters] characterAtIndex:0];
923         
924                         key = convertKey(rawCode,ascii);
925                         ascii= convertRomanToLatin(ascii);
926                         
927                         if ([event type] == NSKeyDown) {
928                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyDown, window, key, ascii) );
929                                 } else {
930                                         pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyUp, window, key, ascii) );
931                                 }
932                         break;
933         
934                 case NSFlagsChanged: 
935                         modifiers = [event modifierFlags];
936                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
937                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
938                         }
939                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
940                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
941                         }
942                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
943                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
944                         }
945                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
946                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
947                         }
948                         
949                         m_modifierMask = modifiers;
950                         break;
951                         
952                 default:
953                         err = eventNotHandledErr;
954                         break;
955         }
956         
957         return err;
958 }
959
960
961 /* System wide mouse clicks are handled directly through systematic event forwarding to Cocoa
962 bool GHOST_SystemCarbon::handleMouseDown(void *eventPtr)
963 {
964         NSEvent *event = (NSEvent *)eventPtr;
965         WindowPtr                       window;
966         short                           part;
967         BitMap                          screenBits;
968     bool                                handled = true;
969     GHOST_WindowCarbon* ghostWindow;
970     Point                               mousePos = {0 , 0};
971         
972         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
973         
974         part = ::FindWindow(mousePos, &window);
975         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
976         
977         switch (part) {
978                 case inMenuBar:
979                         handleMenuCommand(::MenuSelect(mousePos));
980                         break;
981                         
982                 case inDrag:
983                         // *
984                         // * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
985                         // * events. By setting m_ignoreWindowSizedMessages these are suppressed.
986                         // * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
987                         // *
988                         // even worse: scale window also generates a load of events, and nothing 
989                          //  is handled (read: client's event proc called) until you release mouse (ton) 
990                         
991                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
992                         m_ignoreWindowSizedMessages = true;
993                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
994                         m_ignoreWindowSizedMessages = false;
995                         
996                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
997
998                         break;
999                 
1000                 case inContent:
1001                         if (window != ::FrontWindow()) {
1002                                 ::SelectWindow(window);
1003                                 //
1004                                 // * We add a mouse down event on the newly actived window
1005                                 // *            
1006                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
1007                                 EventMouseButton button;
1008                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
1009                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
1010                         } else {
1011                                 handled = false;
1012                         }
1013                         break;
1014                         
1015                 case inGoAway:
1016                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1017                         if (::TrackGoAway(window, mousePos))
1018                         {
1019                                 // todo: add option-close, because itÿs in the HIG
1020                                 // if (event.modifiers & optionKey) {
1021                                         // Close the clean documents, others will be confirmed one by one.
1022                                 //}
1023                                 // else {
1024                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
1025                                 //}
1026                         }
1027                         break;
1028                         
1029                 case inGrow:
1030                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1031                         ::ResizeWindow(window, mousePos, NULL, NULL);
1032                         break;
1033                         
1034                 case inZoomIn:
1035                 case inZoomOut:
1036                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1037                         if (::TrackBox(window, mousePos, part)) {
1038                                 int macState;
1039                                 
1040                                 macState = ghostWindow->getMac_windowState();
1041                                 if ( macState== 0)
1042                                         ::ZoomWindow(window, part, true);
1043                                 else 
1044                                         if (macState == 2) { // always ok
1045                                                         ::ZoomWindow(window, part, true);
1046                                                         ghostWindow->setMac_windowState(1);
1047                                         } else { // need to force size again
1048                                         //      GHOST_TUns32 scr_x,scr_y; //unused
1049                                                 Rect outAvailableRect;
1050                                                 
1051                                                 ghostWindow->setMac_windowState(2);
1052                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1053                                                 
1054                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
1055                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1056                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1057                                         }
1058                                 
1059                         }
1060                         break;
1061
1062                 default:
1063                         handled = false;
1064                         break;
1065         }
1066         
1067         return handled;
1068 }
1069
1070
1071 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1072 {
1073         short           menuID;
1074         short           menuItem;
1075         UInt32          command;
1076         bool            handled;
1077         OSErr           err;
1078         
1079         menuID = HiWord(menuResult);
1080         menuItem = LoWord(menuResult);
1081
1082         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1083
1084         handled = false;
1085         
1086         if (err || command == 0) {
1087         }
1088         else {
1089                 switch(command) {
1090                 }
1091         }
1092
1093         ::HiliteMenu(0);
1094     return handled;
1095 }*/
1096
1097
1098
1099 #pragma mark Clipboard get/set
1100
1101 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1102 {
1103         GHOST_TUns8 * temp_buff;
1104         size_t pastedTextSize;  
1105         
1106         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1107         
1108         if (pasteBoard = nil) {
1109                 return NULL;
1110         }
1111         
1112         NSArray *supportedTypes =
1113                 [NSArray arrayWithObjects: @"public.utf8-plain-text", nil];
1114         
1115         NSString *bestType = [[NSPasteboard generalPasteboard]
1116                                                   availableTypeFromArray:supportedTypes];
1117         
1118         if (bestType == nil) { return NULL; }
1119         
1120         NSString * textPasted = [pasteBoard stringForType:@"public.utf8-plain-text"];
1121
1122         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1123         
1124         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1125
1126         if (temp_buff == NULL) return NULL;
1127         
1128         strncpy((char*)temp_buff, [textPasted UTF8String], pastedTextSize);
1129         
1130         temp_buff[pastedTextSize] = '\0';
1131         
1132         if(temp_buff) {
1133                 return temp_buff;
1134         } else {
1135                 return NULL;
1136         }
1137 }
1138
1139 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1140 {
1141         NSString *textToCopy;
1142         
1143         if(selection) {return;} // for copying the selection, used on X11
1144
1145         
1146         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1147         
1148         if (pasteBoard = nil) {
1149                 return;
1150         }
1151         
1152         NSArray *supportedTypes = [NSArray arrayWithObjects: @"public.utf8-plain-text",nil];
1153         
1154         [pasteBoard declareTypes:supportedTypes owner:nil];
1155         
1156         textToCopy = [NSString stringWithUTF8String:buffer];
1157         
1158         [pasteBoard setString:textToCopy forType:@"public.utf8-plain-text"];
1159         
1160 }
1161
1162 #pragma mark Carbon stuff to remove
1163
1164 #ifdef WITH_CARBON
1165
1166
1167 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1168 {
1169         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1170         
1171         return noErr;
1172 }
1173
1174 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1175 {
1176         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1177         AEDescList docs;
1178         SInt32 ndocs;
1179         OSErr err;
1180         
1181         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
1182         if (err != noErr)  return err;
1183         
1184         err = AECountItems(&docs, &ndocs);
1185         if (err==noErr) {
1186                 int i;
1187                 
1188                 for (i=0; i<ndocs; i++) {
1189                         FSSpec fss;
1190                         AEKeyword kwd;
1191                         DescType actType;
1192                         Size actSize;
1193                         
1194                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
1195                         if (err!=noErr)
1196                                 break;
1197                         
1198                         if (i==0) {
1199                                 FSRef fsref;
1200                                 
1201                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
1202                                         break;
1203                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
1204                                         break;
1205                                 
1206                                 g_hasFirstFile = true;
1207                         }
1208                 }
1209         }
1210         
1211         AEDisposeDesc(&docs);
1212         
1213         return err;
1214 }
1215
1216 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1217 {
1218         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1219         
1220         return noErr;
1221 }
1222
1223 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1224 {
1225         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1226         
1227         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
1228         
1229         return noErr;
1230 }
1231
1232 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1233 {
1234         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1235     OSStatus err = eventNotHandledErr;
1236         GHOST_IWindow* window;
1237         GHOST_TEventNDOFData data;
1238         UInt32 kind;
1239         
1240     switch (::GetEventClass(event))
1241     {
1242                 case kEventClassAppleEvent:
1243                         EventRecord eventrec;
1244                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
1245                                 err = AEProcessAppleEvent(&eventrec);
1246                         }
1247                         break;
1248         case kEventClassMouse:
1249             err = sys->handleMouseEvent(event);
1250             break;
1251                 case kEventClassWindow:
1252                         err = sys->handleWindowEvent(event);
1253                         break;
1254                 case kEventClassKeyboard:
1255                         err = sys->handleKeyEvent(event);
1256                         break;
1257                 case kEventClassBlender :
1258                         window = sys->m_windowManager->getActiveWindow();
1259                         sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1260                         kind = ::GetEventKind(event);
1261                         
1262                         switch (kind)
1263                 {
1264                         case 1:
1265                                 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1266                                 //                              printf("motion\n");
1267                                 break;
1268                         case 2:
1269                                 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1270                                 //                                      printf("button\n");
1271                                 break;
1272                 }
1273                         err = noErr;
1274                         break;
1275                 default : 
1276                         ;
1277                         break;
1278         }
1279         
1280     return err;
1281 }
1282 #endif