sculpt mode ndof transforms.
[blender.git] / intern / ghost / intern / GHOST_SystemCarbon.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL 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. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33
34  * $Id$
35  * Copyright (C) 2001 NaN Technologies B.V.
36  * @author      Maarten Gribnau
37  * @date        May 7, 2001
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "GHOST_SystemCarbon.h"
45
46 #include "GHOST_DisplayManagerCarbon.h"
47 #include "GHOST_EventKey.h"
48 #include "GHOST_EventButton.h"
49 #include "GHOST_EventCursor.h"
50 #include "GHOST_EventWheel.h"
51 #include "GHOST_EventNDOF.h"
52
53 #include "GHOST_TimerManager.h"
54 #include "GHOST_TimerTask.h"
55 #include "GHOST_WindowManager.h"
56 #include "GHOST_WindowCarbon.h"
57 #include "GHOST_NDOFManager.h"
58
59 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
60
61 /* blender class and types events */
62 enum {
63   kEventClassBlender              = 'blnd'
64 };
65
66 enum {
67         kEventBlenderNdofAxis                   = 1,
68         kEventBlenderNdofButtons                = 2
69 };
70
71 const EventTypeSpec     kEvents[] =
72 {
73         { kEventClassAppleEvent, kEventAppleEvent },
74 /*
75         { kEventClassApplication, kEventAppActivated },
76         { kEventClassApplication, kEventAppDeactivated },
77 */      
78         { kEventClassKeyboard, kEventRawKeyDown },
79         { kEventClassKeyboard, kEventRawKeyRepeat },
80         { kEventClassKeyboard, kEventRawKeyUp },
81         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
82         
83         { kEventClassMouse, kEventMouseDown },
84         { kEventClassMouse, kEventMouseUp },
85         { kEventClassMouse, kEventMouseMoved },
86         { kEventClassMouse, kEventMouseDragged },
87         { kEventClassMouse, kEventMouseWheelMoved },
88         
89         { kEventClassWindow, kEventWindowClickZoomRgn } ,  /* for new zoom behaviour */ 
90         { kEventClassWindow, kEventWindowZoom },  /* for new zoom behaviour */ 
91         { kEventClassWindow, kEventWindowExpand } ,  /* for new zoom behaviour */ 
92         { kEventClassWindow, kEventWindowExpandAll },  /* for new zoom behaviour */ 
93
94         { kEventClassWindow, kEventWindowClose },
95         { kEventClassWindow, kEventWindowActivated },
96         { kEventClassWindow, kEventWindowDeactivated },
97         { kEventClassWindow, kEventWindowUpdate },
98         { kEventClassWindow, kEventWindowBoundsChanged },
99         
100         { kEventClassBlender, kEventBlenderNdofAxis },
101         { kEventClassBlender, kEventBlenderNdofButtons }
102         
103         
104         
105 };
106
107
108
109 static GHOST_TButtonMask convertButton(EventMouseButton button)
110 {
111         switch (button) {
112         case kEventMouseButtonPrimary:
113                 return GHOST_kButtonMaskLeft;
114         case kEventMouseButtonSecondary:
115                 return GHOST_kButtonMaskRight;
116         case kEventMouseButtonTertiary:
117         default:
118                 return GHOST_kButtonMaskMiddle;
119         }
120 }
121
122 static GHOST_TKey convertKey(int rawCode) 
123 {       
124                 /* This bit of magic converts the rawCode into a virtual
125                  * Mac key based on the current keyboard mapping, but
126                  * without regard to the modifiers (so we don't get 'a' 
127                  * and 'A' for example.
128                  */
129         static UInt32 dummy= 0;
130         Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
131         unsigned char vk = KeyTranslate(transData, rawCode, &dummy);    
132                 /* Map numpad based on rawcodes first, otherwise they
133                  * look like non-numpad events.
134                  * Added too: mapping the number keys, for french keyboards etc (ton)
135                  */
136         // printf("GHOST: vk: %d %c raw: %d\n", vk, vk, rawCode);
137                  
138         switch (rawCode) {
139         case 18:        return GHOST_kKey1;
140         case 19:        return GHOST_kKey2;
141         case 20:        return GHOST_kKey3;
142         case 21:        return GHOST_kKey4;
143         case 23:        return GHOST_kKey5;
144         case 22:        return GHOST_kKey6;
145         case 26:        return GHOST_kKey7;
146         case 28:        return GHOST_kKey8;
147         case 25:        return GHOST_kKey9;
148         case 29:        return GHOST_kKey0;
149         
150         case 82:        return GHOST_kKeyNumpad0;
151         case 83:        return GHOST_kKeyNumpad1;
152         case 84:        return GHOST_kKeyNumpad2;
153         case 85:        return GHOST_kKeyNumpad3;
154         case 86:        return GHOST_kKeyNumpad4;
155         case 87:        return GHOST_kKeyNumpad5;
156         case 88:        return GHOST_kKeyNumpad6;
157         case 89:        return GHOST_kKeyNumpad7;
158         case 91:        return GHOST_kKeyNumpad8;
159         case 92:        return GHOST_kKeyNumpad9;
160         case 65:        return GHOST_kKeyNumpadPeriod;
161         case 76:        return GHOST_kKeyNumpadEnter;
162         case 69:        return GHOST_kKeyNumpadPlus;
163         case 78:        return GHOST_kKeyNumpadMinus;
164         case 67:        return GHOST_kKeyNumpadAsterisk;
165         case 75:        return GHOST_kKeyNumpadSlash;
166         }
167         
168         if ((vk >= 'a') && (vk <= 'z')) {
169                 return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
170         } else if ((vk >= '0') && (vk <= '9')) {
171                 return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
172         } else if (vk==16) {
173                 switch (rawCode) {
174                 case 122:       return GHOST_kKeyF1;
175                 case 120:       return GHOST_kKeyF2;
176                 case 99:        return GHOST_kKeyF3;
177                 case 118:       return GHOST_kKeyF4;
178                 case 96:        return GHOST_kKeyF5;
179                 case 97:        return GHOST_kKeyF6;
180                 case 98:        return GHOST_kKeyF7;
181                 case 100:       return GHOST_kKeyF8;
182                 case 101:       return GHOST_kKeyF9;
183                 case 109:       return GHOST_kKeyF10;
184                 case 103:       return GHOST_kKeyF11;
185                 case 111:       return GHOST_kKeyF12;  // Never get, is used for ejecting the CD! 
186                 }
187         } else {
188                 switch (vk) {
189                 case kUpArrowCharCode:          return GHOST_kKeyUpArrow;
190                 case kDownArrowCharCode:        return GHOST_kKeyDownArrow;
191                 case kLeftArrowCharCode:        return GHOST_kKeyLeftArrow;
192                 case kRightArrowCharCode:       return GHOST_kKeyRightArrow;
193
194                 case kReturnCharCode:           return GHOST_kKeyEnter;
195                 case kBackspaceCharCode:        return GHOST_kKeyBackSpace;
196                 case kDeleteCharCode:           return GHOST_kKeyDelete;
197                 case kEscapeCharCode:           return GHOST_kKeyEsc;
198                 case kTabCharCode:                      return GHOST_kKeyTab;
199                 case kSpaceCharCode:            return GHOST_kKeySpace;
200
201                 case kHomeCharCode:             return GHOST_kKeyHome;
202                 case kEndCharCode:                      return GHOST_kKeyEnd;
203                 case kPageUpCharCode:           return GHOST_kKeyUpPage;
204                 case kPageDownCharCode:         return GHOST_kKeyDownPage;
205
206                 case '-':       return GHOST_kKeyMinus;
207                 case '=':       return GHOST_kKeyEqual;
208                 case ',':       return GHOST_kKeyComma;
209                 case '.':       return GHOST_kKeyPeriod;
210                 case '/':       return GHOST_kKeySlash;
211                 case ';':       return GHOST_kKeySemicolon;
212                 case '\'':      return GHOST_kKeyQuote;
213                 case '\\':      return GHOST_kKeyBackslash;
214                 case '[':       return GHOST_kKeyLeftBracket;
215                 case ']':       return GHOST_kKeyRightBracket;
216                 case '`':       return GHOST_kKeyAccentGrave;
217                 }
218         }
219         
220         // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
221         
222         return GHOST_kKeyUnknown;
223 }
224
225 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
226  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
227  * I am not sure how international this works...
228  * For cross-platform convention, we'll use the Latin ascii set instead.
229  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
230  * 
231  */
232 static unsigned char convertRomanToLatin(unsigned char ascii)
233 {
234
235         if(ascii<128) return ascii;
236         
237         switch(ascii) {
238         case 128:       return 142;
239         case 129:       return 143;
240         case 130:       return 128;
241         case 131:       return 201;
242         case 132:       return 209;
243         case 133:       return 214;
244         case 134:       return 220;
245         case 135:       return 225;
246         case 136:       return 224;
247         case 137:       return 226;
248         case 138:       return 228;
249         case 139:       return 227;
250         case 140:       return 229;
251         case 141:       return 231;
252         case 142:       return 233;
253         case 143:       return 232;
254         case 144:       return 234;
255         case 145:       return 235;
256         case 146:       return 237;
257         case 147:       return 236;
258         case 148:       return 238;
259         case 149:       return 239;
260         case 150:       return 241;
261         case 151:       return 243;
262         case 152:       return 242;
263         case 153:       return 244;
264         case 154:       return 246;
265         case 155:       return 245;
266         case 156:       return 250;
267         case 157:       return 249;
268         case 158:       return 251;
269         case 159:       return 252;
270         case 160:       return 0;
271         case 161:       return 176;
272         case 162:       return 162;
273         case 163:       return 163;
274         case 164:       return 167;
275         case 165:       return 183;
276         case 166:       return 182;
277         case 167:       return 223;
278         case 168:       return 174;
279         case 169:       return 169;
280         case 170:       return 174;
281         case 171:       return 180;
282         case 172:       return 168;
283         case 173:       return 0;
284         case 174:       return 198;
285         case 175:       return 216;
286         case 176:       return 0;
287         case 177:       return 177;
288         case 178:       return 0;
289         case 179:       return 0;
290         case 180:       return 165;
291         case 181:       return 181;
292         case 182:       return 0;
293         case 183:       return 0;
294         case 184:       return 215;
295         case 185:       return 0;
296         case 186:       return 0;
297         case 187:       return 170;
298         case 188:       return 186;
299         case 189:       return 0;
300         case 190:       return 230;
301         case 191:       return 248;
302         case 192:       return 191;
303         case 193:       return 161;
304         case 194:       return 172;
305         case 195:       return 0;
306         case 196:       return 0;
307         case 197:       return 0;
308         case 198:       return 0;
309         case 199:       return 171;
310         case 200:       return 187;
311         case 201:       return 201;
312         case 202:       return 0;
313         case 203:       return 192;
314         case 204:       return 195;
315         case 205:       return 213;
316         case 206:       return 0;
317         case 207:       return 0;
318         case 208:       return 0;
319         case 209:       return 0;
320         case 210:       return 0;
321         
322         case 214:       return 247;
323
324         case 229:       return 194;
325         case 230:       return 202;
326         case 231:       return 193;
327         case 232:       return 203;
328         case 233:       return 200;
329         case 234:       return 205;
330         case 235:       return 206;
331         case 236:       return 207;
332         case 237:       return 204;
333         case 238:       return 211;
334         case 239:       return 212;
335         case 240:       return 0;
336         case 241:       return 210;
337         case 242:       return 218;
338         case 243:       return 219;
339         case 244:       return 217;
340         case 245:       return 0;
341         case 246:       return 0;
342         case 247:       return 0;
343         case 248:       return 0;
344         case 249:       return 0;
345         case 250:       return 0;
346
347         
348                 default: return 0;
349         }
350
351 }
352
353
354 /***/
355
356 GHOST_SystemCarbon::GHOST_SystemCarbon() :
357         m_modifierMask(0)
358 {
359         m_displayManager = new GHOST_DisplayManagerCarbon ();
360         GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
361         m_displayManager->initialize();
362
363         UnsignedWide micros;
364         ::Microseconds(&micros);
365         m_start_time = UnsignedWideToUInt64(micros)/1000;
366         m_ignoreWindowSizedMessages = false;
367 }
368
369 GHOST_SystemCarbon::~GHOST_SystemCarbon()
370 {
371 }
372
373
374 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
375 {
376         UnsignedWide micros;
377         ::Microseconds(&micros);
378         UInt64 millis;
379         millis = UnsignedWideToUInt64(micros);
380         return (millis / 1000) - m_start_time;
381 }
382
383
384 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
385 {
386         // We do not support multiple monitors at the moment
387         return 1;
388 }
389
390
391 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
392 {
393         BitMap screenBits;
394     Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
395         width = bnds.right - bnds.left;
396         height = bnds.bottom - bnds.top;
397 }
398
399
400 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
401         const STR_String& title, 
402         GHOST_TInt32 left,
403         GHOST_TInt32 top,
404         GHOST_TUns32 width,
405         GHOST_TUns32 height,
406         GHOST_TWindowState state,
407         GHOST_TDrawingContextType type,
408         bool stereoVisual
409 )
410 {
411     GHOST_IWindow* window = 0;
412
413         window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
414
415     if (window) {
416         if (window->getValid()) {
417             // Store the pointer to the window 
418             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
419             m_windowManager->addWindow(window);
420             m_windowManager->setActiveWindow(window);
421             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
422         }
423         else {
424                         GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
425             delete window;
426             window = 0;
427         }
428     }
429         else {
430                 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
431         }
432     return window;
433 }
434
435 /* this is an old style low level event queue.
436   As we want to handle our own timers, this is ok.
437   the full screen hack should be removed */
438 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
439 {
440         bool anyProcessed = false;
441         EventRef event;
442         
443         do {
444                 GHOST_TimerManager* timerMgr = getTimerManager();
445                 
446                 if (waitForEvent) {
447                         GHOST_TUns64 curtime = getMilliSeconds();
448                         GHOST_TUns64 next = timerMgr->nextFireTime();
449                         double timeOut;
450                         
451                         if (next == GHOST_kFireTimeNever) {
452                                 timeOut = kEventDurationForever;
453                         } else {
454                                 if (next<=curtime)
455                                         timeOut = 0.0;
456                                 else
457                                         timeOut = (double) (next - getMilliSeconds())/1000.0;
458                         }
459                         
460                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
461                 }
462                 
463                 if (timerMgr->fireTimers(getMilliSeconds())) {
464                         anyProcessed = true;
465                 }
466
467                 if (getFullScreen()) {
468                         // Check if the full-screen window is dirty
469                         GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
470                         if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
471                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
472                                 anyProcessed = true;
473                         }
474                 }
475
476                 
477                 /* end loop when no more events available */
478                 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
479                         OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
480                         if (status==noErr) {
481                                 anyProcessed = true;
482                         } else {
483                                 UInt32 i= ::GetEventClass(event);
484                                 
485                                         /* Ignore 'cgs ' class, no documentation on what they
486                                          * are, but we get a lot of them
487                                          */
488                                 if (i!='cgs ') {
489                                         if (i!='tblt') {  // tablet event. we use the one packaged in the mouse event
490                                                 printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
491                                         }
492                                 }
493                         }
494                         ::ReleaseEvent(event);
495                 }
496         } while (waitForEvent && !anyProcessed);
497         
498     return anyProcessed;
499 }
500         
501
502 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
503 {
504     Point mouseLoc;
505     // Get the position of the mouse in the active port
506     ::GetGlobalMouse(&mouseLoc);
507     // Convert the coordinates to screen coordinates
508     x = (GHOST_TInt32)mouseLoc.h;
509     y = (GHOST_TInt32)mouseLoc.v;
510     return GHOST_kSuccess;
511 }
512
513
514 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
515 {
516         float xf=(float)x, yf=(float)y;
517
518         CGAssociateMouseAndMouseCursorPosition(false);
519         CGSetLocalEventsSuppressionInterval(0);
520         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
521         CGAssociateMouseAndMouseCursorPosition(true);
522
523 //this doesn't work properly, see game engine mouse-look scripts
524 //      CGWarpMouseCursorPosition(CGPointMake(xf, yf));
525         // this call below sends event, but empties other events (like shift)
526         // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
527
528     return GHOST_kSuccess;
529 }
530
531
532 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
533 {
534     UInt32 modifiers = ::GetCurrentKeyModifiers();
535
536     keys.set(GHOST_kModifierKeyCommand, (modifiers & cmdKey) ? true : false);
537     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
538     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
539     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
540         
541     return GHOST_kSuccess;
542 }
543
544         /* XXX, incorrect for multibutton mice */
545 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
546 {
547     Boolean theOnlyButtonIsDown = ::Button();
548     buttons.clear();
549     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
550     return GHOST_kSuccess;
551 }
552
553 #define FIRSTFILEBUFLG 512
554 static bool g_hasFirstFile = false;
555 static char g_firstFileBuf[512];
556
557 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
558         if (g_hasFirstFile) {
559                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
560                 buf[FIRSTFILEBUFLG - 1] = '\0';
561                 return 1;
562         } else {
563                 return 0; 
564         }
565 }
566
567 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
568 {
569         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
570         
571         return noErr;
572 }
573
574 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
575 {
576         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
577         AEDescList docs;
578         SInt32 ndocs;
579         OSErr err;
580
581         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
582         if (err != noErr)  return err;
583
584         err = AECountItems(&docs, &ndocs);
585         if (err==noErr) {
586                 int i;
587         
588                 for (i=0; i<ndocs; i++) {
589                         FSSpec fss;
590                         AEKeyword kwd;
591                         DescType actType;
592                         Size actSize;
593                 
594                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
595                         if (err!=noErr)
596                                 break;
597                 
598                         if (i==0) {
599                                 FSRef fsref;
600                                 
601                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
602                                         break;
603                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
604                                         break;
605
606                                 g_hasFirstFile = true;
607                         }
608                 }
609         }
610         
611         AEDisposeDesc(&docs);
612         
613         return err;
614 }
615
616 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
617 {
618         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
619         
620         return noErr;
621 }
622
623 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
624 {
625         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
626         
627         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
628         
629         return noErr;
630 }
631
632
633 GHOST_TSuccess GHOST_SystemCarbon::init()
634 {
635  
636     GHOST_TSuccess success = GHOST_System::init();
637     if (success) {
638                 /*
639          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
640          * This initializes the cursor's visibility counter to 0.
641          */
642         ::InitCursor();
643
644                 MenuRef windMenu;
645                 ::CreateStandardWindowMenu(0, &windMenu);
646                 ::InsertMenu(windMenu, 0);
647                 ::DrawMenuBar();
648
649         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
650                 
651                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
652                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
653                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
654                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
655                 
656     }
657     return success;
658 }
659
660
661 GHOST_TSuccess GHOST_SystemCarbon::exit()
662 {
663     return GHOST_System::exit();
664 }
665
666
667 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
668 {
669         WindowRef windowRef;
670         GHOST_WindowCarbon *window;
671         OSStatus err = eventNotHandledErr;
672         
673         // Check if the event was send to a GHOST window
674         ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
675         window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
676         if (!validWindow(window)) {
677                 return err;
678         }
679
680         //if (!getFullScreen()) {
681                 err = noErr;
682                 switch(::GetEventKind(event)) 
683                 {
684                         case kEventWindowClose:
685                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
686                                 break;
687                         case kEventWindowActivated:
688                                 m_windowManager->setActiveWindow(window);
689                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
690                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
691                                 break;
692                         case kEventWindowDeactivated:
693                                 m_windowManager->setWindowInactive(window);
694                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
695                                 break;
696                         case kEventWindowUpdate:
697                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
698                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
699                                 break;
700                         case kEventWindowBoundsChanged:
701                                 if (!m_ignoreWindowSizedMessages)
702                                 {
703                                         window->updateDrawingContext();
704                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
705                                 }
706                                 break;
707                         default:
708                                 err = eventNotHandledErr;
709                                 break;
710                 }
711 //      }
712         //else {
713                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
714                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
715                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
716         //}
717         
718         return err;
719 }
720
721 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
722 {
723         GHOST_IWindow* window = m_windowManager->getActiveWindow();
724         TabletPointRec tabletPointRecord;
725         TabletProximityRec      tabletProximityRecord;
726         UInt32 anInt32;
727         GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
728         OSStatus err = eventNotHandledErr;
729         
730         ct.Pressure = 0;
731         ct.Xtilt = 0;
732         ct.Ytilt = 0;
733         
734         // is there an embedded tablet event inside this mouse event? 
735         if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
736         {
737                 // yes there is one!
738                 // Embedded tablet events can either be a proximity or pointer event.
739                 if(anInt32 == kEventTabletPoint)
740                 {
741                         //GHOST_PRINT("Embedded pointer event!\n");
742                         
743                         // Extract the tablet Pointer Event. If there is no Tablet Pointer data
744                         // in this event, then this call will return an error. Just ignore the
745                         // error and go on. This can occur when a proximity event is embedded in
746                         // a mouse event and you did not check the mouse event to see which type
747                         // of tablet event was embedded.
748                         if(noErr == GetEventParameter(event, kEventParamTabletPointRec,
749                                                                                   typeTabletPointRec, NULL,
750                                                                                   sizeof(TabletPointRec),
751                                                                                   NULL, (void *)&tabletPointRecord))
752                         {
753                                 ct.Pressure = tabletPointRecord.pressure / 65535.0f;
754                                 ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
755                                 ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
756                         }
757                 } else {
758                         //GHOST_PRINT("Embedded proximity event\n");
759                         
760                         // Extract the Tablet Proximity record from the event.
761                         if(noErr == GetEventParameter(event, kEventParamTabletProximityRec,
762                                                                                   typeTabletProximityRec, NULL,
763                                                                                   sizeof(TabletProximityRec),
764                                                                                   NULL, (void *)&tabletProximityRecord))
765                         {
766                                 if (tabletProximityRecord.enterProximity) {
767                                         //pointer is entering tablet area proximity
768                                         
769                                         switch(tabletProximityRecord.pointerType)
770                                         {
771                                                 case 1: /* stylus */
772                                                         ct.Active = 1;
773                                                         break;
774                                                 case 2: /* puck, not supported so far */
775                                                         ct.Active = 0;
776                                                         break;
777                                                 case 3: /* eraser */
778                                                         ct.Active = 2;
779                                                         break;
780                                                 default:
781                                                         ct.Active = 0;
782                                                         break;
783                                         }
784                                 } else {
785                                         // pointer is leaving - return to mouse
786                                         ct.Active = 0;
787                                 }
788                         }
789                 }
790         err = noErr;
791         }
792
793 }
794
795 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
796 {
797     OSStatus err = eventNotHandledErr;
798         GHOST_IWindow* window = m_windowManager->getActiveWindow();
799         UInt32 kind = ::GetEventKind(event);
800                         
801         switch (kind)
802     {
803                 case kEventMouseDown:
804                 case kEventMouseUp:
805                         // Handle Mac application responsibilities
806                         if ((kind == kEventMouseDown) && handleMouseDown(event)) {
807                                 err = noErr;
808                         }
809                         else {
810                                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
811                                 EventMouseButton button;
812                                 
813                                 /* Window still gets mouse up after command-H */
814                                 if (m_windowManager->getActiveWindow()) {
815                                         // handle any tablet events that may have come with the mouse event (optional)
816                                         handleTabletEvent(event);
817                                         
818                                         ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
819                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
820                                         err = noErr;
821                                 }
822                         }
823             break;
824                         
825                 case kEventMouseMoved:
826                 case kEventMouseDragged: {
827                         Point mousePos;
828
829                         if (window) {
830                                 //handle any tablet events that may have come with the mouse event (optional)
831                                 handleTabletEvent(event);
832
833                                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
834                                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
835                                 err = noErr;
836                         }
837                         break;
838                 }
839                 case kEventMouseWheelMoved:
840                         {
841                                 OSStatus status;
842                                 //UInt32 modifiers;
843                                 EventMouseWheelAxis axis;
844                                 SInt32 delta;
845                                 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
846                                 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
847                                 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
848                                 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
849                                 if (axis == kEventMouseWheelAxisY)
850                                 {
851                                         status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
852                                         GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
853                                         /*
854                                          * Limit mouse wheel delta to plus and minus one.
855                                          */
856                                         delta = delta > 0 ? 1 : -1;
857                                         pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
858                                         err = noErr;
859                                 }
860                         }
861                         break;
862                 }
863         
864         return err;
865 }
866
867
868 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
869 {
870     OSStatus err = eventNotHandledErr;
871         GHOST_IWindow* window = m_windowManager->getActiveWindow();
872         UInt32 kind = ::GetEventKind(event);
873         UInt32 modifiers;
874         UInt32 rawCode;
875         GHOST_TKey key;
876         unsigned char ascii;
877
878         /* Can happen, very rarely - seems to only be when command-H makes
879          * the window go away and we still get an HKey up. 
880          */
881         if (!window) {
882                 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
883                 //key = convertKey(rawCode);
884                 return err;
885         }
886         
887         err = noErr;
888         switch (kind) {
889                 case kEventRawKeyDown: 
890                 case kEventRawKeyRepeat: 
891                 case kEventRawKeyUp: 
892                         ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
893                         ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
894         
895                         key = convertKey(rawCode);
896                         ascii= convertRomanToLatin(ascii);
897                         
898         //              if (key!=GHOST_kKeyUnknown) {
899                                 GHOST_TEventType type;
900                                 if (kind == kEventRawKeyDown) {
901                                         type = GHOST_kEventKeyDown;
902                                 } else if (kind == kEventRawKeyRepeat) { 
903                                         type = GHOST_kEventKeyDown;  /* XXX, fixme */
904                                 } else {
905                                         type = GHOST_kEventKeyUp;
906                                 }
907                                 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
908 //                      }
909                         break;
910         
911                 case kEventRawKeyModifiersChanged: 
912                                 /* ugh */
913                         ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
914                         if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
915                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
916                         }
917                         if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
918                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
919                         }
920                         if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
921                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
922                         }
923                         if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
924                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
925                         }
926                         
927                         m_modifierMask = modifiers;
928                         break;
929                         
930                 default:
931                         err = eventNotHandledErr;
932                         break;
933         }
934         
935         return err;
936 }
937
938
939 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
940 {
941         WindowPtr                       window;
942         short                           part;
943         BitMap                          screenBits;
944     bool                                handled = true;
945     GHOST_WindowCarbon* ghostWindow;
946     Point                               mousePos = {0 , 0};
947         
948         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
949         
950         part = ::FindWindow(mousePos, &window);
951         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
952         
953         switch (part) {
954                 case inMenuBar:
955                         handleMenuCommand(::MenuSelect(mousePos));
956                         break;
957                         
958                 case inDrag:
959                         /*
960                          * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
961                          * events. By setting m_ignoreWindowSizedMessages these are suppressed.
962                          * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
963                          */
964                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
965                         m_ignoreWindowSizedMessages = true;
966                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
967                         m_ignoreWindowSizedMessages = false;
968                         break;
969                 
970                 case inContent:
971                         if (window != ::FrontWindow()) {
972                                 ::SelectWindow(window);
973                                 /*
974                                  * We add a mouse down event on the newly actived window
975                                  */             
976                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
977                                 EventMouseButton button;
978                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
979                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
980                         } else {
981                                 handled = false;
982                         }
983                         break;
984                         
985                 case inGoAway:
986                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
987                         if (::TrackGoAway(window, mousePos))
988                         {
989                                 // todo: add option-close, because itÿs in the HIG
990                                 // if (event.modifiers & optionKey) {
991                                         // Close the clean documents, others will be confirmed one by one.
992                                 //}
993                                 // else {
994                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
995                                 //}
996                         }
997                         break;
998                         
999                 case inGrow:
1000                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1001                         ::ResizeWindow(window, mousePos, NULL, NULL);
1002                         break;
1003                         
1004                 case inZoomIn:
1005                 case inZoomOut:
1006                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1007                         if (::TrackBox(window, mousePos, part)) {
1008                                 int macState;
1009                                 
1010                                 macState = ghostWindow->getMac_windowState();
1011                                 if ( macState== 0)
1012                                         ::ZoomWindow(window, part, true);
1013                                 else 
1014                                         if (macState == 2) { // always ok
1015                                                         ::ZoomWindow(window, part, true);
1016                                                         ghostWindow->setMac_windowState(1);
1017                                         } else { // need to force size again
1018                                         //      GHOST_TUns32 scr_x,scr_y; /*unused*/
1019                                                 Rect outAvailableRect;
1020                                                 
1021                                                 ghostWindow->setMac_windowState(2);
1022                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1023                                                 
1024                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
1025                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1026                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1027                                         }
1028                                 
1029                         }
1030                         break;
1031
1032                 default:
1033                         handled = false;
1034                         break;
1035         }
1036         
1037         return handled;
1038 }
1039
1040
1041 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1042 {
1043         short           menuID;
1044         short           menuItem;
1045         UInt32          command;
1046         bool            handled;
1047         OSErr           err;
1048         
1049         menuID = HiWord(menuResult);
1050         menuItem = LoWord(menuResult);
1051
1052         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1053
1054         handled = false;
1055         
1056         if (err || command == 0) {
1057         }
1058         else {
1059                 switch(command) {
1060                 }
1061         }
1062
1063         ::HiliteMenu(0);
1064     return handled;
1065 }
1066
1067
1068 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1069 {
1070         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1071     OSStatus err = eventNotHandledErr;
1072         GHOST_IWindow* window;
1073         GHOST_TEventNDOFData data;
1074         UInt32 kind;
1075         
1076     switch (::GetEventClass(event))
1077     {
1078                 case kEventClassAppleEvent:
1079                         EventRecord eventrec;
1080                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
1081                                 err = AEProcessAppleEvent(&eventrec);
1082                         }
1083                         break;
1084         case kEventClassMouse:
1085             err = sys->handleMouseEvent(event);
1086             break;
1087                 case kEventClassWindow:
1088                         err = sys->handleWindowEvent(event);
1089                         break;
1090                 case kEventClassKeyboard:
1091                         err = sys->handleKeyEvent(event);
1092                         break;
1093                 case kEventClassBlender :
1094                         window = sys->m_windowManager->getActiveWindow();
1095                         sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1096                         kind = ::GetEventKind(event);
1097                         
1098                         switch (kind)
1099                         {
1100                                 case 1:
1101                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1102         //                              printf("motion\n");
1103                                         break;
1104                                 case 2:
1105                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1106 //                                      printf("button\n");
1107                                         break;
1108                         }
1109                         err = noErr;
1110                         break;
1111                 default : 
1112                         ;
1113                         break;
1114    }
1115
1116     return err;
1117 }