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 *****
32 * Copyright (C) 2001 NaN Technologies B.V.
33 * @author Maarten Gribnau
37 #include <Carbon/Carbon.h>
38 #include <ApplicationServices/ApplicationServices.h>
39 #include "GHOST_SystemCarbon.h"
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"
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"
55 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
57 /* blender class and types events */
59 kEventClassBlender = 'blnd'
63 kEventBlenderNdofAxis = 1,
64 kEventBlenderNdofButtons = 2
67 const EventTypeSpec kEvents[] =
69 { kEventClassAppleEvent, kEventAppleEvent },
71 { kEventClassApplication, kEventAppActivated },
72 { kEventClassApplication, kEventAppDeactivated },
74 { kEventClassKeyboard, kEventRawKeyDown },
75 { kEventClassKeyboard, kEventRawKeyRepeat },
76 { kEventClassKeyboard, kEventRawKeyUp },
77 { kEventClassKeyboard, kEventRawKeyModifiersChanged },
79 { kEventClassMouse, kEventMouseDown },
80 { kEventClassMouse, kEventMouseUp },
81 { kEventClassMouse, kEventMouseMoved },
82 { kEventClassMouse, kEventMouseDragged },
83 { kEventClassMouse, kEventMouseWheelMoved },
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 */
90 { kEventClassWindow, kEventWindowClose },
91 { kEventClassWindow, kEventWindowActivated },
92 { kEventClassWindow, kEventWindowDeactivated },
93 { kEventClassWindow, kEventWindowUpdate },
94 { kEventClassWindow, kEventWindowBoundsChanged },
96 { kEventClassBlender, kEventBlenderNdofAxis },
97 { kEventClassBlender, kEventBlenderNdofButtons }
105 static GHOST_TButtonMask convertButton(EventMouseButton button)
108 case kEventMouseButtonPrimary:
109 return GHOST_kButtonMaskLeft;
110 case kEventMouseButtonSecondary:
111 return GHOST_kButtonMaskRight;
112 case kEventMouseButtonTertiary:
114 return GHOST_kButtonMaskMiddle;
118 static GHOST_TKey convertKey(int rawCode)
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.
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)
132 // printf("GHOST: vk: %d %c raw: %d\n", vk, vk, 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;
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;
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);
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!
185 case kUpArrowCharCode: return GHOST_kKeyUpArrow;
186 case kDownArrowCharCode: return GHOST_kKeyDownArrow;
187 case kLeftArrowCharCode: return GHOST_kKeyLeftArrow;
188 case kRightArrowCharCode: return GHOST_kKeyRightArrow;
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;
197 case kHomeCharCode: return GHOST_kKeyHome;
198 case kEndCharCode: return GHOST_kKeyEnd;
199 case kPageUpCharCode: return GHOST_kKeyUpPage;
200 case kPageDownCharCode: return GHOST_kKeyDownPage;
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;
216 // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
218 return GHOST_kKeyUnknown;
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
228 static unsigned char convertRomanToLatin(unsigned char ascii)
231 if(ascii<128) return 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;
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;
280 case 174: return 198;
281 case 175: return 216;
283 case 177: return 177;
286 case 180: return 165;
287 case 181: return 181;
290 case 184: return 215;
293 case 187: return 170;
294 case 188: return 186;
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;
305 case 199: return 171;
306 case 200: return 187;
307 case 201: return 201;
309 case 203: return 192;
310 case 204: return 195;
311 case 205: return 213;
318 case 214: return 247;
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;
332 case 241: return 210;
333 case 242: return 218;
334 case 243: return 219;
335 case 244: return 217;
352 GHOST_SystemCarbon::GHOST_SystemCarbon() :
355 m_displayManager = new GHOST_DisplayManagerCarbon ();
356 GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
357 m_displayManager->initialize();
360 ::Microseconds(µs);
361 m_start_time = UnsignedWideToUInt64(micros)/1000;
362 m_ignoreWindowSizedMessages = false;
365 GHOST_SystemCarbon::~GHOST_SystemCarbon()
370 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
373 ::Microseconds(µs);
375 millis = UnsignedWideToUInt64(micros);
376 return (millis / 1000) - m_start_time;
380 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
382 // We do not support multiple monitors at the moment
387 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
390 Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
391 width = bnds.right - bnds.left;
392 height = bnds.bottom - bnds.top;
396 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
397 const STR_String& title,
402 GHOST_TWindowState state,
403 GHOST_TDrawingContextType type,
405 const GHOST_TUns16 numOfAASamples,
406 const GHOST_TEmbedderWindowID parentWindow
409 GHOST_IWindow* window = 0;
411 window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
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));
422 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
428 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
433 GHOST_TSuccess GHOST_SystemCarbon::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
435 GHOST_TSuccess success = GHOST_kFailure;
437 // need yo make this Carbon all on 10.5 for fullscreen to work correctly
438 CGCaptureAllDisplays();
440 success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
442 if( success != GHOST_kSuccess ) {
443 // fullscreen failed for other reasons, release
444 CGReleaseAllDisplays();
450 GHOST_TSuccess GHOST_SystemCarbon::endFullScreen(void)
452 CGReleaseAllDisplays();
453 return GHOST_System::endFullScreen();
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)
461 bool anyProcessed = false;
464 // SetMouseCoalescingEnabled(false, NULL);
467 GHOST_TimerManager* timerMgr = getTimerManager();
470 GHOST_TUns64 next = timerMgr->nextFireTime();
473 if (next == GHOST_kFireTimeNever) {
474 timeOut = kEventDurationForever;
476 timeOut = (double)(next - getMilliSeconds())/1000.0;
481 ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
484 if (timerMgr->fireTimers(getMilliSeconds())) {
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) );
497 /* end loop when no more events available */
498 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
499 OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
503 UInt32 i= ::GetEventClass(event);
505 /* Ignore 'cgs ' class, no documentation on what they
506 * are, but we get a lot of them
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));
514 ::ReleaseEvent(event);
516 } while (waitForEvent && !anyProcessed);
522 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
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;
534 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
536 float xf=(float)x, yf=(float)y;
538 CGAssociateMouseAndMouseCursorPosition(false);
539 CGSetLocalEventsSuppressionInterval(0);
540 CGWarpMouseCursorPosition(CGPointMake(xf, yf));
541 CGAssociateMouseAndMouseCursorPosition(true);
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);
548 return GHOST_kSuccess;
552 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
554 UInt32 modifiers = ::GetCurrentKeyModifiers();
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);
561 return GHOST_kSuccess;
564 /* XXX, incorrect for multibutton mice */
565 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
567 Boolean theOnlyButtonIsDown = ::Button();
569 buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
570 return GHOST_kSuccess;
573 #define FIRSTFILEBUFLG 512
574 static bool g_hasFirstFile = false;
575 static char g_firstFileBuf[512];
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';
587 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
589 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
594 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
596 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
601 err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
602 if (err != noErr) return err;
604 err = AECountItems(&docs, &ndocs);
608 for (i=0; i<ndocs; i++) {
614 err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
621 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
623 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
626 g_hasFirstFile = true;
631 AEDisposeDesc(&docs);
636 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
638 //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
643 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
645 GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
647 sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
653 GHOST_TSuccess GHOST_SystemCarbon::init()
656 GHOST_TSuccess success = GHOST_System::init();
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.
665 ::CreateStandardWindowMenu(0, &windMenu);
666 ::InsertMenu(windMenu, 0);
669 ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
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);
681 GHOST_TSuccess GHOST_SystemCarbon::exit()
683 return GHOST_System::exit();
687 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
690 GHOST_WindowCarbon *window;
691 OSStatus err = eventNotHandledErr;
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)) {
700 //if (!getFullScreen()) {
702 switch(::GetEventKind(event))
704 case kEventWindowClose:
705 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
707 case kEventWindowActivated:
708 m_windowManager->setActiveWindow(window);
709 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
710 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
712 case kEventWindowDeactivated:
713 m_windowManager->setWindowInactive(window);
714 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
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) );
720 case kEventWindowBoundsChanged:
721 if (!m_ignoreWindowSizedMessages)
723 window->updateDrawingContext();
724 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
728 err = eventNotHandledErr;
733 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
734 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
735 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
741 OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
743 GHOST_IWindow* window = m_windowManager->getActiveWindow();
744 TabletPointRec tabletPointRecord;
745 TabletProximityRec tabletProximityRecord;
747 GHOST_TabletData& ct=((GHOST_WindowCarbon*)window)->GetCarbonTabletData();
748 OSStatus err = eventNotHandledErr;
754 // is there an embedded tablet event inside this mouse event?
755 if(noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
758 // Embedded tablet events can either be a proximity or pointer event.
759 if(anInt32 == kEventTabletPoint)
761 //GHOST_PRINT("Embedded pointer event!\n");
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))
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 */
778 //GHOST_PRINT("Embedded proximity event\n");
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))
786 if (tabletProximityRecord.enterProximity) {
787 //pointer is entering tablet area proximity
789 switch(tabletProximityRecord.pointerType)
792 ct.Active = GHOST_kTabletModeStylus;
794 case 2: /* puck, not supported so far */
795 ct.Active = GHOST_kTabletModeNone;
798 ct.Active = GHOST_kTabletModeEraser;
801 ct.Active = GHOST_kTabletModeNone;
805 // pointer is leaving - return to mouse
806 ct.Active = GHOST_kTabletModeNone;
815 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
817 OSStatus err = eventNotHandledErr;
818 GHOST_IWindow* window = m_windowManager->getActiveWindow();
819 UInt32 kind = ::GetEventKind(event);
823 case kEventMouseDown:
825 // Handle Mac application responsibilities
826 if ((kind == kEventMouseDown) && handleMouseDown(event)) {
830 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
831 EventMouseButton button;
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);
838 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
839 pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
845 case kEventMouseMoved:
846 case kEventMouseDragged: {
850 //handle any tablet events that may have come with the mouse event (optional)
851 handleTabletEvent(event);
853 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
854 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
859 case kEventMouseWheelMoved:
863 EventMouseWheelAxis axis;
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)
871 status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
872 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
874 * Limit mouse wheel delta to plus and minus one.
876 delta = delta > 0 ? 1 : -1;
877 pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
888 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
890 OSStatus err = eventNotHandledErr;
891 GHOST_IWindow* window = m_windowManager->getActiveWindow();
892 UInt32 kind = ::GetEventKind(event);
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.
902 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
903 //key = convertKey(rawCode);
909 case kEventRawKeyDown:
910 case kEventRawKeyRepeat:
912 ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
913 ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
915 key = convertKey(rawCode);
916 ascii= convertRomanToLatin(ascii);
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 */
925 type = GHOST_kEventKeyUp;
927 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
931 case kEventRawKeyModifiersChanged:
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) );
937 if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
938 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
940 if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
941 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
943 if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
944 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
947 m_modifierMask = modifiers;
951 err = eventNotHandledErr;
959 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
965 GHOST_WindowCarbon* ghostWindow;
966 Point mousePos = {0 , 0};
968 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
970 part = ::FindWindow(mousePos, &window);
971 ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
975 handleMenuCommand(::MenuSelect(mousePos));
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)
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) */
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;
992 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
997 if (window != ::FrontWindow()) {
998 ::SelectWindow(window);
1000 * We add a mouse down event on the newly actived window
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)));
1012 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1013 if (::TrackGoAway(window, mousePos))
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.
1020 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
1026 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1027 ::ResizeWindow(window, mousePos, NULL, NULL);
1032 GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1033 if (::TrackBox(window, mousePos, part)) {
1036 macState = ghostWindow->getMac_windowState();
1038 ::ZoomWindow(window, part, true);
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;
1047 ghostWindow->setMac_windowState(2);
1048 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
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);
1067 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1075 menuID = HiWord(menuResult);
1076 menuItem = LoWord(menuResult);
1078 err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1082 if (err || command == 0) {
1094 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
1096 GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
1097 OSStatus err = eventNotHandledErr;
1098 GHOST_IWindow* window;
1099 GHOST_TEventNDOFData data;
1102 switch (::GetEventClass(event))
1104 case kEventClassAppleEvent:
1105 EventRecord eventrec;
1106 if (ConvertEventRefToEventRecord(event, &eventrec)) {
1107 err = AEProcessAppleEvent(&eventrec);
1110 case kEventClassMouse:
1111 err = sys->handleMouseEvent(event);
1113 case kEventClassWindow:
1114 err = sys->handleWindowEvent(event);
1116 case kEventClassKeyboard:
1117 err = sys->handleKeyEvent(event);
1119 case kEventClassBlender :
1120 window = sys->m_windowManager->getActiveWindow();
1121 sys->m_ndofManager->GHOST_NDOFGetDatas(data);
1122 kind = ::GetEventKind(event);
1127 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
1128 // printf("motion\n");
1131 sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
1132 // printf("button\n");
1145 GHOST_TUns8* GHOST_SystemCarbon::getClipboard(bool selection) const
1147 PasteboardRef inPasteboard;
1148 PasteboardItemID itemID;
1149 CFDataRef flavorData;
1150 OSStatus err = noErr;
1151 GHOST_TUns8 * temp_buff;
1155 err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1156 if(err != noErr) { return NULL;}
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;}
1163 err = PasteboardGetItemIdentifier( inPasteboard, 1, &itemID );
1164 if(err != noErr) { return NULL;}
1166 err = PasteboardCopyItemFlavorData( inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
1167 if(err != noErr) { return NULL;}
1169 range = CFRangeMake(0, CFDataGetLength(flavorData));
1171 temp_buff = (GHOST_TUns8*) malloc(range.length+1);
1173 CFDataGetBytes(flavorData, range, (UInt8*)temp_buff);
1175 temp_buff[range.length] = '\0';
1184 void GHOST_SystemCarbon::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1186 if(selection) {return;} // for copying the selection, used on X11
1188 PasteboardRef inPasteboard;
1189 CFDataRef textData = NULL;
1190 OSStatus err = noErr; /*For error checking*/
1193 err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
1194 if(err != noErr) { return;}
1196 syncFlags = PasteboardSynchronize( inPasteboard );
1197 /* as we always put in a new string, we can safely ignore sync flags */
1198 if(syncFlags <0) { return;}
1200 err = PasteboardClear( inPasteboard );
1201 if(err != noErr) { return;}
1203 textData = CFDataCreate(kCFAllocatorDefault, (UInt8*)buffer, strlen(buffer));
1206 err = PasteboardPutItemFlavor( inPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
1208 if(textData) { CFRelease(textData);}
1214 CFRelease(textData);