756de42f227d519be31be04ec3d64f37d9cf08d8
[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                                         printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
490                                 }
491                         }
492                         ::ReleaseEvent(event);
493                 }
494         } while (waitForEvent && !anyProcessed);
495         
496     return anyProcessed;
497 }
498         
499
500 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
501 {
502     Point mouseLoc;
503     // Get the position of the mouse in the active port
504     ::GetGlobalMouse(&mouseLoc);
505     // Convert the coordinates to screen coordinates
506     x = (GHOST_TInt32)mouseLoc.h;
507     y = (GHOST_TInt32)mouseLoc.v;
508     return GHOST_kSuccess;
509 }
510
511
512 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
513 {
514         float xf=(float)x, yf=(float)y;
515
516         CGAssociateMouseAndMouseCursorPosition(false);
517         CGSetLocalEventsSuppressionInterval(0);
518         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
519         CGAssociateMouseAndMouseCursorPosition(true);
520
521 //this doesn't work properly, see game engine mouse-look scripts
522 //      CGWarpMouseCursorPosition(CGPointMake(xf, yf));
523         // this call below sends event, but empties other events (like shift)
524         // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
525
526     return GHOST_kSuccess;
527 }
528
529
530 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
531 {
532     UInt32 modifiers = ::GetCurrentKeyModifiers();
533
534     keys.set(GHOST_kModifierKeyCommand, (modifiers & cmdKey) ? true : false);
535     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
536     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
537     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
538         
539     return GHOST_kSuccess;
540 }
541
542         /* XXX, incorrect for multibutton mice */
543 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
544 {
545     Boolean theOnlyButtonIsDown = ::Button();
546     buttons.clear();
547     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
548     return GHOST_kSuccess;
549 }
550
551 #define FIRSTFILEBUFLG 512
552 static bool g_hasFirstFile = false;
553 static char g_firstFileBuf[512];
554
555 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
556         if (g_hasFirstFile) {
557                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
558                 buf[FIRSTFILEBUFLG - 1] = '\0';
559                 return 1;
560         } else {
561                 return 0; 
562         }
563 }
564
565 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
566 {
567         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
568         
569         return noErr;
570 }
571
572 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
573 {
574         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
575         AEDescList docs;
576         SInt32 ndocs;
577         OSErr err;
578
579         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
580         if (err != noErr)  return err;
581
582         err = AECountItems(&docs, &ndocs);
583         if (err==noErr) {
584                 int i;
585         
586                 for (i=0; i<ndocs; i++) {
587                         FSSpec fss;
588                         AEKeyword kwd;
589                         DescType actType;
590                         Size actSize;
591                 
592                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
593                         if (err!=noErr)
594                                 break;
595                 
596                         if (i==0) {
597                                 FSRef fsref;
598                                 
599                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
600                                         break;
601                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
602                                         break;
603
604                                 g_hasFirstFile = true;
605                         }
606                 }
607         }
608         
609         AEDisposeDesc(&docs);
610         
611         return err;
612 }
613
614 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
615 {
616         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
617         
618         return noErr;
619 }
620
621 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
622 {
623         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
624         
625         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
626         
627         return noErr;
628 }
629
630
631 GHOST_TSuccess GHOST_SystemCarbon::init()
632 {
633  
634     GHOST_TSuccess success = GHOST_System::init();
635     if (success) {
636                 /*
637          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
638          * This initializes the cursor's visibility counter to 0.
639          */
640         ::InitCursor();
641
642                 MenuRef windMenu;
643                 ::CreateStandardWindowMenu(0, &windMenu);
644                 ::InsertMenu(windMenu, 0);
645                 ::DrawMenuBar();
646
647         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
648                 
649                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
650                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
651                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
652                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
653                 
654     }
655     return success;
656 }
657
658
659 GHOST_TSuccess GHOST_SystemCarbon::exit()
660 {
661     return GHOST_System::exit();
662 }
663
664
665 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
666 {
667         WindowRef windowRef;
668         GHOST_WindowCarbon *window;
669         OSStatus err = eventNotHandledErr;
670         
671         // Check if the event was send to a GHOST window
672         ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
673         window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
674         if (!validWindow(window)) {
675                 return err;
676         }
677
678         //if (!getFullScreen()) {
679                 err = noErr;
680                 switch(::GetEventKind(event)) 
681                 {
682                         case kEventWindowClose:
683                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
684                                 break;
685                         case kEventWindowActivated:
686                                 m_windowManager->setActiveWindow(window);
687                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
688                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
689                                 break;
690                         case kEventWindowDeactivated:
691                                 m_windowManager->setWindowInactive(window);
692                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
693                                 break;
694                         case kEventWindowUpdate:
695                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
696                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
697                                 break;
698                         case kEventWindowBoundsChanged:
699                                 if (!m_ignoreWindowSizedMessages)
700                                 {
701                                         window->updateDrawingContext();
702                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
703                                 }
704                                 break;
705                         default:
706                                 err = eventNotHandledErr;
707                                 break;
708                 }
709 //      }
710         //else {
711                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
712                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
713                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
714         //}
715         
716         return err;
717 }
718
719 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
720 {
721         GHOST_IWindow* window = m_windowManager->getActiveWindow();
722         TabletPointRec tabletPointRecord;
723         TabletProximityRec      tabletProximityRecord;
724         UInt32 anInt32;
725         GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
726         OSStatus err = eventNotHandledErr;
727         
728         ct.Pressure = 0;
729         ct.Xtilt = 0;
730         ct.Ytilt = 0;
731         
732         // is there an embedded tablet event inside this mouse event? 
733         if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
734         {
735                 // yes there is one!
736                 // Embedded tablet events can either be a proximity or pointer event.
737                 if(anInt32 == kEventTabletPoint)
738                 {
739                         //GHOST_PRINT("Embedded pointer event!\n");
740                         
741                         // Extract the tablet Pointer Event. If there is no Tablet Pointer data
742                         // in this event, then this call will return an error. Just ignore the
743                         // error and go on. This can occur when a proximity event is embedded in
744                         // a mouse event and you did not check the mouse event to see which type
745                         // of tablet event was embedded.
746                         if(noErr == GetEventParameter(event, kEventParamTabletPointRec,
747                                                                                   typeTabletPointRec, NULL,
748                                                                                   sizeof(TabletPointRec),
749                                                                                   NULL, (void *)&tabletPointRecord))
750                         {
751                                 ct.Pressure = tabletPointRecord.pressure / 65535.0f;
752                                 ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
753                                 ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
754                         }
755                 } else {
756                         //GHOST_PRINT("Embedded proximity event\n");
757                         
758                         // Extract the Tablet Proximity record from the event.
759                         if(noErr == GetEventParameter(event, kEventParamTabletProximityRec,
760                                                                                   typeTabletProximityRec, NULL,
761                                                                                   sizeof(TabletProximityRec),
762                                                                                   NULL, (void *)&tabletProximityRecord))
763                         {
764                                 if (tabletProximityRecord.enterProximity) {
765                                         //pointer is entering tablet area proximity
766                                         
767                                         switch(tabletProximityRecord.pointerType)
768                                         {
769                                                 case 1: /* stylus */
770                                                         ct.Active = 1;
771                                                         break;
772                                                 case 2: /* puck, not supported so far */
773                                                         ct.Active = 0;
774                                                         break;
775                                                 case 3: /* eraser */
776                                                         ct.Active = 2;
777                                                         break;
778                                                 default:
779                                                         ct.Active = 0;
780                                                         break;
781                                         }
782                                 } else {
783                                         // pointer is leaving - return to mouse
784                                         ct.Active = 0;
785                                 }
786                         }
787                 }
788         err = noErr;
789         }
790
791 }
792
793 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
794 {
795     OSStatus err = eventNotHandledErr;
796         GHOST_IWindow* window = m_windowManager->getActiveWindow();
797         UInt32 kind = ::GetEventKind(event);
798                         
799         switch (kind)
800     {
801                 case kEventMouseDown:
802                 case kEventMouseUp:
803                         // Handle Mac application responsibilities
804                         if ((kind == kEventMouseDown) && handleMouseDown(event)) {
805                                 err = noErr;
806                         }
807                         else {
808                                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
809                                 EventMouseButton button;
810                                 
811                                 /* Window still gets mouse up after command-H */
812                                 if (m_windowManager->getActiveWindow()) {
813                                         // handle any tablet events that may have come with the mouse event (optional)
814                                         handleTabletEvent(event);
815                                         
816                                         ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
817                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
818                                         err = noErr;
819                                 }
820                         }
821             break;
822                         
823                 case kEventMouseMoved:
824                 case kEventMouseDragged: {
825                         Point mousePos;
826
827                         if (window) {
828                                 //handle any tablet events that may have come with the mouse event (optional)
829                                 handleTabletEvent(event);
830
831                                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
832                                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
833                                 err = noErr;
834                         }
835                         break;
836                 }
837                 case kEventMouseWheelMoved:
838                         {
839                                 OSStatus status;
840                                 //UInt32 modifiers;
841                                 EventMouseWheelAxis axis;
842                                 SInt32 delta;
843                                 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
844                                 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
845                                 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
846                                 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
847                                 if (axis == kEventMouseWheelAxisY)
848                                 {
849                                         status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
850                                         GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
851                                         /*
852                                          * Limit mouse wheel delta to plus and minus one.
853                                          */
854                                         delta = delta > 0 ? 1 : -1;
855                                         pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
856                                         err = noErr;
857                                 }
858                         }
859                         break;
860                 }
861         
862         return err;
863 }
864
865
866 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
867 {
868     OSStatus err = eventNotHandledErr;
869         GHOST_IWindow* window = m_windowManager->getActiveWindow();
870         UInt32 kind = ::GetEventKind(event);
871         UInt32 modifiers;
872         UInt32 rawCode;
873         GHOST_TKey key;
874         unsigned char ascii;
875
876         /* Can happen, very rarely - seems to only be when command-H makes
877          * the window go away and we still get an HKey up. 
878          */
879         if (!window) {
880                 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
881                 //key = convertKey(rawCode);
882                 return err;
883         }
884         
885         err = noErr;
886         switch (kind) {
887                 case kEventRawKeyDown: 
888                 case kEventRawKeyRepeat: 
889                 case kEventRawKeyUp: 
890                         ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
891                         ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
892         
893                         key = convertKey(rawCode);
894                         ascii= convertRomanToLatin(ascii);
895                         
896         //              if (key!=GHOST_kKeyUnknown) {
897                                 GHOST_TEventType type;
898                                 if (kind == kEventRawKeyDown) {
899                                         type = GHOST_kEventKeyDown;
900                                 } else if (kind == kEventRawKeyRepeat) { 
901                                         type = GHOST_kEventKeyDown;  /* XXX, fixme */
902                                 } else {
903                                         type = GHOST_kEventKeyUp;
904                                 }
905                                 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
906 //                      }
907                         break;
908         
909                 case kEventRawKeyModifiersChanged: 
910                                 /* ugh */
911                         ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
912                         if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
913                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
914                         }
915                         if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
916                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
917                         }
918                         if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
919                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
920                         }
921                         if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
922                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
923                         }
924                         
925                         m_modifierMask = modifiers;
926                         break;
927                         
928                 default:
929                         err = eventNotHandledErr;
930                         break;
931         }
932         
933         return err;
934 }
935
936
937 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
938 {
939         WindowPtr                       window;
940         short                           part;
941         BitMap                          screenBits;
942     bool                                handled = true;
943     GHOST_WindowCarbon* ghostWindow;
944     Point                               mousePos = {0 , 0};
945         
946         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
947         
948         part = ::FindWindow(mousePos, &window);
949         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
950         
951         switch (part) {
952                 case inMenuBar:
953                         handleMenuCommand(::MenuSelect(mousePos));
954                         break;
955                         
956                 case inDrag:
957                         /*
958                          * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
959                          * events. By setting m_ignoreWindowSizedMessages these are suppressed.
960                          * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
961                          */
962                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
963                         m_ignoreWindowSizedMessages = true;
964                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
965                         m_ignoreWindowSizedMessages = false;
966                         break;
967                 
968                 case inContent:
969                         if (window != ::FrontWindow()) {
970                                 ::SelectWindow(window);
971                                 /*
972                                  * We add a mouse down event on the newly actived window
973                                  */             
974                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
975                                 EventMouseButton button;
976                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
977                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
978                         } else {
979                                 handled = false;
980                         }
981                         break;
982                         
983                 case inGoAway:
984                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
985                         if (::TrackGoAway(window, mousePos))
986                         {
987                                 // todo: add option-close, because itÿs in the HIG
988                                 // if (event.modifiers & optionKey) {
989                                         // Close the clean documents, others will be confirmed one by one.
990                                 //}
991                                 // else {
992                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
993                                 //}
994                         }
995                         break;
996                         
997                 case inGrow:
998                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
999                         ::ResizeWindow(window, mousePos, NULL, NULL);
1000                         break;
1001                         
1002                 case inZoomIn:
1003                 case inZoomOut:
1004                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1005                         if (::TrackBox(window, mousePos, part)) {
1006                                 int macState;
1007                                 
1008                                 macState = ghostWindow->getMac_windowState();
1009                                 if ( macState== 0)
1010                                         ::ZoomWindow(window, part, true);
1011                                 else 
1012                                         if (macState == 2) { // always ok
1013                                                         ::ZoomWindow(window, part, true);
1014                                                         ghostWindow->setMac_windowState(1);
1015                                         } else { // need to force size again
1016                                         //      GHOST_TUns32 scr_x,scr_y; /*unused*/
1017                                                 Rect outAvailableRect;
1018                                                 
1019                                                 ghostWindow->setMac_windowState(2);
1020                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1021                                                 
1022                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
1023                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1024                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1025                                         }
1026                                 
1027                         }
1028                         break;
1029
1030                 default:
1031                         handled = false;
1032                         break;
1033         }
1034         
1035         return handled;
1036 }
1037
1038
1039 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1040 {
1041         short           menuID;
1042         short           menuItem;
1043         UInt32          command;
1044         bool            handled;
1045         OSErr           err;
1046         
1047         menuID = HiWord(menuResult);
1048         menuItem = LoWord(menuResult);
1049
1050         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1051
1052         handled = false;
1053         
1054         if (err || command == 0) {
1055         }
1056         else {
1057                 switch(command) {
1058                 }
1059         }
1060
1061         ::HiliteMenu(0);
1062     return handled;
1063 }
1064
1065
1066 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1067 {
1068         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1069     OSStatus err = eventNotHandledErr;
1070         GHOST_IWindow* window;
1071         GHOST_TEventNDOFData data;
1072         UInt32 kind;
1073         
1074     switch (::GetEventClass(event))
1075     {
1076                 case kEventClassAppleEvent:
1077                         EventRecord eventrec;
1078                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
1079                                 err = AEProcessAppleEvent(&eventrec);
1080                         }
1081                         break;
1082         case kEventClassMouse:
1083             err = sys->handleMouseEvent(event);
1084             break;
1085                 case kEventClassWindow:
1086                         err = sys->handleWindowEvent(event);
1087                         break;
1088                 case kEventClassKeyboard:
1089                         err = sys->handleKeyEvent(event);
1090                         break;
1091                 case kEventClassBlender :
1092                         window = sys->m_windowManager->getActiveWindow();
1093                         sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1094                         kind = ::GetEventKind(event);
1095                         
1096                         switch (kind)
1097                         {
1098                                 case 1:
1099                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1100         //                              printf("motion\n");
1101                                         break;
1102                                 case 2:
1103                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1104 //                                      printf("button\n");
1105                                         break;
1106                         }
1107                         err = noErr;
1108                         break;
1109                 default : 
1110                         ;
1111                         break;
1112    }
1113
1114     return err;
1115 }