3 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file ghost/intern/GHOST_SystemCarbon.cpp
37 * Copyright (C) 2001 NaN Technologies B.V.
38 * @author Maarten Gribnau
42 #include <Carbon/Carbon.h>
43 #include <ApplicationServices/ApplicationServices.h>
44 #include "GHOST_SystemCarbon.h"
46 #include "GHOST_DisplayManagerCarbon.h"
47 #include "GHOST_EventKey.h"
48 #include "GHOST_EventButton.h"
49 #include "GHOST_EventCursor.h"
50 #include "GHOST_EventWheel.h"
51 #include "GHOST_EventNDOF.h"
53 #include "GHOST_TimerManager.h"
54 #include "GHOST_TimerTask.h"
55 #include "GHOST_WindowManager.h"
56 #include "GHOST_WindowCarbon.h"
57 #include "GHOST_NDOFManager.h"
58 #include "AssertMacros.h"
60 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
62 /* blender class and types events */
64 kEventClassBlender = 'blnd'
68 kEventBlenderNdofAxis = 1,
69 kEventBlenderNdofButtons = 2
72 const EventTypeSpec kEvents[] =
74 { kEventClassAppleEvent, kEventAppleEvent },
76 { kEventClassApplication, kEventAppActivated },
77 { kEventClassApplication, kEventAppDeactivated },
79 { kEventClassKeyboard, kEventRawKeyDown },
80 { kEventClassKeyboard, kEventRawKeyRepeat },
81 { kEventClassKeyboard, kEventRawKeyUp },
82 { kEventClassKeyboard, kEventRawKeyModifiersChanged },
84 { kEventClassMouse, kEventMouseDown },
85 { kEventClassMouse, kEventMouseUp },
86 { kEventClassMouse, kEventMouseMoved },
87 { kEventClassMouse, kEventMouseDragged },
88 { kEventClassMouse, kEventMouseWheelMoved },
90 { kEventClassWindow, kEventWindowClickZoomRgn } , /* for new zoom behaviour */
91 { kEventClassWindow, kEventWindowZoom }, /* for new zoom behaviour */
92 { kEventClassWindow, kEventWindowExpand } , /* for new zoom behaviour */
93 { kEventClassWindow, kEventWindowExpandAll }, /* for new zoom behaviour */
95 { kEventClassWindow, kEventWindowClose },
96 { kEventClassWindow, kEventWindowActivated },
97 { kEventClassWindow, kEventWindowDeactivated },
98 { kEventClassWindow, kEventWindowUpdate },
99 { kEventClassWindow, kEventWindowBoundsChanged },
101 { kEventClassBlender, kEventBlenderNdofAxis },
102 { kEventClassBlender, kEventBlenderNdofButtons }
110 static GHOST_TButtonMask convertButton(EventMouseButton button)
113 case kEventMouseButtonPrimary:
114 return GHOST_kButtonMaskLeft;
115 case kEventMouseButtonSecondary:
116 return GHOST_kButtonMaskRight;
117 case kEventMouseButtonTertiary:
119 return GHOST_kButtonMaskMiddle;
123 static GHOST_TKey convertKey(int rawCode)
125 /* This bit of magic converts the rawCode into a virtual
126 * Mac key based on the current keyboard mapping, but
127 * without regard to the modifiers (so we don't get 'a'
128 * and 'A' for example.
130 static UInt32 dummy= 0;
131 Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
132 unsigned char vk = KeyTranslate(transData, rawCode, &dummy);
133 /* Map numpad based on rawcodes first, otherwise they
134 * look like non-numpad events.
135 * Added too: mapping the number keys, for french keyboards etc (ton)
137 // printf("GHOST: vk: %d %c raw: %d\n", vk, vk, rawCode);
140 case 18: return GHOST_kKey1;
141 case 19: return GHOST_kKey2;
142 case 20: return GHOST_kKey3;
143 case 21: return GHOST_kKey4;
144 case 23: return GHOST_kKey5;
145 case 22: return GHOST_kKey6;
146 case 26: return GHOST_kKey7;
147 case 28: return GHOST_kKey8;
148 case 25: return GHOST_kKey9;
149 case 29: return GHOST_kKey0;
151 case 82: return GHOST_kKeyNumpad0;
152 case 83: return GHOST_kKeyNumpad1;
153 case 84: return GHOST_kKeyNumpad2;
154 case 85: return GHOST_kKeyNumpad3;
155 case 86: return GHOST_kKeyNumpad4;
156 case 87: return GHOST_kKeyNumpad5;
157 case 88: return GHOST_kKeyNumpad6;
158 case 89: return GHOST_kKeyNumpad7;
159 case 91: return GHOST_kKeyNumpad8;
160 case 92: return GHOST_kKeyNumpad9;
161 case 65: return GHOST_kKeyNumpadPeriod;
162 case 76: return GHOST_kKeyNumpadEnter;
163 case 69: return GHOST_kKeyNumpadPlus;
164 case 78: return GHOST_kKeyNumpadMinus;
165 case 67: return GHOST_kKeyNumpadAsterisk;
166 case 75: return GHOST_kKeyNumpadSlash;
169 if ((vk >= 'a') && (vk <= 'z')) {
170 return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
171 } else if ((vk >= '0') && (vk <= '9')) {
172 return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
175 case 122: return GHOST_kKeyF1;
176 case 120: return GHOST_kKeyF2;
177 case 99: return GHOST_kKeyF3;
178 case 118: return GHOST_kKeyF4;
179 case 96: return GHOST_kKeyF5;
180 case 97: return GHOST_kKeyF6;
181 case 98: return GHOST_kKeyF7;
182 case 100: return GHOST_kKeyF8;
183 case 101: return GHOST_kKeyF9;
184 case 109: return GHOST_kKeyF10;
185 case 103: return GHOST_kKeyF11;
186 case 111: return GHOST_kKeyF12; // Never get, is used for ejecting the CD!
190 case kUpArrowCharCode: return GHOST_kKeyUpArrow;
191 case kDownArrowCharCode: return GHOST_kKeyDownArrow;
192 case kLeftArrowCharCode: return GHOST_kKeyLeftArrow;
193 case kRightArrowCharCode: return GHOST_kKeyRightArrow;
195 case kReturnCharCode: return GHOST_kKeyEnter;
196 case kBackspaceCharCode: return GHOST_kKeyBackSpace;
197 case kDeleteCharCode: return GHOST_kKeyDelete;
198 case kEscapeCharCode: return GHOST_kKeyEsc;
199 case kTabCharCode: return GHOST_kKeyTab;
200 case kSpaceCharCode: return GHOST_kKeySpace;
202 case kHomeCharCode: return GHOST_kKeyHome;
203 case kEndCharCode: return GHOST_kKeyEnd;
204 case kPageUpCharCode: return GHOST_kKeyUpPage;
205 case kPageDownCharCode: return GHOST_kKeyDownPage;
207 case '-': return GHOST_kKeyMinus;
208 case '=': return GHOST_kKeyEqual;
209 case ',': return GHOST_kKeyComma;
210 case '.': return GHOST_kKeyPeriod;
211 case '/': return GHOST_kKeySlash;
212 case ';': return GHOST_kKeySemicolon;
213 case '\'': return GHOST_kKeyQuote;
214 case '\\': return GHOST_kKeyBackslash;
215 case '[': return GHOST_kKeyLeftBracket;
216 case ']': return GHOST_kKeyRightBracket;
217 case '`': return GHOST_kKeyAccentGrave;
221 // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
223 return GHOST_kKeyUnknown;
226 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
227 * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
228 * I am not sure how international this works...
229 * For cross-platform convention, we'll use the Latin ascii set instead.
230 * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
233 static unsigned char convertRomanToLatin(unsigned char ascii)
236 if(ascii<128) return ascii;
239 case 128: return 142;
240 case 129: return 143;
241 case 130: return 128;
242 case 131: return 201;
243 case 132: return 209;
244 case 133: return 214;
245 case 134: return 220;
246 case 135: return 225;
247 case 136: return 224;
248 case 137: return 226;
249 case 138: return 228;
250 case 139: return 227;
251 case 140: return 229;
252 case 141: return 231;
253 case 142: return 233;
254 case 143: return 232;
255 case 144: return 234;
256 case 145: return 235;
257 case 146: return 237;
258 case 147: return 236;
259 case 148: return 238;
260 case 149: return 239;
261 case 150: return 241;
262 case 151: return 243;
263 case 152: return 242;
264 case 153: return 244;
265 case 154: return 246;
266 case 155: return 245;
267 case 156: return 250;
268 case 157: return 249;
269 case 158: return 251;
270 case 159: return 252;
272 case 161: return 176;
273 case 162: return 162;
274 case 163: return 163;
275 case 164: return 167;
276 case 165: return 183;
277 case 166: return 182;
278 case 167: return 223;
279 case 168: return 174;
280 case 169: return 169;
281 case 170: return 174;
282 case 171: return 180;
283 case 172: return 168;
285 case 174: return 198;
286 case 175: return 216;
288 case 177: return 177;
291 case 180: return 165;
292 case 181: return 181;
295 case 184: return 215;
298 case 187: return 170;
299 case 188: return 186;
301 case 190: return 230;
302 case 191: return 248;
303 case 192: return 191;
304 case 193: return 161;
305 case 194: return 172;
310 case 199: return 171;
311 case 200: return 187;
312 case 201: return 201;
314 case 203: return 192;
315 case 204: return 195;
316 case 205: return 213;
323 case 214: return 247;
325 case 229: return 194;
326 case 230: return 202;
327 case 231: return 193;
328 case 232: return 203;
329 case 233: return 200;
330 case 234: return 205;
331 case 235: return 206;
332 case 236: return 207;
333 case 237: return 204;
334 case 238: return 211;
335 case 239: return 212;
337 case 241: return 210;
338 case 242: return 218;
339 case 243: return 219;
340 case 244: return 217;
357 GHOST_SystemCarbon::GHOST_SystemCarbon() :
360 m_displayManager = new GHOST_DisplayManagerCarbon ();
361 GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
362 m_displayManager->initialize();
365 ::Microseconds(µs);
366 m_start_time = UnsignedWideToUInt64(micros)/1000;
367 m_ignoreWindowSizedMessages = false;
370 GHOST_SystemCarbon::~GHOST_SystemCarbon()
375 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
378 ::Microseconds(µs);
380 millis = UnsignedWideToUInt64(micros);
381 return (millis / 1000) - m_start_time;
385 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
387 // We do not support multiple monitors at the moment
392 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
395 Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
396 width = bnds.right - bnds.left;
397 height = bnds.bottom - bnds.top;
401 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
402 const STR_String& title,
407 GHOST_TWindowState state,
408 GHOST_TDrawingContextType type,
410 const GHOST_TUns16 numOfAASamples,
411 const GHOST_TEmbedderWindowID parentWindow
414 GHOST_IWindow* window = 0;
416 window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
419 if (window->getValid()) {
420 // Store the pointer to the window
421 GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
422 m_windowManager->addWindow(window);
423 m_windowManager->setActiveWindow(window);
424 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
427 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
433 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
438 GHOST_TSuccess GHOST_SystemCarbon::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
440 GHOST_TSuccess success = GHOST_kFailure;
442 // need yo make this Carbon all on 10.5 for fullscreen to work correctly
443 CGCaptureAllDisplays();
445 success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
447 if( success != GHOST_kSuccess ) {
448 // fullscreen failed for other reasons, release
449 CGReleaseAllDisplays();
455 GHOST_TSuccess GHOST_SystemCarbon::endFullScreen(void)
457 CGReleaseAllDisplays();
458 return GHOST_System::endFullScreen();
461 /* this is an old style low level event queue.
462 As we want to handle our own timers, this is ok.
463 the full screen hack should be removed */
464 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
466 bool anyProcessed = false;
469 // SetMouseCoalescingEnabled(false, NULL);
472 GHOST_TimerManager* timerMgr = getTimerManager();
475 GHOST_TUns64 next = timerMgr->nextFireTime();
478 if (next == GHOST_kFireTimeNever) {
479 timeOut = kEventDurationForever;
481 timeOut = (double)(next - getMilliSeconds())/1000.0;
486 ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
489 if (timerMgr->fireTimers(getMilliSeconds())) {
493 if (getFullScreen()) {
494 // Check if the full-screen window is dirty
495 GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
496 if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
497 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
502 /* end loop when no more events available */
503 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
504 OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
508 UInt32 i= ::GetEventClass(event);
510 /* Ignore 'cgs ' class, no documentation on what they
511 * are, but we get a lot of them
514 if (i!='tblt') { // tablet event. we use the one packaged in the mouse event
515 ; //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
519 ::ReleaseEvent(event);
521 } while (waitForEvent && !anyProcessed);
527 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
530 // Get the position of the mouse in the active port
531 ::GetGlobalMouse(&mouseLoc);
532 // Convert the coordinates to screen coordinates
533 x = (GHOST_TInt32)mouseLoc.h;
534 y = (GHOST_TInt32)mouseLoc.v;
535 return GHOST_kSuccess;
539 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
541 float xf=(float)x, yf=(float)y;
543 CGAssociateMouseAndMouseCursorPosition(false);
544 CGSetLocalEventsSuppressionInterval(0);
545 CGWarpMouseCursorPosition(CGPointMake(xf, yf));
546 CGAssociateMouseAndMouseCursorPosition(true);
548 //this doesn't work properly, see game engine mouse-look scripts
549 // CGWarpMouseCursorPosition(CGPointMake(xf, yf));
550 // this call below sends event, but empties other events (like shift)
551 // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
553 return GHOST_kSuccess;
557 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
559 UInt32 modifiers = ::GetCurrentKeyModifiers();
561 keys.set(GHOST_kModifierKeyOS, (modifiers & cmdKey) ? true : false);
562 keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
563 keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
564 keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
566 return GHOST_kSuccess;
569 /* XXX, incorrect for multibutton mice */
570 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
572 Boolean theOnlyButtonIsDown = ::Button();
574 buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
575 return GHOST_kSuccess;
578 #define FIRSTFILEBUFLG 512
579 static bool g_hasFirstFile = false;
580 static char g_firstFileBuf[512];
582 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) {
583 if (g_hasFirstFile) {
584 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
585 buf[FIRSTFILEBUFLG - 1] = '\0';
592 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
594 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
599 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
601 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
606 err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
607 if (err != noErr) return err;
609 err = AECountItems(&docs, &ndocs);
613 for (i=0; i<ndocs; i++) {
619 err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
626 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
628 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
631 g_hasFirstFile = true;
636 AEDisposeDesc(&docs);
641 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
643 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
648 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
650 GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
652 sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
658 GHOST_TSuccess GHOST_SystemCarbon::init()
661 GHOST_TSuccess success = GHOST_System::init();
664 * Initialize the cursor to the standard arrow shape (so that we can change it later on).
665 * This initializes the cursor's visibility counter to 0.
670 ::CreateStandardWindowMenu(0, &windMenu);
671 ::InsertMenu(windMenu, 0);
674 ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
676 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
677 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
678 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
679 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
686 GHOST_TSuccess GHOST_SystemCarbon::exit()
688 return GHOST_System::exit();
692 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
695 GHOST_WindowCarbon *window;
696 OSStatus err = eventNotHandledErr;
698 // Check if the event was send to a GHOST window
699 ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
700 window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
701 if (!validWindow(window)) {
705 //if (!getFullScreen()) {
707 switch(::GetEventKind(event))
709 case kEventWindowClose:
710 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
712 case kEventWindowActivated:
713 m_windowManager->setActiveWindow(window);
714 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
715 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
717 case kEventWindowDeactivated:
718 m_windowManager->setWindowInactive(window);
719 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
721 case kEventWindowUpdate:
722 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
723 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
725 case kEventWindowBoundsChanged:
726 if (!m_ignoreWindowSizedMessages)
728 window->updateDrawingContext();
729 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
733 err = eventNotHandledErr;
738 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
739 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
740 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
746 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
748 GHOST_IWindow* window = m_windowManager->getActiveWindow();
749 TabletPointRec tabletPointRecord;
750 TabletProximityRec tabletProximityRecord;
752 GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
753 OSStatus err = eventNotHandledErr;
759 // is there an embedded tablet event inside this mouse event?
760 if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
763 // Embedded tablet events can either be a proximity or pointer event.
764 if(anInt32 == kEventTabletPoint)
766 //GHOST_PRINT("Embedded pointer event!\n");
768 // Extract the tablet Pointer Event. If there is no Tablet Pointer data
769 // in this event, then this call will return an error. Just ignore the
770 // error and go on. This can occur when a proximity event is embedded in
771 // a mouse event and you did not check the mouse event to see which type
772 // of tablet event was embedded.
773 if(noErr == GetEventParameter(event, kEventParamTabletPointRec,
774 typeTabletPointRec, NULL,
775 sizeof(TabletPointRec),
776 NULL, (void *)&tabletPointRecord))
778 ct.Pressure = tabletPointRecord.pressure / 65535.0f;
779 ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
780 ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
783 //GHOST_PRINT("Embedded proximity event\n");
785 // Extract the Tablet Proximity record from the event.
786 if(noErr == GetEventParameter(event, kEventParamTabletProximityRec,
787 typeTabletProximityRec, NULL,
788 sizeof(TabletProximityRec),
789 NULL, (void *)&tabletProximityRecord))
791 if (tabletProximityRecord.enterProximity) {
792 //pointer is entering tablet area proximity
794 switch(tabletProximityRecord.pointerType)
797 ct.Active = GHOST_kTabletModeStylus;
799 case 2: /* puck, not supported so far */
800 ct.Active = GHOST_kTabletModeNone;
803 ct.Active = GHOST_kTabletModeEraser;
806 ct.Active = GHOST_kTabletModeNone;
810 // pointer is leaving - return to mouse
811 ct.Active = GHOST_kTabletModeNone;
820 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
822 OSStatus err = eventNotHandledErr;
823 GHOST_IWindow* window = m_windowManager->getActiveWindow();
824 UInt32 kind = ::GetEventKind(event);
828 case kEventMouseDown:
830 // Handle Mac application responsibilities
831 if ((kind == kEventMouseDown) && handleMouseDown(event)) {
835 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
836 EventMouseButton button;
838 /* Window still gets mouse up after command-H */
839 if (m_windowManager->getActiveWindow()) {
840 // handle any tablet events that may have come with the mouse event (optional)
841 handleTabletEvent(event);
843 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
844 pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
850 case kEventMouseMoved:
851 case kEventMouseDragged: {
855 //handle any tablet events that may have come with the mouse event (optional)
856 handleTabletEvent(event);
858 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
859 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
864 case kEventMouseWheelMoved:
868 EventMouseWheelAxis axis;
870 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
871 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
872 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
873 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
874 if (axis == kEventMouseWheelAxisY)
876 status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
877 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
879 * Limit mouse wheel delta to plus and minus one.
881 delta = delta > 0 ? 1 : -1;
882 pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
893 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
895 OSStatus err = eventNotHandledErr;
896 GHOST_IWindow* window = m_windowManager->getActiveWindow();
897 UInt32 kind = ::GetEventKind(event);
903 /* Can happen, very rarely - seems to only be when command-H makes
904 * the window go away and we still get an HKey up.
907 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
908 //key = convertKey(rawCode);
914 case kEventRawKeyDown:
915 case kEventRawKeyRepeat:
917 ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
918 ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
920 key = convertKey(rawCode);
921 ascii= convertRomanToLatin(ascii);
923 // if (key!=GHOST_kKeyUnknown) {
924 GHOST_TEventType type;
925 if (kind == kEventRawKeyDown) {
926 type = GHOST_kEventKeyDown;
927 } else if (kind == kEventRawKeyRepeat) {
928 type = GHOST_kEventKeyDown; /* XXX, fixme */
930 type = GHOST_kEventKeyUp;
932 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
936 case kEventRawKeyModifiersChanged:
938 ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
939 if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
940 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
942 if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
943 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
945 if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
946 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
948 if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
949 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyOS) );
952 m_modifierMask = modifiers;
956 err = eventNotHandledErr;
964 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
970 GHOST_WindowCarbon* ghostWindow;
971 Point mousePos = {0 , 0};
973 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
975 part = ::FindWindow(mousePos, &window);
976 ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
980 handleMenuCommand(::MenuSelect(mousePos));
985 * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
986 * events. By setting m_ignoreWindowSizedMessages these are suppressed.
987 * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
989 /* even worse: scale window also generates a load of events, and nothing
990 is handled (read: client's event proc called) until you release mouse (ton) */
992 GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
993 m_ignoreWindowSizedMessages = true;
994 ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
995 m_ignoreWindowSizedMessages = false;
997 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
1002 if (window != ::FrontWindow()) {
1003 ::SelectWindow(window);
1005 * We add a mouse down event on the newly actived window
1007 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
1008 EventMouseButton button;
1009 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
1010 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
1017 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1018 if (::TrackGoAway(window, mousePos))
1020 // todo: add option-close, because itÿs in the HIG
1021 // if (event.modifiers & optionKey) {
1022 // Close the clean documents, others will be confirmed one by one.
1025 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
1031 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1032 ::ResizeWindow(window, mousePos, NULL, NULL);
1037 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1038 if (::TrackBox(window, mousePos, part)) {
1041 macState = ghostWindow->getMac_windowState();
1043 ::ZoomWindow(window, part, true);
1045 if (macState == 2) { // always ok
1046 ::ZoomWindow(window, part, true);
1047 ghostWindow->setMac_windowState(1);
1048 } else { // need to force size again
1049 // GHOST_TUns32 scr_x,scr_y; /*unused*/
1050 Rect outAvailableRect;
1052 ghostWindow->setMac_windowState(2);
1053 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1055 //this->getMainDisplayDimensions(scr_x,scr_y);
1056 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1057 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1072 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1080 menuID = HiWord(menuResult);
1081 menuItem = LoWord(menuResult);
1083 err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1087 if (err || command == 0) {
1099 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1101 GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1102 OSStatus err = eventNotHandledErr;
1103 GHOST_IWindow* window;
1104 GHOST_TEventNDOFData data;
1107 switch (::GetEventClass(event))
1109 case kEventClassAppleEvent:
1110 EventRecord eventrec;
1111 if (ConvertEventRefToEventRecord(event, &eventrec)) {
1112 err = AEProcessAppleEvent(&eventrec);
1115 case kEventClassMouse:
1116 err = sys->handleMouseEvent(event);
1118 case kEventClassWindow:
1119 err = sys->handleWindowEvent(event);
1121 case kEventClassKeyboard:
1122 err = sys->handleKeyEvent(event);
1124 case kEventClassBlender :
1125 window = sys->m_windowManager->getActiveWindow();
1126 sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1127 kind = ::GetEventKind(event);
1132 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1133 // printf("motion\n");
1136 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1137 // printf("button\n");
1150 GHOST_TUns8* GHOST_SystemCarbon::getClipboard(bool selection) const
1152 PasteboardRef inPasteboard;
1153 PasteboardItemID itemID;
1154 CFDataRef flavorData;
1155 OSStatus err = noErr;
1156 GHOST_TUns8 * temp_buff;
1160 err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1161 if(err != noErr) { return NULL;}
1163 syncFlags = PasteboardSynchronize( inPasteboard );
1164 /* as we always get in a new string, we can safely ignore sync flags if not an error*/
1165 if(syncFlags <0) { return NULL;}
1168 err = PasteboardGetItemIdentifier( inPasteboard, 1, &itemID );
1169 if(err != noErr) { return NULL;}
1171 err = PasteboardCopyItemFlavorData( inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
1172 if(err != noErr) { return NULL;}
1174 range = CFRangeMake(0, CFDataGetLength(flavorData));
1176 temp_buff = (GHOST_TUns8*) malloc(range.length+1);
1178 CFDataGetBytes(flavorData, range, (UInt8*)temp_buff);
1180 temp_buff[range.length] = '\0';
1189 void GHOST_SystemCarbon::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1191 if(selection) {return;} // for copying the selection, used on X11
1193 PasteboardRef inPasteboard;
1194 CFDataRef textData = NULL;
1195 OSStatus err = noErr; /*For error checking*/
1198 err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1199 if(err != noErr) { return;}
1201 syncFlags = PasteboardSynchronize( inPasteboard );
1202 /* as we always put in a new string, we can safely ignore sync flags */
1203 if(syncFlags <0) { return;}
1205 err = PasteboardClear( inPasteboard );
1206 if(err != noErr) { return;}
1208 textData = CFDataCreate(kCFAllocatorDefault, (UInt8*)buffer, strlen(buffer));
1211 err = PasteboardPutItemFlavor( inPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
1213 if(textData) { CFRelease(textData);}
1219 CFRelease(textData);