/* Adapted from
https://github.com/openscad/openscad/blob/master/src/OffscreenContextGLX.cc
There are also versions for Windows (OffscreenContextWGL.cc) and for
Apple (OffscreenContextCGL.mm), but they haven't been adapted yet.
by S. Popinet, 2017
*/
/*
Create an OpenGL context without creating an OpenGL Window. for Linux.
See also
glxgears.c by Brian Paul from mesa-demos (mesa3d.org)
http://cgit.freedesktop.org/mesa/demos/tree/src/xdemos?id=mesa-demos-8.0.1
http://www.opengl.org/sdk/docs/man/xhtml/glXIntro.xml
http://www.mesa3d.org/brianp/sig97/offscrn.htm
http://glprogramming.com/blue/ch07.html
OffscreenContext.mm (Mac OSX version)
*/
/*
* Some portions of the code below are:
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
*29/span>
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*36/span>
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*39/span>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include "system-gl.h"
#include <GL/glx.h>
#include "fbo.h"
#include "OffscreenContext.h"
struct _OffscreenContext {
GLXContext openGLContext;
Display *xdisplay;
Window xwindow;
int width, height;
fbo_t *fbo;
unsigned char *image;
unsigned int *depth;
GLuint depth_texture;
};
#include "OffscreenContextAll.h"
static XErrorHandler original_xlib_handler = NULL;
static bool XCreateWindow_failed = false;
static int XCreateWindow_error(Display *dpy, XErrorEvent *event)
{
fprintf (stderr, "XCreateWindow failed: XID: %d request: %d minor: %d\n",
(int) event->resourceid, event->request_code, event->minor_code);
char description[1024];
XGetErrorText( dpy, event->error_code, description, 1023 );
fprintf (stderr, " error message: %s\n", description);
XCreateWindow_failed = true;
return 0;
}
/*
create a dummy X window without showing it. (without 'mapping' it)
and save information to the ctx.
This purposely does not use glxCreateWindow, to avoid crashes,
"failed to create drawable" errors, and Mesa "WARNING: Application calling
GLX 1.3 function when GLX 1.3 is not supported! This is an application bug!"
This function will alter ctx.openGLContext and ctx.xwindow if successfull
*/
static bool create_glx_dummy_window (OffscreenContext * ctx)
{
int attributes[] = {
//support all 3, for OpenCSG
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 0,
GLX_DOUBLEBUFFER, true,
None
};
Display * dpy = ctx->xdisplay;
int num_returned = 0;
GLXFBConfig * fbconfigs = glXChooseFBConfig( dpy, DefaultScreen(dpy),
attributes, &num_returned );
if (fbconfigs == NULL) {
fprintf (stderr, "glXChooseFBConfig failed\n");
return false;
}
XVisualInfo * visinfo = glXGetVisualFromFBConfig( dpy, fbconfigs[0] );
if (visinfo == NULL) {
fprintf (stderr, "glXGetVisualFromFBConfig failed\n");
XFree(fbconfigs);
return false;
}
// can't depend on xWin==NULL at failure. use a custom Xlib error
// handler instead.
original_xlib_handler = XSetErrorHandler(XCreateWindow_error);
Window root = DefaultRootWindow(dpy);
XSetWindowAttributes xwin_attr;
int width = ctx->width;
int height = ctx->height;
xwin_attr.background_pixmap = None;
xwin_attr.background_pixel = 0;
xwin_attr.border_pixel = 0;
xwin_attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
xwin_attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
unsigned long int mask = CWBackPixel | CWBorderPixel |
CWColormap | CWEventMask;
Window xWin = XCreateWindow( dpy, root, 0, 0, width, height,
0, visinfo->depth, InputOutput,
visinfo->visual, mask, &xwin_attr );
// Window xWin = XCreateSimpleWindow( dpy, DefaultRootWindow(dpy),
// 0,0,42,42, 0,0,0 );
XSync(dpy, false);
if (XCreateWindow_failed) {
XFree(visinfo);
XFree(fbconfigs);
return false;
}
XSetErrorHandler(original_xlib_handler);
// Most programs would call XMapWindow here. But we don't, to keep
// the window hidden
// XMapWindow( dpy, xWin );
GLXContext context = glXCreateNewContext(dpy, fbconfigs[0],
GLX_RGBA_TYPE, NULL, true);
if (context == NULL) {
fprintf (stderr, "glXCreateNewContext failed\n");
XDestroyWindow(dpy, xWin);
XFree(visinfo);
XFree(fbconfigs);
return false;
}
//GLXWindow glxWin = glXCreateWindow( dpy, fbconfigs[0], xWin, NULL );
if (!glXMakeContextCurrent( dpy, xWin, xWin, context )) {
//if (!glXMakeContextCurrent( dpy, glxWin, glxWin, context )) {
fprintf (stderr, "glXMakeContextCurrent failed\n");
glXDestroyContext(dpy, context);
XDestroyWindow(dpy, xWin);
XFree(visinfo);
XFree(fbconfigs);
return false;
}
ctx->openGLContext = context;
ctx->xwindow = xWin;
XFree(visinfo);
XFree(fbconfigs);
return true;
}
static bool create_glx_dummy_context (OffscreenContext * ctx);
OffscreenContext * create_offscreen_context(int w, int h)
{
OffscreenContext * ctx = calloc (1, sizeof (OffscreenContext));
ctx->width = w, ctx->height = h;
// before an FBO can be setup, a GLX context must be created
// this call alters ctx->xDisplay and ctx->openGLContext
// and ctx->xwindow if successful
if (!create_glx_dummy_context(ctx)) {
free (ctx);
return NULL;
}
return create_offscreen_context_common (ctx);
}
bool teardown_offscreen_context(OffscreenContext *ctx)
{
if (ctx) {
if (!teardown_offscreen_context_common(ctx))
return false;
fbo_unbind(ctx->fbo);
fbo_delete(ctx->fbo);
XDestroyWindow( ctx->xdisplay, ctx->xwindow );
glXDestroyContext( ctx->xdisplay, ctx->openGLContext );
XCloseDisplay( ctx->xdisplay );
free (ctx);
return true;
}
return false;
}
bool save_framebuffer(OffscreenContext * ctx, FILE * output)
{
glXSwapBuffers(ctx->xdisplay, ctx->xwindow);
return save_framebuffer_common(ctx, output);
}
unsigned char * get_framebuffer_pixels (OffscreenContext * ctx)
{
glXSwapBuffers(ctx->xdisplay, ctx->xwindow);
return get_framebuffer_pixels_common(ctx);
}
unsigned int * get_framebuffer_depth (OffscreenContext * ctx)
{
if (!ctx) return NULL;
return get_framebuffer_depth_common(ctx);
}
#pragma GCC diagnostic ignored "-Waddress"
static bool create_glx_dummy_context(OffscreenContext * ctx)
{
// This will alter ctx->openGLContext and ctx->xdisplay and
// ctx->xwindow if successfull
int major;
int minor;
int result = false;
ctx->xdisplay = XOpenDisplay(NULL);
if (ctx->xdisplay == NULL) {
fprintf (stderr, "Unable to open a connection to the X server.\n");
char * dpyenv = getenv ("DISPLAY");
fprintf (stderr, "DISPLAY=%s\n", dpyenv ? dpyenv : "");
return false;
}
// glxQueryVersion is not always reliable. Use it, but then
// also check to see if GLX 1.3 functions exist
glXQueryVersion(ctx->xdisplay, &major, &minor);
if (major==1 && minor<=2 && glXGetVisualFromFBConfig==NULL) {
fprintf (stderr, "Error: GLX version 1.3 functions missing. "
"Your GLX version: %d.%d\n", major, minor);
} else {
result = create_glx_dummy_window(ctx);
}
if (!result) XCloseDisplay(ctx->xdisplay);
return result;
}
#if TEST
#include <GL/glut.h>
int main(int argc, char * argv[]) {
glutInit(&argc, argv);
OffscreenContext * ctx = create_offscreen_context(800, 600);
///////////////////////////
glClearColor( 0.0, 0.0, 0.0, 1. );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable( GL_DEPTH_TEST );
glShadeModel( GL_FLAT );
glViewport( 0, 0, ctx->width, ctx->height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
gluPerspective( 90., 1.,0.1, 1000. );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 0., 0., 3., 0., 0., 0., 0., 1., 0. );
// glTranslatef( TransXYZ[0], TransXYZ[1], TransXYZ[2] );
// glMultMatrixf( RotMatrix );
glRotatef (45, 1, 0, 0);
// glScalef( scale, scale, scale );
glColor3f( 1., 1., 1. );
glutWireTeapot( 1. );
////////////////
assert (save_framebuffer (ctx, stdout));
assert (teardown_offscreen_context (ctx));
return 0;
}
#endif