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

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

trunk: Merged in qt 4.6.1 sources.

File size: 57.7 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#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 qgl_share_reg()->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 QGLOverlayWidget (Internal overlay class for X11)
1137 *****************************************************************************/
1138
1139class QGLOverlayWidget : public QGLWidget
1140{
1141 Q_OBJECT
1142public:
1143 QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent, const QGLWidget* shareWidget=0);
1144
1145protected:
1146 void initializeGL();
1147 void paintGL();
1148 void resizeGL(int w, int h);
1149 bool x11Event(XEvent *e) { return realWidget->x11Event(e); }
1150
1151private:
1152 QGLWidget* realWidget;
1153
1154private:
1155 Q_DISABLE_COPY(QGLOverlayWidget)
1156};
1157
1158
1159QGLOverlayWidget::QGLOverlayWidget(const QGLFormat& format, QGLWidget* parent,
1160 const QGLWidget* shareWidget)
1161 : QGLWidget(format, parent, shareWidget ? shareWidget->d_func()->olw : 0)
1162{
1163 setAttribute(Qt::WA_X11OpenGLOverlay);
1164 realWidget = parent;
1165}
1166
1167
1168
1169void QGLOverlayWidget::initializeGL()
1170{
1171 QColor transparentColor = context()->overlayTransparentColor();
1172 if (transparentColor.isValid())
1173 qglClearColor(transparentColor);
1174 else
1175 qWarning("QGLOverlayWidget::initializeGL(): Could not get transparent color");
1176 realWidget->initializeOverlayGL();
1177}
1178
1179
1180void QGLOverlayWidget::resizeGL(int w, int h)
1181{
1182 glViewport(0, 0, w, h);
1183 realWidget->resizeOverlayGL(w, h);
1184}
1185
1186
1187void QGLOverlayWidget::paintGL()
1188{
1189 realWidget->paintOverlayGL();
1190}
1191
1192#undef Bool
1193QT_BEGIN_INCLUDE_NAMESPACE
1194#include "qgl_x11.moc"
1195QT_END_INCLUDE_NAMESPACE
1196
1197/*****************************************************************************
1198 QGLWidget UNIX/GLX-specific code
1199 *****************************************************************************/
1200void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget)
1201{
1202 Q_Q(QGLWidget);
1203 initContext(context, shareWidget);
1204 olw = 0;
1205
1206 if (q->isValid() && context->format().hasOverlay()) {
1207 QString olwName = q->objectName();
1208 olwName += QLatin1String("-QGL_internal_overlay_widget");
1209 olw = new QGLOverlayWidget(QGLFormat::defaultOverlayFormat(), q, shareWidget);
1210 olw->setObjectName(olwName);
1211 if (olw->isValid()) {
1212 olw->setAutoBufferSwap(false);
1213 olw->setFocusProxy(q);
1214 }
1215 else {
1216 delete olw;
1217 olw = 0;
1218 glcx->d_func()->glFormat.setOverlay(false);
1219 }
1220 }
1221}
1222
1223bool QGLWidgetPrivate::renderCxPm(QPixmap* pm)
1224{
1225 Q_Q(QGLWidget);
1226 if (((XVisualInfo*)glcx->d_func()->vi)->depth != pm->depth())
1227 return false;
1228
1229 GLXPixmap glPm;
1230#if defined(GLX_MESA_pixmap_colormap) && defined(QGL_USE_MESA_EXT)
1231 glPm = glXCreateGLXPixmapMESA(X11->display,
1232 (XVisualInfo*)glcx->vi,
1233 (Pixmap)pm->handle(),
1234 qt_gl_choose_cmap(pm->X11->display,
1235 (XVisualInfo*)glcx->vi));
1236#else
1237 glPm = (quint32)glXCreateGLXPixmap(X11->display,
1238 (XVisualInfo*)glcx->d_func()->vi,
1239 (Pixmap)pm->handle());
1240#endif
1241
1242 if (!glXMakeCurrent(X11->display, glPm, (GLXContext)glcx->d_func()->cx)) {
1243 glXDestroyGLXPixmap(X11->display, glPm);
1244 return false;
1245 }
1246
1247 glDrawBuffer(GL_FRONT);
1248 if (!glcx->initialized())
1249 q->glInit();
1250 q->resizeGL(pm->width(), pm->height());
1251 q->paintGL();
1252 glFlush();
1253 q->makeCurrent();
1254 glXDestroyGLXPixmap(X11->display, glPm);
1255 q->resizeGL(q->width(), q->height());
1256 return true;
1257}
1258
1259/*! \internal
1260 Free up any allocated colormaps. This fn is only called for
1261 top-level widgets.
1262*/
1263void QGLWidgetPrivate::cleanupColormaps()
1264{
1265 if (!cmap.handle()) {
1266 return;
1267 } else {
1268 XFreeColormap(X11->display, (Colormap) cmap.handle());
1269 cmap.setHandle(0);
1270 }
1271}
1272
1273void QGLWidget::setMouseTracking(bool enable)
1274{
1275 Q_D(QGLWidget);
1276 if (d->olw)
1277 d->olw->setMouseTracking(enable);
1278 QWidget::setMouseTracking(enable);
1279}
1280
1281
1282void QGLWidget::resizeEvent(QResizeEvent *)
1283{
1284 Q_D(QGLWidget);
1285 if (!isValid())
1286 return;
1287 makeCurrent();
1288 if (!d->glcx->initialized())
1289 glInit();
1290 glXWaitX();
1291 resizeGL(width(), height());
1292 if (d->olw)
1293 d->olw->setGeometry(rect());
1294}
1295
1296const QGLContext* QGLWidget::overlayContext() const
1297{
1298 Q_D(const QGLWidget);
1299 if (d->olw)
1300 return d->olw->context();
1301 else
1302 return 0;
1303}
1304
1305
1306void QGLWidget::makeOverlayCurrent()
1307{
1308 Q_D(QGLWidget);
1309 if (d->olw)
1310 d->olw->makeCurrent();
1311}
1312
1313
1314void QGLWidget::updateOverlayGL()
1315{
1316 Q_D(QGLWidget);
1317 if (d->olw)
1318 d->olw->updateGL();
1319}
1320
1321/*!
1322 \internal
1323
1324 Sets a new QGLContext, \a context, for this QGLWidget, using the
1325 shared context, \a shareContext. If \a deleteOldContext is true,
1326 the original context is deleted; otherwise it is overridden.
1327*/
1328void QGLWidget::setContext(QGLContext *context,
1329 const QGLContext* shareContext,
1330 bool deleteOldContext)
1331{
1332 Q_D(QGLWidget);
1333 if (context == 0) {
1334 qWarning("QGLWidget::setContext: Cannot set null context");
1335 return;
1336 }
1337 if (!context->deviceIsPixmap() && context->device() != this) {
1338 qWarning("QGLWidget::setContext: Context must refer to this widget");
1339 return;
1340 }
1341
1342 if (d->glcx)
1343 d->glcx->doneCurrent();
1344 QGLContext* oldcx = d->glcx;
1345 d->glcx = context;
1346
1347 if (parentWidget()) {
1348 // force creation of delay-created widgets
1349 parentWidget()->winId();
1350 if (parentWidget()->x11Info().screen() != x11Info().screen())
1351 d_func()->xinfo = parentWidget()->d_func()->xinfo;
1352 }
1353
1354 // If the application has set WA_TranslucentBackground and not explicitly set
1355 // the alpha buffer size to zero, modify the format so it have an alpha channel
1356 QGLFormat& fmt = d->glcx->d_func()->glFormat;
1357 if (testAttribute(Qt::WA_TranslucentBackground) && fmt.alphaBufferSize() == -1)
1358 fmt.setAlphaBufferSize(1);
1359
1360 bool createFailed = false;
1361 if (!d->glcx->isValid()) {
1362 if (!d->glcx->create(shareContext ? shareContext : oldcx))
1363 createFailed = true;
1364 }
1365 if (createFailed) {
1366 if (deleteOldContext)
1367 delete oldcx;
1368 return;
1369 }
1370
1371 if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
1372 if (deleteOldContext)
1373 delete oldcx;
1374 return;
1375 }
1376
1377 bool visible = isVisible();
1378 if (visible)
1379 hide();
1380
1381 XVisualInfo *vi = (XVisualInfo*)d->glcx->d_func()->vi;
1382 XSetWindowAttributes a;
1383
1384 QColormap colmap = QColormap::instance(vi->screen);
1385 a.colormap = qt_gl_choose_cmap(QX11Info::display(), vi); // find best colormap
1386 a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
1387 a.border_pixel = colmap.pixel(Qt::black);
1388 Window p = RootWindow(X11->display, vi->screen);
1389 if (parentWidget())
1390 p = parentWidget()->winId();
1391
1392 Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(),
1393 0, vi->depth, InputOutput, vi->visual,
1394 CWBackPixel|CWBorderPixel|CWColormap, &a);
1395 Window *cmw;
1396 Window *cmwret;
1397 int count;
1398 if (XGetWMColormapWindows(X11->display, window()->winId(),
1399 &cmwret, &count)) {
1400 cmw = new Window[count+1];
1401 memcpy((char *)cmw, (char *)cmwret, sizeof(Window)*count);
1402 XFree((char *)cmwret);
1403 int i;
1404 for (i=0; i<count; i++) {
1405 if (cmw[i] == winId()) { // replace old window
1406 cmw[i] = w;
1407 break;
1408 }
1409 }
1410 if (i >= count) // append new window
1411 cmw[count++] = w;
1412 } else {
1413 count = 1;
1414 cmw = new Window[count];
1415 cmw[0] = w;
1416 }
1417
1418#if defined(GLX_MESA_release_buffers) && defined(QGL_USE_MESA_EXT)
1419 if (oldcx && oldcx->windowCreated())
1420 glXReleaseBuffersMESA(X11->display, winId());
1421#endif
1422 if (deleteOldContext)
1423 delete oldcx;
1424 oldcx = 0;
1425
1426 if (testAttribute(Qt::WA_WState_Created))
1427 create(w);
1428 else
1429 d->createWinId(w);
1430 XSetWMColormapWindows(X11->display, window()->winId(), cmw, count);
1431 delete [] cmw;
1432
1433 // calling QWidget::create() will always result in a new paint
1434 // engine being created - get rid of it and replace it with our
1435 // own
1436
1437 if (visible)
1438 show();
1439 XFlush(X11->display);
1440 d->glcx->setWindowCreated(true);
1441}
1442
1443const QGLColormap & QGLWidget::colormap() const
1444{
1445 Q_D(const QGLWidget);
1446 return d->cmap;
1447}
1448
1449/*\internal
1450 Store color values in the given colormap.
1451*/
1452static void qStoreColors(QWidget * tlw, Colormap cmap,
1453 const QGLColormap & cols)
1454{
1455 Q_UNUSED(tlw);
1456 XColor c;
1457 QRgb color;
1458
1459 for (int i = 0; i < cols.size(); i++) {
1460 color = cols.entryRgb(i);
1461 c.pixel = i;
1462 c.red = (ushort)((qRed(color) / 255.0) * 65535.0 + 0.5);
1463 c.green = (ushort)((qGreen(color) / 255.0) * 65535.0 + 0.5);
1464 c.blue = (ushort)((qBlue(color) / 255.0) * 65535.0 + 0.5);
1465 c.flags = DoRed | DoGreen | DoBlue;
1466 XStoreColor(X11->display, cmap, &c);
1467 }
1468}
1469
1470/*\internal
1471 Check whether the given visual supports dynamic colormaps or not.
1472*/
1473static bool qCanAllocColors(QWidget * w)
1474{
1475 bool validVisual = false;
1476 int numVisuals;
1477 long mask;
1478 XVisualInfo templ;
1479 XVisualInfo * visuals;
1480 VisualID id = XVisualIDFromVisual((Visual *) w->window()->x11Info().visual());
1481
1482 mask = VisualScreenMask;
1483 templ.screen = w->x11Info().screen();
1484 visuals = XGetVisualInfo(X11->display, mask, &templ, &numVisuals);
1485
1486 for (int i = 0; i < numVisuals; i++) {
1487 if (visuals[i].visualid == id) {
1488 switch (visuals[i].c_class) {
1489 case TrueColor:
1490 case StaticColor:
1491 case StaticGray:
1492 case XGrayScale:
1493 validVisual = false;
1494 break;
1495 case DirectColor:
1496 case PseudoColor:
1497 validVisual = true;
1498 break;
1499 }
1500 break;
1501 }
1502 }
1503 XFree(visuals);
1504
1505 if (!validVisual)
1506 return false;
1507 return true;
1508}
1509
1510
1511void QGLWidget::setColormap(const QGLColormap & c)
1512{
1513 Q_D(QGLWidget);
1514 QWidget * tlw = window(); // must return a valid widget
1515
1516 d->cmap = c;
1517 if (!d->cmap.handle())
1518 return;
1519
1520 if (!qCanAllocColors(this)) {
1521 qWarning("QGLWidget::setColormap: Cannot create a read/write "
1522 "colormap for this visual");
1523 return;
1524 }
1525
1526 // If the child GL widget is not of the same visual class as the
1527 // toplevel widget we will get in trouble..
1528 Window wid = tlw->winId();
1529 Visual * vis = (Visual *) tlw->x11Info().visual();;
1530 VisualID cvId = XVisualIDFromVisual((Visual *) x11Info().visual());
1531 VisualID tvId = XVisualIDFromVisual((Visual *) tlw->x11Info().visual());
1532 if (cvId != tvId) {
1533 wid = winId();
1534 vis = (Visual *) x11Info().visual();
1535 }
1536
1537 if (!d->cmap.handle()) // allocate a cmap if necessary
1538 d->cmap.setHandle(XCreateColormap(X11->display, wid, vis, AllocAll));
1539
1540 qStoreColors(this, (Colormap) d->cmap.handle(), c);
1541 XSetWindowColormap(X11->display, wid, (Colormap) d->cmap.handle());
1542
1543 // tell the wm that this window has a special colormap
1544 Window * cmw;
1545 Window * cmwret;
1546 int count;
1547 if (XGetWMColormapWindows(X11->display, tlw->winId(), &cmwret, &count))
1548 {
1549 cmw = new Window[count+1];
1550 memcpy((char *) cmw, (char *) cmwret, sizeof(Window) * count);
1551 XFree((char *) cmwret);
1552 int i;
1553 for (i = 0; i < count; i++) {
1554 if (cmw[i] == winId()) {
1555 break;
1556 }
1557 }
1558 if (i >= count) // append new window only if not in the list
1559 cmw[count++] = winId();
1560 } else {
1561 count = 1;
1562 cmw = new Window[count];
1563 cmw[0] = winId();
1564 }
1565 XSetWMColormapWindows(X11->display, tlw->winId(), cmw, count);
1566 delete [] cmw;
1567}
1568
1569void QGLExtensions::init()
1570{
1571 static bool init_done = false;
1572
1573 if (init_done)
1574 return;
1575 init_done = true;
1576
1577 QGLWidget dmy;
1578 dmy.makeCurrent();
1579 init_extensions();
1580
1581 // nvidia 9x.xx unix drivers contain a bug which requires us to call glFinish before releasing an fbo
1582 // to avoid painting artifacts
1583 const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
1584 const int pos = versionString.indexOf("NVIDIA");
1585 if (pos >= 0) {
1586 const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
1587 nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
1588 }
1589}
1590
1591// Solaris defines glXBindTexImageEXT as part of the GL library
1592#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1593typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*);
1594typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int);
1595static qt_glXBindTexImageEXT glXBindTexImageEXT = 0;
1596static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0;
1597
1598static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice)
1599{
1600 static bool resolvedTextureFromPixmap = false;
1601
1602 if (!resolvedTextureFromPixmap) {
1603 resolvedTextureFromPixmap = true;
1604
1605 // Check to see if we have NPOT texture support
1606 if ( !(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures) &&
1607 !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0))
1608 {
1609 return false; // Can't use TFP without NPOT
1610 }
1611 const QX11Info *xinfo = qt_x11Info(paintDevice);
1612 QGLExtensionMatcher extensions(glXQueryExtensionsString(xinfo->display(), xinfo->screen()));
1613 if (extensions.match("GLX_EXT_texture_from_pixmap")) {
1614 glXBindTexImageEXT = (qt_glXBindTexImageEXT) qglx_getProcAddress("glXBindTexImageEXT");
1615 glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) qglx_getProcAddress("glXReleaseTexImageEXT");
1616 }
1617 }
1618
1619 return glXBindTexImageEXT && glXReleaseTexImageEXT;
1620}
1621#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1622
1623
1624QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key,
1625 QGLContext::BindOptions options)
1626{
1627#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
1628 return 0;
1629#else
1630 Q_Q(QGLContext);
1631
1632 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1633
1634 if (!qt_resolveTextureFromPixmap(paintDevice))
1635 return 0;
1636
1637 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1638 const QX11Info &x11Info = pixmapData->xinfo;
1639
1640 // Store the configs (Can be static because configs aren't dependent on current context)
1641 static GLXFBConfig glxRGBPixmapConfig = 0;
1642 static bool RGBConfigInverted = false;
1643 static GLXFBConfig glxRGBAPixmapConfig = 0;
1644 static bool RGBAConfigInverted = false;
1645
1646 bool hasAlpha = pixmapData->hasAlphaChannel();
1647
1648 // Check to see if we need a config
1649 if ( (hasAlpha && !glxRGBAPixmapConfig) || (!hasAlpha && !glxRGBPixmapConfig) ) {
1650 GLXFBConfig *configList = 0;
1651 int configCount = 0;
1652
1653 int configAttribs[] = {
1654 hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True,
1655 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
1656 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
1657 // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can:
1658 GLX_Y_INVERTED_EXT, options & QGLContext::CanFlipNativePixmapBindOption ? GLX_DONT_CARE : False,
1659 XNone
1660 };
1661 configList = glXChooseFBConfig(x11Info.display(), x11Info.screen(), configAttribs, &configCount);
1662 if (!configList)
1663 return 0;
1664
1665 int yInv;
1666 glXGetFBConfigAttrib(x11Info.display(), configList[0], GLX_Y_INVERTED_EXT, &yInv);
1667
1668 if (hasAlpha) {
1669 glxRGBAPixmapConfig = configList[0];
1670 RGBAConfigInverted = yInv;
1671 }
1672 else {
1673 glxRGBPixmapConfig = configList[0];
1674 RGBConfigInverted = yInv;
1675 }
1676
1677 XFree(configList);
1678 }
1679
1680 // Check to see if the surface is still valid
1681 if (pixmapData->gl_surface &&
1682 hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
1683 {
1684 // Surface is invalid!
1685 destroyGlSurfaceForPixmap(pixmapData);
1686 }
1687
1688 // Check to see if we need a surface
1689 if (!pixmapData->gl_surface) {
1690 GLXPixmap glxPixmap;
1691 int pixmapAttribs[] = {
1692 GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
1693 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
1694 GLX_MIPMAP_TEXTURE_EXT, False, // Maybe needs to be don't care
1695 XNone
1696 };
1697
1698 // Wrap the X Pixmap into a GLXPixmap:
1699 glxPixmap = glXCreatePixmap(x11Info.display(),
1700 hasAlpha ? glxRGBAPixmapConfig : glxRGBPixmapConfig,
1701 pixmapData->handle(), pixmapAttribs);
1702
1703 if (!glxPixmap)
1704 return 0;
1705
1706 pixmapData->gl_surface = (Qt::HANDLE)glxPixmap;
1707
1708 // Make sure the cleanup hook gets called so we can delete the glx pixmap
1709 QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData);
1710 }
1711
1712 GLuint textureId;
1713 glGenTextures(1, &textureId);
1714 glBindTexture(GL_TEXTURE_2D, textureId);
1715 glXBindTexImageEXT(x11Info.display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT, 0);
1716
1717 glBindTexture(GL_TEXTURE_2D, textureId);
1718
1719 if (!((hasAlpha && RGBAConfigInverted) || (!hasAlpha && RGBConfigInverted)))
1720 options &= ~QGLContext::InvertedYBindOption;
1721
1722 QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
1723 if (texture->options & QGLContext::InvertedYBindOption)
1724 pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
1725
1726 // We assume the cost of bound pixmaps is zero
1727 QGLTextureCache::instance()->insert(q, key, texture, 0);
1728
1729 return texture;
1730#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
1731}
1732
1733
1734void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
1735{
1736#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1737 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1738 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1739 if (pixmapData->gl_surface) {
1740 glXDestroyPixmap(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface);
1741 pixmapData->gl_surface = 0;
1742 }
1743#endif
1744}
1745
1746void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
1747{
1748#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX)
1749 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
1750 Q_ASSERT(QGLContext::currentContext());
1751 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
1752 if (pixmapData->gl_surface)
1753 glXReleaseTexImageEXT(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT);
1754#endif
1755}
1756
1757QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.