Blender V2.61 - r43446

GHOST_SystemCarbon.cpp

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): none yet.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00040 #include <Carbon/Carbon.h>
00041 #include <ApplicationServices/ApplicationServices.h>
00042 #include "GHOST_SystemCarbon.h"
00043 
00044 #include "GHOST_DisplayManagerCarbon.h"
00045 #include "GHOST_EventKey.h"
00046 #include "GHOST_EventButton.h"
00047 #include "GHOST_EventCursor.h"
00048 #include "GHOST_EventWheel.h"
00049 #ifdef WITH_INPUT_NDOF
00050 #include "GHOST_EventNDOF.h"
00051 #endif
00052 
00053 #include "GHOST_TimerManager.h"
00054 #include "GHOST_TimerTask.h"
00055 #include "GHOST_WindowManager.h"
00056 #include "GHOST_WindowCarbon.h"
00057 #include "GHOST_NDOFManager.h"
00058 #include "AssertMacros.h"
00059 
00060 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
00061 
00062 /* blender class and types events */
00063 enum {
00064   kEventClassBlender              = 'blnd'
00065 };
00066 
00067 enum {
00068     kEventBlenderNdofAxis           = 1,
00069     kEventBlenderNdofButtons        = 2
00070 };
00071 
00072 const EventTypeSpec kEvents[] =
00073 {
00074     { kEventClassAppleEvent, kEventAppleEvent },
00075 /*
00076     { kEventClassApplication, kEventAppActivated },
00077     { kEventClassApplication, kEventAppDeactivated },
00078 */  
00079     { kEventClassKeyboard, kEventRawKeyDown },
00080     { kEventClassKeyboard, kEventRawKeyRepeat },
00081     { kEventClassKeyboard, kEventRawKeyUp },
00082     { kEventClassKeyboard, kEventRawKeyModifiersChanged },
00083     
00084     { kEventClassMouse, kEventMouseDown },
00085     { kEventClassMouse, kEventMouseUp },
00086     { kEventClassMouse, kEventMouseMoved },
00087     { kEventClassMouse, kEventMouseDragged },
00088     { kEventClassMouse, kEventMouseWheelMoved },
00089     
00090     { kEventClassWindow, kEventWindowClickZoomRgn } ,  /* for new zoom behaviour */ 
00091     { kEventClassWindow, kEventWindowZoom },  /* for new zoom behaviour */ 
00092     { kEventClassWindow, kEventWindowExpand } ,  /* for new zoom behaviour */ 
00093     { kEventClassWindow, kEventWindowExpandAll },  /* for new zoom behaviour */ 
00094 
00095     { kEventClassWindow, kEventWindowClose },
00096     { kEventClassWindow, kEventWindowActivated },
00097     { kEventClassWindow, kEventWindowDeactivated },
00098     { kEventClassWindow, kEventWindowUpdate },
00099     { kEventClassWindow, kEventWindowBoundsChanged },
00100     
00101     { kEventClassBlender, kEventBlenderNdofAxis },
00102     { kEventClassBlender, kEventBlenderNdofButtons }
00103     
00104     
00105     
00106 };
00107 
00108 
00109 
00110 static GHOST_TButtonMask convertButton(EventMouseButton button)
00111 {
00112     switch (button) {
00113     case kEventMouseButtonPrimary:
00114         return GHOST_kButtonMaskLeft;
00115     case kEventMouseButtonSecondary:
00116         return GHOST_kButtonMaskRight;
00117     case kEventMouseButtonTertiary:
00118     default:
00119         return GHOST_kButtonMaskMiddle;
00120     }
00121 }
00122 
00123 static GHOST_TKey convertKey(int rawCode) 
00124 {   
00125         /* This bit of magic converts the rawCode into a virtual
00126          * Mac key based on the current keyboard mapping, but
00127          * without regard to the modifiers (so we don't get 'a' 
00128          * and 'A' for example.
00129          */
00130     static UInt32 dummy= 0;
00131     Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
00132     unsigned char vk = KeyTranslate(transData, rawCode, &dummy);    
00133         /* Map numpad based on rawcodes first, otherwise they
00134          * look like non-numpad events.
00135          * Added too: mapping the number keys, for french keyboards etc (ton)
00136          */
00137     // printf("GHOST: vk: %d %c raw: %d\n", vk, vk, rawCode);
00138          
00139     switch (rawCode) {
00140     case 18:    return GHOST_kKey1;
00141     case 19:    return GHOST_kKey2;
00142     case 20:    return GHOST_kKey3;
00143     case 21:    return GHOST_kKey4;
00144     case 23:    return GHOST_kKey5;
00145     case 22:    return GHOST_kKey6;
00146     case 26:    return GHOST_kKey7;
00147     case 28:    return GHOST_kKey8;
00148     case 25:    return GHOST_kKey9;
00149     case 29:    return GHOST_kKey0;
00150     
00151     case 82:    return GHOST_kKeyNumpad0;
00152     case 83:    return GHOST_kKeyNumpad1;
00153     case 84:    return GHOST_kKeyNumpad2;
00154     case 85:    return GHOST_kKeyNumpad3;
00155     case 86:    return GHOST_kKeyNumpad4;
00156     case 87:    return GHOST_kKeyNumpad5;
00157     case 88:    return GHOST_kKeyNumpad6;
00158     case 89:    return GHOST_kKeyNumpad7;
00159     case 91:    return GHOST_kKeyNumpad8;
00160     case 92:    return GHOST_kKeyNumpad9;
00161     case 65:    return GHOST_kKeyNumpadPeriod;
00162     case 76:    return GHOST_kKeyNumpadEnter;
00163     case 69:    return GHOST_kKeyNumpadPlus;
00164     case 78:    return GHOST_kKeyNumpadMinus;
00165     case 67:    return GHOST_kKeyNumpadAsterisk;
00166     case 75:    return GHOST_kKeyNumpadSlash;
00167     }
00168     
00169     if ((vk >= 'a') && (vk <= 'z')) {
00170         return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
00171     } else if ((vk >= '0') && (vk <= '9')) {
00172         return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
00173     } else if (vk==16) {
00174         switch (rawCode) {
00175         case 122:   return GHOST_kKeyF1;
00176         case 120:   return GHOST_kKeyF2;
00177         case 99:    return GHOST_kKeyF3;
00178         case 118:   return GHOST_kKeyF4;
00179         case 96:    return GHOST_kKeyF5;
00180         case 97:    return GHOST_kKeyF6;
00181         case 98:    return GHOST_kKeyF7;
00182         case 100:   return GHOST_kKeyF8;
00183         case 101:   return GHOST_kKeyF9;
00184         case 109:   return GHOST_kKeyF10;
00185         case 103:   return GHOST_kKeyF11;
00186         case 111:   return GHOST_kKeyF12;  // Never get, is used for ejecting the CD! 
00187         }
00188     } else {
00189         switch (vk) {
00190         case kUpArrowCharCode:      return GHOST_kKeyUpArrow;
00191         case kDownArrowCharCode:    return GHOST_kKeyDownArrow;
00192         case kLeftArrowCharCode:    return GHOST_kKeyLeftArrow;
00193         case kRightArrowCharCode:   return GHOST_kKeyRightArrow;
00194 
00195         case kReturnCharCode:       return GHOST_kKeyEnter;
00196         case kBackspaceCharCode:    return GHOST_kKeyBackSpace;
00197         case kDeleteCharCode:       return GHOST_kKeyDelete;
00198         case kEscapeCharCode:       return GHOST_kKeyEsc;
00199         case kTabCharCode:          return GHOST_kKeyTab;
00200         case kSpaceCharCode:        return GHOST_kKeySpace;
00201 
00202         case kHomeCharCode:         return GHOST_kKeyHome;
00203         case kEndCharCode:          return GHOST_kKeyEnd;
00204         case kPageUpCharCode:       return GHOST_kKeyUpPage;
00205         case kPageDownCharCode:     return GHOST_kKeyDownPage;
00206 
00207         case '-':   return GHOST_kKeyMinus;
00208         case '=':   return GHOST_kKeyEqual;
00209         case ',':   return GHOST_kKeyComma;
00210         case '.':   return GHOST_kKeyPeriod;
00211         case '/':   return GHOST_kKeySlash;
00212         case ';':   return GHOST_kKeySemicolon;
00213         case '\'':  return GHOST_kKeyQuote;
00214         case '\\':  return GHOST_kKeyBackslash;
00215         case '[':   return GHOST_kKeyLeftBracket;
00216         case ']':   return GHOST_kKeyRightBracket;
00217         case '`':   return GHOST_kKeyAccentGrave;
00218         }
00219     }
00220     
00221     // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
00222     
00223     return GHOST_kKeyUnknown;
00224 }
00225 
00226 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
00227  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
00228  * I am not sure how international this works...
00229  * For cross-platform convention, we'll use the Latin ascii set instead.
00230  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
00231  * 
00232  */
00233 static unsigned char convertRomanToLatin(unsigned char ascii)
00234 {
00235 
00236     if(ascii<128) return ascii;
00237     
00238     switch(ascii) {
00239     case 128:   return 142;
00240     case 129:   return 143;
00241     case 130:   return 128;
00242     case 131:   return 201;
00243     case 132:   return 209;
00244     case 133:   return 214;
00245     case 134:   return 220;
00246     case 135:   return 225;
00247     case 136:   return 224;
00248     case 137:   return 226;
00249     case 138:   return 228;
00250     case 139:   return 227;
00251     case 140:   return 229;
00252     case 141:   return 231;
00253     case 142:   return 233;
00254     case 143:   return 232;
00255     case 144:   return 234;
00256     case 145:   return 235;
00257     case 146:   return 237;
00258     case 147:   return 236;
00259     case 148:   return 238;
00260     case 149:   return 239;
00261     case 150:   return 241;
00262     case 151:   return 243;
00263     case 152:   return 242;
00264     case 153:   return 244;
00265     case 154:   return 246;
00266     case 155:   return 245;
00267     case 156:   return 250;
00268     case 157:   return 249;
00269     case 158:   return 251;
00270     case 159:   return 252;
00271     case 160:   return 0;
00272     case 161:   return 176;
00273     case 162:   return 162;
00274     case 163:   return 163;
00275     case 164:   return 167;
00276     case 165:   return 183;
00277     case 166:   return 182;
00278     case 167:   return 223;
00279     case 168:   return 174;
00280     case 169:   return 169;
00281     case 170:   return 174;
00282     case 171:   return 180;
00283     case 172:   return 168;
00284     case 173:   return 0;
00285     case 174:   return 198;
00286     case 175:   return 216;
00287     case 176:   return 0;
00288     case 177:   return 177;
00289     case 178:   return 0;
00290     case 179:   return 0;
00291     case 180:   return 165;
00292     case 181:   return 181;
00293     case 182:   return 0;
00294     case 183:   return 0;
00295     case 184:   return 215;
00296     case 185:   return 0;
00297     case 186:   return 0;
00298     case 187:   return 170;
00299     case 188:   return 186;
00300     case 189:   return 0;
00301     case 190:   return 230;
00302     case 191:   return 248;
00303     case 192:   return 191;
00304     case 193:   return 161;
00305     case 194:   return 172;
00306     case 195:   return 0;
00307     case 196:   return 0;
00308     case 197:   return 0;
00309     case 198:   return 0;
00310     case 199:   return 171;
00311     case 200:   return 187;
00312     case 201:   return 201;
00313     case 202:   return 0;
00314     case 203:   return 192;
00315     case 204:   return 195;
00316     case 205:   return 213;
00317     case 206:   return 0;
00318     case 207:   return 0;
00319     case 208:   return 0;
00320     case 209:   return 0;
00321     case 210:   return 0;
00322     
00323     case 214:   return 247;
00324 
00325     case 229:   return 194;
00326     case 230:   return 202;
00327     case 231:   return 193;
00328     case 232:   return 203;
00329     case 233:   return 200;
00330     case 234:   return 205;
00331     case 235:   return 206;
00332     case 236:   return 207;
00333     case 237:   return 204;
00334     case 238:   return 211;
00335     case 239:   return 212;
00336     case 240:   return 0;
00337     case 241:   return 210;
00338     case 242:   return 218;
00339     case 243:   return 219;
00340     case 244:   return 217;
00341     case 245:   return 0;
00342     case 246:   return 0;
00343     case 247:   return 0;
00344     case 248:   return 0;
00345     case 249:   return 0;
00346     case 250:   return 0;
00347 
00348     
00349         default: return 0;
00350     }
00351 
00352 }
00353 
00354 
00355 /***/
00356 
00357 GHOST_SystemCarbon::GHOST_SystemCarbon() :
00358     m_modifierMask(0)
00359 {
00360     m_displayManager = new GHOST_DisplayManagerCarbon ();
00361     GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
00362     m_displayManager->initialize();
00363 
00364     UnsignedWide micros;
00365     ::Microseconds(&micros);
00366     m_start_time = UnsignedWideToUInt64(micros)/1000;
00367     m_ignoreWindowSizedMessages = false;
00368 }
00369 
00370 GHOST_SystemCarbon::~GHOST_SystemCarbon()
00371 {
00372 }
00373 
00374 
00375 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
00376 {
00377     UnsignedWide micros;
00378     ::Microseconds(&micros);
00379     UInt64 millis;
00380     millis = UnsignedWideToUInt64(micros);
00381     return (millis / 1000) - m_start_time;
00382 }
00383 
00384 
00385 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
00386 {
00387     // We do not support multiple monitors at the moment
00388     return 1;
00389 }
00390 
00391 
00392 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
00393 {
00394     BitMap screenBits;
00395     Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
00396     width = bnds.right - bnds.left;
00397     height = bnds.bottom - bnds.top;
00398 }
00399 
00400 
00401 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
00402     const STR_String& title, 
00403     GHOST_TInt32 left,
00404     GHOST_TInt32 top,
00405     GHOST_TUns32 width,
00406     GHOST_TUns32 height,
00407     GHOST_TWindowState state,
00408     GHOST_TDrawingContextType type,
00409     bool stereoVisual,
00410     const GHOST_TUns16 numOfAASamples,
00411     const GHOST_TEmbedderWindowID parentWindow
00412 )
00413 {
00414     GHOST_IWindow* window = 0;
00415 
00416     window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
00417 
00418     if (window) {
00419         if (window->getValid()) {
00420             // Store the pointer to the window
00421             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
00422             m_windowManager->addWindow(window);
00423             m_windowManager->setActiveWindow(window);
00424             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
00425         }
00426         else {
00427             GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
00428             delete window;
00429             window = 0;
00430         }
00431     }
00432     else {
00433         GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
00434     }
00435     return window;
00436 }
00437 
00438 GHOST_TSuccess GHOST_SystemCarbon::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
00439 {   
00440     GHOST_TSuccess success = GHOST_kFailure;
00441 
00442     // need yo make this Carbon all on 10.5 for fullscreen to work correctly
00443     CGCaptureAllDisplays();
00444     
00445     success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
00446     
00447     if( success != GHOST_kSuccess ) {
00448             // fullscreen failed for other reasons, release
00449             CGReleaseAllDisplays(); 
00450     }
00451 
00452     return success;
00453 }
00454 
00455 GHOST_TSuccess GHOST_SystemCarbon::endFullScreen(void)
00456 {   
00457     CGReleaseAllDisplays();
00458     return GHOST_System::endFullScreen();
00459 }
00460 
00461 /* this is an old style low level event queue.
00462   As we want to handle our own timers, this is ok.
00463   the full screen hack should be removed */
00464 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
00465 {
00466     bool anyProcessed = false;
00467     EventRef event;
00468     
00469 //  SetMouseCoalescingEnabled(false, NULL);
00470 
00471     do {
00472         GHOST_TimerManager* timerMgr = getTimerManager();
00473         
00474         if (waitForEvent) {
00475             GHOST_TUns64 next = timerMgr->nextFireTime();
00476             double timeOut;
00477             
00478             if (next == GHOST_kFireTimeNever) {
00479                 timeOut = kEventDurationForever;
00480             } else {
00481                 timeOut = (double)(next - getMilliSeconds())/1000.0;
00482                 if (timeOut < 0.0)
00483                     timeOut = 0.0;
00484             }
00485             
00486             ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
00487         }
00488         
00489         if (timerMgr->fireTimers(getMilliSeconds())) {
00490             anyProcessed = true;
00491         }
00492 
00493         if (getFullScreen()) {
00494             // Check if the full-screen window is dirty
00495             GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
00496             if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
00497                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
00498                 anyProcessed = true;
00499             }
00500         }
00501 
00502         /* end loop when no more events available */
00503         while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
00504             OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
00505             if (status==noErr) {
00506                 anyProcessed = true;
00507             } else {
00508                 UInt32 i= ::GetEventClass(event);
00509                 
00510                     /* Ignore 'cgs ' class, no documentation on what they
00511                      * are, but we get a lot of them
00512                      */
00513                 if (i!='cgs ') {
00514                     if (i!='tblt') {  // tablet event. we use the one packaged in the mouse event
00515                         ; //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
00516                     }
00517                 }
00518             }
00519             ::ReleaseEvent(event);
00520         }
00521     } while (waitForEvent && !anyProcessed);
00522     
00523     return anyProcessed;
00524 }
00525     
00526 
00527 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
00528 {
00529     Point mouseLoc;
00530     // Get the position of the mouse in the active port
00531     ::GetGlobalMouse(&mouseLoc);
00532     // Convert the coordinates to screen coordinates
00533     x = (GHOST_TInt32)mouseLoc.h;
00534     y = (GHOST_TInt32)mouseLoc.v;
00535     return GHOST_kSuccess;
00536 }
00537 
00538 
00539 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
00540 {
00541     float xf=(float)x, yf=(float)y;
00542 
00543     CGAssociateMouseAndMouseCursorPosition(false);
00544     CGSetLocalEventsSuppressionInterval(0);
00545     CGWarpMouseCursorPosition(CGPointMake(xf, yf));
00546     CGAssociateMouseAndMouseCursorPosition(true);
00547 
00548 //this doesn't work properly, see game engine mouse-look scripts
00549 //  CGWarpMouseCursorPosition(CGPointMake(xf, yf));
00550     // this call below sends event, but empties other events (like shift)
00551     // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
00552 
00553     return GHOST_kSuccess;
00554 }
00555 
00556 
00557 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
00558 {
00559     UInt32 modifiers = ::GetCurrentKeyModifiers();
00560 
00561     keys.set(GHOST_kModifierKeyOS, (modifiers & cmdKey) ? true : false);
00562     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
00563     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
00564     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
00565     
00566     return GHOST_kSuccess;
00567 }
00568 
00569 /* XXX, incorrect for multibutton mice */
00570 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
00571 {
00572     Boolean theOnlyButtonIsDown = ::Button();
00573     buttons.clear();
00574     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
00575     return GHOST_kSuccess;
00576 }
00577 
00578 #define FIRSTFILEBUFLG 512
00579 static bool g_hasFirstFile = false;
00580 static char g_firstFileBuf[512];
00581 
00582 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
00583 {
00584     if (g_hasFirstFile) {
00585         strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
00586         buf[FIRSTFILEBUFLG - 1] = '\0';
00587         return 1;
00588     } else {
00589         return 0; 
00590     }
00591 }
00592 
00593 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
00594 {
00595     //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
00596     
00597     return noErr;
00598 }
00599 
00600 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
00601 {
00602     //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
00603     AEDescList docs;
00604     SInt32 ndocs;
00605     OSErr err;
00606 
00607     err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
00608     if (err != noErr)  return err;
00609 
00610     err = AECountItems(&docs, &ndocs);
00611     if (err==noErr) {
00612         int i;
00613     
00614         for (i=0; i<ndocs; i++) {
00615             FSSpec fss;
00616             AEKeyword kwd;
00617             DescType actType;
00618             Size actSize;
00619         
00620             err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
00621             if (err!=noErr)
00622                 break;
00623         
00624             if (i==0) {
00625                 FSRef fsref;
00626                 
00627                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
00628                     break;
00629                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
00630                     break;
00631 
00632                 g_hasFirstFile = true;
00633             }
00634         }
00635     }
00636     
00637     AEDisposeDesc(&docs);
00638     
00639     return err;
00640 }
00641 
00642 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
00643 {
00644     //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
00645     
00646     return noErr;
00647 }
00648 
00649 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
00650 {
00651     GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
00652     
00653     sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
00654     
00655     return noErr;
00656 }
00657 
00658 
00659 GHOST_TSuccess GHOST_SystemCarbon::init()
00660 {
00661 
00662     GHOST_TSuccess success = GHOST_System::init();
00663     if (success) {
00664         /*
00665          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
00666          * This initializes the cursor's visibility counter to 0.
00667          */
00668         ::InitCursor();
00669 
00670         MenuRef windMenu;
00671         ::CreateStandardWindowMenu(0, &windMenu);
00672         ::InsertMenu(windMenu, 0);
00673         ::DrawMenuBar();
00674 
00675         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
00676         
00677         ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
00678         ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
00679         ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
00680         ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
00681         
00682     }
00683     return success;
00684 }
00685 
00686 
00687 GHOST_TSuccess GHOST_SystemCarbon::exit()
00688 {
00689     return GHOST_System::exit();
00690 }
00691 
00692 
00693 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
00694 {
00695     WindowRef windowRef;
00696     GHOST_WindowCarbon *window;
00697     OSStatus err = eventNotHandledErr;
00698     
00699     // Check if the event was send to a GHOST window
00700     ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
00701     window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
00702     if (!validWindow(window)) {
00703         return err;
00704     }
00705 
00706     //if (!getFullScreen()) {
00707         err = noErr;
00708         switch(::GetEventKind(event)) 
00709         {
00710             case kEventWindowClose:
00711                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
00712                 break;
00713             case kEventWindowActivated:
00714                 m_windowManager->setActiveWindow(window);
00715                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
00716                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
00717                 break;
00718             case kEventWindowDeactivated:
00719                 m_windowManager->setWindowInactive(window);
00720                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
00721                 break;
00722             case kEventWindowUpdate:
00723                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
00724                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
00725                 break;
00726             case kEventWindowBoundsChanged:
00727                 if (!m_ignoreWindowSizedMessages)
00728                 {
00729                     window->updateDrawingContext();
00730                     pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
00731                 }
00732                 break;
00733             default:
00734                 err = eventNotHandledErr;
00735                 break;
00736         }
00737 //  }
00738     //else {
00739         //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
00740         //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
00741         //::RemoveEventFromQueue(::GetMainEventQueue(), event);
00742     //}
00743     
00744     return err;
00745 }
00746 
00747 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
00748 {
00749     GHOST_IWindow* window = m_windowManager->getActiveWindow();
00750     TabletPointRec tabletPointRecord;
00751     TabletProximityRec  tabletProximityRecord;
00752     UInt32 anInt32;
00753     GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
00754     OSStatus err = eventNotHandledErr;
00755     
00756     ct.Pressure = 0;
00757     ct.Xtilt = 0;
00758     ct.Ytilt = 0;
00759     
00760     // is there an embedded tablet event inside this mouse event? 
00761     if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
00762     {
00763         // yes there is one!
00764         // Embedded tablet events can either be a proximity or pointer event.
00765         if(anInt32 == kEventTabletPoint)
00766         {
00767             //GHOST_PRINT("Embedded pointer event!\n");
00768             
00769             // Extract the tablet Pointer Event. If there is no Tablet Pointer data
00770             // in this event, then this call will return an error. Just ignore the
00771             // error and go on. This can occur when a proximity event is embedded in
00772             // a mouse event and you did not check the mouse event to see which type
00773             // of tablet event was embedded.
00774             if(noErr == GetEventParameter(event, kEventParamTabletPointRec,
00775                                           typeTabletPointRec, NULL,
00776                                           sizeof(TabletPointRec),
00777                                           NULL, (void *)&tabletPointRecord))
00778             {
00779                 ct.Pressure = tabletPointRecord.pressure / 65535.0f;
00780                 ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
00781                 ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
00782             }
00783         } else {
00784             //GHOST_PRINT("Embedded proximity event\n");
00785             
00786             // Extract the Tablet Proximity record from the event.
00787             if(noErr == GetEventParameter(event, kEventParamTabletProximityRec,
00788                                           typeTabletProximityRec, NULL,
00789                                           sizeof(TabletProximityRec),
00790                                           NULL, (void *)&tabletProximityRecord))
00791             {
00792                 if (tabletProximityRecord.enterProximity) {
00793                     //pointer is entering tablet area proximity
00794                     
00795                     switch(tabletProximityRecord.pointerType)
00796                     {
00797                         case 1: /* stylus */
00798                             ct.Active = GHOST_kTabletModeStylus;
00799                             break;
00800                         case 2: /* puck, not supported so far */
00801                             ct.Active = GHOST_kTabletModeNone;
00802                             break;
00803                         case 3: /* eraser */
00804                             ct.Active = GHOST_kTabletModeEraser;
00805                             break;
00806                         default:
00807                             ct.Active = GHOST_kTabletModeNone;
00808                             break;
00809                     }
00810                 } else {
00811                     // pointer is leaving - return to mouse
00812                     ct.Active = GHOST_kTabletModeNone;
00813                 }
00814             }
00815         }
00816     err = noErr;
00817     }
00818     return err;
00819 }
00820 
00821 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
00822 {
00823     OSStatus err = eventNotHandledErr;
00824     GHOST_IWindow* window = m_windowManager->getActiveWindow();
00825     UInt32 kind = ::GetEventKind(event);
00826             
00827     switch (kind)
00828     {
00829         case kEventMouseDown:
00830         case kEventMouseUp:
00831             // Handle Mac application responsibilities
00832             if ((kind == kEventMouseDown) && handleMouseDown(event)) {
00833                 err = noErr;
00834             }
00835             else {
00836                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
00837                 EventMouseButton button;
00838                 
00839                 /* Window still gets mouse up after command-H */
00840                 if (m_windowManager->getActiveWindow()) {
00841                     // handle any tablet events that may have come with the mouse event (optional)
00842                     handleTabletEvent(event);
00843                     
00844                     ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
00845                     pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
00846                     err = noErr;
00847                 }
00848             }
00849             break;
00850             
00851         case kEventMouseMoved:
00852         case kEventMouseDragged: {
00853             Point mousePos;
00854 
00855             if (window) {
00856                 //handle any tablet events that may have come with the mouse event (optional)
00857                 handleTabletEvent(event);
00858 
00859                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
00860                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
00861                 err = noErr;
00862             }
00863             break;
00864         }
00865         case kEventMouseWheelMoved:
00866             {
00867                 OSStatus status;
00868                 //UInt32 modifiers;
00869                 EventMouseWheelAxis axis;
00870                 SInt32 delta;
00871                 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
00872                 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
00873                 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
00874                 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
00875                 if (axis == kEventMouseWheelAxisY)
00876                 {
00877                     status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
00878                     GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
00879                     /*
00880                      * Limit mouse wheel delta to plus and minus one.
00881                      */
00882                     delta = delta > 0 ? 1 : -1;
00883                     pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
00884                     err = noErr;
00885                 }
00886             }
00887             break;
00888         }
00889     
00890     return err;
00891 }
00892 
00893 
00894 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
00895 {
00896     OSStatus err = eventNotHandledErr;
00897     GHOST_IWindow* window = m_windowManager->getActiveWindow();
00898     UInt32 kind = ::GetEventKind(event);
00899     UInt32 modifiers;
00900     UInt32 rawCode;
00901     GHOST_TKey key;
00902     unsigned char ascii;
00903 
00904     /* Can happen, very rarely - seems to only be when command-H makes
00905      * the window go away and we still get an HKey up. 
00906      */
00907     if (!window) {
00908         //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
00909         //key = convertKey(rawCode);
00910         return err;
00911     }
00912     
00913     err = noErr;
00914     switch (kind) {
00915         case kEventRawKeyDown: 
00916         case kEventRawKeyRepeat: 
00917         case kEventRawKeyUp: 
00918             ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
00919             ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
00920     
00921             key = convertKey(rawCode);
00922             ascii= convertRomanToLatin(ascii);
00923             
00924     //      if (key!=GHOST_kKeyUnknown) {
00925                 GHOST_TEventType type;
00926                 if (kind == kEventRawKeyDown) {
00927                     type = GHOST_kEventKeyDown;
00928                 } else if (kind == kEventRawKeyRepeat) { 
00929                     type = GHOST_kEventKeyDown;  /* XXX, fixme */
00930                 } else {
00931                     type = GHOST_kEventKeyUp;
00932                 }
00933                 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii, NULL) );
00934 //          }
00935             break;
00936     
00937         case kEventRawKeyModifiersChanged: 
00938                 /* ugh */
00939             ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
00940             if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
00941                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
00942             }
00943             if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
00944                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
00945             }
00946             if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
00947                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
00948             }
00949             if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
00950                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) );
00951             }
00952             
00953             m_modifierMask = modifiers;
00954             break;
00955             
00956         default:
00957             err = eventNotHandledErr;
00958             break;
00959     }
00960     
00961     return err;
00962 }
00963 
00964 
00965 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
00966 {
00967     WindowPtr           window;
00968     short               part;
00969     BitMap              screenBits;
00970     bool                handled = true;
00971     GHOST_WindowCarbon* ghostWindow;
00972     Point               mousePos = {0 , 0};
00973     
00974     ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
00975     
00976     part = ::FindWindow(mousePos, &window);
00977     ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
00978     
00979     switch (part) {
00980         case inMenuBar:
00981             handleMenuCommand(::MenuSelect(mousePos));
00982             break;
00983             
00984         case inDrag:
00985             /*
00986              * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
00987              * events. By setting m_ignoreWindowSizedMessages these are suppressed.
00988              * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
00989              */
00990             /* even worse: scale window also generates a load of events, and nothing 
00991                is handled (read: client's event proc called) until you release mouse (ton) */
00992             
00993             GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
00994             m_ignoreWindowSizedMessages = true;
00995             ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
00996             m_ignoreWindowSizedMessages = false;
00997             
00998             pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
00999 
01000             break;
01001         
01002         case inContent:
01003             if (window != ::FrontWindow()) {
01004                 ::SelectWindow(window);
01005                 /*
01006                  * We add a mouse down event on the newly actived window
01007                  */     
01008                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
01009                 EventMouseButton button;
01010                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
01011                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
01012             } else {
01013                 handled = false;
01014             }
01015             break;
01016             
01017         case inGoAway:
01018             GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
01019             if (::TrackGoAway(window, mousePos))
01020             {
01021                 // todo: add option-close, because itÿs in the HIG
01022                 // if (event.modifiers & optionKey) {
01023                     // Close the clean documents, others will be confirmed one by one.
01024                 //}
01025                 // else {
01026                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
01027                 //}
01028             }
01029             break;
01030             
01031         case inGrow:
01032             GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
01033             ::ResizeWindow(window, mousePos, NULL, NULL);
01034             break;
01035             
01036         case inZoomIn:
01037         case inZoomOut:
01038             GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
01039             if (::TrackBox(window, mousePos, part)) {
01040                 int macState;
01041                 
01042                 macState = ghostWindow->getMac_windowState();
01043                 if ( macState== 0)
01044                     ::ZoomWindow(window, part, true);
01045                 else 
01046                     if (macState == 2) { // always ok
01047                             ::ZoomWindow(window, part, true);
01048                             ghostWindow->setMac_windowState(1);
01049                     } else { // need to force size again
01050                     //  GHOST_TUns32 scr_x,scr_y; /*unused*/
01051                         Rect outAvailableRect;
01052                         
01053                         ghostWindow->setMac_windowState(2);
01054                         ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
01055                         
01056                         //this->getMainDisplayDimensions(scr_x,scr_y);
01057                         ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
01058                         ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
01059                     }
01060                 
01061             }
01062             break;
01063 
01064         default:
01065             handled = false;
01066             break;
01067     }
01068     
01069     return handled;
01070 }
01071 
01072 
01073 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
01074 {
01075     short       menuID;
01076     short       menuItem;
01077     UInt32      command;
01078     bool        handled;
01079     OSErr       err;
01080     
01081     menuID = HiWord(menuResult);
01082     menuItem = LoWord(menuResult);
01083 
01084     err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
01085 
01086     handled = false;
01087     
01088     if (err || command == 0) {
01089     }
01090     else {
01091         switch(command) {
01092         }
01093     }
01094 
01095     ::HiliteMenu(0);
01096     return handled;
01097 }
01098 
01099 
01100 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
01101 {
01102     GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
01103     OSStatus err = eventNotHandledErr;
01104     GHOST_IWindow* window;
01105 #ifdef WITH_INPUT_NDOF
01106     GHOST_TEventNDOFData data;
01107 #endif
01108     UInt32 kind;
01109     
01110     switch (::GetEventClass(event))
01111     {
01112         case kEventClassAppleEvent:
01113             EventRecord eventrec;
01114             if (ConvertEventRefToEventRecord(event, &eventrec)) {
01115                 err = AEProcessAppleEvent(&eventrec);
01116             }
01117             break;
01118         case kEventClassMouse:
01119             err = sys->handleMouseEvent(event);
01120             break;
01121         case kEventClassWindow:
01122             err = sys->handleWindowEvent(event);
01123             break;
01124         case kEventClassKeyboard:
01125             err = sys->handleKeyEvent(event);
01126             break;
01127         case kEventClassBlender :
01128 #ifdef WITH_INPUT_NDOF
01129             window = sys->m_windowManager->getActiveWindow();
01130             sys->m_ndofManager->GHOST_NDOFGetDatas(data);
01131             kind = ::GetEventKind(event);
01132             
01133             switch (kind)
01134             {
01135                 case 1:
01136                     sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
01137     //              printf("motion\n");
01138                     break;
01139                 case 2:
01140                     sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
01141 //                  printf("button\n");
01142                     break;
01143             }
01144 #endif
01145             err = noErr;
01146             break;
01147         default : 
01148             ;
01149             break;
01150     }
01151 
01152     return err;
01153 }
01154 
01155 GHOST_TUns8* GHOST_SystemCarbon::getClipboard(bool selection) const
01156 {
01157     PasteboardRef inPasteboard;
01158     PasteboardItemID itemID;
01159     CFDataRef flavorData;
01160     OSStatus err = noErr;
01161     GHOST_TUns8 * temp_buff;
01162     CFRange range;
01163     OSStatus syncFlags;
01164     
01165     err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
01166     if(err != noErr) { return NULL;}
01167 
01168     syncFlags = PasteboardSynchronize( inPasteboard );
01169         /* as we always get in a new string, we can safely ignore sync flags if not an error*/
01170     if(syncFlags <0) { return NULL;}
01171 
01172 
01173     err = PasteboardGetItemIdentifier( inPasteboard, 1, &itemID );
01174     if(err != noErr) { return NULL;}
01175 
01176     err = PasteboardCopyItemFlavorData( inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
01177     if(err != noErr) { return NULL;}
01178 
01179     range = CFRangeMake(0, CFDataGetLength(flavorData));
01180     
01181     temp_buff = (GHOST_TUns8*) malloc(range.length+1); 
01182 
01183     CFDataGetBytes(flavorData, range, (UInt8*)temp_buff);
01184     
01185     temp_buff[range.length] = '\0';
01186     
01187     if(temp_buff) {
01188         return temp_buff;
01189     } else {
01190         return NULL;
01191     }
01192 }
01193 
01194 void GHOST_SystemCarbon::putClipboard(GHOST_TInt8 *buffer, bool selection) const
01195 {
01196     if(selection) {return;} // for copying the selection, used on X11
01197 
01198     PasteboardRef inPasteboard;
01199     CFDataRef textData = NULL;
01200     OSStatus err = noErr; /*For error checking*/
01201     OSStatus syncFlags;
01202     
01203     err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
01204     if(err != noErr) { return;}
01205     
01206     syncFlags = PasteboardSynchronize( inPasteboard ); 
01207     /* as we always put in a new string, we can safely ignore sync flags */
01208     if(syncFlags <0) { return;}
01209     
01210     err = PasteboardClear( inPasteboard );
01211     if(err != noErr) { return;}
01212     
01213     textData = CFDataCreate(kCFAllocatorDefault, (UInt8*)buffer, strlen(buffer));
01214     
01215     if (textData) {
01216         err = PasteboardPutItemFlavor( inPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
01217             if(err != noErr) { 
01218                 if(textData) { CFRelease(textData);}
01219                 return;
01220             }
01221     }
01222     
01223     if(textData) {
01224         CFRelease(textData);
01225     }
01226 }