source: trunk/src/opengl/qgl_mac.mm@ 567

Last change on this file since 567 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 30.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtOpenGL module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qgl.h"
43
44// There are functions that are deprecated in 10.5, but really there's no way around them
45// for Carbon, so just undefine them.
46#undef DEPRECATED_ATTRIBUTE
47#define DEPRECATED_ATTRIBUTE
48#if defined(Q_WS_MAC)
49#ifndef QT_MAC_USE_COCOA
50#ifdef qDebug
51# undef qDebug
52# include <AGL/agl.h>
53# include <AGL/aglRenderers.h>
54# include <OpenGL/gl.h>
55# ifdef QT_NO_DEBUG
56# define qDebug qt_noop(),1?(void)0:qDebug
57# endif
58#else
59# include <AGL/agl.h>
60# include <AGL/aglRenderers.h>
61# include <OpenGL/gl.h>
62#endif
63#else
64#include <private/qcocoaview_mac_p.h>
65#endif
66
67
68#include <OpenGL/gl.h>
69#include <CoreServices/CoreServices.h>
70#include <private/qfont_p.h>
71#include <private/qfontengine_p.h>
72#include <private/qgl_p.h>
73#include <private/qpaintengine_opengl_p.h>
74#include <private/qt_mac_p.h>
75#include <qpixmap.h>
76#include <qtimer.h>
77#include <qapplication.h>
78#include <qstack.h>
79#include <qdesktopwidget.h>
80#include <qdebug.h>
81
82QT_BEGIN_NAMESPACE
83#ifdef QT_MAC_USE_COCOA
84QT_END_NAMESPACE
85
86QT_FORWARD_DECLARE_CLASS(QWidget)
87QT_FORWARD_DECLARE_CLASS(QWidgetPrivate)
88QT_FORWARD_DECLARE_CLASS(QGLWidgetPrivate)
89
90QT_BEGIN_NAMESPACE
91
92void *qt_current_nsopengl_context()
93{
94 return [NSOpenGLContext currentContext];
95}
96
97static GLint attribValue(NSOpenGLPixelFormat *fmt, NSOpenGLPixelFormatAttribute attrib)
98{
99 GLint res;
100 [fmt getValues:&res forAttribute:attrib forVirtualScreen:0];
101 return res;
102}
103
104static int def(int val, int defVal)
105{
106 return val != -1 ? val : defVal;
107}
108#else
109QRegion qt_mac_get_widget_rgn(const QWidget *widget);
110#endif
111
112extern quint32 *qt_mac_pixmap_get_base(const QPixmap *);
113extern int qt_mac_pixmap_get_bytes_per_line(const QPixmap *);
114extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
115extern void qt_mac_dispose_rgn(RgnHandle); //qregion_mac.cpp
116extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
117extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); //qglobal.cpp
118
119bool QGLFormat::hasOpenGL()
120{
121 return true;
122}
123
124bool QGLFormat::hasOpenGLOverlays()
125{
126 return false;
127}
128
129bool QGLContext::chooseContext(const QGLContext *shareContext)
130{
131 QMacCocoaAutoReleasePool pool;
132 Q_D(QGLContext);
133 d->cx = 0;
134 d->vi = chooseMacVisual(0);
135 if (!d->vi)
136 return false;
137
138#ifndef QT_MAC_USE_COCOA
139 AGLPixelFormat fmt = (AGLPixelFormat)d->vi;
140 GLint res;
141 aglDescribePixelFormat(fmt, AGL_LEVEL, &res);
142 d->glFormat.setPlane(res);
143 if (deviceIsPixmap())
144 res = 0;
145 else
146 aglDescribePixelFormat(fmt, AGL_DOUBLEBUFFER, &res);
147 d->glFormat.setDoubleBuffer(res);
148 aglDescribePixelFormat(fmt, AGL_DEPTH_SIZE, &res);
149 d->glFormat.setDepth(res);
150 if (d->glFormat.depth())
151 d->glFormat.setDepthBufferSize(res);
152 aglDescribePixelFormat(fmt, AGL_RGBA, &res);
153 d->glFormat.setRgba(res);
154 aglDescribePixelFormat(fmt, AGL_RED_SIZE, &res);
155 d->glFormat.setRedBufferSize(res);
156 aglDescribePixelFormat(fmt, AGL_GREEN_SIZE, &res);
157 d->glFormat.setGreenBufferSize(res);
158 aglDescribePixelFormat(fmt, AGL_BLUE_SIZE, &res);
159 d->glFormat.setBlueBufferSize(res);
160 aglDescribePixelFormat(fmt, AGL_ALPHA_SIZE, &res);
161 d->glFormat.setAlpha(res);
162 if (d->glFormat.alpha())
163 d->glFormat.setAlphaBufferSize(res);
164 aglDescribePixelFormat(fmt, AGL_ACCUM_RED_SIZE, &res);
165 // Bug in Apple OpenGL (rdr://5015603), when we don't have an accumulation
166 // buffer, it still claims that we have a 16-bit one (which is pretty rare).
167 // So, we just assume we can never have a buffer that small.
168 d->glFormat.setAccum(res > 5);
169 if (d->glFormat.accum())
170 d->glFormat.setAccumBufferSize(res);
171 aglDescribePixelFormat(fmt, AGL_STENCIL_SIZE, &res);
172 d->glFormat.setStencil(res);
173 if (d->glFormat.stencil())
174 d->glFormat.setStencilBufferSize(res);
175 aglDescribePixelFormat(fmt, AGL_STEREO, &res);
176 d->glFormat.setStereo(res);
177 aglDescribePixelFormat(fmt, AGL_SAMPLE_BUFFERS_ARB, &res);
178 d->glFormat.setSampleBuffers(res);
179 if (d->glFormat.sampleBuffers()) {
180 aglDescribePixelFormat(fmt, AGL_SAMPLES_ARB, &res);
181 d->glFormat.setSamples(res);
182 }
183#else
184 NSOpenGLPixelFormat *fmt = static_cast<NSOpenGLPixelFormat *>(d->vi);
185
186 d->glFormat = QGLFormat();
187
188 // ### make sure to reset other options
189 d->glFormat.setDoubleBuffer(attribValue(fmt, NSOpenGLPFADoubleBuffer));
190
191 int depthSize = attribValue(fmt, NSOpenGLPFADepthSize);
192 d->glFormat.setDepth(depthSize > 0);
193 if (depthSize > 0)
194 d->glFormat.setDepthBufferSize(depthSize);
195
196 int alphaSize = attribValue(fmt, NSOpenGLPFAAlphaSize);
197 d->glFormat.setAlpha(alphaSize > 0);
198 if (alphaSize > 0)
199 d->glFormat.setAlphaBufferSize(alphaSize);
200
201 int accumSize = attribValue(fmt, NSOpenGLPFAAccumSize);
202 d->glFormat.setAccum(accumSize > 0);
203 if (accumSize > 0)
204 d->glFormat.setAccumBufferSize(accumSize);
205
206 int stencilSize = attribValue(fmt, NSOpenGLPFAStencilSize);
207 d->glFormat.setStencil(stencilSize > 0);
208 if (stencilSize > 0)
209 d->glFormat.setStencilBufferSize(stencilSize);
210
211 d->glFormat.setStereo(attribValue(fmt, NSOpenGLPFAStereo));
212
213 int sampleBuffers = attribValue(fmt, NSOpenGLPFASampleBuffers);
214 d->glFormat.setSampleBuffers(sampleBuffers);
215 if (sampleBuffers > 0)
216 d->glFormat.setSamples(attribValue(fmt, NSOpenGLPFASamples));
217#endif
218 if (shareContext && (!shareContext->isValid() || !shareContext->d_func()->cx)) {
219 qWarning("QGLContext::chooseContext: Cannot share with invalid context");
220 shareContext = 0;
221 }
222
223 // sharing between rgba and color-index will give wrong colors
224 if (shareContext && (format().rgba() != shareContext->format().rgba()))
225 shareContext = 0;
226
227#ifndef QT_MAC_USE_COCOA
228 AGLContext ctx = aglCreateContext(fmt, (AGLContext) (shareContext ? shareContext->d_func()->cx : 0));
229#else
230 NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:fmt
231 shareContext:(shareContext ? static_cast<NSOpenGLContext *>(shareContext->d_func()->cx)
232 : 0)];
233#endif
234 if (!ctx) {
235#ifndef QT_MAC_USE_COCOA
236 GLenum err = aglGetError();
237 if (err == AGL_BAD_MATCH || err == AGL_BAD_CONTEXT) {
238 if (shareContext && shareContext->d_func()->cx) {
239 qWarning("QGLContext::chooseContext(): Context sharing mismatch!");
240 if (!(ctx = aglCreateContext(fmt, 0)))
241 return false;
242 shareContext = 0;
243 }
244 }
245#else
246 if (shareContext) {
247 ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:0];
248 if (ctx) {
249 qWarning("QGLContext::chooseContext: Context sharing mismatch");
250 shareContext = 0;
251 }
252 }
253#endif
254 if (!ctx) {
255 qWarning("QGLContext::chooseContext: Unable to create QGLContext");
256 return false;
257 }
258 }
259 d->cx = ctx;
260 if (shareContext && shareContext->d_func()->cx) {
261 QGLContext *share = const_cast<QGLContext *>(shareContext);
262 d->sharing = true;
263 share->d_func()->sharing = true;
264 }
265 if (deviceIsPixmap())
266 updatePaintDevice();
267
268 // vblank syncing
269 GLint interval = d->reqFormat.swapInterval();
270 if (interval != -1) {
271#ifndef QT_MAC_USE_COCOA
272 aglSetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
273 if (interval != 0)
274 aglEnable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
275 else
276 aglDisable((AGLContext)d->cx, AGL_SWAP_INTERVAL);
277#else
278 [ctx setValues:&interval forParameter:NSOpenGLCPSwapInterval];
279#endif
280 }
281#ifndef QT_MAC_USE_COCOA
282 aglGetInteger((AGLContext)d->cx, AGL_SWAP_INTERVAL, &interval);
283#else
284 [ctx getValues:&interval forParameter:NSOpenGLCPSwapInterval];
285#endif
286 d->glFormat.setSwapInterval(interval);
287 return true;
288}
289
290void *QGLContextPrivate::tryFormat(const QGLFormat &format)
291{
292 static const int Max = 40;
293#ifndef QT_MAC_USE_COCOA
294 GLint attribs[Max], cnt = 0;
295 bool device_is_pixmap = (paintDevice->devType() == QInternal::Pixmap);
296
297 attribs[cnt++] = AGL_RGBA;
298 attribs[cnt++] = AGL_BUFFER_SIZE;
299 attribs[cnt++] = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
300 attribs[cnt++] = AGL_LEVEL;
301 attribs[cnt++] = format.plane();
302
303 if (format.redBufferSize() != -1) {
304 attribs[cnt++] = AGL_RED_SIZE;
305 attribs[cnt++] = format.redBufferSize();
306 }
307 if (format.greenBufferSize() != -1) {
308 attribs[cnt++] = AGL_GREEN_SIZE;
309 attribs[cnt++] = format.greenBufferSize();
310 }
311 if (format.blueBufferSize() != -1) {
312 attribs[cnt++] = AGL_BLUE_SIZE;
313 attribs[cnt++] = format.blueBufferSize();
314 }
315 if (device_is_pixmap) {
316 attribs[cnt++] = AGL_PIXEL_SIZE;
317 attribs[cnt++] = static_cast<QPixmap *>(paintDevice)->depth();
318 attribs[cnt++] = AGL_OFFSCREEN;
319 if (!format.alpha()) {
320 attribs[cnt++] = AGL_ALPHA_SIZE;
321 attribs[cnt++] = 8;
322 }
323 } else {
324 if (format.doubleBuffer())
325 attribs[cnt++] = AGL_DOUBLEBUFFER;
326 }
327
328 if (format.stereo())
329 attribs[cnt++] = AGL_STEREO;
330 if (format.alpha()) {
331 attribs[cnt++] = AGL_ALPHA_SIZE;
332 attribs[cnt++] = format.alphaBufferSize() == -1 ? 8 : format.alphaBufferSize();
333 }
334 if (format.stencil()) {
335 attribs[cnt++] = AGL_STENCIL_SIZE;
336 attribs[cnt++] = format.stencilBufferSize() == -1 ? 8 : format.stencilBufferSize();
337 }
338 if (format.depth()) {
339 attribs[cnt++] = AGL_DEPTH_SIZE;
340 attribs[cnt++] = format.depthBufferSize() == -1 ? 32 : format.depthBufferSize();
341 }
342 if (format.accum()) {
343 attribs[cnt++] = AGL_ACCUM_RED_SIZE;
344 attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
345 attribs[cnt++] = AGL_ACCUM_BLUE_SIZE;
346 attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
347 attribs[cnt++] = AGL_ACCUM_GREEN_SIZE;
348 attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
349 attribs[cnt++] = AGL_ACCUM_ALPHA_SIZE;
350 attribs[cnt++] = format.accumBufferSize() == -1 ? 1 : format.accumBufferSize();
351 }
352 if (format.sampleBuffers()) {
353 attribs[cnt++] = AGL_SAMPLE_BUFFERS_ARB;
354 attribs[cnt++] = 1;
355 attribs[cnt++] = AGL_SAMPLES_ARB;
356 attribs[cnt++] = format.samples() == -1 ? 4 : format.samples();
357 }
358
359 attribs[cnt] = AGL_NONE;
360 Q_ASSERT(cnt < Max);
361 return aglChoosePixelFormat(0, 0, attribs);
362#else
363 NSOpenGLPixelFormatAttribute attribs[Max];
364 int cnt = 0;
365 int devType = paintDevice->devType();
366 bool device_is_pixmap = (devType == QInternal::Pixmap);
367 int depth = device_is_pixmap ? static_cast<QPixmap *>(paintDevice)->depth() : 32;
368
369 attribs[cnt++] = NSOpenGLPFAColorSize;
370 attribs[cnt++] = depth;
371
372 if (device_is_pixmap) {
373 attribs[cnt++] = NSOpenGLPFAOffScreen;
374 } else {
375 if (format.doubleBuffer())
376 attribs[cnt++] = NSOpenGLPFADoubleBuffer;
377 }
378 if (glFormat.stereo())
379 attribs[cnt++] = NSOpenGLPFAStereo;
380 if (device_is_pixmap || format.alpha()) {
381 attribs[cnt++] = NSOpenGLPFAAlphaSize;
382 attribs[cnt++] = def(format.alphaBufferSize(), 8);
383 }
384 if (format.stencil()) {
385 attribs[cnt++] = NSOpenGLPFAStencilSize;
386 attribs[cnt++] = def(format.stencilBufferSize(), 8);
387 }
388 if (format.depth()) {
389 attribs[cnt++] = NSOpenGLPFADepthSize;
390 attribs[cnt++] = def(format.depthBufferSize(), 32);
391 }
392 if (format.accum()) {
393 attribs[cnt++] = NSOpenGLPFAAccumSize;
394 attribs[cnt++] = def(format.accumBufferSize(), 1);
395 }
396 if (format.sampleBuffers()) {
397 attribs[cnt++] = NSOpenGLPFASampleBuffers;
398 attribs[cnt++] = 1;
399 attribs[cnt++] = NSOpenGLPFASamples;
400 attribs[cnt++] = def(format.samples(), 4);
401 }
402
403 if (format.directRendering())
404 attribs[cnt++] = NSOpenGLPFAAccelerated;
405
406 if (devType == QInternal::Pbuffer)
407 attribs[cnt++] = NSOpenGLPFAPixelBuffer;
408
409 attribs[cnt] = 0;
410 Q_ASSERT(cnt < Max);
411 return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
412#endif
413}
414
415void QGLContextPrivate::clearDrawable()
416{
417 [static_cast<NSOpenGLContext *>(cx) clearDrawable];
418}
419
420/*!
421 \bold{Mac OS X only:} This virtual function tries to find a visual that
422 matches the format, reducing the demands if the original request
423 cannot be met.
424
425 The algorithm for reducing the demands of the format is quite
426 simple-minded, so override this method in your subclass if your
427 application has spcific requirements on visual selection.
428
429 The \a handle argument is always zero and is not used
430
431 \sa chooseContext()
432*/
433
434void *QGLContext::chooseMacVisual(GDHandle /* handle */)
435{
436 Q_D(QGLContext);
437
438 void *fmt = d->tryFormat(d->glFormat);
439 if (!fmt && d->glFormat.stereo()) {
440 d->glFormat.setStereo(false);
441 fmt = d->tryFormat(d->glFormat);
442 }
443 if (!fmt && d->glFormat.sampleBuffers()) {
444 d->glFormat.setSampleBuffers(false);
445 fmt = d->tryFormat(d->glFormat);
446 }
447 if (!fmt)
448 qWarning("QGLContext::chooseMacVisual: Unable to choose a pixel format");
449 return fmt;
450}
451
452void QGLContext::reset()
453{
454 Q_D(QGLContext);
455 if (!d->valid)
456 return;
457 d->cleanup();
458 doneCurrent();
459#ifndef QT_MAC_USE_COCOA
460 if (d->cx)
461 aglDestroyContext((AGLContext)d->cx);
462#else
463 QMacCocoaAutoReleasePool pool;
464 [static_cast<NSOpenGLContext *>(d->cx) release];
465#endif
466 d->cx = 0;
467#ifndef QT_MAC_USE_COCOA
468 if (d->vi)
469 aglDestroyPixelFormat((AGLPixelFormat)d->vi);
470#else
471 [static_cast<NSOpenGLPixelFormat *>(d->vi) release];
472#endif
473 d->vi = 0;
474 d->crWin = false;
475 d->sharing = false;
476 d->valid = false;
477 d->transpColor = QColor();
478 d->initDone = false;
479 qgl_share_reg()->removeShare(this);
480}
481
482void QGLContext::makeCurrent()
483{
484 Q_D(QGLContext);
485
486 if (!d->valid) {
487 qWarning("QGLContext::makeCurrent: Cannot make invalid context current");
488 return;
489 }
490#ifndef QT_MAC_USE_COCOA
491 aglSetCurrentContext((AGLContext)d->cx);
492 if (d->update)
493 updatePaintDevice();
494#else
495 [static_cast<NSOpenGLContext *>(d->cx) makeCurrentContext];
496#endif
497 QGLContextPrivate::setCurrentContext(this);
498}
499
500#ifndef QT_MAC_USE_COCOA
501/*
502 Returns the effective scale factor for a widget. For this value to be
503 different than 1, the following must be true:
504 - The system scale factor must be greater than 1.
505 - The widget window must have WA_MacFrameworkScaled set.
506*/
507float qt_mac_get_scale_factor(QWidget *widget)
508{
509 if (!widget | !widget->window())
510 return 1;
511
512 if (widget->window()->testAttribute(Qt::WA_MacFrameworkScaled) == false)
513 return 1;
514
515 float systemScale = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4 ? HIGetScaleFactor() : 1;
516 if (systemScale == float(1))
517 return 1;
518
519 return systemScale;
520}
521#endif
522
523/*! \internal
524*/
525void QGLContext::updatePaintDevice()
526{
527 Q_D(QGLContext);
528#ifndef QT_MAC_USE_COCOA
529 d->update = false;
530 if (d->paintDevice->devType() == QInternal::Widget) {
531 //get control information
532 QWidget *w = (QWidget *)d->paintDevice;
533 HIViewRef hiview = (HIViewRef)w->winId();
534 WindowRef window = HIViewGetWindow(hiview);
535#ifdef DEBUG_OPENGL_REGION_UPDATE
536 static int serial_no_gl = 0;
537 qDebug("[%d] %p setting on %s::%s %p/%p [%s]", ++serial_no_gl, w,
538 w->metaObject()->className(), w->objectName().toLatin1().constData(),
539 hiview, window, w->handle() ? "Inside" : "Outside");
540#endif
541
542 //update drawable
543 if (0 && w->isWindow() && w->isFullScreen()) {
544 aglSetDrawable((AGLContext)d->cx, 0);
545 aglSetFullScreen((AGLContext)d->cx, w->width(), w->height(), 0, QApplication::desktop()->screenNumber(w));