This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/ghost/intern/GHOST_SystemCarbon.cpp

1241 lines
36 KiB
C++

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_SystemCarbon.cpp
* \ingroup GHOST
*/
/**
* Copyright (C) 2001 NaN Technologies B.V.
* \author Maarten Gribnau
* \date May 7, 2001
*/
#include <Carbon/Carbon.h>
#include <ApplicationServices/ApplicationServices.h>
#include "GHOST_SystemCarbon.h"
#include "GHOST_DisplayManagerCarbon.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventCursor.h"
#include "GHOST_EventWheel.h"
#ifdef WITH_INPUT_NDOF
#include "GHOST_EventNDOF.h"
#endif
#include "GHOST_TimerManager.h"
#include "GHOST_TimerTask.h"
#include "GHOST_WindowManager.h"
#include "GHOST_WindowCarbon.h"
#include "GHOST_NDOFManager.h"
#include "AssertMacros.h"
/* blender class and types events */
enum {
kEventClassBlender = 'blnd'
};
enum {
kEventBlenderNdofAxis = 1,
kEventBlenderNdofButtons = 2
};
const EventTypeSpec kEvents[] =
{
{ kEventClassAppleEvent, kEventAppleEvent },
#if 0
{ kEventClassApplication, kEventAppActivated },
{ kEventClassApplication, kEventAppDeactivated },
#endif
{ kEventClassKeyboard, kEventRawKeyDown },
{ kEventClassKeyboard, kEventRawKeyRepeat },
{ kEventClassKeyboard, kEventRawKeyUp },
{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
{ kEventClassMouse, kEventMouseDown },
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
{ kEventClassMouse, kEventMouseWheelMoved },
{ kEventClassWindow, kEventWindowClickZoomRgn }, /* for new zoom behaviour */
{ kEventClassWindow, kEventWindowZoom }, /* for new zoom behaviour */
{ kEventClassWindow, kEventWindowExpand }, /* for new zoom behaviour */
{ kEventClassWindow, kEventWindowExpandAll }, /* for new zoom behaviour */
{ kEventClassWindow, kEventWindowClose },
{ kEventClassWindow, kEventWindowActivated },
{ kEventClassWindow, kEventWindowDeactivated },
{ kEventClassWindow, kEventWindowUpdate },
{ kEventClassWindow, kEventWindowBoundsChanged },
{ kEventClassBlender, kEventBlenderNdofAxis },
{ kEventClassBlender, kEventBlenderNdofButtons }
};
static GHOST_TButtonMask convertButton(EventMouseButton button)
{
switch (button) {
case kEventMouseButtonPrimary:
return GHOST_kButtonMaskLeft;
case kEventMouseButtonSecondary:
return GHOST_kButtonMaskRight;
case kEventMouseButtonTertiary:
default:
return GHOST_kButtonMaskMiddle;
}
}
static GHOST_TKey convertKey(int rawCode)
{
/* This bit of magic converts the rawCode into a virtual
* Mac key based on the current keyboard mapping, but
* without regard to the modifiers (so we don't get 'a'
* and 'A' for example.
*/
static UInt32 dummy = 0;
Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
unsigned char vk = KeyTranslate(transData, rawCode, &dummy);
/* Map numpad based on rawcodes first, otherwise they
* look like non-numpad events.
* Added too: mapping the number keys, for french keyboards etc (ton)
*/
// printf("GHOST: vk: %d %c raw: %d\n", vk, vk, rawCode);
switch (rawCode) {
case 18: return GHOST_kKey1;
case 19: return GHOST_kKey2;
case 20: return GHOST_kKey3;
case 21: return GHOST_kKey4;
case 23: return GHOST_kKey5;
case 22: return GHOST_kKey6;
case 26: return GHOST_kKey7;
case 28: return GHOST_kKey8;
case 25: return GHOST_kKey9;
case 29: return GHOST_kKey0;
case 82: return GHOST_kKeyNumpad0;
case 83: return GHOST_kKeyNumpad1;
case 84: return GHOST_kKeyNumpad2;
case 85: return GHOST_kKeyNumpad3;
case 86: return GHOST_kKeyNumpad4;
case 87: return GHOST_kKeyNumpad5;
case 88: return GHOST_kKeyNumpad6;
case 89: return GHOST_kKeyNumpad7;
case 91: return GHOST_kKeyNumpad8;
case 92: return GHOST_kKeyNumpad9;
case 65: return GHOST_kKeyNumpadPeriod;
case 76: return GHOST_kKeyNumpadEnter;
case 69: return GHOST_kKeyNumpadPlus;
case 78: return GHOST_kKeyNumpadMinus;
case 67: return GHOST_kKeyNumpadAsterisk;
case 75: return GHOST_kKeyNumpadSlash;
}
if ((vk >= 'a') && (vk <= 'z')) {
return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
}
else if ((vk >= '0') && (vk <= '9')) {
return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
}
else if (vk == 16) {
switch (rawCode) {
case 122: return GHOST_kKeyF1;
case 120: return GHOST_kKeyF2;
case 99: return GHOST_kKeyF3;
case 118: return GHOST_kKeyF4;
case 96: return GHOST_kKeyF5;
case 97: return GHOST_kKeyF6;
case 98: return GHOST_kKeyF7;
case 100: return GHOST_kKeyF8;
case 101: return GHOST_kKeyF9;
case 109: return GHOST_kKeyF10;
case 103: return GHOST_kKeyF11;
case 111: return GHOST_kKeyF12; // Never get, is used for ejecting the CD!
}
}
else {
switch (vk) {
case kUpArrowCharCode: return GHOST_kKeyUpArrow;
case kDownArrowCharCode: return GHOST_kKeyDownArrow;
case kLeftArrowCharCode: return GHOST_kKeyLeftArrow;
case kRightArrowCharCode: return GHOST_kKeyRightArrow;
case kReturnCharCode: return GHOST_kKeyEnter;
case kBackspaceCharCode: return GHOST_kKeyBackSpace;
case kDeleteCharCode: return GHOST_kKeyDelete;
case kEscapeCharCode: return GHOST_kKeyEsc;
case kTabCharCode: return GHOST_kKeyTab;
case kSpaceCharCode: return GHOST_kKeySpace;
case kHomeCharCode: return GHOST_kKeyHome;
case kEndCharCode: return GHOST_kKeyEnd;
case kPageUpCharCode: return GHOST_kKeyUpPage;
case kPageDownCharCode: return GHOST_kKeyDownPage;
case '-': return GHOST_kKeyMinus;
case '=': return GHOST_kKeyEqual;
case ',': return GHOST_kKeyComma;
case '.': return GHOST_kKeyPeriod;
case '/': return GHOST_kKeySlash;
case ';': return GHOST_kKeySemicolon;
case '\'': return GHOST_kKeyQuote;
case '\\': return GHOST_kKeyBackslash;
case '[': return GHOST_kKeyLeftBracket;
case ']': return GHOST_kKeyRightBracket;
case '`': return GHOST_kKeyAccentGrave;
}
}
// printf("GHOST: unknown key: %d %d\n", vk, rawCode);
return GHOST_kKeyUnknown;
}
/* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
* as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
* I am not sure how international this works...
* For cross-platform convention, we'll use the Latin ascii set instead.
* As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
*
*/
static unsigned char convertRomanToLatin(unsigned char ascii)
{
if (ascii < 128) return ascii;
switch (ascii) {
case 128: return 142;
case 129: return 143;
case 130: return 128;
case 131: return 201;
case 132: return 209;
case 133: return 214;
case 134: return 220;
case 135: return 225;
case 136: return 224;
case 137: return 226;
case 138: return 228;
case 139: return 227;
case 140: return 229;
case 141: return 231;
case 142: return 233;
case 143: return 232;
case 144: return 234;
case 145: return 235;
case 146: return 237;
case 147: return 236;
case 148: return 238;
case 149: return 239;
case 150: return 241;
case 151: return 243;
case 152: return 242;
case 153: return 244;
case 154: return 246;
case 155: return 245;
case 156: return 250;
case 157: return 249;
case 158: return 251;
case 159: return 252;
case 160: return 0;
case 161: return 176;
case 162: return 162;
case 163: return 163;
case 164: return 167;
case 165: return 183;
case 166: return 182;
case 167: return 223;
case 168: return 174;
case 169: return 169;
case 170: return 174;
case 171: return 180;
case 172: return 168;
case 173: return 0;
case 174: return 198;
case 175: return 216;
case 176: return 0;
case 177: return 177;
case 178: return 0;
case 179: return 0;
case 180: return 165;
case 181: return 181;
case 182: return 0;
case 183: return 0;
case 184: return 215;
case 185: return 0;
case 186: return 0;
case 187: return 170;
case 188: return 186;
case 189: return 0;
case 190: return 230;
case 191: return 248;
case 192: return 191;
case 193: return 161;
case 194: return 172;
case 195: return 0;
case 196: return 0;
case 197: return 0;
case 198: return 0;
case 199: return 171;
case 200: return 187;
case 201: return 201;
case 202: return 0;
case 203: return 192;
case 204: return 195;
case 205: return 213;
case 206: return 0;
case 207: return 0;
case 208: return 0;
case 209: return 0;
case 210: return 0;
case 214: return 247;
case 229: return 194;
case 230: return 202;
case 231: return 193;
case 232: return 203;
case 233: return 200;
case 234: return 205;
case 235: return 206;
case 236: return 207;
case 237: return 204;
case 238: return 211;
case 239: return 212;
case 240: return 0;
case 241: return 210;
case 242: return 218;
case 243: return 219;
case 244: return 217;
case 245: return 0;
case 246: return 0;
case 247: return 0;
case 248: return 0;
case 249: return 0;
case 250: return 0;
default: return 0;
}
}
/***/
GHOST_SystemCarbon::GHOST_SystemCarbon() :
m_modifierMask(0)
{
m_displayManager = new GHOST_DisplayManagerCarbon();
GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
m_displayManager->initialize();
UnsignedWide micros;
::Microseconds(&micros);
m_start_time = UnsignedWideToUInt64(micros) / 1000;
m_ignoreWindowSizedMessages = false;
}
GHOST_SystemCarbon::~GHOST_SystemCarbon()
{
}
GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
{
UnsignedWide micros;
::Microseconds(&micros);
UInt64 millis;
millis = UnsignedWideToUInt64(micros);
return (millis / 1000) - m_start_time;
}
GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
{
// We do not support multiple monitors at the moment
return 1;
}
void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
{
BitMap screenBits;
Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
width = bnds.right - bnds.left;
height = bnds.bottom - bnds.top;
}
void GHOST_SystemCarbon::getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
{
/* TODO */
getMainDisplayDimensions(width, height);
}
GHOST_IWindow *GHOST_SystemCarbon::createWindow(
const STR_String& title,
GHOST_TInt32 left,
GHOST_TInt32 top,
GHOST_TUns32 width,
GHOST_TUns32 height,
GHOST_TWindowState state,
GHOST_TDrawingContextType type,
bool stereoVisual,
const bool exclusive,
const GHOST_TUns16 numOfAASamples,
const GHOST_TEmbedderWindowID parentWindow)
{
GHOST_IWindow *window = 0;
window = new GHOST_WindowCarbon(title, left, top, width, height, state, type);
if (window) {
if (window->getValid()) {
// Store the pointer to the window
GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
}
else {
GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
delete window;
window = 0;
}
}
else {
GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
}
return window;
}
GHOST_TSuccess GHOST_SystemCarbon::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window, const bool stereoVisual)
{
GHOST_TSuccess success = GHOST_kFailure;
// need yo make this Carbon all on 10.5 for fullscreen to work correctly
CGCaptureAllDisplays();
success = GHOST_System::beginFullScreen(setting, window, stereoVisual);
if (success != GHOST_kSuccess) {
// fullscreen failed for other reasons, release
CGReleaseAllDisplays();
}
return success;
}
GHOST_TSuccess GHOST_SystemCarbon::endFullScreen(void)
{
CGReleaseAllDisplays();
return GHOST_System::endFullScreen();
}
/* this is an old style low level event queue.
* As we want to handle our own timers, this is ok.
* the full screen hack should be removed */
bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
{
bool anyProcessed = false;
EventRef event;
// SetMouseCoalescingEnabled(false, NULL);
do {
GHOST_TimerManager *timerMgr = getTimerManager();
if (waitForEvent) {
GHOST_TUns64 next = timerMgr->nextFireTime();
double timeOut;
if (next == GHOST_kFireTimeNever) {
timeOut = kEventDurationForever;
}
else {
timeOut = (double)(next - getMilliSeconds()) / 1000.0;
if (timeOut < 0.0)
timeOut = 0.0;
}
::ReceiveNextEvent(0, NULL, timeOut, false, &event);
}
if (timerMgr->fireTimers(getMilliSeconds())) {
anyProcessed = true;
}
if (getFullScreen()) {
// Check if the full-screen window is dirty
GHOST_IWindow *window = m_windowManager->getFullScreenWindow();
if (((GHOST_WindowCarbon *)window)->getFullScreenDirty()) {
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
anyProcessed = true;
}
}
/* end loop when no more events available */
while (::ReceiveNextEvent(0, NULL, 0, true, &event) == noErr) {
OSStatus status = ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
if (status == noErr) {
anyProcessed = true;
}
else {
UInt32 i = ::GetEventClass(event);
/* Ignore 'cgs ' class, no documentation on what they
* are, but we get a lot of them
*/
if (i != 'cgs ') {
if (i != 'tblt') { // tablet event. we use the one packaged in the mouse event
; //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
}
}
}
::ReleaseEvent(event);
}
} while (waitForEvent && !anyProcessed);
return anyProcessed;
}
GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
{
Point mouseLoc;
// Get the position of the mouse in the active port
::GetGlobalMouse(&mouseLoc);
// Convert the coordinates to screen coordinates
x = (GHOST_TInt32)mouseLoc.h;
y = (GHOST_TInt32)mouseLoc.v;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
{
float xf = (float)x, yf = (float)y;
CGAssociateMouseAndMouseCursorPosition(false);
CGSetLocalEventsSuppressionInterval(0);
CGWarpMouseCursorPosition(CGPointMake(xf, yf));
CGAssociateMouseAndMouseCursorPosition(true);
//this doesn't work properly, see game engine mouse-look scripts
// CGWarpMouseCursorPosition(CGPointMake(xf, yf));
// this call below sends event, but empties other events (like shift)
// CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
{
UInt32 modifiers = ::GetCurrentKeyModifiers();
keys.set(GHOST_kModifierKeyOS, (modifiers & cmdKey) ? true : false);
keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
return GHOST_kSuccess;
}
/* XXX, incorrect for multibutton mice */
GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
{
Boolean theOnlyButtonIsDown = ::Button();
buttons.clear();
buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
return GHOST_kSuccess;
}
#define FIRSTFILEBUFLG 512
static bool g_hasFirstFile = false;
static char g_firstFileBuf[512];
extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
{
if (g_hasFirstFile) {
strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
buf[FIRSTFILEBUFLG - 1] = '\0';
return 1;
}
else {
return 0;
}
}
OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
{
//GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
return noErr;
}
OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
{
//GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
AEDescList docs;
SInt32 ndocs;
OSErr err;
err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
if (err != noErr) return err;
err = AECountItems(&docs, &ndocs);
if (err == noErr) {
int i;
for (i = 0; i < ndocs; i++) {
FSSpec fss;
AEKeyword kwd;
DescType actType;
Size actSize;
err = AEGetNthPtr(&docs, i + 1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
if (err != noErr)
break;
if (i == 0) {
FSRef fsref;
if (FSpMakeFSRef(&fss, &fsref) != noErr)
break;
if (FSRefMakePath(&fsref, (UInt8 *) g_firstFileBuf, sizeof(g_firstFileBuf)) != noErr)
break;
g_hasFirstFile = true;
}
}
}
AEDisposeDesc(&docs);
return err;
}
OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
{
//GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
return noErr;
}
OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
{
GHOST_SystemCarbon *sys = (GHOST_SystemCarbon *) refCon;
sys->pushEvent(new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
return noErr;
}
GHOST_TSuccess GHOST_SystemCarbon::init()
{
GHOST_TSuccess success = GHOST_System::init();
if (success) {
/*
* Initialize the cursor to the standard arrow shape (so that we can change it later on).
* This initializes the cursor's visibility counter to 0.
*/
::InitCursor();
MenuRef windMenu;
::CreateStandardWindowMenu(0, &windMenu);
::InsertMenu(windMenu, 0);
::DrawMenuBar();
::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
}
return success;
}
GHOST_TSuccess GHOST_SystemCarbon::exit()
{
return GHOST_System::exit();
}
OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
{
WindowRef windowRef;
GHOST_WindowCarbon *window;
OSStatus err = eventNotHandledErr;
// Check if the event was send to a GHOST window
::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
window = (GHOST_WindowCarbon *) ::GetWRefCon(windowRef);
if (!validWindow(window)) {
return err;
}
//if (!getFullScreen()) {
err = noErr;
switch (::GetEventKind(event))
{
case kEventWindowClose:
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
break;
case kEventWindowActivated:
m_windowManager->setActiveWindow(window);
window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
break;
case kEventWindowDeactivated:
m_windowManager->setWindowInactive(window);
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
break;
case kEventWindowUpdate:
//if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
break;
case kEventWindowBoundsChanged:
if (!m_ignoreWindowSizedMessages)
{
window->updateDrawingContext();
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
}
break;
default:
err = eventNotHandledErr;
break;
}
// }
//else {
//window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
//GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
//::RemoveEventFromQueue(::GetMainEventQueue(), event);
//}
return err;
}
OSStatus GHOST_SystemCarbon::handleTabletEvent(EventRef event)
{
GHOST_IWindow *window = m_windowManager->getActiveWindow();
TabletPointRec tabletPointRecord;
TabletProximityRec tabletProximityRecord;
UInt32 anInt32;
GHOST_TabletData& ct = ((GHOST_WindowCarbon *)window)->GetCarbonTabletData();
OSStatus err = eventNotHandledErr;
ct.Pressure = 0;
ct.Xtilt = 0;
ct.Ytilt = 0;
// is there an embedded tablet event inside this mouse event?
if (noErr == GetEventParameter(event, kEventParamTabletEventType, typeUInt32, NULL, sizeof(UInt32), NULL, (void *)&anInt32))
{
// yes there is one!
// Embedded tablet events can either be a proximity or pointer event.
if (anInt32 == kEventTabletPoint)
{
//GHOST_PRINT("Embedded pointer event!\n");
// Extract the tablet Pointer Event. If there is no Tablet Pointer data
// in this event, then this call will return an error. Just ignore the
// error and go on. This can occur when a proximity event is embedded in
// a mouse event and you did not check the mouse event to see which type
// of tablet event was embedded.
if (noErr == GetEventParameter(event, kEventParamTabletPointRec,
typeTabletPointRec, NULL,
sizeof(TabletPointRec),
NULL, (void *)&tabletPointRecord))
{
ct.Pressure = tabletPointRecord.pressure / 65535.0f;
ct.Xtilt = tabletPointRecord.tiltX / 32767.0f; /* can be positive or negative */
ct.Ytilt = tabletPointRecord.tiltY / 32767.0f; /* can be positive or negative */
}
}
else {
//GHOST_PRINT("Embedded proximity event\n");
// Extract the Tablet Proximity record from the event.
if (noErr == GetEventParameter(event, kEventParamTabletProximityRec,
typeTabletProximityRec, NULL,
sizeof(TabletProximityRec),
NULL, (void *)&tabletProximityRecord))
{
if (tabletProximityRecord.enterProximity) {
//pointer is entering tablet area proximity
switch (tabletProximityRecord.pointerType)
{
case 1: /* stylus */
ct.Active = GHOST_kTabletModeStylus;
break;
case 2: /* puck, not supported so far */
ct.Active = GHOST_kTabletModeNone;
break;
case 3: /* eraser */
ct.Active = GHOST_kTabletModeEraser;
break;
default:
ct.Active = GHOST_kTabletModeNone;
break;
}
}
else {
// pointer is leaving - return to mouse
ct.Active = GHOST_kTabletModeNone;
}
}
}
err = noErr;
}
return err;
}
OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
{
OSStatus err = eventNotHandledErr;
GHOST_IWindow *window = m_windowManager->getActiveWindow();
UInt32 kind = ::GetEventKind(event);
switch (kind)
{
case kEventMouseDown:
case kEventMouseUp:
// Handle Mac application responsibilities
if ((kind == kEventMouseDown) && handleMouseDown(event)) {
err = noErr;
}
else {
GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
EventMouseButton button;
/* Window still gets mouse up after command-H */
if (m_windowManager->getActiveWindow()) {
// handle any tablet events that may have come with the mouse event (optional)
handleTabletEvent(event);
::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
err = noErr;
}
}
break;
case kEventMouseMoved:
case kEventMouseDragged: {
Point mousePos;
if (window) {
//handle any tablet events that may have come with the mouse event (optional)
handleTabletEvent(event);
::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
err = noErr;
}
break;
}
case kEventMouseWheelMoved:
{
OSStatus status;
//UInt32 modifiers;
EventMouseWheelAxis axis;
SInt32 delta;
//status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
//GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
if (axis == kEventMouseWheelAxisY)
{
status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
/*
* Limit mouse wheel delta to plus and minus one.
*/
delta = delta > 0 ? 1 : -1;
pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
err = noErr;
}
}
break;
}
return err;
}
OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
{
OSStatus err = eventNotHandledErr;
GHOST_IWindow *window = m_windowManager->getActiveWindow();
UInt32 kind = ::GetEventKind(event);
UInt32 modifiers;
UInt32 rawCode;
GHOST_TKey key;
unsigned char ascii;
/* Can happen, very rarely - seems to only be when command-H makes
* the window go away and we still get an HKey up.
*/
if (!window) {
//::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
//key = convertKey(rawCode);
return err;
}
err = noErr;
switch (kind) {
case kEventRawKeyDown:
case kEventRawKeyRepeat:
case kEventRawKeyUp:
::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
key = convertKey(rawCode);
ascii = convertRomanToLatin(ascii);
// if (key!=GHOST_kKeyUnknown) {
GHOST_TEventType type;
if (kind == kEventRawKeyDown) {
type = GHOST_kEventKeyDown;
}
else if (kind == kEventRawKeyRepeat) {
type = GHOST_kEventKeyDown; /* XXX, fixme */
}
else {
type = GHOST_kEventKeyUp;
}
pushEvent(new GHOST_EventKey(getMilliSeconds(), type, window, key, ascii, NULL) );
// }
break;
case kEventRawKeyModifiersChanged:
/* ugh */
::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
pushEvent(new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
}
if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
pushEvent(new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
}
if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
pushEvent(new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
}
if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
pushEvent(new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, GHOST_kKeyOS) );
}
m_modifierMask = modifiers;
break;
default:
err = eventNotHandledErr;
break;
}
return err;
}
bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
{
WindowPtr window;
short part;
BitMap screenBits;
bool handled = true;
GHOST_WindowCarbon *ghostWindow;
Point mousePos = {0, 0};
::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
part = ::FindWindow(mousePos, &window);
ghostWindow = (GHOST_WindowCarbon *) ::GetWRefCon(window);
switch (part) {
case inMenuBar:
handleMenuCommand(::MenuSelect(mousePos));
break;
case inDrag:
/*
* The DragWindow() routine creates a lot of kEventWindowBoundsChanged
* events. By setting m_ignoreWindowSizedMessages these are suppressed.
* \see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
*/
/* even worse: scale window also generates a load of events, and nothing
* is handled (read: client's event proc called) until you release mouse (ton) */
GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
m_ignoreWindowSizedMessages = true;
::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
m_ignoreWindowSizedMessages = false;
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
break;
case inContent:
if (window != ::FrontWindow()) {
::SelectWindow(window);
/*
* We add a mouse down event on the newly actived window
*/
//GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
EventMouseButton button;
::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
}
else {
handled = false;
}
break;
case inGoAway:
GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
if (::TrackGoAway(window, mousePos))
{
// todo: add option-close, because it's in the HIG
// if (event.modifiers & optionKey) {
// Close the clean documents, others will be confirmed one by one.
//}
// else {
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
//}
}
break;
case inGrow:
GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
::ResizeWindow(window, mousePos, NULL, NULL);
break;
case inZoomIn:
case inZoomOut:
GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
if (::TrackBox(window, mousePos, part)) {
int macState;
macState = ghostWindow->getMac_windowState();
if (macState == 0)
::ZoomWindow(window, part, true);
else
if (macState == 2) { // always ok
::ZoomWindow(window, part, true);
ghostWindow->setMac_windowState(1);
}
else { // need to force size again
// GHOST_TUns32 scr_x,scr_y; /*unused*/
Rect outAvailableRect;
ghostWindow->setMac_windowState(2);
::GetAvailableWindowPositioningBounds(GetMainDevice(), &outAvailableRect);
//this->getMainDisplayDimensions(scr_x,scr_y);
::SizeWindow(window, outAvailableRect.right - outAvailableRect.left, outAvailableRect.bottom - outAvailableRect.top - 1, false);
::MoveWindow(window, outAvailableRect.left, outAvailableRect.top, true);
}
}
break;
default:
handled = false;
break;
}
return handled;
}
bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
{
short menuID;
short menuItem;
UInt32 command;
bool handled;
OSErr err;
menuID = HiWord(menuResult);
menuItem = LoWord(menuResult);
err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
handled = false;
if (err || command == 0) {
}
else {
switch (command) {
}
}
::HiliteMenu(0);
return handled;
}
OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void *userData)
{
GHOST_SystemCarbon *sys = (GHOST_SystemCarbon *) userData;
OSStatus err = eventNotHandledErr;
GHOST_IWindow *window;
#ifdef WITH_INPUT_NDOF
GHOST_TEventNDOFData data;
#endif
UInt32 kind;
switch (::GetEventClass(event))
{
case kEventClassAppleEvent:
EventRecord eventrec;
if (ConvertEventRefToEventRecord(event, &eventrec)) {
err = AEProcessAppleEvent(&eventrec);
}
break;
case kEventClassMouse:
err = sys->handleMouseEvent(event);
break;
case kEventClassWindow:
err = sys->handleWindowEvent(event);
break;
case kEventClassKeyboard:
err = sys->handleKeyEvent(event);
break;
case kEventClassBlender:
#ifdef WITH_INPUT_NDOF
window = sys->m_windowManager->getActiveWindow();
sys->m_ndofManager->GHOST_NDOFGetDatas(data);
kind = ::GetEventKind(event);
switch (kind)
{
case 1:
sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFMotion, window, data));
// printf("motion\n");
break;
case 2:
sys->m_eventManager->pushEvent(new GHOST_EventNDOF(sys->getMilliSeconds(), GHOST_kEventNDOFButton, window, data));
// printf("button\n");
break;
}
#endif
err = noErr;
break;
default:
;
break;
}
return err;
}
GHOST_TUns8 *GHOST_SystemCarbon::getClipboard(bool selection) const
{
PasteboardRef inPasteboard;
PasteboardItemID itemID;
CFDataRef flavorData;
OSStatus err = noErr;
GHOST_TUns8 *temp_buff;
CFRange range;
OSStatus syncFlags;
err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
if (err != noErr) { return NULL; }
syncFlags = PasteboardSynchronize(inPasteboard);
/* as we always get in a new string, we can safely ignore sync flags if not an error*/
if (syncFlags < 0) { return NULL; }
err = PasteboardGetItemIdentifier(inPasteboard, 1, &itemID);
if (err != noErr) { return NULL; }
err = PasteboardCopyItemFlavorData(inPasteboard, itemID, CFSTR("public.utf8-plain-text"), &flavorData);
if (err != noErr) { return NULL; }
range = CFRangeMake(0, CFDataGetLength(flavorData));
temp_buff = (GHOST_TUns8 *) malloc(range.length + 1);
CFDataGetBytes(flavorData, range, (UInt8 *)temp_buff);
temp_buff[range.length] = '\0';
if (temp_buff) {
return temp_buff;
}
else {
return NULL;
}
}
void GHOST_SystemCarbon::putClipboard(GHOST_TInt8 *buffer, bool selection) const
{
if (selection) {return; } // for copying the selection, used on X11
PasteboardRef inPasteboard;
CFDataRef textData = NULL;
OSStatus err = noErr; /*For error checking*/
OSStatus syncFlags;
err = PasteboardCreate(kPasteboardClipboard, &inPasteboard);
if (err != noErr) { return; }
syncFlags = PasteboardSynchronize(inPasteboard);
/* as we always put in a new string, we can safely ignore sync flags */
if (syncFlags < 0) { return; }
err = PasteboardClear(inPasteboard);
if (err != noErr) { return; }
textData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)buffer, strlen(buffer));
if (textData) {
err = PasteboardPutItemFlavor(inPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
if (err != noErr) {
if (textData) { CFRelease(textData); }
return;
}
}
if (textData) {
CFRelease(textData);
}
}