source: trunk/src/opengl/qgl_x11.cpp@ 780

Last change on this file since 780 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 59.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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#include "qgl_p.h"
44
45#include "qmap.h"
46#include "qapplication.h"
47#include "qcolormap.h"
48#include "qdesktopwidget.h"
49#include "qpixmap.h"
50#include "qhash.h"
51#include "qlibrary.h"
52#include "qdebug.h"
53#include <private/qfontengine_ft_p.h>
54#include <private/qt_x11_p.h>
55#include <private/qpixmap_x11_p.h>
56#include <private/qimagepixmapcleanuphooks_p.h>
57#ifdef Q_OS_HPUX
58// for GLXPBuffer
59#include <private/qglpixelbuffer_p.h>
60#endif
61
62// We always define GLX_EXT_texture_from_pixmap ourselves because
63// we can't trust system headers to do it properly
64#define GLX_EXT_texture_from_pixmap 1
65
66#define INT8 dummy_INT8
67#define INT32 dummy_INT32
68#include <GL/glx.h>
69#undef INT8
70#undef INT32
71
72#include <X11/Xlib.h>
73#include <X11/Xutil.h>
74#include <X11/Xos.h>
75#ifdef Q_OS_VXWORS
76# ifdef open
77# undef open
78# endif
79# ifdef getpid
80# undef getpid
81# endif
82#endif // Q_OS_VXWORKS
83#include <X11/Xatom.h>
84
85#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
86#include <dlfcn.h>
87#endif
88
89QT_BEGIN_NAMESPACE
90
91extern Drawable qt_x11Handle(const QPaintDevice *pd);
92extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
93
94#ifndef GLX_ARB_multisample
95#define GLX_SAMPLE_BUFFERS_ARB 100000
96#define GLX_SAMPLES_ARB 100001
97#endif
98
99#ifndef GLX_TEXTURE_2D_BIT_EXT
100#define GLX_TEXTURE_2D_BIT_EXT 0x00000002
101#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004
102#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0
103#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1
104#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2
105#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3
106#define GLX_Y_INVERTED_EXT 0x20D4
107#define GLX_TEXTURE_FORMAT_EXT 0x20D5
108#define GLX_TEXTURE_TARGET_EXT 0x20D6
109#define GLX_MIPMAP_TEXTURE_EXT 0x20D7
110#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8
111#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9
112#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA
113#define GLX_TEXTURE_2D_EXT 0x20DC
114#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD
115#define GLX_FRONT_LEFT_EXT 0x20DE
116#endif
117
118/*
119 The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext()
120 and GLX (not Windows). If the application can't find any sharable
121 colormaps, it must at least create as few colormaps as possible. The
122 dictionary solution below ensures only one colormap is created per visual.
123 Colormaps are also deleted when the application terminates.
124*/
125
126struct QCMapEntry {
127 QCMapEntry();
128 ~QCMapEntry();
129
130 Colormap cmap;
131 bool alloc;
132 XStandardColormap scmap;
133};
134
135QCMapEntry::QCMapEntry()
136{
137 cmap = 0;
138 alloc = false;
139 scmap.colormap = 0;
140}
141
142QCMapEntry::~QCMapEntry()
143{
144 if (alloc)
145 XFreeColormap(X11->display, cmap);
146}
147typedef QHash<int, QCMapEntry *> CMapEntryHash;
148typedef QHash<int, QMap<int, QRgb> > GLCMapHash;
149static bool mesa_gl = false;
150static bool first_time = true;
151
152static void cleanup_cmaps();
153
154struct QGLCMapCleanupHandler {
155 QGLCMapCleanupHandler() {
156 cmap_hash = new CMapEntryHash;
157 qglcmap_hash = new GLCMapHash;
158 }
159 ~QGLCMapCleanupHandler() {
160 delete cmap_hash;
161 delete qglcmap_hash;
162 }
163 CMapEntryHash *cmap_hash;
164 GLCMapHash *qglcmap_hash;
165};
166Q_GLOBAL_STATIC(QGLCMapCleanupHandler, cmap_handler)
167
168static void cleanup_cmaps()
169{
170 CMapEntryHash *hash = cmap_handler()->cmap_hash;
171 QHash<int, QCMapEntry *>::ConstIterator it = hash->constBegin();
172 while (it != hash->constEnd()) {
173 delete it.value();
174 ++it;
175 }
176
177 hash->clear();
178 cmap_handler()->qglcmap_hash->clear();
179}
180
181Colormap qt_gl_choose_cmap(Display *dpy, XVisualInfo *vi)
182{
183 if (first_time) {
184 const char *v = glXQueryServerString(dpy, vi->screen, GLX_VERSION);
185 if (v)
186 mesa_gl = (strstr(v, "Mesa") != 0);
187 first_time = false;
188 }
189
190 CMapEntryHash *hash = cmap_handler()->cmap_hash;
191 CMapEntryHash::ConstIterator it = hash->constFind((long) vi->visualid + (vi->screen * 256));
192 if (it != hash->constEnd())
193 return it.value()->cmap; // found colormap for visual
194
195 if (vi->visualid ==
196 XVisualIDFromVisual((Visual *) QX11Info::appVisual(vi->screen))) {
197 // qDebug("Using x11AppColormap");
198 return QX11Info::appColormap(vi->screen);
199 }
200
201 QCMapEntry *x = new QCMapEntry();
202
203 XStandardColormap *c;
204 int n, i;
205
206 // qDebug("Choosing cmap for vID %0x", vi->visualid);
207
208 if (mesa_gl) { // we're using MesaGL
209 Atom hp_cmaps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", true);
210 if (hp_cmaps && vi->visual->c_class == TrueColor && vi->depth == 8) {
211 if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
212 hp_cmaps)) {
213 i = 0;
214 while (i < n && x->cmap == 0) {
215 if (c[i].visualid == vi->visual->visualid) {
216 x->cmap = c[i].colormap;
217 x->scmap = c[i];
218 //qDebug("Using HP_RGB scmap");
219
220 }
221 i++;
222 }
223 XFree((char *)c);
224 }
225 }
226 }
227 if (!x->cmap) {
228 if (XGetRGBColormaps(dpy,RootWindow(dpy,vi->screen),&c,&n,
229 XA_RGB_DEFAULT_MAP)) {
230 for (int i = 0; i < n && x->cmap == 0; ++i) {
231 if (!c[i].red_max ||
232 !c[i].green_max ||
233 !c[i].blue_max ||
234 !c[i].red_mult ||
235 !c[i].green_mult ||
236 !c[i].blue_mult)
237 continue; // invalid stdcmap
238 if (c[i].visualid == vi->visualid) {
239 x->cmap = c[i].colormap;
240 x->scmap = c[i];
241 //qDebug("Using RGB_DEFAULT scmap");
242 }
243 }
244 XFree((char *)c);
245 }
246 }
247 if (!x->cmap) { // no shared cmap found
248 x->cmap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), vi->visual,
249 AllocNone);
250 x->alloc = true;
251 // qDebug("Allocating cmap");
252 }
253
254 // colormap hash should be cleanup only when the QApplication dtor is called
255 if (hash->isEmpty())
256 qAddPostRoutine(cleanup_cmaps);
257
258 // associate cmap with visualid
259 hash->insert((long) vi->visualid + (vi->screen * 256), x);
260 return x->cmap;
261}
262
263struct QTransColor
264{
265 VisualID vis;
266 int screen;
267 long color;
268};
269
270static QVector<QTransColor> trans_colors;
271static int trans_colors_init = false;
272
273static void find_trans_colors()
274{
275 struct OverlayProp {
276 long visual;
277 long type;
278 long value;
279 long layer;
280 };
281
282 trans_colors_init = true;
283
284 Display* appDisplay = X11->display;
285
286 int scr;
287 int lastsize = 0;
288 for (scr = 0; scr < ScreenCount(appDisplay); scr++) {
289 QWidget* rootWin = QApplication::desktop()->screen(scr);
290 if (!rootWin)
291 return; // Should not happen
292 Atom overlayVisualsAtom = XInternAtom(appDisplay,
293 "SERVER_OVERLAY_VISUALS", True);
294 if (overlayVisualsAtom == XNone)
295 return; // Server has no overlays
296
297 Atom actualType;
298 int actualFormat;
299 ulong nItems;
300 ulong bytesAfter;
301 unsigned char *retval = 0;
302 int res = XGetWindowProperty(appDisplay, rootWin->winId(),
303 overlayVisualsAtom, 0, 10000, False,
304 overlayVisualsAtom, &actualType,
305 &actualFormat, &nItems, &bytesAfter,
306 &retval);
307
308 if (res != Success || actualType != overlayVisualsAtom
309 || actualFormat != 32 || nItems < 4 || !retval)
310 return; // Error reading property
311
312 OverlayProp *overlayProps = (OverlayProp *)retval;
313
314 int numProps = nItems / 4;
315 trans_colors.resize(lastsize + numProps);
316 int j = lastsize;
317 for (int i = 0; i < numProps; i++) {
318 if (overlayProps[i].type == 1) {
319 trans_colors[j].vis = (VisualID)overlayProps[i].visual;
320 trans_colors[j].screen = scr;
321 trans_colors[j].color = (int)overlayProps[i].value;
322 j++;
323 }
324 }
325 XFree(overlayProps);
326 lastsize = j;
327 trans_colors.resize(lastsize);
328 }
329}
330
331/*****************************************************************************
332 QGLFormat UNIX/GLX-specific code
333 *****************************************************************************/
334
335void* qglx_getProcAddress(const char* procName)
336{
337 // On systems where the GL driver is pluggable (like Mesa), we have to use
338 // the glXGetProcAddressARB extension to resolve other function pointers as
339 // the symbols wont be in the GL library, but rather in a plugin loaded by
340 // the GL library.
341 typedef void* (*qt_glXGetProcAddressARB)(const char *);
342 static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
343 static bool triedResolvingGlxGetProcAddress = false;
344 if (!triedResolvingGlxGetProcAddress) {
345 triedResolvingGlxGetProcAddress = true;
346 QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
347 if (extensions.match("GLX_ARB_get_proc_address")) {
348#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
349 void *handle = dlopen(NULL, RTLD_LAZY);
350 if (handle) {
351 glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
352 dlclose(handle);
353 }
354 if (!glXGetProcAddressARB)
355#endif
356 {
357#if !defined(QT_NO_LIBRARY)
358 extern const QString qt_gl_library_name();
359 QLibrary lib(qt_gl_library_name());
360 glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
361#endif
362 }
363 }
364 }
365
366 void *procAddress = 0;
367 if (glXGetProcAddressARB)
368 procAddress = glXGetProcAddressARB(procName);
369
370 // If glXGetProcAddress didn't work, try looking the symbol up in the GL library
371#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
372 if (!procAddress) {
373 void *handle = dlopen(NULL, RTLD_LAZY);
374 if (handle) {
375 procAddress = dlsym(handle, procName);
376 dlclose(handle);
377 }
378 }
379#endif
380#if !defined(QT_NO_LIBRARY)
381 if (!procAddress) {
382 extern const QString qt_gl_library_name();
383 QLibrary lib(qt_gl_library_name());
384 procAddress = lib.resolve(procName);
385 }
386#endif
387
388 return procAddress;
389}
390
391bool QGLFormat::hasOpenGL()
392{
393 return glXQueryExtension(X11->display, 0, 0) != 0;
394}
395
396
397bool QGLFormat::hasOpenGLOverlays()
398{
399 if (!trans_colors_init)
400 find_trans_colors();
401 return trans_colors.size() > 0;
402}
403
404/*****************************************************************************
405 QGLContext UNIX/GLX-specific code
406 *****************************************************************************/
407
408bool QGLContext::chooseContext(const QGLContext* shareContext)
409{
410 Q_D(QGLContext);
411 const QX11Info *xinfo = qt_x11Info(d->paintDevice);
412
413 Display* disp = xinfo->display();
414 d->vi = chooseVisual();
415 if (!d->vi)
416 return false;
417
418 if (deviceIsPixmap() &&
419 (((XVisualInfo*)d->vi)->depth != xinfo->depth() ||
420 ((XVisualInfo*)d->vi)->screen != xinfo->screen()))
421 {
422 XFree(d->vi);
423 XVisualInfo appVisInfo;
424 memset(&appVisInfo, 0, sizeof(XVisualInfo));
425 appVisInfo.visualid = XVisualIDFromVisual((Visual *) xinfo->visual());
426 appVisInfo.screen = xinfo->screen();
427 int nvis;
428 d->vi = XGetVisualInfo(disp, VisualIDMask | VisualScreenMask, &appVisInfo, &nvis);
429 if (!d->vi)
430 return false;
431
432 int useGL;
433 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_USE_GL, &useGL);
434 if (!useGL)
435 return false; //# Chickening out already...
436 }
437 int res;
438 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_LEVEL, &res);
439 d->glFormat.setPlane(res);
440 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DOUBLEBUFFER, &res);
441 d->glFormat.setDoubleBuffer(res);
442 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_DEPTH_SIZE, &res);
443 d->glFormat.setDepth(res);
444 if (d->glFormat.depth())
445 d->glFormat.setDepthBufferSize(res);
446 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RGBA, &res);
447 d->glFormat.setRgba(res);
448 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_RED_SIZE, &res);
449 d->glFormat.setRedBufferSize(res);
450 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_GREEN_SIZE, &res);
451 d->glFormat.setGreenBufferSize(res);
452 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_BLUE_SIZE, &res);
453 d->glFormat.setBlueBufferSize(res);
454 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ALPHA_SIZE, &res);
455 d->glFormat.setAlpha(res);
456 if (d->glFormat.alpha())
457 d->glFormat.setAlphaBufferSize(res);
458 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_ACCUM_RED_SIZE, &res);
459 d->glFormat.setAccum(res);
460 if (d->glFormat.accum())
461 d->glFormat.setAccumBufferSize(res);
462 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STENCIL_SIZE, &res);
463 d->glFormat.setStencil(res);
464 if (d->glFormat.stencil())
465 d->glFormat.setStencilBufferSize(res);
466 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_STEREO, &res);
467 d->glFormat.setStereo(res);
468 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLE_BUFFERS_ARB, &res);
469 d->glFormat.setSampleBuffers(res);
470 if (d->glFormat.sampleBuffers()) {
471 glXGetConfig(disp, (XVisualInfo*)d->vi, GLX_SAMPLES_ARB, &res);
472 d->glFormat.setSamples(res);
473 }
474
475 Bool direct = format().directRendering() ? True : False;
476
477 if (shareContext &&
478 (!shareContext->isValid() || !shareContext->d_func()->cx)) {
479 qWarning("QGLContext::chooseContext(): Cannot share with invalid context");
480 shareContext = 0;
481 }
482
483 // 1. Sharing between rgba and color-index will give wrong colors.
484 // 2. Contexts cannot be shared btw. direct/non-direct renderers.
485 // 3. Pixmaps cannot share contexts that are set up for direct rendering.
486 // 4. If the contexts are not created on the same screen, they can't be shared
487
488 if (shareContext
489 && (format().rgba() != shareContext->format().rgba()
490 || (deviceIsPixmap() && glXIsDirect(disp, (GLXContext)shareContext->d_func()->cx))
491 || (shareContext->d_func()->screen != xinfo->screen())))
492 {
493 shareContext = 0;
494 }
495
496 d->cx = 0;
497 if (shareContext) {
498 d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi,
499 (GLXContext)shareContext->d_func()->cx, direct);
500 d->screen = ((XVisualInfo*)d->vi)->screen;
501 if (d->cx) {
502 QGLContext *share = const_cast<QGLContext *>(shareContext);
503 d->sharing = true;
504 share->d_func()->sharing = true;
505 }
506 }
507 if (!d->cx) {
508 d->cx = glXCreateContext(disp, (XVisualInfo *)d->vi, NULL, direct);
509 d->screen = ((XVisualInfo*)d->vi)->screen;
510 }
511 if (!d->cx)
512 return false;
513 d->glFormat.setDirectRendering(glXIsDirect(disp, (GLXContext)d->cx));
514 if (deviceIsPixmap()) {
515#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
516 d->gpm = glXCreateGLXPixmapMESA(disp, (XVisualInfo *)d->vi,
517 qt_x11Handle(d->paintDevice),
518 qt_gl_choose_cmap(disp, (XVisualInfo *)d->vi));
519#else
520 d->gpm = (quint32)glXCreateGLXPixmap(disp, (XVisualInfo *)d->vi,
521 qt_x11Handle(d->paintDevice));
522#endif
523 if (!d->gpm)
524 return false;
525 }
526 QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
527 if (extensions.match("GLX_SGI_video_sync")) {
528 if (d->glFormat.swapInterval() == -1)
529 d->glFormat.setSwapInterval(0);
530 } else {
531 d->glFormat.setSwapInterval(-1);
532 }
533 return true;
534}
535
536/*
537 See qgl.cpp for qdoc comment.
538 */
539void *QGLContext::chooseVisual()
540{
541 Q_D(QGLContext);
542 static const int bufDepths[] = { 8, 4, 2, 1 }; // Try 16, 12 also?
543 //todo: if pixmap, also make sure that vi->depth == pixmap->depth
544 void* vis = 0;
545 int i = 0;
546 bool fail = false;
547 QGLFormat fmt = format();
548 bool tryDouble = !fmt.doubleBuffer(); // Some GL impl's only have double
549 bool triedDouble = false;
550 bool triedSample = false;
551 if (fmt.sampleBuffers())
552 fmt.setSampleBuffers(QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers);
553 while(!fail && !(vis = tryVisual(fmt, bufDepths[i]))) {
554 if (!fmt.rgba() && bufDepths[i] > 1) {
555 i++;
556 continue;
557 }
558 if (tryDouble) {
559 fmt.setDoubleBuffer(true);
560 tryDouble = false;
561 triedDouble = true;
562 continue;
563 } else if (triedDouble) {
564 fmt.setDoubleBuffer(false);
565 triedDouble = false;
566 }
567 if (!triedSample && fmt.sampleBuffers()) {
568 fmt.setSampleBuffers(false);
569 triedSample = true;
570 continue;
571 }
572 if (fmt.stereo()) {
573 fmt.setStereo(false);
574 continue;
575 }
576 if (fmt.accum()) {
577 fmt.setAccum(false);
578 continue;
579 }
580 if (fmt.stencil()) {
581 fmt.setStencil(false);
582 continue;
583 }
584 if (fmt.alpha()) {
585 fmt.setAlpha(false);
586 continue;
587 }
588 if (fmt.depth()) {
589 fmt.setDepth(false);
590 continue;
591 }
592 if (fmt.doubleBuffer()) {
593 fmt.setDoubleBuffer(false);
594 continue;
595 }
596 fail = true;
597 }
598 d->glFormat = fmt;
599 return vis;
600}
601
602/*
603 See qgl.cpp for qdoc comment.
604 */
605void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
606{
607 Q_D(QGLContext);
608 int spec[45];
609 int i = 0;
610 spec[i++] = GLX_LEVEL;
611 spec[i++] = f.plane();
612 const QX11Info *xinfo = qt_x11Info(d->paintDevice);
613 bool useFBConfig = false;
614
615#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) && !defined(Q_OS_HPUX)
616 /*
617 HPUX defines GLX_VERSION_1_3 but does not implement the corresponding functions.
618 Specifically glXChooseFBConfig and glXGetVisualFromFBConfig are not implemented.
619 */
620 QWidget* widget = 0;
621 if (d->paintDevice->devType() == QInternal::Widget)
622 widget = static_cast<QWidget*>(d->paintDevice);
623
624 // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual
625 if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender)
626 useFBConfig = true;
627#endif
628
629#if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info)
630 static bool useTranspExt = false;
631 static bool useTranspExtChecked = false;
632 if (f.plane() && !useTranspExtChecked && d->paintDevice) {
633 QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
634 useTranspExt = extensions.match("GLX_EXT_visual_info");
635 //# (A bit simplistic; that could theoretically be a substring)
636 if (useTranspExt) {
637 QByteArray cstr(glXGetClientString(xinfo->display(), GLX_VENDOR));
638 useTranspExt = !cstr.contains("Xi Graphics"); // bug workaround
639 if (useTranspExt) {
640 // bug workaround - some systems (eg. FireGL) refuses to return an overlay
641 // visual if the GLX_TRANSPARENT_TYPE_EXT attribute is specified, even if
642 // the implementation supports transparent overlays
643 int tmpSpec[] = { GLX_LEVEL, f.plane(), GLX_TRANSPARENT_TYPE_EXT,
644 f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT,
645 XNone };
646 XVisualInfo * vinf = glXChooseVisual(xinfo->display(), xinfo->screen(), tmpSpec);
647 if (!vinf) {
648 useTranspExt = false;
649 }
650 }
651 }
652
653 useTranspExtChecked = true;
654 }
655 if (f.plane() && useTranspExt && !useFBConfig) {
656 // Required to avoid non-transparent overlay visual(!) on some systems
657 spec[i++] = GLX_TRANSPARENT_TYPE_EXT;
658 spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT;
659 }
660#endif
661
662#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
663 // GLX_RENDER_TYPE is only in glx >=1.3
664 if (useFBConfig) {
665 spec[i++] = GLX_RENDER_TYPE;
666 spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
667 }
668#endif
669
670 if (f.doubleBuffer())
671 spec[i++] = GLX_DOUBLEBUFFER;
672 if (useFBConfig)
673 spec[i++] = True;
674 if (f.depth()) {
675 spec[i++] = GLX_DEPTH_SIZE;
676 spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize();
677 }
678 if (f.stereo()) {
679 spec[i++] = GLX_STEREO;
680 if (useFBConfig)
681 spec[i++] = True;
682 }
683 if (f.stencil()) {
684 spec[i++] = GLX_STENCIL_SIZE;
685 spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize();
686 }
687 if (f.rgba()) {
688 if (!useFBConfig)
689 spec[i++] = GLX_RGBA;
690 spec[i++] = GLX_RED_SIZE;
691 spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize();
692 spec[i++] = GLX_GREEN_SIZE;
693 spec[i++] = f.greenBufferSize() == -1 ? 1 : f.greenBufferSize();
694 spec[i++] = GLX_BLUE_SIZE;
695 spec[i++] = f.blueBufferSize() == -1 ? 1 : f.blueBufferSize();
696 if (f.alpha()) {
697 spec[i++] = GLX_ALPHA_SIZE;
698 spec[i++] = f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize();
699 }
700 if (f.accum()) {
701 spec[i++] = GLX_ACCUM_RED_SIZE;
702 spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
703 spec[i++] = GLX_ACCUM_GREEN_SIZE;
704 spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
705 spec[i++] = GLX_ACCUM_BLUE_SIZE;
706 spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
707 if (f.alpha()) {
708 spec[i++] = GLX_ACCUM_ALPHA_SIZE;
709 spec[i++] = f.accumBufferSize() == -1 ? 1 : f.accumBufferSize();
710 }
711 }
712 } else {
713 spec[i++] = GLX_BUFFER_SIZE;
714 spec[i++] = bufDepth;
715 }
716
717 if (f.sampleBuffers()) {
718 spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
719 spec[i++] = 1;
720 spec[i++] = GLX_SAMPLES_ARB;
721 spec[i++] = f.samples() == -1 ? 4 : f.samples();
722 }
723
724#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
725 if (useFBConfig) {
726 spec[i++] = GLX_DRAWABLE_TYPE;
727 switch(d->paintDevice->devType()) {
728 case QInternal::Pixmap:
729 spec[i++] = GLX_PIXMAP_BIT;
730 break;
731 case QInternal::Pbuffer:
732 spec[i++] = GLX_PBUFFER_BIT;
733 break;
734 default:
735 qWarning("QGLContext: Unknown paint device type %d", d->paintDevice->devType());
736 // Fall-through & assume it's a window
737 case QInternal::Widget:
738 spec[i++] = GLX_WINDOW_BIT;
739 break;
740 };
741 }
742#endif
743
744 spec[i] = XNone;
745
746
747 XVisualInfo* chosenVisualInfo = 0;
748
749#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
750 while (useFBConfig) {
751 GLXFBConfig *configs;
752 int configCount = 0;
753 configs = glXChooseFBConfig(xinfo->display(), xinfo->screen(), spec, &configCount);
754
755 if (!configs)
756 break; // fallback to trying glXChooseVisual
757
758 for (i = 0; i < configCount; ++i) {
759 XVisualInfo* vi;
760 vi = glXGetVisualFromFBConfig(xinfo->display(), configs[i]);
761 if (!vi)
762 continue;
763
764#if !defined(QT_NO_XRENDER)
765 QWidget* w = 0;
766 if (d->paintDevice->devType() == QInternal::Widget)
767 w = static_cast<QWidget*>(d->paintDevice);
768
769 if (w && w->testAttribute(Qt::WA_TranslucentBackground) && f.alpha()) {
770 // Attempt to find a config who's visual has a proper alpha channel
771 XRenderPictFormat *pictFormat;
772 pictFormat = XRenderFindVisualFormat(xinfo->display(), vi->visual);
773
774 if (pictFormat && (pictFormat->type == PictTypeDirect) && pictFormat->direct.alphaMask) {
775 // The pict format for the visual matching the FBConfig indicates ARGB
776 if (chosenVisualInfo)
777 XFree(chosenVisualInfo);
778 chosenVisualInfo = vi;
779 break;
780 }
781 } else
782#endif //QT_NO_XRENDER
783 if (chosenVisualInfo) {
784 // If we've got a visual we can use and we're not trying to find one with a
785 // real alpha channel, we might as well just use the one we've got
786 break;
787 }
788
789 if (!chosenVisualInfo)
790 chosenVisualInfo = vi; // Have something to fall back to
791 else
792 XFree(vi);
793 }
794
795 XFree(configs);
796 break;
797 }
798#endif // defined(GLX_VERSION_1_3)
799
800 if (!chosenVisualInfo)
801 chosenVisualInfo = glXChooseVisual(xinfo->display(), xinfo->screen(), spec);
802
803 return chosenVisualInfo;
804}
805
806
807void QGLContext::reset()
808{
809 Q_D(QGLContext);
810 if (!d->valid)
811 return;
812 d->cleanup();
813 const QX11Info *xinfo = qt_x11Info(d->paintDevice);
814 doneCurrent();
815 if (d->gpm)
816 glXDestroyGLXPixmap(xinfo->display(), (GLXPixmap)d->gpm);
817 d->gpm = 0;
818 glXDestroyContext(xinfo->display(), (GLXContext)d->cx);
819 if (d->vi)
820 XFree(d->vi);
821 d->vi = 0;
822 d->cx = 0;
823 d->crWin = false;
824 d->sharing = false;
825 d->valid = false;
826 d->transpColor = QColor();
827 d->initDone = false;
828 QGLContextGroup::removeShare(this);
829}
830
831
832void QGLContext::makeCurrent()
833{
834 Q_D(QGLContext);
835 if (!d->valid) {
836 qWarning("QGLContext::makeCurrent(): Cannot make invalid context current.");
837 return;
838 }
839 const QX11Info *xinfo = qt_x11Info(d->paintDevice);
840 bool ok = true;
841 if (d->paintDevice->devType() == QInternal::Pixmap) {
842 ok = glXMakeCurrent(xinfo->display(), (GLXPixmap)d->gpm, (GLXContext)d->cx);
843 } else if (d->paintDevice->devType() == QInternal::Pbuffer) {
844 ok = glXMakeCurrent(xinfo->display(), (GLXPbuffer)d->pbuf, (GLXContext)d->cx);
845 } else if (d->paintDevice->devType() == QInternal::Widget) {
846 ok = glXMakeCurrent(xinfo->display(), ((QWidget *)d->paintDevice)->winId(), (GLXContext)d->cx);
847 }
848 if (!ok)
849 qWarning("QGLContext::makeCurrent(): Failed.");
850
851 if (ok)
852 QGLContextPrivate::setCurrentContext(this);
853}
854
855void QGLContext::doneCurrent()
856{
857 Q_D(QGLContext);
858 glXMakeCurrent(qt_x11Info(d->paintDevice)->display(), 0, 0);
859 QGLContextPrivate::setCurrentContext(0);
860}
861
862
863void QGLContext::swapBuffers() const
864{
865 Q_D(const QGLContext);
866 if (!d->valid)
867 return;
868 if (!deviceIsPixmap()) {
869 int interval = d->glFormat.swapInterval();
870 if (interval > 0) {
871 typedef int (*qt_glXGetVideoSyncSGI)(uint *);
872 typedef int (*qt_glXWaitVideoSyncSGI)(int, int, uint *);
873 static qt_glXGetVideoSyncSGI glXGetVideoSyncSGI = 0;
874 static qt_glXWaitVideoSyncSGI glXWaitVideoSyncSGI = 0;
875 static bool resolved = false;
876 if (!resolved) {
877 const QX11Info *xinfo = qt_x11Info(d->paintDevice);
878 QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
879 if (extensions.match("GLX_SGI_video_sync")) {
880 glXGetVideoSyncSGI = (qt_glXGetVideoSyncSGI)qglx_getProcAddress("glXGetVideoSyncSGI");
881 glXWaitVideoSyncSGI = (qt_glXWaitVideoSyncSGI)qglx_getProcAddress("glXWaitVideoSyncSGI");
882 }
883 resolved = true;
884 }
885 if (glXGetVideoSyncSGI && glXWaitVideoSyncSGI) {
886 uint counter;
887 if (!glXGetVideoSyncSGI(&counter))
888 glXWaitVideoSyncSGI(interval + 1, (counter + interval) % (interval + 1), &counter);
889 }
890 }
891 glXSwapBuffers(qt_x11Info(d->paintDevice)->display(),
892 static_cast<QWidget *>(d->paintDevice)->winId());
893 }
894}
895
896QColor QGLContext::overlayTransparentColor() const
897{
898 if (isValid())
899 return Qt::transparent;
900 return QColor(); // Invalid color
901}
902
903static uint qt_transparent_pixel(VisualID id, int screen)
904{
905 for (int i = 0; i < trans_colors.size(); i++) {
906 if (trans_colors[i].vis == id && trans_colors[i].screen == screen)
907 return trans_colors[i].color;
908 }
909 return 0;
910}
911
912uint QGLContext::colorIndex(const QColor& c) const
913{
914 Q_D(const QGLContext);
915 int screen = ((XVisualInfo *)d->vi)->screen;
916 QColormap colmap = QColormap::instance(screen);
917 if (isValid()) {
918 if (format().plane() && c == Qt::transparent) {
919 return qt_transparent_pixel(((XVisualInfo *)d->vi)->visualid,
920 ((XVisualInfo *)d->vi)->screen);
921 }
922 if (((XVisualInfo*)d->vi)->visualid ==
923 XVisualIDFromVisual((Visual *) QX11Info::appVisual(screen)))
924 return colmap.pixel(c); // We're using QColor's cmap
925
926 XVisualInfo *info = (XVisualInfo *) d->vi;
927 CMapEntryHash *hash = cmap_handler()->cmap_hash;
928 CMapEntryHash::ConstIterator it = hash->constFind(long(info->visualid)
929 + (info->screen * 256));
930 QCMapEntry *x = 0;
931 if (it != hash->constEnd())
932 x = it.value();
933 if (x && !x->alloc) { // It's a standard colormap
934 int rf = (int)(((float)c.red() * (x->scmap.red_max+1))/256.0);
935 int gf = (int)(((float)c.green() * (x->scmap.green_max+1))/256.0);
936 int bf = (int)(((float)c.blue() * (x->scmap.blue_max+1))/256.0);
937 uint p = x->scmap.base_pixel
938 + (rf * x->scmap.red_mult)
939 + (gf * x->scmap.green_mult)
940 + (bf * x->scmap.blue_mult);
941 return p;
942 } else {
943 QMap<int, QRgb> &cmap = (*cmap_handler()->qglcmap_hash)[(long)info->visualid];
944
945 // already in the map?
946 QRgb target = c.rgb();
947 QMap<int, QRgb>::Iterator it = cmap.begin();
948 for (; it != cmap.end(); ++it) {
949 if ((*it) == target)
950 return it.key();
951 }
952
953 // need to alloc color
954 unsigned long plane_mask[2];
955 unsigned long color_map_entry;
956 if (!XAllocColorCells (QX11Info::display(), x->cmap, true, plane_mask, 0,
957 &color_map_entry, 1))
958 return colmap.pixel(c);
959
960 XColor col;
961 col.flags = DoRed | DoGreen | DoBlue;
962 col.pixel = color_map_entry;
963 col.red = (ushort)((qRed(c.rgb()) / 255.0) * 65535.0 + 0.5);
964 col.green = (ushort)((qGreen(c.rgb()) / 255.0) * 65535.0 + 0.5);
965 col.blue = (ushort)((qBlue(c.rgb()) / 255.0) * 65535.0 + 0.5);
966 XStoreColor(QX11Info::display(), x->cmap, &col);
967
968 cmap.insert(color_map_entry, target);
969 return color_map_entry;
970 }
971 }
972 return 0;
973}
974
975#ifndef QT_NO_FONTCONFIG
976/*! \internal
977 This is basically a substitute for glxUseXFont() which can only
978 handle XLFD fonts. This version relies on freetype to render the
979 glyphs, but it works with all fonts that fontconfig provides - both
980 antialiased and aliased bitmap and outline fonts.
981*/
982static void qgl_use_font(QFontEngineFT *engine, int first, int count, int listBase)
983{
984 GLfloat color[4];
985 glGetFloatv(GL_CURRENT_COLOR, color);
986
987 // save the pixel unpack state
988 GLint gl_swapbytes, gl_lsbfirst, gl_rowlength, gl_skiprows, gl_skippixels, gl_alignment;
989 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &gl_swapbytes);
990 glGetIntegerv (GL_UNPACK_LSB_FIRST, &gl_lsbfirst);
991 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &gl_rowlength);
992 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &gl_skiprows);
993 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &gl_skippixels);
994 glGetIntegerv (GL_UNPACK_ALIGNMENT, &gl_alignment);
995
996 glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
997 glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
998 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
999 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
1000 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
1001 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1002
1003 const bool antialiased = engine->drawAntialiased();
1004 FT_Face face = engine->lockFace();
1005
1006 // start generating font glyphs
1007 for (int i = first; i < count; ++i) {
1008 int list = listBase + i;
1009 GLfloat x0, y0, dx, dy;
1010
1011 FT_Error err;
1012
1013 err = FT_Load_Glyph(face, FT_Get_Char_Index(face, i), FT_LOAD_DEFAULT);
1014 if (err) {
1015 qDebug("failed loading glyph %d from font", i);
1016 Q_ASSERT(!err);
1017 }
1018 err = FT_Render_Glyph(face->glyph, (antialiased ? FT_RENDER_MODE_NORMAL
1019 : FT_RENDER_MODE_MONO));
1020 if (err) {
1021 qDebug("failed rendering glyph %d from font", i);
1022 Q_ASSERT(!err);
1023 }
1024
1025 FT_Bitmap bm = face->glyph->bitmap;
1026 x0 = face->glyph->metrics.horiBearingX >> 6;
1027 y0 = (face->glyph->metrics.height - face->glyph->metrics.horiBearingY) >> 6;
1028 dx = face->glyph->metrics.horiAdvance >> 6;
1029 dy = 0;
1030 int sz = bm.pitch * bm.rows;
1031 uint *aa_glyph = 0;
1032 uchar *ua_glyph = 0;
1033
1034 if (antialiased)
1035 aa_glyph = new uint[sz];
1036 else
1037 ua_glyph = new uchar[sz];
1038
1039 // convert to GL format
1040 for (int y = 0; y < bm.rows; ++y) {
1041 for (int x = 0; x < bm.pitch; ++x) {
1042 int c1 = y*bm.pitch + x;
1043 int c2 = (bm.rows - y - 1) > 0 ? (bm.rows-y-1)*bm.pitch + x : x;
1044 if (antialiased) {
1045 aa_glyph[c1] = (int(color[0]*255) << 24)
1046 | (int(color[1]*255) << 16)
1047 | (int(color[2]*255) << 8) | bm.buffer[c2];
1048 } else {
1049 ua_glyph[c1] = bm.buffer[c2];
1050 }
1051 }
1052 }
1053
1054 glNewList(list, GL_COMPILE);
1055 if (antialiased) {
1056 // calling glBitmap() is just a trick to move the current
1057 // raster pos, since glGet*() won't work in display lists
1058 glBitmap(0, 0, 0, 0, x0, -y0, 0);
1059 glDrawPixels(bm.pitch, bm.rows, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, aa_glyph);
1060 glBitmap(0, 0, 0, 0, dx-x0, y0, 0);
1061 } else {
1062 glBitmap(bm.pitch*8, bm.rows, -x0, y0, dx, dy, ua_glyph);
1063 }
1064 glEndList();
1065 antialiased ? delete[] aa_glyph : delete[] ua_glyph;
1066 }
1067
1068 engine->unlockFace();
1069
1070 // restore pixel unpack settings
1071 glPixelStorei(GL_UNPACK_SWAP_BYTES, gl_swapbytes);
1072 glPixelStorei(GL_UNPACK_LSB_FIRST, gl_lsbfirst);
1073 glPixelStorei(GL_UNPACK_ROW_LENGTH, gl_rowlength);
1074 glPixelStorei(GL_UNPACK_SKIP_ROWS, gl_skiprows);
1075 glPixelStorei(GL_UNPACK_SKIP_PIXELS, gl_skippixels);
1076 glPixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment);
1077}
1078#endif
1079
1080#undef d
1081void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase)
1082{
1083 QFont f(fnt);
1084 QFontEngine *engine = f.d->engineForScript(QUnicodeTables::Common);
1085
1086 if (engine->type() == QFontEngine::Multi)
1087 engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
1088#ifndef QT_NO_FONTCONFIG
1089 if(engine->type() == QFontEngine::Freetype) {
1090 qgl_use_font(static_cast<QFontEngineFT *>(engine), 0, 256, listBase);
1091 return;
1092 }
1093#endif
1094 // glXUseXFont() only works with XLFD font structures and a few GL
1095 // drivers crash if 0 is passed as the font handle
1096 f.setStyleStrategy(QFont::OpenGLCompatible);
1097 if (f.handle() && engine->type() == QFontEngine::XLFD)
1098 glXUseXFont(static_cast<Font>(f.handle()), 0, 256, listBase);
1099}
1100
1101void *QGLContext::getProcAddress(const QString &proc) const
1102{
1103 typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *);
1104 static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
1105 static bool resolved = false;
1106
1107 if (resolved && !glXGetProcAddressARB)
1108 return 0;
1109 if (!glXGetProcAddressARB) {
1110 QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
1111 if (extensions.match("GLX_ARB_get_proc_address")) {
1112#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
1113 void *handle = dlopen(NULL, RTLD_LAZY);
1114 if (handle) {
1115 glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
1116 dlclose(handle);
1117 }
1118 if (!glXGetProcAddressARB)
1119#endif
1120 {
1121#if !defined(QT_NO_LIBRARY)
1122 extern const QString qt_gl_library_name();
1123 QLibrary lib(qt_gl_library_name());
1124 glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
1125#endif
1126 }
1127 }
1128 resolved = true;
1129 }
1130 if (!glXGetProcAddressARB)
1131 return 0;
1132 return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(proc.toLatin1().data()));
1133}
1134
1135/*
1136 QGLTemporaryContext implementation
1137*/
1138
1139class QGLTemporaryContextPrivate {
1140public:
1141 bool initialized;
1142 Window drawable;
1143 GLXContext context;
1144 GLXDrawable oldDrawable;
1145 GLXContext oldContext;
1146};
1147
1148QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
1149 : d(new QGLTemporaryContextPrivate)
1150{
1151 d->initialized = false;
1152 d->oldDrawable = 0;
1153 d->oldContext = 0;
1154 int screen = 0;
1155
1156 int attribs[] = {GLX_RGBA, XNone};
1157 XVisualInfo *vi = glXChooseVisual(X11->display, screen, attribs);
1158 if (!vi) {
1159 qWarning("QGLTempContext: No GL capable X visuals available.");
1160 return;
1161 }
1162
1163 int useGL;
1164 glXGetConfig(X11->display, vi, GLX_USE_GL, &useGL);
1165 if (!useGL) {
1166 XFree(vi);
1167 return;
1168 }
1169
1170 d->oldDrawable = glXGetCurrentDrawable();
1171 d->oldContext = glXGetCurrentContext();
1172
1173 XSetWindowAttributes a;
1174 a.colormap = qt_gl_choose_cmap(X11->display, vi);
1175 d->drawable = XCreateWindow(X11->display, RootWindow(X11->display, screen),
1176 0, 0, 1, 1, 0,
1177 vi->depth, InputOutput, vi->visual,
1178 CWColormap, &a);
1179 d->context = glXCreateContext(X11->display, vi, 0, True);
1180 if (d->context && glXMakeCurrent(X11->display, d->drawable, d->context)) {
1181 d->initialized = true;
1182 } else {
1183 qWarning("QGLTempContext: Unable to create GL context.");
1184 XDestroyWindow(X11->display, d->drawable);
1185 }
1186 XFree(vi);
1187}
1188
1189QGLTemporaryContext::~QGLTemporaryContext()
1190{
1191 if (d->initialized) {
1192 glXMakeCurrent(X11->display, 0, 0);
1193 glXDestroyContext(X11->display, d->context);
1194 XDestroyWindow(X11->display, d->drawable);
1195 }
1196 if (d->oldDrawable && d->oldContext)
1197 glXMakeCurrent(X11->display, d->oldDrawable, d->oldContext);
1198}
1199
1200/*****************************************************************************
1201 QGLOverlayWidget (Internal overlay class for X11)
1202 *****************************************************************************/
1203
1204class QGLOverlayWidget : public QGLWidget
1205{
1206 Q_OBJECT
1207public:
1208 QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, const QGLWidget* shareWidget=0);
1209
1210protected:
1211 void initializeGL();
1212 void paintGL();
1213 void resizeGL(int w, int h);
1214 bool x11Event(XEvent *e) { return realWidget->x11Event(e); }
1215
1216private:
1217 QGLWidget* realWidget;
1218
1219private:
1220 Q_DISABLE_COPY(QGLOverlayWidget)
1221};
1222
1223
1224QGLOverlayWidget::QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent,
1225 const QGLWidget* shareWidget)
1226 : QGLWidget(format, parent, shareWidget ? shareWidget->d_func()->olw : 0)
1227{
1228 setAttribute(Qt::WA_X11OpenGLOverlay);
1229 realWidget = parent;
1230}
1231
1232
1233
1234void QGLOverlayWidget::initializeGL()
1235{
1236 QColor transparentColor = context()->overlayTransparentColor();
1237 if (transparentColor.isValid())
1238 qglClearColor(transparentColor);
1239 else
1240 qWarning("QGLOverlayWidget::initializeGL(): Could not get transparent color");
1241 realWidget->initializeOverlayGL();
1242}
1243
1244
1245void QGLOverlayWidget::resizeGL(int w, int h)
1246{
1247 glViewport(0, 0, w, h);
1248 realWidget->resizeOverlayGL(w, h);
1249}
1250
1251
1252void QGLOverlayWidget::paintGL()
1253{
1254 realWidget->paintOverlayGL();
1255}
1256
1257#undef Bool
1258QT_BEGIN_INCLUDE_NAMESPACE
1259#include "qgl_x11.moc"
1260QT_END_INCLUDE_NAMESPACE
1261
1262/*****************************************************************************
1263 QGLWidget UNIX/GLX-specific code
1264 *****************************************************************************/
1265void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
1266{
1267 Q_Q(QGLWidget);
1268 initContext(context, shareWidget);
1269 olw = 0;
1270
1271 if (q->isValid() && context->format().hasOverlay()) {
1272 QString olwName = q->objectName();
1273 olwName += QLatin1String("-QGL_internal_overlay_widget");
1274 olw = new QGLOverlayWidget(QGLFormat::defaultOverlayFormat(), q, shareWidget);
1275 olw->setObjectName(olwName);
1276 if (olw->isValid()) {
1277 olw->setAutoBufferSwap(false);
1278 olw->setFocusProxy(q);
1279 }
1280 else {
1281 delete olw;
1282 olw = 0;
1283 glcx->d_func()->glFormat.setOverlay(false);
1284 }
1285 }
1286}
1287
1288bool QGLWidgetPrivate::renderCxPm(QPixmap* pm)
1289{
1290 Q_Q(QGLWidget);
1291 if (((XVisualInfo*)glcx->d_func()->vi)->depth != pm->depth())
1292 return false;
1293
1294 GLXPixmap glPm;
1295#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
1296 glPm = glXCreateGLXPixmapMESA(X11->display,
1297 (XVisualInfo*)glcx->vi,
1298 (Pixmap)pm->handle(),
1299 qt_gl_choose_cmap(pm->X11->display,
1300 (XVisualInfo*)glcx->vi));
1301#else
1302 glPm = (quint32)glXCreateGLXPixmap(X11->display,
1303 (XVisualInfo*)glcx->d_func()->vi,
1304 (Pixmap)pm->handle());
1305#endif
1306
1307 if (!glXMakeCurrent(X11->display, glPm, (GLXContext)glcx->d_func()->cx)) {
1308 glXDestroyGLXPixmap(X11->display, glPm);
1309 return false;
1310 }
1311
1312 glDrawBuffer(GL_FRONT);
1313 if (!glcx->initialized())
1314 q->glInit();
1315 q->resizeGL(pm->width(), pm->height());
1316 q->paintGL();
1317 glFlush();
1318 q->makeCurrent();
1319 glXDestroyGLXPixmap(X11->display, glPm);
1320 q->resizeGL(q->width(), q->height());
1321 return true;
1322}
1323
1324/*! \internal
1325 Free up any allocated colormaps. This fn is only called for
1326 top-level widgets.
1327*/
1328void QGLWidgetPrivate::cleanupColormaps()
1329{
1330 if (!cmap.handle()) {
1331 return;
1332 } else {
1333 XFreeColormap(X11->display, (Colormap) cmap.handle());
1334 cmap.setHandle(0);
1335 }
1336}
1337
1338void QGLWidget::setMouseTracking(bool enable)
1339{
1340 Q_D(QGLWidget);
1341 if (d->olw)
1342 d->olw->setMouseTracking(enable);
1343 QWidget::setMouseTracking(enable);
1344}
1345
1346
1347void QGLWidget::resizeEvent(QResizeEvent *)
1348{
1349 Q_D(QGLWidget);
1350 if (!isValid())
1351 return;
1352 makeCurrent();
1353 if (!d->glcx->initialized())
1354 glInit();
1355 glXWaitX();
1356 resizeGL(width(), height());
1357 if (d->olw)
1358 d->olw->setGeometry(rect());
1359}
1360
1361const QGLContext* QGLWidget::overlayContext() const
1362{
1363 Q_D(const QGLWidget);
1364 if (d->olw)
1365 return d->olw->context();
1366 else
1367 return 0;
1368}
1369
1370
1371void QGLWidget::makeOverlayCurrent()
1372{
1373 Q_D(QGLWidget);
1374 if (d->olw)
1375 d->olw->makeCurrent();
1376}
1377
1378
1379void QGLWidget::updateOverlayGL()
1380{
1381 Q_D(QGLWidget);
1382 if (d->olw)
1383 d->olw->updateGL();
1384}
1385
1386/*!
1387 \internal
1388
1389 Sets a new QGLContext, \a context, for this QGLWidget, using the
1390 shared context, \a shareContext. If \a deleteOldContext is true,
1391 the original context is deleted; otherwise it is overridden.
1392*/
1393void QGLWidget::setContext(QGLContext *context,
1394 const QGLContext* shareContext,
1395 bool deleteOldContext)
1396{
1397 Q_D(QGLWidget);
1398 if (context == 0) {
1399 qWarning("QGLWidget::setContext: Cannot set null context");
1400 return;
1401 }
1402 if (!context->deviceIsPixmap() && context->device() != this) {
1403 qWarning("QGLWidget::setContext: Context must refer to this widget");
1404 return;
1405 }
1406
1407 if (d->glcx)
1408 d->glcx->doneCurrent();
1409 QGLContext* oldcx = d->glcx;
1410 d->glcx = context;
1411
1412 if (parentWidget()) {
1413 // force creation of delay-created widgets
1414 parentWidget()->winId();
1415 if (parentWidget()->x11Info().screen() != x11Info().screen())
1416 d_func()->xinfo = parentWidget()->d_func()->xinfo;
1417 }
1418
1419 // If the application has set WA_TranslucentBackground and not explicitly set
1420 // the alpha buffer size to zero, modify the format so it have an alpha channel
1421 QGLFormat& fmt = d->glcx->d_func()->glFormat;
1422 if (testAttribute(Qt::WA_TranslucentBackground) && fmt.alphaBufferSize() == -1)
1423 fmt.setAlphaBufferSize(1);
1424
1425 bool createFailed = false;
1426 if (!d->glcx->isValid()) {
1427 if (!d->glcx->create(shareContext ? shareContext : oldcx))
1428 createFailed = true;
1429 }
1430 if (createFailed) {
1431 if (deleteOldContext)
1432 delete oldcx;
1433 return;
1434 }
1435
1436 if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
1437 if (deleteOldContext)
1438 delete oldcx;
1439 return;
1440 }
1441
1442 bool visible = isVisible();
1443 if (visible)
1444 hide();
1445
1446 XVisualInfo *vi = (XVisualInfo*)d->glcx->d_func()->vi;
1447 XSetWindowAttributes a;
1448
1449 QColormap colmap = QColormap::instance(vi->screen);
1450 a.colormap = qt_gl_choose_cmap(QX11Info::display(), vi); // find best colormap
1451 a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
1452 a.border_pixel = colmap.pixel(Qt::black);
1453 Window p = RootWindow(X11->display, vi->screen);
1454 if (parentWidget())
1455 p = parentWidget()->winId();
1456
1457 Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(),
1458 0, vi->depth, InputOutput, vi->visual,
1459 CWBackPixel|CWBorderPixel|CWColormap, &a);
1460 Window *cmw;
1461 Window *cmwret;
1462 int count;
1463 if (XGetWMColormapWindows(X11->display, window()->winId(),
1464 &cmwret, &count)) {
1465 cmw = new Window[count+1];
1466 memcpy((char *)cmw, (char *)cmwret, sizeof(Window)*count);
1467 XFree((char *)cmwret);
1468 int i;
1469 for (i=0; i<count; i++) {
1470 if (cmw[i] == winId()) { // replace old window
1471 cmw[i] = w;
1472 break;
1473 }
1474 }
1475 if (i >= count) // append new window
1476 cmw[count++] = w;
1477 } else {
1478 count = 1;
1479 cmw = new Window[count];
1480 cmw[0] = w;
1481 }
1482
1483#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
1484 if (oldcx && oldcx->windowCreated())
1485 glXReleaseBuffersMESA(X11->display, winId());
1486#endif
1487 if (deleteOldContext)
1488 delete oldcx;
1489 oldcx = 0;
1490
1491 if (testAttribute(Qt::WA_WState_Created))
1492 create(w);
1493 else
1494 d->createWinId(w);
1495 XSetWMColormapWindows(X11->display, window()->winId(), cmw, count);
1496 delete [] cmw;
1497
1498 // calling QWidget::create() will always result in a new paint
1499 // engine being created - get rid of it and replace it with our
1500 // own
1501
1502 if (visible)
1503 show();
1504 XFlush(X11->display);
1505 d->glcx->setWindowCreated(true);
1506}
1507
1508const QGLColormap & QGLWidget::colormap() const
1509{
1510 Q_D(const QGLWidget);
1511 return d->cmap;
1512}
1513
1514/*\internal
1515 Store color values in the given colormap.
1516*/
1517static void qStoreColors(QWidget * tlw, Colormap cmap,
1518 const QGLColormap & cols)
1519{
1520 Q_UNUSED(tlw);
1521 XColor c;
1522 QRgb color;
1523
1524 for (int i = 0; i < cols.size(); i++) {
1525 color = cols.entryRgb(i);
1526 c.pixel = i;
1527 c.red = (ushort)((qRed(color) / 255.0) * 65535.0 + 0.5);
1528 c.green = (ushort)((qGreen(color) / 255.0) * 65535.0 + 0.5);
1529 c.blue = (ushort)((qBlue(color) / 255.0) * 65535.0 + 0.5);
1530 c.flags = DoRed | DoGreen | DoBlue;
1531 XStoreColor(X11->display, cmap, &c);
1532 }
1533}
1534
1535/*\internal
1536 Check whether the given visual supports dynamic colormaps or not.
1537*/
1538static bool qCanAllocColors(QWidget * w)
1539{
1540 bool validVisual = false;
1541 int numVisuals;
1542 long mask;
1543 XVisualInfo templ;
1544 XVisualInfo * visuals;
1545 VisualID id = XVisualIDFromVisual((Visual *) w->window()->x11Info().visual());
1546
1547 mask = VisualScreenMask;
1548 templ.screen = w->x11Info().screen();
1549 visuals = XGetVisualInfo(X11->display, mask, &templ, &numVisuals);
1550
1551 for (int i = 0; i < numVisuals; i++) {
1552 if (visuals[i].visualid == id) {
1553 switch (visuals[i].c_class) {
1554 case TrueColor:
1555 case StaticColor:
1556 case StaticGray:
1557 case XGrayScale:
1558 validVisual = false;
1559 break;
1560 case DirectColor:
1561 case PseudoColor:
1562 validVisual = true;
1563 break;
1564 }
1565 break;
1566 }
1567 }
1568 XFree(visuals);
1569
1570 if (!validVisual)
1571 return false;
1572 return true;
1573}
1574
1575
1576void QGLWidget::setColormap(const QGLColormap & c)
1577{
1578 Q_D(QGLWidget);
1579 QWidget * tlw = window(); // must return a valid widget
1580
1581 d->cmap = c;
1582 if (!d->cmap.handle())
1583 return;
1584
1585 if (!qCanAllocColors(this)) {
1586 qWarning("QGLWidget::setColormap: Cannot create a read/write "
1587 "colormap for this visual");
1588 return;
1589 }
1590
1591 // If the child GL widget is not of the same visual class as the
1592 // toplevel widget we will get in trouble..
1593 Window wid = tlw->winId();
1594 Visual * vis = (Visual *) tlw->x11Info().visual();;
1595 VisualID cvId = XVisualIDFromVisual((Visual *) x11Info().visual());
1596 VisualID tvId = XVisualIDFromVisual((Visual *) tlw->x11Info().visual());
1597 if (cvId != tvId) {
1598 wid = winId();
1599 vis = (Visual *) x11Info().visual();
1600 }
1601
1602 if (!d->cmap.handle()) // allocate a cmap if necessary
1603 d->cmap.setHandle(XCreateColormap(X11->display, wid, vis, AllocAll));
1604
1605 qStoreColors(this, (Colormap) d->cmap.handle(), c);
1606 XSetWindowColormap(X11->display, wid, (Colormap) d->cmap.handle());
1607
1608 // tell the wm that this window has a special colormap
1609 Window * cmw;
1610 Window * cmwret;
1611 int count;
1612 if (XGetWMColormapWindows(X11->display, tlw->winId(), &cmwret, &count))
1613 {
1614 cmw = new Window[count+1];
1615 memcpy((char *) cmw, (char *) cmwret, sizeof(Window) * count);
1616 XFree((char *) cmwret);
1617 int i;
1618 for (i = 0; i < count; i++) {
1619 if (cmw[i] == winId()) {
1620 break;
1621 }
1622 }
1623 if (i >= count) // append new window only if not in the list
1624 cmw[count++] = winId();
1625 } else {
1626 count = 1;
1627 cmw = new Window[count];
1628 cmw[0] = winId();
1629 }
1630 XSetWMColormapWindows(X11->display, tlw->winId(), cmw, count);
1631 delete [] cmw;
1632}
1633
1634// Solaris defines glXBindTexImageEXT as part of the GL library
1635#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1636typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*);
1637typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int);
1638static qt_glXBindTexImageEXT glXBindTexImageEXT = 0;
1639static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0;
1640
1641static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice)
1642{
1643 static bool resolvedTextureFromPixmap = false;
1644
1645 if (!resolvedTextureFromPixmap) {
1646 resolvedTextureFromPixmap = true;
1647
1648 // Check to see if we have NPOT texture support
1649 if ( !(QGLExtensions::glExtensions() & QGLExtensions::NPOTTextures) &&
1650 !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0))
1651 {
1652 return false; // Can't use TFP without NPOT
1653 }
1654
1655 const QX11Info *xinfo = qt_x11Info(paintDevice);
1656 Display *display = xinfo ? xinfo->display() : X11->display;
1657 int screen = xinfo ? xinfo->screen() : X11->defaultScreen;
1658
1659 QGLExtensionMatcher serverExtensions(glXQueryExtensionsString(display, screen));
1660 QGLExtensionMatcher clientExtensions(glXGetClientString(display, GLX_EXTENSIONS));
1661 if (serverExtensions.match("GLX_EXT_texture_from_pixmap")
1662 && clientExtensions.match("GLX_EXT_texture_from_pixmap"))
1663 {
1664 glXBindTexImageEXT = (qt_glXBindTexImageEXT) qglx_getProcAddress("glXBindTexImageEXT");
1665 glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) qglx_getProcAddress("glXReleaseTexImageEXT");
1666 }
1667 }
1668
1669 return glXBindTexImageEXT && glXReleaseTexImageEXT;
1670}
1671#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1672
1673
1674QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key,
1675 QGLContext::BindOptions options)
1676{
1677#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
1678 return 0;
1679#else
1680 Q_Q(QGLContext);
1681
1682 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1683
1684 if (!qt_resolveTextureFromPixmap(paintDevice))
1685 return 0;
1686
1687 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1688 const QX11Info &x11Info = pixmapData->xinfo;
1689
1690 // Store the configs (Can be static because configs aren't dependent on current context)
1691 static GLXFBConfig glxRGBPixmapConfig = 0;
1692 static bool RGBConfigInverted = false;
1693 static GLXFBConfig glxRGBAPixmapConfig = 0;
1694 static bool RGBAConfigInverted = false;
1695
1696 bool hasAlpha = pixmapData->hasAlphaChannel();
1697
1698 // Check to see if we need a config
1699 if ( (hasAlpha && !glxRGBAPixmapConfig) || (!hasAlpha && !glxRGBPixmapConfig) ) {
1700 GLXFBConfig *configList = 0;
1701 int configCount = 0;
1702
1703 int configAttribs[] = {
1704 hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True,
1705 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
1706 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
1707 // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can:
1708 GLX_Y_INVERTED_EXT, options & QGLContext::CanFlipNativePixmapBindOption ? GLX_DONT_CARE : False,
1709 XNone
1710 };
1711 configList = glXChooseFBConfig(x11Info.display(), x11Info.screen(), configAttribs, &configCount);
1712 if (!configList)
1713 return 0;
1714
1715 int yInv;
1716 glXGetFBConfigAttrib(x11Info.display(), configList[0], GLX_Y_INVERTED_EXT, &yInv);
1717
1718 if (hasAlpha) {
1719 glxRGBAPixmapConfig = configList[0];
1720 RGBAConfigInverted = yInv;
1721 }
1722 else {
1723 glxRGBPixmapConfig = configList[0];
1724 RGBConfigInverted = yInv;
1725 }
1726
1727 XFree(configList);
1728 }
1729
1730 // Check to see if the surface is still valid
1731 if (pixmapData->gl_surface &&
1732 hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
1733 {
1734 // Surface is invalid!
1735 destroyGlSurfaceForPixmap(pixmapData);
1736 }
1737
1738 // Check to see if we need a surface
1739 if (!pixmapData->gl_surface) {
1740 GLXPixmap glxPixmap;
1741 int pixmapAttribs[] = {
1742 GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
1743 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
1744 GLX_MIPMAP_TEXTURE_EXT, False, // Maybe needs to be don't care
1745 XNone
1746 };
1747
1748 // Wrap the X Pixmap into a GLXPixmap:
1749 glxPixmap = glXCreatePixmap(x11Info.display(),
1750 hasAlpha ? glxRGBAPixmapConfig : glxRGBPixmapConfig,
1751 pixmapData->handle(), pixmapAttribs);
1752
1753 if (!glxPixmap)
1754 return 0;
1755
1756 pixmapData->gl_surface = (Qt::HANDLE)glxPixmap;
1757
1758 // Make sure the cleanup hook gets called so we can delete the glx pixmap
1759 QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData);
1760 }
1761
1762 GLuint textureId;
1763 glGenTextures(1, &textureId);
1764 glBindTexture(GL_TEXTURE_2D, textureId);
1765 glXBindTexImageEXT(x11Info.display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT, 0);
1766
1767 glBindTexture(GL_TEXTURE_2D, textureId);
1768
1769 if (!((hasAlpha && RGBAConfigInverted) || (!hasAlpha && RGBConfigInverted)))
1770 options &= ~QGLContext::InvertedYBindOption;
1771
1772 QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
1773 if (texture->options & QGLContext::InvertedYBindOption)
1774 pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
1775
1776 // We assume the cost of bound pixmaps is zero
1777 QGLTextureCache::instance()->insert(q, key, texture, 0);
1778
1779 return texture;
1780#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
1781}
1782
1783
1784void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
1785{
1786#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1787 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1788 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1789 if (pixmapData->gl_surface) {
1790 glXDestroyPixmap(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface);
1791 pixmapData->gl_surface = 0;
1792 }
1793#endif
1794}
1795
1796void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
1797{
1798#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1799 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1800 Q_ASSERT(QGLContext::currentContext());
1801 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1802 if (pixmapData->gl_surface)
1803 glXReleaseTexImageEXT(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT);
1804#endif
1805}
1806
1807QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.