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