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