1241 lines
36 KiB
C++
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(µs);
|
|
m_start_time = UnsignedWideToUInt64(micros) / 1000;
|
|
m_ignoreWindowSizedMessages = false;
|
|
}
|
|
|
|
GHOST_SystemCarbon::~GHOST_SystemCarbon()
|
|
{
|
|
}
|
|
|
|
|
|
GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
|
|
{
|
|
UnsignedWide micros;
|
|
::Microseconds(µs);
|
|
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);
|
|
}
|
|
}
|