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