Initial revision
[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 #include "GHOST_SystemCarbon.h"
41
42 #include "GHOST_DisplayManagerCarbon.h"
43 #include "GHOST_EventKey.h"
44 #include "GHOST_EventButton.h"
45 #include "GHOST_EventCursor.h"
46 #include "GHOST_TimerManager.h"
47 #include "GHOST_TimerTask.h"
48 #include "GHOST_WindowManager.h"
49 #include "GHOST_WindowCarbon.h"
50
51 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
52
53 const EventTypeSpec     kEvents[] =
54 {
55         { kEventClassAppleEvent, kEventAppleEvent },
56         
57 /*
58         { kEventClassApplication, kEventAppActivated },
59         { kEventClassApplication, kEventAppDeactivated },
60 */      
61
62         { kEventClassKeyboard, kEventRawKeyDown },
63         { kEventClassKeyboard, kEventRawKeyRepeat },
64         { kEventClassKeyboard, kEventRawKeyUp },
65         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
66         
67         { kEventClassMouse, kEventMouseDown },
68         { kEventClassMouse, kEventMouseUp },
69         { kEventClassMouse, kEventMouseMoved },
70         { kEventClassMouse, kEventMouseDragged },
71
72         { kEventClassWindow, kEventWindowClose },
73         { kEventClassWindow, kEventWindowActivated },
74         { kEventClassWindow, kEventWindowDeactivated },
75         { kEventClassWindow, kEventWindowUpdate },
76         { kEventClassWindow, kEventWindowBoundsChanged }
77 };
78
79 static GHOST_TButtonMask convertButton(EventMouseButton button)
80 {
81         switch (button) {
82         case kEventMouseButtonPrimary:
83                 return GHOST_kButtonMaskLeft;
84         case kEventMouseButtonSecondary:
85                 return GHOST_kButtonMaskRight;
86         case kEventMouseButtonTertiary:
87         default:
88                 return GHOST_kButtonMaskMiddle;
89         }
90 }
91
92 static GHOST_TKey convertKey(int rawCode) 
93 {       
94                 /* This bit of magic converts the rawCode into a virtual
95                  * Mac key based on the current keyboard mapping, but
96                  * without regard to the modifiers (so we don't get 'a' 
97                  * and 'A' for example.
98                  */
99         UInt32 dummy= 0;
100         Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
101         char vk = KeyTranslate(transData, rawCode, &dummy);
102         
103                 /* Map numpad based on rawcodes first, otherwise they
104                  * look like non-numpad events.
105                  */
106         switch (rawCode) {
107         case 82:        return GHOST_kKeyNumpad0;
108         case 83:        return GHOST_kKeyNumpad1;
109         case 84:        return GHOST_kKeyNumpad2;
110         case 85:        return GHOST_kKeyNumpad3;
111         case 86:        return GHOST_kKeyNumpad4;
112         case 87:        return GHOST_kKeyNumpad5;
113         case 88:        return GHOST_kKeyNumpad6;
114         case 89:        return GHOST_kKeyNumpad7;
115         case 91:        return GHOST_kKeyNumpad8;
116         case 92:        return GHOST_kKeyNumpad9;
117         case 65:        return GHOST_kKeyNumpadPeriod;
118         case 76:        return GHOST_kKeyNumpadEnter;
119         case 69:        return GHOST_kKeyNumpadPlus;
120         case 78:        return GHOST_kKeyNumpadMinus;
121         case 67:        return GHOST_kKeyNumpadAsterisk;
122         case 75:        return GHOST_kKeyNumpadSlash;
123         }
124         
125         if ((vk >= 'a') && (vk <= 'z')) {
126                 return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
127         } else if ((vk >= '0') && (vk <= '9')) {
128                 return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
129         } else if (vk==16) {
130                 switch (rawCode) {
131                 case 122:       return GHOST_kKeyF1;
132                 case 120:       return GHOST_kKeyF2;
133                 case 99:        return GHOST_kKeyF3;
134                 case 118:       return GHOST_kKeyF4;
135                 case 96:        return GHOST_kKeyF5;
136                 case 97:        return GHOST_kKeyF6;
137                 case 98:        return GHOST_kKeyF7;
138                 case 100:       return GHOST_kKeyF8;
139                 case 101:       return GHOST_kKeyF9;
140                 case 109:       return GHOST_kKeyF10;
141                 case 103:       return GHOST_kKeyF11;
142                 case 111:       return GHOST_kKeyF12;  // Never get, is used for ejecting the CD! 
143                 }
144         } else {
145                 switch (vk) {
146                 case kUpArrowCharCode:          return GHOST_kKeyUpArrow;
147                 case kDownArrowCharCode:        return GHOST_kKeyDownArrow;
148                 case kLeftArrowCharCode:        return GHOST_kKeyLeftArrow;
149                 case kRightArrowCharCode:       return GHOST_kKeyRightArrow;
150
151                 case kReturnCharCode:           return GHOST_kKeyEnter;
152                 case kBackspaceCharCode:        return GHOST_kKeyBackSpace;
153                 case kDeleteCharCode:           return GHOST_kKeyDelete;
154                 case kEscapeCharCode:           return GHOST_kKeyEsc;
155                 case kTabCharCode:                      return GHOST_kKeyTab;
156                 case kSpaceCharCode:            return GHOST_kKeySpace;
157
158                 case kHomeCharCode:             return GHOST_kKeyHome;
159                 case kEndCharCode:                      return GHOST_kKeyEnd;
160                 case kPageUpCharCode:           return GHOST_kKeyUpPage;
161                 case kPageDownCharCode:         return GHOST_kKeyDownPage;
162
163                 case '-':       return GHOST_kKeyMinus;
164                 case '=':       return GHOST_kKeyEqual;
165                 case ',':       return GHOST_kKeyComma;
166                 case '.':       return GHOST_kKeyPeriod;
167                 case '/':       return GHOST_kKeySlash;
168                 case ';':       return GHOST_kKeySemicolon;
169                 case '\'':      return GHOST_kKeyQuote;
170                 case '\\':      return GHOST_kKeyBackslash;
171                 case '[':       return GHOST_kKeyLeftBracket;
172                 case ']':       return GHOST_kKeyRightBracket;
173                 case '`':       return GHOST_kKeyAccentGrave;
174                 }
175         }
176         
177         printf("GHOST: unknown key: %d %d\n", vk, rawCode);
178         
179         return GHOST_kKeyUnknown;
180 }
181
182 /***/
183
184 GHOST_SystemCarbon::GHOST_SystemCarbon() :
185         m_modifierMask(0)
186 {
187         m_displayManager = new GHOST_DisplayManagerCarbon ();
188         GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
189         m_displayManager->initialize();
190
191         UnsignedWide micros;
192         ::Microseconds(&micros);
193         UInt64 millis;
194         m_start_time = UnsignedWideToUInt64(micros)/1000;
195 }
196
197 GHOST_SystemCarbon::~GHOST_SystemCarbon()
198 {
199 }
200
201
202 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
203 {
204         UnsignedWide micros;
205         ::Microseconds(&micros);
206         UInt64 millis;
207         millis = UnsignedWideToUInt64(micros);
208         return (millis / 1000) - m_start_time;
209 }
210
211
212 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
213 {
214         // We do not support multiple monitors at the moment
215         return 1;
216 }
217
218
219 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
220 {
221         BitMap screenBits;
222     Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
223         width = bnds.right - bnds.left;
224         height = bnds.bottom - bnds.top;
225 }
226
227
228 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
229         const STR_String& title, 
230         GHOST_TInt32 left,
231         GHOST_TInt32 top,
232         GHOST_TUns32 width,
233         GHOST_TUns32 height,
234         GHOST_TWindowState state,
235         GHOST_TDrawingContextType type,
236         bool stereoVisual
237 )
238 {
239     GHOST_IWindow* window = 0;
240     window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
241     if (window) {
242         if (window->getValid()) {
243             // Store the pointer to the window 
244             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
245             m_windowManager->addWindow(window);
246             m_windowManager->setActiveWindow(window);
247             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
248         }
249         else {
250                         GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
251             delete window;
252             window = 0;
253         }
254     }
255         else {
256                 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
257         }
258     return window;
259 }
260
261
262 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
263 {
264         bool anyProcessed = false;
265         EventRef event;
266         
267         do {
268                 GHOST_TimerManager* timerMgr = getTimerManager();
269
270                 if (waitForEvent) {
271                         GHOST_TUns64 curtime = getMilliSeconds();
272                         GHOST_TUns64 next = timerMgr->nextFireTime();
273                         double timeOut;
274                         
275                         if (next == GHOST_kFireTimeNever) {
276                                 timeOut = kEventDurationForever;
277                         } else {
278                                 if (next<=curtime)
279                                         timeOut = 0.0;
280                                 else
281                                         timeOut = (double) (next - getMilliSeconds())/1000.0;
282                         }
283                         
284                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
285                 }
286                 
287                 if (timerMgr->fireTimers(getMilliSeconds())) {
288                         anyProcessed = true;
289                 }
290
291                 if (getFullScreen()) {
292                         // Check if the full-screen window is dirty
293                         GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
294                         if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
295                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
296                                 anyProcessed = true;
297                         }
298                 }
299
300                 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
301                         OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
302                         if (status==noErr) {
303                                 anyProcessed = true;
304                         } else {
305                                 UInt32 i= ::GetEventClass(event);
306                                 
307                                         /* Ignore 'cgs ' class, no documentation on what they
308                                          * are, but we get a lot of them
309                                          */
310                                 if (i!='cgs ') {
311                                         //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
312                                 }
313                         }
314                         ::ReleaseEvent(event);
315                 }
316         } while (waitForEvent && !anyProcessed);
317         
318     return anyProcessed;
319 }
320         
321
322 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
323 {
324     Point mouseLoc;
325     // Get the position of the mouse in the active port
326     ::GetGlobalMouse(&mouseLoc);
327     // Convert the coordinates to screen coordinates
328     x = (GHOST_TInt32)mouseLoc.h;
329     y = (GHOST_TInt32)mouseLoc.v;
330     return GHOST_kSuccess;
331 }
332
333
334 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 /*x*/, GHOST_TInt32 /*y*/) const
335 {
336     // Not supported in Carbon!
337     return GHOST_kFailure;
338 }
339
340
341 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
342 {
343     UInt32 modifiers = ::GetCurrentKeyModifiers();
344
345     keys.set(GHOST_kModifierKeyCommand, (modifiers & cmdKey) ? true : false);
346     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
347     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
348     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
349         
350     return GHOST_kSuccess;
351 }
352
353         /* XXX, incorrect for multibutton mice */
354 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
355 {
356     Boolean theOnlyButtonIsDown = ::Button();
357     buttons.clear();
358     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
359     return GHOST_kSuccess;
360 }
361
362 static bool g_hasFirstFile = false;
363 static char g_firstFileBuf[512];
364
365 extern "C" int GHOST_HACK_getFirstFile(char buf[512]) { 
366         if (g_hasFirstFile) {
367                 strcpy(buf, g_firstFileBuf);
368                 return 1;
369         } else {
370                 return 0; 
371         }
372 }
373
374 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
375 {
376         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
377         
378         return noErr;
379 }
380
381 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
382 {
383         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
384         AEDescList docs;
385         SInt32 ndocs;
386         OSErr err;
387
388         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
389         if (err != noErr)  return err;
390
391         err = AECountItems(&docs, &ndocs);
392         if (err==noErr) {
393                 int i;
394         
395                 for (i=0; i<ndocs; i++) {
396                         FSSpec fss;
397                         AEKeyword kwd;
398                         DescType actType;
399                         Size actSize;
400                 
401                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
402                         if (err!=noErr)
403                                 break;
404                 
405                         if (i==0) {
406                                 FSRef fsref;
407                                 
408                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
409                                         break;
410                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
411                                         break;
412
413                                 g_hasFirstFile = true;
414                         }
415                 }
416         }
417         
418         AEDisposeDesc(&docs);
419         
420         return err;
421 }
422
423 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
424 {
425         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
426         
427         return noErr;
428 }
429
430 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
431 {
432         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
433         
434         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
435         
436         return noErr;
437 }
438
439
440 GHOST_TSuccess GHOST_SystemCarbon::init()
441 {
442     GHOST_TSuccess success = GHOST_System::init();
443     if (success) {
444                 /*
445          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
446          * This initializes the cursor's visibility counter to 0.
447          */
448         ::InitCursor();
449
450                 MenuRef windMenu;
451                 ::CreateStandardWindowMenu(0, &windMenu);
452                 ::InsertMenu(windMenu, 0);
453                 ::DrawMenuBar();
454
455         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
456                 
457                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
458                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
459                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
460                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
461     }
462     return success;
463 }
464
465
466 GHOST_TSuccess GHOST_SystemCarbon::exit()
467 {
468     return GHOST_System::exit();
469 }
470
471
472 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
473 {
474         GHOST_WindowCarbon *window;
475         
476         if (!getFullScreen()) {
477                 WindowRef windowref;
478                 ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowref);
479                 window = (GHOST_WindowCarbon*) ::GetWRefCon(windowref);
480                 
481                 if (validWindow(window)) {
482                         switch(::GetEventKind(event)) 
483                         {
484                                 case kEventWindowClose:
485                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
486                                         break;
487                                 case kEventWindowActivated:
488                                         m_windowManager->setActiveWindow(window);
489                                         window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
490                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
491                                         break;
492                                 case kEventWindowDeactivated:
493                                         m_windowManager->setWindowInactive(window);
494                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
495                                         break;
496                                 case kEventWindowUpdate:
497                                         //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
498                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
499                                         break;
500                                 case kEventWindowBoundsChanged:
501                                         window->updateDrawingContext();
502                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
503                                         break;
504                         }
505                 }
506         }
507         //else {
508                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
509                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
510                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
511         //}
512         
513         return noErr;
514 }
515
516 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
517 {
518         GHOST_IWindow* window = m_windowManager->getActiveWindow();
519         UInt32 kind = ::GetEventKind(event);
520         
521     switch (kind)
522     {
523                 case kEventMouseDown:
524                 case kEventMouseUp:
525                                 // Handle Mac application responsibilities
526                         if ((kind == kEventMouseDown) && handleMouseDown(event)) {
527                                 ;
528                         } else {
529                                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
530                                 EventMouseButton button;
531                                 
532                                         /* Window still gets mouse up after command-H */
533                                 if (window) {
534                                         ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
535                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
536                                 }
537                         }
538             break;
539                         
540                 case kEventMouseMoved:
541         case kEventMouseDragged:
542                         Point mousePos;
543                         if (window) {
544                                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
545                                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
546                         }
547             break;
548         }
549         
550         return noErr;
551 }
552
553
554 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
555 {
556         GHOST_IWindow* window = m_windowManager->getActiveWindow();
557         UInt32 kind = ::GetEventKind(event);
558         UInt32 modifiers;
559         UInt32 rawCode;
560         GHOST_TKey key;
561         char ascii;
562         
563                 /* Can happen, very rarely - seems to only be when command-H makes
564                  * the window go away and we still get an HKey up. 
565                  */
566         if (!window) {
567                 ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
568                 key = convertKey(rawCode);
569                 return noErr;
570         }
571         
572         switch (kind) {
573         case kEventRawKeyDown: 
574         case kEventRawKeyRepeat: 
575         case kEventRawKeyUp: 
576                 ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
577                 ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
578                 key = convertKey(rawCode);
579                 if (key!=GHOST_kKeyUnknown) {
580                         GHOST_TEventType type;
581                         if (kind == kEventRawKeyDown) {
582                                 type = GHOST_kEventKeyDown;
583                         } else if (kind == kEventRawKeyRepeat) { 
584                                 type = GHOST_kEventKeyDown;  /* XXX, fixme */
585                         } else {
586                                 type = GHOST_kEventKeyUp;
587                         }
588                         pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
589                 }
590                 break;
591
592         case kEventRawKeyModifiersChanged: 
593                         /* ugh */
594                 ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
595                 if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
596                         pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
597                 }
598                 if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
599                         pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
600                 }
601                 if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
602                         pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
603                 }
604                 if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
605                         pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
606                 }
607                 
608                 m_modifierMask = modifiers;
609                 break;
610         }
611         
612         return noErr;
613 }
614
615
616 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
617 {
618         WindowPtr                       window;
619         short                           part;
620         BitMap                          screenBits;
621     bool                                handled = true;
622     GHOST_IWindow*              ghostWindow;
623     Point                               mousePos = {0 , 0};
624         
625         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
626         
627         part = ::FindWindow(mousePos, &window);
628         ghostWindow = (GHOST_IWindow*) ::GetWRefCon(window);
629         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseEvent: invalid window");
630         
631         switch (part) {
632                 case inMenuBar:
633                         handleMenuCommand(::MenuSelect(mousePos));
634                         break;
635                         
636                 case inDrag:
637                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
638                         break;
639                 
640                 case inContent:
641                         if (window != ::FrontWindow()) {
642                                 ::SelectWindow(window);
643                         } else {
644                                 handled = false;
645                         }
646                         break;
647                         
648                 case inGoAway:
649                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
650                         if (::TrackGoAway(window, mousePos))
651                         {
652                                 // todo: add option-close, because itÕs in the HIG
653                                 // if (event.modifiers & optionKey) {
654                                         // Close the clean documents, others will be confirmed one by one.
655                                 //}
656                                 // else {
657                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
658                                 //}
659                         }
660                         break;
661                         
662                 case inGrow:
663                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
664                         ::ResizeWindow(window, mousePos, NULL, NULL);
665                         break;
666                         
667                 case inZoomIn:
668                 case inZoomOut:
669                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
670                         if (::TrackBox(window, mousePos, part)) {
671                                 ::ZoomWindow(window, part, true);
672                         }
673                         break;
674
675                 default:
676                         handled = false;
677                         break;
678         }
679         
680         return handled;
681 }
682
683
684 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
685 {
686         short           menuID;
687         short           menuItem;
688         UInt32          command;
689         bool            handled;
690         OSErr           err;
691         
692         menuID = HiWord(menuResult);
693         menuItem = LoWord(menuResult);
694
695         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
696
697         handled = false;
698         
699         if (err || command == 0) {
700         }
701         else {
702                 switch(command) {
703                 }
704         }
705
706         ::HiliteMenu(0);
707     return handled;
708 }
709
710 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
711 {
712         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
713     OSStatus err = eventNotHandledErr;
714
715     switch (::GetEventClass(event))
716     {
717                 case kEventClassAppleEvent:
718                         EventRecord eventrec;
719                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
720                                 err = AEProcessAppleEvent(&eventrec);
721                         }
722                         break;
723         case kEventClassMouse:
724             err = sys->handleMouseEvent(event);
725             break;
726                 case kEventClassWindow:
727                         err = sys->handleWindowEvent(event);
728                         break;
729                 case kEventClassKeyboard:
730                         err = sys->handleKeyEvent(event);
731                         break;
732     }
733
734     return err;
735 }