Merge remote branch 'staging/master' into refactor
[profile/ivi/qtbase.git] / src / widgets / platforms / mac / qregion_mac.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qt_mac_p.h>
43 #include "qcoreapplication.h"
44 #include <qlibrary.h>
45
46 QT_BEGIN_NAMESPACE
47
48 QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
49
50 #if defined(Q_WS_MAC32) && !defined(QT_MAC_USE_COCOA)
51 #define RGN_CACHE_SIZE 200
52 #ifdef RGN_CACHE_SIZE
53 static bool rgncache_init = false;
54 static int rgncache_used;
55 static RgnHandle rgncache[RGN_CACHE_SIZE];
56 static void qt_mac_cleanup_rgncache()
57 {
58     rgncache_init = false;
59     for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
60         if(rgncache[i]) {
61             --rgncache_used;
62             DisposeRgn(rgncache[i]);
63             rgncache[i] = 0;
64         }
65     }
66 }
67 #endif
68
69 Q_GUI_EXPORT RgnHandle qt_mac_get_rgn()
70 {
71 #ifdef RGN_CACHE_SIZE
72     if(!rgncache_init) {
73         rgncache_used = 0;
74         rgncache_init = true;
75         for(int i = 0; i < RGN_CACHE_SIZE; ++i)
76             rgncache[i] = 0;
77         qAddPostRoutine(qt_mac_cleanup_rgncache);
78     } else if(rgncache_used) {
79         for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
80             if(rgncache[i]) {
81                 RgnHandle ret = rgncache[i];
82                 SetEmptyRgn(ret);
83                 rgncache[i] = 0;
84                 --rgncache_used;
85                 return ret;
86             }
87         }
88     }
89 #endif
90     return NewRgn();
91 }
92
93 Q_GUI_EXPORT void qt_mac_dispose_rgn(RgnHandle r)
94 {
95 #ifdef RGN_CACHE_SIZE
96     if(rgncache_init && rgncache_used < RGN_CACHE_SIZE) {
97         for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
98             if(!rgncache[i]) {
99                 ++rgncache_used;
100                 rgncache[i] = r;
101                 return;
102             }
103         }
104     }
105 #endif
106     DisposeRgn(r);
107 }
108
109 static OSStatus qt_mac_get_rgn_rect(UInt16 msg, RgnHandle, const Rect *rect, void *reg)
110 {
111     if(msg == kQDRegionToRectsMsgParse) {
112         QRect rct(rect->left, rect->top, (rect->right - rect->left), (rect->bottom - rect->top));
113         if(!rct.isEmpty())
114             *((QRegion *)reg) += rct;
115     }
116     return noErr;
117 }
118
119 Q_GUI_EXPORT QRegion qt_mac_convert_mac_region(RgnHandle rgn)
120 {
121     return QRegion::fromQDRgn(rgn);
122 }
123
124 QRegion QRegion::fromQDRgn(RgnHandle rgn)
125 {
126     QRegion ret;
127     ret.detach();
128     OSStatus oss = QDRegionToRects(rgn, kQDParseRegionFromTopLeft, qt_mac_get_rgn_rect, (void *)&ret);
129     if(oss != noErr)
130         return QRegion();
131     return ret;
132 }
133
134 /*!
135     \internal
136      Create's a RegionHandle, it's the caller's responsibility to release.
137 */
138 RgnHandle QRegion::toQDRgn() const
139 {
140     RgnHandle rgnHandle = qt_mac_get_rgn();
141     if(d->qt_rgn && d->qt_rgn->numRects) {
142         RgnHandle tmp_rgn = qt_mac_get_rgn();
143         int n = d->qt_rgn->numRects;
144         const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
145         while (n--) {
146             SetRectRgn(tmp_rgn,
147                        qMax(SHRT_MIN, qt_r->x()),
148                        qMax(SHRT_MIN, qt_r->y()),
149                        qMin(SHRT_MAX, qt_r->right() + 1),
150                        qMin(SHRT_MAX, qt_r->bottom() + 1));
151             UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
152             ++qt_r;
153         }
154         qt_mac_dispose_rgn(tmp_rgn);
155     }
156     return rgnHandle;
157 }
158
159 /*!
160     \internal
161      Create's a RegionHandle, it's the caller's responsibility to release.
162      Returns 0 if the QRegion overflows.
163 */
164 RgnHandle QRegion::toQDRgnForUpdate_sys() const
165 {
166     RgnHandle rgnHandle = qt_mac_get_rgn();
167     if(d->qt_rgn && d->qt_rgn->numRects) {
168         RgnHandle tmp_rgn = qt_mac_get_rgn();
169         int n = d->qt_rgn->numRects;
170         const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
171         while (n--) {
172
173             // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion
174             // in QWidgetPrivate::update_sys().
175             enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value
176             if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) {
177                 qt_mac_dispose_rgn(tmp_rgn);
178                 qt_mac_dispose_rgn(rgnHandle);
179                 return 0;
180             }
181
182             SetRectRgn(tmp_rgn,
183                        qMax(SHRT_MIN, qt_r->x()),
184                        qMax(SHRT_MIN, qt_r->y()),
185                        qMin(SHRT_MAX, qt_r->right() + 1),
186                        qMin(SHRT_MAX, qt_r->bottom() + 1));
187             UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
188             ++qt_r;
189         }
190         qt_mac_dispose_rgn(tmp_rgn);
191     }
192     return rgnHandle;
193 }
194
195 #endif
196
197 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
198 OSStatus QRegion::shape2QRegionHelper(int inMessage, HIShapeRef,
199                                       const CGRect *inRect, void *inRefcon)
200 {
201     QRegion *region = static_cast<QRegion *>(inRefcon);
202     if (!region)
203         return paramErr;
204
205     switch (inMessage) {
206     case kHIShapeEnumerateRect:
207         *region += QRect(inRect->origin.x, inRect->origin.y,
208                          inRect->size.width, inRect->size.height);
209         break;
210     case kHIShapeEnumerateInit:
211         // Assume the region is already setup correctly
212     case kHIShapeEnumerateTerminate:
213     default:
214         break;
215     }
216     return noErr;
217 }
218 #endif
219
220 /*!
221     \internal
222      Create's a mutable shape, it's the caller's responsibility to release.
223      WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
224 */
225 HIMutableShapeRef QRegion::toHIMutableShape() const
226 {
227     HIMutableShapeRef shape = HIShapeCreateMutable();
228 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
229     if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
230         if (d->qt_rgn && d->qt_rgn->numRects) {
231             int n = d->qt_rgn->numRects;
232             const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
233             while (n--) {
234                 CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
235                 HIShapeUnionWithRect(shape, &cgRect);
236                 ++qt_r;
237             }
238         }
239     } else
240 #endif
241     {
242 #ifndef QT_MAC_USE_COCOA
243         QCFType<HIShapeRef> qdShape = HIShapeCreateWithQDRgn(QMacSmartQuickDrawRegion(toQDRgn()));
244         HIShapeUnion(qdShape, shape, shape);
245 #endif
246     }
247     return shape;
248 }
249
250 #if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
251 typedef OSStatus (*PtrHIShapeGetAsQDRgn)(HIShapeRef, RgnHandle);
252 static PtrHIShapeGetAsQDRgn ptrHIShapeGetAsQDRgn = 0;
253 #endif
254
255
256 QRegion QRegion::fromHIShapeRef(HIShapeRef shape)
257 {
258     QRegion returnRegion;
259     returnRegion.detach();
260     // Begin gratuitous #if-defery
261 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
262 # ifndef Q_WS_MAC64
263     if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
264 # endif
265         HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, shape2QRegionHelper, &returnRegion);
266 # ifndef Q_WS_MAC64
267     } else
268 # endif
269 #endif
270     {
271 #if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
272         if (ptrHIShapeGetAsQDRgn == 0) {
273             QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
274             library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
275                     ptrHIShapeGetAsQDRgn = reinterpret_cast<PtrHIShapeGetAsQDRgn>(library.resolve("HIShapeGetAsQDRgn"));
276         }
277         RgnHandle rgn = qt_mac_get_rgn();
278         ptrHIShapeGetAsQDRgn(shape, rgn);
279         returnRegion = QRegion::fromQDRgn(rgn);
280         qt_mac_dispose_rgn(rgn);
281 #endif
282     }
283     return returnRegion;
284 }
285
286 QT_END_NAMESPACE