File/installation paths: osx (cocoa & carbon) update in ghost
[blender.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_TUns16 numOfAASamples,
406         const GHOST_TEmbedderWindowID parentWindow
407 )
408 {
409     GHOST_IWindow* window = 0;
410
411         window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
412
413     if (window) {
414         if (window->getValid()) {
415             // Store the pointer to the window 
416             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
417             m_windowManager->addWindow(window);
418             m_windowManager->setActiveWindow(window);
419             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
420         }
421         else {
422                         GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
423             delete window;
424             window = 0;
425         }
426     }
427         else {
428                 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
429         }
430     return window;
431 }
432
433 GHOST_TSuccess GHOST_SystemCarbon::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
434 {       
435         GHOST_TSuccess success = GHOST_kFailure;
436
437         // need yo make this Carbon all on 10.5 for fullscreen to work correctly
438         CGCaptureAllDisplays();
439         
440         success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
441         
442         if( success != GHOST_kSuccess ) {
443                         // fullscreen failed for other reasons, release
444                         CGReleaseAllDisplays(); 
445         }
446
447         return success;
448 }
449
450 GHOST_TSuccess GHOST_SystemCarbon::endFullScreen(void)
451 {       
452         CGReleaseAllDisplays();
453         return GHOST_System::endFullScreen();
454 }
455
456 /* this is an old style low level event queue.
457   As we want to handle our own timers, this is ok.
458   the full screen hack should be removed */
459 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
460 {
461         bool anyProcessed = false;
462         EventRef event;
463         
464 //      SetMouseCoalescingEnabled(false, NULL);
465
466         do {
467                 GHOST_TimerManager* timerMgr = getTimerManager();
468                 
469                 if (waitForEvent) {
470                         GHOST_TUns64 next = timerMgr->nextFireTime();
471                         double timeOut;
472                         
473                         if (next == GHOST_kFireTimeNever) {
474                                 timeOut = kEventDurationForever;
475                         } else {
476                                 timeOut = (double)(next - getMilliSeconds())/1000.0;
477                                 if (timeOut < 0.0)
478                                         timeOut = 0.0;
479                         }
480                         
481                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
482                 }
483                 
484                 if (timerMgr->fireTimers(getMilliSeconds())) {
485                         anyProcessed = true;
486                 }
487
488                 if (getFullScreen()) {
489                         // Check if the full-screen window is dirty
490                         GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
491                         if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
492                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
493                                 anyProcessed = true;
494                         }
495                 }
496
497                 /* end loop when no more events available */
498                 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
499                         OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
500                         if (status==noErr) {
501                                 anyProcessed = true;
502                         } else {
503                                 UInt32 i= ::GetEventClass(event);
504                                 
505                                         /* Ignore 'cgs ' class, no documentation on what they
506                                          * are, but we get a lot of them
507                                          */
508                                 if (i!='cgs ') {
509                                         if (i!='tblt') {  // tablet event. we use the one packaged in the mouse event
510                                                 ; //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
511                                         }
512                                 }
513                         }
514                         ::ReleaseEvent(event);
515                 }
516         } while (waitForEvent && !anyProcessed);
517         
518     return anyProcessed;
519 }
520         
521
522 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
523 {
524     Point mouseLoc;
525     // Get the position of the mouse in the active port
526     ::GetGlobalMouse(&mouseLoc);
527     // Convert the coordinates to screen coordinates
528     x = (GHOST_TInt32)mouseLoc.h;
529     y = (GHOST_TInt32)mouseLoc.v;
530     return GHOST_kSuccess;
531 }
532
533
534 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
535 {
536         float xf=(float)x, yf=(float)y;
537
538         CGAssociateMouseAndMouseCursorPosition(false);
539         CGSetLocalEventsSuppressionInterval(0);
540         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
541         CGAssociateMouseAndMouseCursorPosition(true);
542
543 //this doesn't work properly, see game engine mouse-look scripts
544 //      CGWarpMouseCursorPosition(CGPointMake(xf, yf));
545         // this call below sends event, but empties other events (like shift)
546         // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
547
548     return GHOST_kSuccess;
549 }
550
551
552 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
553 {
554     UInt32 modifiers = ::GetCurrentKeyModifiers();
555
556     keys.set(GHOST_kModifierKeyCommand, (modifiers & cmdKey) ? true : false);
557     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
558     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
559     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
560         
561     return GHOST_kSuccess;
562 }
563
564         /* XXX, incorrect for multibutton mice */
565 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
566 {
567     Boolean theOnlyButtonIsDown = ::Button();
568     buttons.clear();
569     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
570     return GHOST_kSuccess;
571 }
572
573 #define FIRSTFILEBUFLG 512
574 static bool g_hasFirstFile = false;
575 static char g_firstFileBuf[512];
576
577 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
578         if (g_hasFirstFile) {
579                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
580                 buf[FIRSTFILEBUFLG - 1] = '\0';
581                 return 1;
582         } else {
583                 return 0; 
584         }
585 }
586
587 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
588 {
589         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
590         
591         return noErr;
592 }
593
594 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
595 {
596         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
597         AEDescList docs;
598         SInt32 ndocs;
599         OSErr err;
600
601         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
602         if (err != noErr)  return err;
603
604         err = AECountItems(&docs, &ndocs);
605         if (err==noErr) {
606                 int i;
607         
608                 for (i=0; i<ndocs; i++) {
609                         FSSpec fss;
610                         AEKeyword kwd;
611                         DescType actType;
612                         Size actSize;
613                 
614                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
615                         if (err!=noErr)
616                                 break;
617                 
618                         if (i==0) {
619                                 FSRef fsref;
620                                 
621                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
622                                         break;
623                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
624                                         break;
625
626                                 g_hasFirstFile = true;
627                         }
628                 }
629         }
630         
631         AEDisposeDesc(&docs);
632         
633         return err;
634 }
635
636 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
637 {
638         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
639         
640         return noErr;
641 }
642
643 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
644 {
645         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
646         
647         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
648         
649         return noErr;
650 }
651
652
653 GHOST_TSuccess GHOST_SystemCarbon::init()
654 {
655  
656     GHOST_TSuccess success = GHOST_System::init();
657     if (success) {
658                 /*
659          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
660          * This initializes the cursor's visibility counter to 0.
661          */
662         ::InitCursor();
663
664                 MenuRef windMenu;
665                 ::CreateStandardWindowMenu(0, &windMenu);
666                 ::InsertMenu(windMenu, 0);
667                 ::DrawMenuBar();
668
669         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
670                 
671                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
672                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
673                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
674                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
675                 
676     }
677     return success;
678 }
679
680
681 GHOST_TSuccess GHOST_SystemCarbon::exit()
682 {
683     return GHOST_System::exit();
684 }
685
686
687 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
688 {
689         WindowRef windowRef;
690         GHOST_WindowCarbon *window;
691         OSStatus err = eventNotHandledErr;
692         
693         // Check if the event was send to a GHOST window
694         ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
695         window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
696         if (!validWindow(window)) {
697                 return err;
698         }
699
700         //if (!getFullScreen()) {
701                 err = noErr;
702                 switch(::GetEventKind(event)) 
703                 {
704                         case kEventWindowClose:
705                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
706                                 break;
707                         case kEventWindowActivated:
708                                 m_windowManager->setActiveWindow(window);
709                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
710                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
711                                 break;
712                         case kEventWindowDeactivated:
713                                 m_windowManager->setWindowInactive(window);
714                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
715                                 break;
716                         case kEventWindowUpdate:
717                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
718                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
719                                 break;
720                         case kEventWindowBoundsChanged:
721                                 if (!m_ignoreWindowSizedMessages)
722                                 {
723                                         window->updateDrawingContext();
724                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
725                                 }
726                                 break;
727                         default:
728                                 err = eventNotHandledErr;
729                                 break;
730                 }
731 //      }
732         //else {
733                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
734                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
735                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
736         //}
737         
738         return err;
739 }
740
741 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
742 {
743         GHOST_IWindow* window = m_windowManager->getActiveWindow();
744         TabletPointRec tabletPointRecord;
745         TabletProximityRec      tabletProximityRecord;
746         UInt32 anInt32;
747         GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
748         OSStatus err = eventNotHandledErr;
749         
750         ct.Pressure = 0;
751         ct.Xtilt = 0;
752         ct.Ytilt = 0;
753         
754         // is there an embedded tablet event inside this mouse event? 
755         if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
756         {
757                 // yes there is one!
758                 // Embedded tablet events can either be a proximity or pointer event.
759                 if(anInt32 == kEventTabletPoint)
760                 {
761                         //GHOST_PRINT("Embedded pointer event!\n");
762                         
763                         // Extract the tablet Pointer Event. If there is no Tablet Pointer data
764                         // in this event, then this call will return an error. Just ignore the
765                         // error and go on. This can occur when a proximity event is embedded in
766                         // a mouse event and you did not check the mouse event to see which type
767                         // of tablet event was embedded.
768                         if(noErr == GetEventParameter(event, kEventParamTabletPointRec,
769                                                                                   typeTabletPointRec, NULL,
770                                                                                   sizeof(TabletPointRec),
771                                                                                   NULL, (void *)&tabletPointRecord))
772                         {
773                                 ct.Pressure = tabletPointRecord.pressure / 65535.0f;
774                                 ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
775                                 ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
776                         }
777                 } else {
778                         //GHOST_PRINT("Embedded proximity event\n");
779                         
780                         // Extract the Tablet Proximity record from the event.
781                         if(noErr == GetEventParameter(event, kEventParamTabletProximityRec,
782                                                                                   typeTabletProximityRec, NULL,
783                                                                                   sizeof(TabletProximityRec),
784                                                                                   NULL, (void *)&tabletProximityRecord))
785                         {
786                                 if (tabletProximityRecord.enterProximity) {
787                                         //pointer is entering tablet area proximity
788                                         
789                                         switch(tabletProximityRecord.pointerType)
790                                         {
791                                                 case 1: /* stylus */
792                                                         ct.Active = GHOST_kTabletModeStylus;
793                                                         break;
794                                                 case 2: /* puck, not supported so far */
795                                                         ct.Active = GHOST_kTabletModeNone;
796                                                         break;
797                                                 case 3: /* eraser */
798                                                         ct.Active = GHOST_kTabletModeEraser;
799                                                         break;
800                                                 default:
801                                                         ct.Active = GHOST_kTabletModeNone;
802                                                         break;
803                                         }
804                                 } else {
805                                         // pointer is leaving - return to mouse
806                                         ct.Active = GHOST_kTabletModeNone;
807                                 }
808                         }
809                 }
810         err = noErr;
811         }
812         return err;
813 }
814
815 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
816 {
817     OSStatus err = eventNotHandledErr;
818         GHOST_IWindow* window = m_windowManager->getActiveWindow();
819         UInt32 kind = ::GetEventKind(event);
820                         
821         switch (kind)
822     {
823                 case kEventMouseDown:
824                 case kEventMouseUp:
825                         // Handle Mac application responsibilities
826                         if ((kind == kEventMouseDown) && handleMouseDown(event)) {
827                                 err = noErr;
828                         }
829                         else {
830                                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
831                                 EventMouseButton button;
832                                 
833                                 /* Window still gets mouse up after command-H */
834                                 if (m_windowManager->getActiveWindow()) {
835                                         // handle any tablet events that may have come with the mouse event (optional)
836                                         handleTabletEvent(event);
837                                         
838                                         ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
839                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
840                                         err = noErr;
841                                 }
842                         }
843             break;
844                         
845                 case kEventMouseMoved:
846                 case kEventMouseDragged: {
847                         Point mousePos;
848
849                         if (window) {
850                                 //handle any tablet events that may have come with the mouse event (optional)
851                                 handleTabletEvent(event);
852
853                                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
854                                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
855                                 err = noErr;
856                         }
857                         break;
858                 }
859                 case kEventMouseWheelMoved:
860                         {
861                                 OSStatus status;
862                                 //UInt32 modifiers;
863                                 EventMouseWheelAxis axis;
864                                 SInt32 delta;
865                                 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
866                                 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
867                                 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
868                                 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
869                                 if (axis == kEventMouseWheelAxisY)
870                                 {
871                                         status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
872                                         GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
873                                         /*
874                                          * Limit mouse wheel delta to plus and minus one.
875                                          */
876                                         delta = delta > 0 ? 1 : -1;
877                                         pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
878                                         err = noErr;
879                                 }
880                         }
881                         break;
882                 }
883         
884         return err;
885 }
886
887
888 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
889 {
890     OSStatus err = eventNotHandledErr;
891         GHOST_IWindow* window = m_windowManager->getActiveWindow();
892         UInt32 kind = ::GetEventKind(event);
893         UInt32 modifiers;
894         UInt32 rawCode;
895         GHOST_TKey key;
896         unsigned char ascii;
897
898         /* Can happen, very rarely - seems to only be when command-H makes
899          * the window go away and we still get an HKey up. 
900          */
901         if (!window) {
902                 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
903                 //key = convertKey(rawCode);
904                 return err;
905         }
906         
907         err = noErr;
908         switch (kind) {
909                 case kEventRawKeyDown: 
910                 case kEventRawKeyRepeat: 
911                 case kEventRawKeyUp: 
912                         ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
913                         ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
914         
915                         key = convertKey(rawCode);
916                         ascii= convertRomanToLatin(ascii);
917                         
918         //              if (key!=GHOST_kKeyUnknown) {
919                                 GHOST_TEventType type;
920                                 if (kind == kEventRawKeyDown) {
921                                         type = GHOST_kEventKeyDown;
922                                 } else if (kind == kEventRawKeyRepeat) { 
923                                         type = GHOST_kEventKeyDown;  /* XXX, fixme */
924                                 } else {
925                                         type = GHOST_kEventKeyUp;
926                                 }
927                                 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
928 //                      }
929                         break;
930         
931                 case kEventRawKeyModifiersChanged: 
932                                 /* ugh */
933                         ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
934                         if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
935                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
936                         }
937                         if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
938                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
939                         }
940                         if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
941                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
942                         }
943                         if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
944                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
945                         }
946                         
947                         m_modifierMask = modifiers;
948                         break;
949                         
950                 default:
951                         err = eventNotHandledErr;
952                         break;
953         }
954         
955         return err;
956 }
957
958
959 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
960 {
961         WindowPtr                       window;
962         short                           part;
963         BitMap                          screenBits;
964     bool                                handled = true;
965     GHOST_WindowCarbon* ghostWindow;
966     Point                               mousePos = {0 , 0};
967         
968         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
969         
970         part = ::FindWindow(mousePos, &window);
971         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
972         
973         switch (part) {
974                 case inMenuBar:
975                         handleMenuCommand(::MenuSelect(mousePos));
976                         break;
977                         
978                 case inDrag:
979                         /*
980                          * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
981                          * events. By setting m_ignoreWindowSizedMessages these are suppressed.
982                          * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
983                          */
984                         /* even worse: scale window also generates a load of events, and nothing 
985                            is handled (read: client's event proc called) until you release mouse (ton) */
986                         
987                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
988                         m_ignoreWindowSizedMessages = true;
989                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
990                         m_ignoreWindowSizedMessages = false;
991                         
992                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
993
994                         break;
995                 
996                 case inContent:
997                         if (window != ::FrontWindow()) {
998                                 ::SelectWindow(window);
999                                 /*
1000                                  * We add a mouse down event on the newly actived window
1001                                  */             
1002                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
1003                                 EventMouseButton button;
1004                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
1005                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
1006                         } else {
1007                                 handled = false;
1008                         }
1009                         break;
1010                         
1011                 case inGoAway:
1012                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1013                         if (::TrackGoAway(window, mousePos))
1014                         {
1015                                 // todo: add option-close, because itÿs in the HIG
1016                                 // if (event.modifiers & optionKey) {
1017                                         // Close the clean documents, others will be confirmed one by one.
1018                                 //}
1019                                 // else {
1020                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
1021                                 //}
1022                         }
1023                         break;
1024                         
1025                 case inGrow:
1026                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1027                         ::ResizeWindow(window, mousePos, NULL, NULL);
1028                         break;
1029                         
1030                 case inZoomIn:
1031                 case inZoomOut:
1032                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1033                         if (::TrackBox(window, mousePos, part)) {
1034                                 int macState;
1035                                 
1036                                 macState = ghostWindow->getMac_windowState();
1037                                 if ( macState== 0)
1038                                         ::ZoomWindow(window, part, true);
1039                                 else 
1040                                         if (macState == 2) { // always ok
1041                                                         ::ZoomWindow(window, part, true);
1042                                                         ghostWindow->setMac_windowState(1);
1043                                         } else { // need to force size again
1044                                         //      GHOST_TUns32 scr_x,scr_y; /*unused*/
1045                                                 Rect outAvailableRect;
1046                                                 
1047                                                 ghostWindow->setMac_windowState(2);
1048                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1049                                                 
1050                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
1051                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1052                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1053                                         }
1054                                 
1055                         }
1056                         break;
1057
1058                 default:
1059                         handled = false;
1060                         break;
1061         }
1062         
1063         return handled;
1064 }
1065
1066
1067 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1068 {
1069         short           menuID;
1070         short           menuItem;
1071         UInt32          command;
1072         bool            handled;
1073         OSErr           err;
1074         
1075         menuID = HiWord(menuResult);
1076         menuItem = LoWord(menuResult);
1077
1078         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1079
1080         handled = false;
1081         
1082         if (err || command == 0) {
1083         }
1084         else {
1085                 switch(command) {
1086                 }
1087         }
1088
1089         ::HiliteMenu(0);
1090     return handled;
1091 }
1092
1093
1094 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1095 {
1096         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1097     OSStatus err = eventNotHandledErr;
1098         GHOST_IWindow* window;
1099         GHOST_TEventNDOFData data;
1100         UInt32 kind;
1101         
1102     switch (::GetEventClass(event))
1103     {
1104                 case kEventClassAppleEvent:
1105                         EventRecord eventrec;
1106                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
1107                                 err = AEProcessAppleEvent(&eventrec);
1108                         }
1109                         break;
1110         case kEventClassMouse:
1111             err = sys->handleMouseEvent(event);
1112             break;
1113                 case kEventClassWindow:
1114                         err = sys->handleWindowEvent(event);
1115                         break;
1116                 case kEventClassKeyboard:
1117                         err = sys->handleKeyEvent(event);
1118                         break;
1119                 case kEventClassBlender :
1120                         window = sys->m_windowManager->getActiveWindow();
1121                         sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1122                         kind = ::GetEventKind(event);
1123                         
1124                         switch (kind)
1125                         {
1126                                 case 1:
1127                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1128         //                              printf("motion\n");
1129                                         break;
1130                                 case 2:
1131                                         sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1132 //                                      printf("button\n");
1133                                         break;
1134                         }
1135                         err = noErr;
1136                         break;
1137                 default : 
1138                         ;
1139                         break;
1140    }
1141
1142     return err;
1143 }
1144
1145 GHOST_TUns8* GHOST_SystemCarbon::getClipboard(bool selection) const
1146 {
1147         PasteboardRef inPasteboard;
1148         PasteboardItemID itemID;
1149         CFDataRef flavorData;
1150         OSStatus err = noErr;
1151         GHOST_TUns8 * temp_buff;
1152         CFRange range;
1153         OSStatus syncFlags;
1154         
1155         err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1156         if(err != noErr) { return NULL;}
1157
1158         syncFlags = PasteboardSynchronize( inPasteboard );
1159                 /* as we always get in a new string, we can safely ignore sync flags if not an error*/
1160         if(syncFlags <0) { return NULL;}
1161
1162
1163         err = PasteboardGetItemIdentifier( inPasteboard, 1, &itemID );
1164         if(err != noErr) { return NULL;}
1165
1166         err = PasteboardCopyItemFlavorData( inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
1167         if(err != noErr) { return NULL;}
1168
1169         range = CFRangeMake(0, CFDataGetLength(flavorData));
1170         
1171         temp_buff = (GHOST_TUns8*) malloc(range.length+1); 
1172
1173         CFDataGetBytes(flavorData, range, (UInt8*)temp_buff);
1174         
1175         temp_buff[range.length] = '\0';
1176         
1177         if(temp_buff) {
1178                 return temp_buff;
1179         } else {
1180                 return NULL;
1181         }
1182 }
1183
1184 void GHOST_SystemCarbon::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1185 {
1186         if(selection) {return;} // for copying the selection, used on X11
1187
1188         PasteboardRef inPasteboard;
1189         CFDataRef textData = NULL;
1190         OSStatus err = noErr; /*For error checking*/
1191         OSStatus syncFlags;
1192         
1193         err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1194         if(err != noErr) { return;}
1195         
1196         syncFlags = PasteboardSynchronize( inPasteboard ); 
1197         /* as we always put in a new string, we can safely ignore sync flags */
1198         if(syncFlags <0) { return;}
1199         
1200         err = PasteboardClear( inPasteboard );
1201         if(err != noErr) { return;}
1202         
1203         textData = CFDataCreate(kCFAllocatorDefault, (UInt8*)buffer, strlen(buffer));
1204         
1205         if (textData) {
1206                 err = PasteboardPutItemFlavor( inPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
1207                         if(err != noErr) { 
1208                                 if(textData) { CFRelease(textData);}
1209                                 return;
1210                         }
1211         }
1212         
1213         if(textData) {
1214                 CFRelease(textData);
1215         }
1216 }
1217
1218
1219 const GHOST_TUns8* GHOST_SystemCarbon::getSystemDir() const
1220 {
1221         return (GHOST_TUns8*)"/Library/Application Support";
1222 }
1223
1224 const GHOST_TUns8* GHOST_SystemCarbon::getUserDir() const
1225 {
1226         static char usrPath[256] = "";
1227         char* env = getenv("HOME");
1228         
1229         if (env) {
1230                 strncpy(usrPath, env, 245);
1231                 usrPath[245]=0;
1232                 strcat(usrPath, "/Library/Application Support");
1233                 return (GHOST_TUns8*) usrPath;
1234         }
1235         else
1236                 return NULL;
1237 }
1238
1239 const GHOST_TUns8* GHOST_SystemCarbon::getBinaryDir() const
1240 {
1241         CFURLRef bundleURL;
1242         CFStringRef pathStr;
1243         static char path[256];
1244         CFBundleRef mainBundle = CFBundleGetMainBundle();
1245         
1246         bundleURL = CFBundleCopyBundleURL(mainBundle);
1247         pathStr = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
1248         CFStringGetCString(pathStr, path, 255, kCFStringEncodingASCII);
1249         CFRelease(pathStr);
1250         CFRelease(bundleURL);
1251         return (GHOST_TUns8*)path;
1252 }