/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include <private/qt_mac_p.h> | |
#include "qcoreapplication.h" | |
#include <qlibrary.h> | |
QT_BEGIN_NAMESPACE | |
QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 }; | |
#if defined(Q_WS_MAC32) && !defined(QT_MAC_USE_COCOA) | |
#define RGN_CACHE_SIZE 200 | |
#ifdef RGN_CACHE_SIZE | |
static bool rgncache_init = false; | |
static int rgncache_used; | |
static RgnHandle rgncache[RGN_CACHE_SIZE]; | |
static void qt_mac_cleanup_rgncache() | |
{ | |
rgncache_init = false; | |
for(int i = 0; i < RGN_CACHE_SIZE; ++i) { | |
if(rgncache[i]) { | |
--rgncache_used; | |
DisposeRgn(rgncache[i]); | |
rgncache[i] = 0; | |
} | |
} | |
} | |
#endif | |
Q_GUI_EXPORT RgnHandle qt_mac_get_rgn() | |
{ | |
#ifdef RGN_CACHE_SIZE | |
if(!rgncache_init) { | |
rgncache_used = 0; | |
rgncache_init = true; | |
for(int i = 0; i < RGN_CACHE_SIZE; ++i) | |
rgncache[i] = 0; | |
qAddPostRoutine(qt_mac_cleanup_rgncache); | |
} else if(rgncache_used) { | |
for(int i = 0; i < RGN_CACHE_SIZE; ++i) { | |
if(rgncache[i]) { | |
RgnHandle ret = rgncache[i]; | |
SetEmptyRgn(ret); | |
rgncache[i] = 0; | |
--rgncache_used; | |
return ret; | |
} | |
} | |
} | |
#endif | |
return NewRgn(); | |
} | |
Q_GUI_EXPORT void qt_mac_dispose_rgn(RgnHandle r) | |
{ | |
#ifdef RGN_CACHE_SIZE | |
if(rgncache_init && rgncache_used < RGN_CACHE_SIZE) { | |
for(int i = 0; i < RGN_CACHE_SIZE; ++i) { | |
if(!rgncache[i]) { | |
++rgncache_used; | |
rgncache[i] = r; | |
return; | |
} | |
} | |
} | |
#endif | |
DisposeRgn(r); | |
} | |
static OSStatus qt_mac_get_rgn_rect(UInt16 msg, RgnHandle, const Rect *rect, void *reg) | |
{ | |
if(msg == kQDRegionToRectsMsgParse) { | |
QRect rct(rect->left, rect->top, (rect->right - rect->left), (rect->bottom - rect->top)); | |
if(!rct.isEmpty()) | |
*((QRegion *)reg) += rct; | |
} | |
return noErr; | |
} | |
Q_GUI_EXPORT QRegion qt_mac_convert_mac_region(RgnHandle rgn) | |
{ | |
return QRegion::fromQDRgn(rgn); | |
} | |
QRegion QRegion::fromQDRgn(RgnHandle rgn) | |
{ | |
QRegion ret; | |
ret.detach(); | |
OSStatus oss = QDRegionToRects(rgn, kQDParseRegionFromTopLeft, qt_mac_get_rgn_rect, (void *)&ret); | |
if(oss != noErr) | |
return QRegion(); | |
return ret; | |
} | |
/*! | |
\internal | |
Create's a RegionHandle, it's the caller's responsibility to release. | |
*/ | |
RgnHandle QRegion::toQDRgn() const | |
{ | |
RgnHandle rgnHandle = qt_mac_get_rgn(); | |
if(d->qt_rgn && d->qt_rgn->numRects) { | |
RgnHandle tmp_rgn = qt_mac_get_rgn(); | |
int n = d->qt_rgn->numRects; | |
const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData(); | |
while (n--) { | |
SetRectRgn(tmp_rgn, | |
qMax(SHRT_MIN, qt_r->x()), | |
qMax(SHRT_MIN, qt_r->y()), | |
qMin(SHRT_MAX, qt_r->right() + 1), | |
qMin(SHRT_MAX, qt_r->bottom() + 1)); | |
UnionRgn(rgnHandle, tmp_rgn, rgnHandle); | |
++qt_r; | |
} | |
qt_mac_dispose_rgn(tmp_rgn); | |
} | |
return rgnHandle; | |
} | |
/*! | |
\internal | |
Create's a RegionHandle, it's the caller's responsibility to release. | |
Returns 0 if the QRegion overflows. | |
*/ | |
RgnHandle QRegion::toQDRgnForUpdate_sys() const | |
{ | |
RgnHandle rgnHandle = qt_mac_get_rgn(); | |
if(d->qt_rgn && d->qt_rgn->numRects) { | |
RgnHandle tmp_rgn = qt_mac_get_rgn(); | |
int n = d->qt_rgn->numRects; | |
const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData(); | |
while (n--) { | |
// detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion | |
// in QWidgetPrivate::update_sys(). | |
enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value | |
if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) { | |
qt_mac_dispose_rgn(tmp_rgn); | |
qt_mac_dispose_rgn(rgnHandle); | |
return 0; | |
} | |
SetRectRgn(tmp_rgn, | |
qMax(SHRT_MIN, qt_r->x()), | |
qMax(SHRT_MIN, qt_r->y()), | |
qMin(SHRT_MAX, qt_r->right() + 1), | |
qMin(SHRT_MAX, qt_r->bottom() + 1)); | |
UnionRgn(rgnHandle, tmp_rgn, rgnHandle); | |
++qt_r; | |
} | |
qt_mac_dispose_rgn(tmp_rgn); | |
} | |
return rgnHandle; | |
} | |
#endif | |
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) | |
OSStatus QRegion::shape2QRegionHelper(int inMessage, HIShapeRef, | |
const CGRect *inRect, void *inRefcon) | |
{ | |
QRegion *region = static_cast<QRegion *>(inRefcon); | |
if (!region) | |
return paramErr; | |
switch (inMessage) { | |
case kHIShapeEnumerateRect: | |
*region += QRect(inRect->origin.x, inRect->origin.y, | |
inRect->size.width, inRect->size.height); | |
break; | |
case kHIShapeEnumerateInit: | |
// Assume the region is already setup correctly | |
case kHIShapeEnumerateTerminate: | |
default: | |
break; | |
} | |
return noErr; | |
} | |
#endif | |
/*! | |
\internal | |
Create's a mutable shape, it's the caller's responsibility to release. | |
WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below. | |
*/ | |
HIMutableShapeRef QRegion::toHIMutableShape() const | |
{ | |
HIMutableShapeRef shape = HIShapeCreateMutable(); | |
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) | |
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { | |
if (d->qt_rgn && d->qt_rgn->numRects) { | |
int n = d->qt_rgn->numRects; | |
const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData(); | |
while (n--) { | |
CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height()); | |
HIShapeUnionWithRect(shape, &cgRect); | |
++qt_r; | |
} | |
} | |
} else | |
#endif | |
{ | |
#ifndef QT_MAC_USE_COCOA | |
QCFType<HIShapeRef> qdShape = HIShapeCreateWithQDRgn(QMacSmartQuickDrawRegion(toQDRgn())); | |
HIShapeUnion(qdShape, shape, shape); | |
#endif | |
} | |
return shape; | |
} | |
#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA) | |
typedef OSStatus (*PtrHIShapeGetAsQDRgn)(HIShapeRef, RgnHandle); | |
static PtrHIShapeGetAsQDRgn ptrHIShapeGetAsQDRgn = 0; | |
#endif | |
QRegion QRegion::fromHIShapeRef(HIShapeRef shape) | |
{ | |
QRegion returnRegion; | |
returnRegion.detach(); | |
// Begin gratuitous #if-defery | |
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) | |
# ifndef Q_WS_MAC64 | |
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { | |
# endif | |
HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, shape2QRegionHelper, &returnRegion); | |
# ifndef Q_WS_MAC64 | |
} else | |
# endif | |
#endif | |
{ | |
#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA) | |
if (ptrHIShapeGetAsQDRgn == 0) { | |
QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon")); | |
library.setLoadHints(QLibrary::ExportExternalSymbolsHint); | |
ptrHIShapeGetAsQDRgn = reinterpret_cast<PtrHIShapeGetAsQDRgn>(library.resolve("HIShapeGetAsQDRgn")); | |
} | |
RgnHandle rgn = qt_mac_get_rgn(); | |
ptrHIShapeGetAsQDRgn(shape, rgn); | |
returnRegion = QRegion::fromQDRgn(rgn); | |
qt_mac_dispose_rgn(rgn); | |
#endif | |
} | |
return returnRegion; | |
} | |
QT_END_NAMESPACE |