1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
34 #include <cairo/cairo-xlib.h>
40 static Display *display = NULL;
41 static int screen = 0;
43 static unsigned glxVersion = 0;
44 static const char *extensions = 0;
45 static bool has_GLX_ARB_create_context = false;
48 class GlxVisual : public Visual
66 processEvent(XEvent &event) {
70 std::cerr << "ConfigureNotify";
73 std::cerr << "Expose";
76 std::cerr << "KeyPress";
79 std::cerr << "MapNotify";
82 std::cerr << "ReparentNotify";
85 std::cerr << "Event " << event.type;
87 std::cerr << " " << event.xany.window << "\n";
95 XLookupString(&event.xkey, buffer, sizeof buffer - 1, &keysym, NULL);
96 if (keysym == XK_Escape) {
104 class GlxDrawable : public Drawable
109 GlxDrawable(const Visual *vis, int w, int h, bool pbuffer) :
110 Drawable(vis, w, h, pbuffer)
112 XVisualInfo *visinfo = static_cast<const GlxVisual *>(visual)->visinfo;
114 Window root = RootWindow(display, screen);
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;
124 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
128 window = XCreateWindow(
138 XSizeHints sizehints;
141 sizehints.width = width;
142 sizehints.height = height;
143 sizehints.flags = USSize | USPosition;
144 XSetNormalHints(display, window, &sizehints);
146 const char *name = "glretrace";
147 XSetStandardProperties(
148 display, window, name, name,
149 None, (char **)NULL, 0, &sizehints);
154 GlxDrawable(const unsigned pixmap_id, int width, int height) :
155 Drawable(NULL, width, height, false)
160 void processKeys(void) {
162 while (XCheckWindowEvent(display, window, StructureNotifyMask | KeyPressMask, &event)) {
167 void waitForEvent(int type) {
170 XWindowEvent(display, window, StructureNotifyMask | KeyPressMask, &event);
172 } while (event.type != type);
176 XDestroyWindow(display, window);
180 resize(int w, int h) {
181 if (w == width && h == height) {
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);
192 Drawable::resize(w, h);
194 XResizeWindow(display, window, w, h);
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);
203 waitForEvent(ConfigureNotify);
215 XMapWindow(display, window);
217 waitForEvent(MapNotify);
224 void swapBuffers(void) {
225 glXSwapBuffers(display, window);
231 class GlxContext : public Context
236 GlxContext(const Visual *vis, Profile prof, GLXContext ctx) :
242 glXDestroyContext(display, context);
250 display = XOpenDisplay(NULL);
252 std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
256 screen = DefaultScreen(display);
258 int major = 0, minor = 0;
259 glXQueryVersion(display, &major, &minor);
260 glxVersion = (major << 8) | minor;
262 extensions = glXQueryExtensionsString(display, screen);
263 has_GLX_ARB_create_context = checkExtension("GLX_ARB_create_context", extensions);
269 XCloseDisplay(display);
275 createVisual(bool doubleBuffer, Profile profile) {
276 if (profile != PROFILE_COMPAT &&
277 profile != PROFILE_CORE) {
281 GlxVisual *visual = new GlxVisual;
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);
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);
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);
312 attribs.add(GLX_DOUBLEBUFFER);
314 attribs.add(GLX_DEPTH_SIZE, 1);
315 attribs.add(GLX_STENCIL_SIZE, 1);
318 visual->visinfo = glXChooseVisual(display, screen, attribs);
325 createDrawable(const Visual *visual, int width, int height, bool pbuffer)
327 return new GlxDrawable(visual, width, height, pbuffer);
331 createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
333 const GlxVisual *visual = static_cast<const GlxVisual *>(_visual);
334 GLXContext share_context = NULL;
338 share_context = static_cast<GlxContext*>(shareContext)->context;
341 if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) {
342 Attributes<int> attribs;
344 attribs.add(GLX_RENDER_TYPE, GLX_RGBA_TYPE);
346 attribs.add(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB);
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);
365 context = glXCreateContextAttribsARB(display, visual->fbconfig, share_context, True, attribs);
367 if (profile != PROFILE_COMPAT) {
371 if (glxVersion >= 0x103) {
372 context = glXCreateNewContext(display, visual->fbconfig, GLX_RGBA_TYPE, share_context, True);
374 context = glXCreateContext(display, visual->visinfo, share_context, True);
382 return new GlxContext(visual, profile, context);
386 makeCurrent(Drawable *drawable, Context *context)
388 if (!drawable || !context) {
389 return glXMakeCurrent(display, None, NULL);
391 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
392 GlxContext *glxContext = static_cast<GlxContext *>(context);
394 return glXMakeCurrent(display, glxDrawable->window, glxContext->context);
399 processEvents(void) {
400 while (XPending(display) > 0) {
402 XNextEvent(display, &event);
409 createWindow(Drawable *drawable, const Visual *_visual)
411 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
412 const GlxVisual *visual = static_cast<const GlxVisual *>(_visual);
414 glXCreateWindow(display, visual->fbconfig, glxDrawable->window, NULL);
418 createPixmap(unsigned width, unsigned height, unsigned depth)
422 pixmap = XCreatePixmap(display, DefaultRootWindow(display),
423 width, height, depth);
425 printf ("Replayed XCreatePixmap to create pixmap %ld\n", pixmap);
427 return new GlxDrawable(pixmap, width, height);
431 createGLPixmap(GLXFBConfig fbconfig, Drawable *_pixmap,
432 unsigned width, unsigned height, int *attrib_list)
434 GlxDrawable *pixmap = static_cast<GlxDrawable *>(_pixmap);
437 gl_pixmap = glXCreatePixmap(display, fbconfig, pixmap->window, attrib_list);
439 printf ("And replayed glXCreatePixmap to create pixmap %ld\n", gl_pixmap);
441 return new GlxDrawable(gl_pixmap, width, height);
445 destroyWindow(Drawable *drawable)
448 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
450 glXDestroyWindow(display, glxDrawable->window);
455 bindTexImage(Drawable *drawable, int buffer)
457 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
459 glXBindTexImageEXT(display, glxDrawable->window, buffer, NULL);
463 releaseTexImage(Drawable *drawable, int buffer)
465 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
467 glXReleaseTexImageEXT(display, glxDrawable->window, buffer);
471 copySubBuffer(Drawable *drawable, int x, int y, int width, int height)
473 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
475 glXCopySubBufferMESA(display, glxDrawable->window, x, y, width, height);
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)
483 GlxDrawable *glxDrawable = static_cast<GlxDrawable *>(drawable);
487 printf ("Calling XPutImage to pixmap %ld\n", glxDrawable->window);
490 image.height = height;
491 image.format = ZPixmap;
493 image.byte_order = byte_order;
494 image.bitmap_unit = 32;
495 image.bitmap_bit_order = byte_order;
496 image.bitmap_pad = 32;
498 image.bytes_per_line = bytes_per_line;
499 image.bits_per_pixel = bits_per_pixel;
501 image.green_mask = 0;
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);
515 gc = XCreateGC(display, glxDrawable->window, 0, NULL);
516 XPutImage(display, glxDrawable->window, gc, &image, 0, 0, 0, 0, width, height);
519 static int count = 0;
520 char filename[] = "put-image-data-X.png";
521 cairo_surface_t *surface;
523 surface = cairo_xlib_surface_create(display, glxDrawable->window, DefaultVisual(display, DefaultScreen(display)), width, height);
525 filename[15] = '0' + count;
526 cairo_surface_write_to_png(surface, filename);
528 cairo_surface_destroy(surface);
535 chooseConfig(int *attrib_list)
537 GLXFBConfig *configs;
539 configs = glXChooseFBConfig(display, DefaultScreen(display), attrib_list, &nelements);
541 return configs[nelements - 1];
546 } /* drawable glws */