]> git.cworth.org Git - apitrace/blob - retrace/glws_glx.cpp
ce742ac260001e815d7ed2f55ad9413c729f443f
[apitrace] / retrace / glws_glx.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include <assert.h>
27 #include <stdlib.h>
28
29 #include <iostream>
30
31 #include "glproc.hpp"
32 #include "glws.hpp"
33
34 #include <cairo/cairo-xlib.h>
35
36
37 namespace glws {
38
39
40 static Display *display = NULL;
41 static int screen = 0;
42
43 static unsigned glxVersion = 0;
44 static const char *extensions = 0;
45 static bool has_GLX_ARB_create_context = false;
46
47
48 class GlxVisual : public Visual
49 {
50 public:
51     GLXFBConfig fbconfig;
52     XVisualInfo *visinfo;
53
54     GlxVisual() :
55         fbconfig(0),
56         visinfo(0)
57     {}
58
59     ~GlxVisual() {
60         XFree(visinfo);
61     }
62 };
63
64
65 static void
66 processEvent(XEvent &event) {
67     if (0) {
68         switch (event.type) {
69         case ConfigureNotify:
70             std::cerr << "ConfigureNotify";
71             break;
72         case Expose:
73             std::cerr << "Expose";
74             break;
75         case KeyPress:
76             std::cerr << "KeyPress";
77             break;
78         case MapNotify:
79             std::cerr << "MapNotify";
80             break;
81         case ReparentNotify:
82             std::cerr << "ReparentNotify";
83             break;
84         default:
85             std::cerr << "Event " << event.type;
86         }
87         std::cerr << " " << event.xany.window << "\n";
88     }
89
90     switch (event.type) {
91     case KeyPress:
92         {
93             char buffer[32];
94             KeySym keysym;
95             XLookupString(&event.xkey, buffer, sizeof buffer - 1, &keysym, NULL);
96             if (keysym == XK_Escape) {
97                 exit(0);
98             }
99         }
100         break;
101     }
102 }
103
104 class GlxDrawable : public Drawable
105 {
106 public:
107     Window window;
108
109     GlxDrawable(const Visual *vis, int w, int h, bool pbuffer) :
110         Drawable(vis, w, h, pbuffer)
111     {
112         XVisualInfo *visinfo = static_cast<const GlxVisual *>(visual)->visinfo;
113
114         Window root = RootWindow(display, screen);
115
116         /* window attributes */
117         XSetWindowAttributes attr;
118         attr.background_pixel = 0;
119         attr.border_pixel = 0;
120         attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone);
121         attr.event_mask = StructureNotifyMask | KeyPressMask;
122
123         unsigned long mask;
124         mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
125
126         int x = 0, y = 0;
127
128         window = XCreateWindow(
129             display, root,
130             x, y, width, height,
131             0,
132             visinfo->depth,
133             InputOutput,
134             visinfo->visual,
135             mask,
136             &attr);
137
138         XSizeHints sizehints;
139         sizehints.x = x;
140         sizehints.y = y;
141         sizehints.width  = width;
142         sizehints.height = height;
143         sizehints.flags = USSize | USPosition;
144         XSetNormalHints(display, window, &sizehints);
145
146         const char *name = "glretrace";
147         XSetStandardProperties(
148             display, window, name, name,
149             None, (char **)NULL, 0, &sizehints);
150
151         glXWaitX();
152     }
153
154     GlxDrawable(const unsigned pixmap_id, int width, int height) :
155         Drawable(NULL, width, height, false)
156     {
157         window = pixmap_id;
158     }
159
160     void processKeys(void) {
161         XEvent event;
162         while (XCheckWindowEvent(display, window, StructureNotifyMask | KeyPressMask, &event)) {
163             processEvent(event);
164         }
165     }
166
167     void waitForEvent(int type) {
168         XEvent event;
169         do {
170             XWindowEvent(display, window, StructureNotifyMask | KeyPressMask, &event);
171             processEvent(event);
172         } while (event.type != type);
173     }
174
175     ~GlxDrawable() {
176         XDestroyWindow(display, window);
177     }
178
179     void
180     resize(int w, int h) {
181         if (w == width && h == height) {
182             return;
183         }
184
185         glXWaitGL();
186
187         // We need to ensure that pending events are processed here, and XSync
188         // with discard = True guarantees that, but it appears the limited
189         // event processing we do so far is sufficient
190         //XSync(display, True);
191
192         Drawable::resize(w, h);
193
194         XResizeWindow(display, window, w, h);
195
196         // Tell the window manager to respect the requested size
197         XSizeHints size_hints;
198         size_hints.max_width  = size_hints.min_width  = w;
199         size_hints.max_height = size_hints.min_height = h;
200         size_hints.flags = PMinSize | PMaxSize;
201         XSetWMNormalHints(display, window, &size_hints);
202
203         waitForEvent(ConfigureNotify);
204
205         glXWaitX();
206     }
207
208     void show(void) {
209         if (visible) {
210             return;
211         }
212
213         glXWaitGL();
214
215         XMapWindow(display, window);
216
217         waitForEvent(MapNotify);
218
219         glXWaitX();
220
221         Drawable::show();
222     }
223
224     void swapBuffers(void) {
225         glXSwapBuffers(display, window);
226
227         processKeys();
228     }
229 };
230
231 class GlxContext : public Context
232 {
233 public:
234     GLXContext context;
235
236     GlxContext(const Visual *vis, Profile prof, GLXContext ctx) :
237         Context(vis, prof),
238         context(ctx)
239     {}
240
241     ~GlxContext() {
242         glXDestroyContext(display, context);
243     }
244 };
245
246 void
247 init(void) {
248     XInitThreads();
249
250     display = XOpenDisplay(NULL);
251     if (!display) {
252         std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
253         exit(1);
254     }
255
256     screen = DefaultScreen(display);
257
258     int major = 0, minor = 0;
259     glXQueryVersion(display, &major, &minor);
260     glxVersion = (major << 8) | minor;
261
262     extensions = glXQueryExtensionsString(display, screen);
263     has_GLX_ARB_create_context = checkExtension("GLX_ARB_create_context", extensions);
264 }
265
266 void
267 cleanup(void) {
268     if (display) {
269         XCloseDisplay(display);
270         display = NULL;
271     }
272 }
273
274 Visual *
275 createVisual(bool doubleBuffer, Profile profile) {
276     if (profile != PROFILE_COMPAT &&
277         profile != PROFILE_CORE) {
278         return NULL;
279     }
280
281     GlxVisual *visual = new GlxVisual;
282
283     if (glxVersion >= 0x0103) {
284         Attributes<int> attribs;
285         attribs.add(GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT);
286         attribs.add(GLX_RENDER_TYPE, GLX_RGBA_BIT);
287         attribs.add(GLX_RED_SIZE, 1);
288         attribs.add(GLX_GREEN_SIZE, 1);
289         attribs.add(GLX_BLUE_SIZE, 1);
290         attribs.add(GLX_ALPHA_SIZE, 1);
291         attribs.add(GLX_DOUBLEBUFFER, doubleBuffer ? GL_TRUE : GL_FALSE);
292         attribs.add(GLX_DEPTH_SIZE, 1);
293         attribs.add(GLX_STENCIL_SIZE, 1);
294         attribs.end();
295
296         int num_configs = 0;
297         GLXFBConfig * fbconfigs;
298         fbconfigs = glXChooseFBConfig(display, screen, attribs, &num_configs);
299         assert(num_configs && fbconfigs);
300         visual->fbconfig = fbconfigs[0];
301         assert(visual->fbconfig);
302         visual->visinfo = glXGetVisualFromFBConfig(display, visual->fbconfig);
303         assert(visual->visinfo);
304     } else {
305         Attributes<int> attribs;
306         attribs.add(GLX_RGBA);
307         attribs.add(GLX_RED_SIZE, 1);
308         attribs.add(GLX_GREEN_SIZE, 1);
309         attribs.add(GLX_BLUE_SIZE, 1);
310         attribs.add(GLX_ALPHA_SIZE, 1);
311         if (doubleBuffer) {
312             attribs.add(GLX_DOUBLEBUFFER);
313         }
314         attribs.add(GLX_DEPTH_SIZE, 1);
315         attribs.add(GLX_STENCIL_SIZE, 1);
316         attribs.end();
317
318         visual->visinfo = glXChooseVisual(display, screen, attribs);
319     }
320
321     return visual;
322 }
323
324 Drawable *
325 createDrawable(const Visual *visual, int width, int height, bool pbuffer)
326 {
327     return new GlxDrawable(visual, width, height, pbuffer);
328 }
329
330 Context *
331 createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
332 {
333     const GlxVisual *visual = static_cast<const GlxVisual *>(_visual);
334     GLXContext share_context = NULL;
335     GLXContext context;
336
337     if (shareContext) {
338         share_context = static_cast<GlxContext*>(shareContext)->context;
339     }
340
341     if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) {
342         Attributes<int> attribs;
343         
344         attribs.add(GLX_RENDER_TYPE, GLX_RGBA_TYPE);
345         if (debug) {
346             attribs.add(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB);
347         }
348
349         switch (profile) {
350         case PROFILE_COMPAT:
351             break;
352         case PROFILE_CORE:
353             // XXX: This will invariable return a 3.2 context, when supported.
354             // We probably should have a PROFILE_CORE_XX per version.
355             attribs.add(GLX_CONTEXT_MAJOR_VERSION_ARB, 3);
356             attribs.add(GLX_CONTEXT_MINOR_VERSION_ARB, 2);
357             attribs.add(GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
358             break;
359         default:
360             return NULL;
361         }
362         
363         attribs.end();
364
365         context = glXCreateContextAttribsARB(display, visual->fbconfig, share_context, True, attribs);
366     } else {
367         if (profile != PROFILE_COMPAT) {
368             return NULL;
369         }
370
371         if (glxVersion >= 0x103) {
372             context = glXCreateNewContext(display, visual->fbconfig, GLX_RGBA_TYPE, share_context, True);
373         } else {
374             context = glXCreateContext(display, visual->visinfo, share_context, True);
375         }
376     }
377
378     if (!context) {
379         return NULL;
380     }
381
382     return new GlxContext(visual, profile, context);
383 }
384
385 bool
386 makeCurrent(Drawable *drawable, Context *context)
387 {
388     if (!drawable || !context) {
389         return glXMakeCurrent(display, None, NULL);
390     } else {
391         GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
392         GlxContext *glxContext = static_cast<GlxContext *>(context);
393
394         return glXMakeCurrent(display, glxDrawable->window, glxContext->context);
395     }
396 }
397
398 bool
399 processEvents(void) {
400     while (XPending(display) > 0) {
401         XEvent event;
402         XNextEvent(display, &event);
403         processEvent(event);
404     }
405     return true;
406 }
407
408 void
409 createWindow(Drawable *drawable, const Visual *_visual)
410 {
411     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
412     const GlxVisual *visual = static_cast<const GlxVisual *>(_visual);
413
414     glXCreateWindow(display, visual->fbconfig, glxDrawable->window, NULL);
415 }
416
417 Drawable *
418 createPixmap(unsigned width, unsigned height, unsigned depth)
419 {
420     Pixmap pixmap;
421
422     pixmap = XCreatePixmap(display, DefaultRootWindow(display),
423                            width, height, depth);
424
425     printf ("Replayed XCreatePixmap to create pixmap %ld\n", pixmap);
426
427     return new GlxDrawable(pixmap, width, height);
428 }
429
430 Drawable *
431 createGLPixmap(GLXFBConfig fbconfig, Drawable *_pixmap,
432              unsigned width, unsigned height, int *attrib_list)
433 {
434     GlxDrawable *pixmap = static_cast<GlxDrawable *>(_pixmap);
435     Pixmap gl_pixmap;
436
437     gl_pixmap = glXCreatePixmap(display, fbconfig, pixmap->window, attrib_list);
438
439     printf ("And replayed glXCreatePixmap to create pixmap %ld\n", gl_pixmap);
440
441     return new GlxDrawable(gl_pixmap, width, height);
442 }
443
444 void
445 destroyWindow(Drawable *drawable)
446 {
447 /*
448     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
449
450     glXDestroyWindow(display, glxDrawable->window);
451 */
452 }
453
454 void
455 bindTexImage(Drawable *drawable, int buffer)
456 {
457     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
458
459     glXBindTexImageEXT(display, glxDrawable->window, buffer, NULL);
460 }
461
462 void
463 releaseTexImage(Drawable *drawable, int buffer)
464 {
465     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
466
467     glXReleaseTexImageEXT(display, glxDrawable->window, buffer);
468 }
469
470 void
471 copySubBuffer(Drawable *drawable, int x, int y, int width, int height)
472 {
473     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
474
475     glXCopySubBufferMESA(display, glxDrawable->window, x, y, width, height);
476 }
477
478 void
479 putImageData(glws::Drawable *drawable, char *data,
480              int width, int height, int depth,
481              int bits_per_pixel, int bytes_per_line, int byte_order)
482 {
483     GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
484     GC gc;
485     XImage image;
486
487     printf ("Calling XPutImage to pixmap %ld\n", glxDrawable->window);
488
489     image.width = width;
490     image.height = height;
491     image.format = ZPixmap;
492     image.data = data;
493     image.byte_order = byte_order;
494     image.bitmap_unit = 32;
495     image.bitmap_bit_order = byte_order;
496     image.bitmap_pad = 32;
497     image.depth = depth,
498     image.bytes_per_line = bytes_per_line;
499     image.bits_per_pixel = bits_per_pixel;
500     image.red_mask = 0;
501     image.green_mask = 0;
502     image.blue_mask = 0;
503     image.obdata = 0;
504
505     XInitImage(&image);
506
507     {
508         Window root;
509         int x_ignore, y_ignore;
510         unsigned width, height, depth, border_width_ignore;
511         XGetGeometry(display, glxDrawable->window, &root, &x_ignore, &y_ignore,
512                      &width, &height, &border_width_ignore, &depth);
513     }
514
515     gc = XCreateGC(display, glxDrawable->window, 0, NULL);
516     XPutImage(display, glxDrawable->window, gc, &image, 0, 0, 0, 0, width, height);
517
518     {
519         static int count = 0;
520         char filename[] = "put-image-data-X.png";
521         cairo_surface_t *surface;
522
523         surface = cairo_xlib_surface_create(display, glxDrawable->window, DefaultVisual(display, DefaultScreen(display)), width, height);
524
525         filename[15] = '0' + count;
526         cairo_surface_write_to_png(surface, filename);
527
528         cairo_surface_destroy(surface);
529         count++;
530     }
531
532 }
533
534 GLXFBConfig
535 chooseConfig(int *attrib_list)
536 {
537     GLXFBConfig *configs;
538     int nelements;
539     configs = glXChooseFBConfig(display, DefaultScreen(display), attrib_list, &nelements);
540     if (nelements)
541         return configs[nelements - 1];
542     else
543         return NULL;
544 }
545
546 } /* drawable glws */