How to initialize OpenGL with glXChooseFBConfig and ctypes module?

1

I want to make some simple OpenGL animation on my tkinter window. I dont want to include any needless dependencies so I'm writing everything from scratch with ctypes. So far I have this:

#!/usr/bin/env python3

import tkinter
import ctypes
from ctypes import cdll

GLX_PIXMAP_BIT = 0x00000002
GLX_WINDOW_BIT = 0x00000001
GLX_PBUFFER_BIT = 0x00000004
GLX_RGBA_BIT = 0x00000001
GLX_COLOR_INDEX_BIT = 0x00000002
GLX_PBUFFER_CLOBBER_MASK = 0x08000000
GLX_FRONT_LEFT_BUFFER_BIT = 0x00000001
GLX_FRONT_RIGHT_BUFFER_BIT = 0x00000002
GLX_BACK_LEFT_BUFFER_BIT = 0x00000004
GLX_BACK_RIGHT_BUFFER_BIT = 0x00000008
GLX_AUX_BUFFERS_BIT = 0x00000010
GLX_DEPTH_BUFFER_BIT = 0x00000020
GLX_STENCIL_BUFFER_BIT = 0x00000040
GLX_ACCUM_BUFFER_BIT = 0x00000080
GLX_CONFIG_CAVEAT = 0x20
GLX_X_VISUAL_TYPE = 0x22
GLX_TRANSPARENT_TYPE = 0x23
GLX_TRANSPARENT_INDEX_VALUE = 0x24
GLX_TRANSPARENT_RED_VALUE = 0x25
GLX_TRANSPARENT_GREEN_VALUE = 0x26
GLX_TRANSPARENT_BLUE_VALUE = 0x27
GLX_TRANSPARENT_ALPHA_VALUE = 0x28
GLX_DONT_CARE = 0xFFFFFFFF
GLX_NONE = 0x8000
GLX_SLOW_CONFIG = 0x8001
GLX_TRUE_COLOR = 0x8002
GLX_DIRECT_COLOR = 0x8003
GLX_PSEUDO_COLOR = 0x8004
GLX_STATIC_COLOR = 0x8005
GLX_GRAY_SCALE = 0x8006
GLX_STATIC_GRAY = 0x8007
GLX_TRANSPARENT_RGB = 0x8008
GLX_TRANSPARENT_INDEX = 0x8009
GLX_VISUAL_ID = 0x800B
GLX_SCREEN = 0x800C
GLX_NON_CONFORMANT_CONFIG = 0x800D
GLX_DRAWABLE_TYPE = 0x8010
GLX_RENDER_TYPE = 0x8011
GLX_X_RENDERABLE = 0x8012
GLX_FBCONFIG_ID = 0x8013
GLX_RGBA_TYPE = 0x8014
GLX_COLOR_INDEX_TYPE = 0x8015
GLX_MAX_PBUFFER_WIDTH = 0x8016
GLX_MAX_PBUFFER_HEIGHT = 0x8017
GLX_MAX_PBUFFER_PIXELS = 0x8018
GLX_PRESERVED_CONTENTS = 0x801B
GLX_LARGEST_PBUFFER = 0x801C
GLX_WIDTH = 0x801D
GLX_HEIGHT = 0x801E
GLX_EVENT_MASK = 0x801F
GLX_DAMAGED = 0x8020
GLX_SAVED = 0x8021
GLX_WINDOW = 0x8022
GLX_PBUFFER = 0x8023
GLX_PBUFFER_HEIGHT = 0x8040
GLX_PBUFFER_WIDTH = 0x8041
GLX_ACCUM_ALPHA_SIZE = 17
GLX_ACCUM_BLUE_SIZE = 16
GLX_ACCUM_GREEN_SIZE = 15
GLX_ACCUM_RED_SIZE = 14
GLX_ALPHA_SIZE = 11
GLX_AUX_BUFFERS = 7
GLX_BAD_ATTRIBUTE = 2
GLX_BAD_CONTEXT = 5
GLX_BAD_ENUM = 7
GLX_BAD_SCREEN = 1
GLX_BAD_VALUE = 6
GLX_BAD_VISUAL = 4
GLX_BLUE_SIZE = 10
GLX_BUFFER_SIZE = 2
GLX_BufferSwapComplete = 1
GLX_DEPTH_SIZE = 12
GLX_DOUBLEBUFFER = 5
GLX_GREEN_SIZE = 9
GLX_LEVEL = 3
GLX_NO_EXTENSION = 3
GLX_PbufferClobber = 0
GLX_RED_SIZE = 8
GLX_RGBA = 4
GLX_STENCIL_SIZE = 13
GLX_STEREO = 6
GLX_USE_GL = 1


class OpenGLView(tkinter.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._x_window_id = self.winfo_id()
        self._init_gl()

    def _init_gl(self):
        self._xlib = cdll.LoadLibrary('libX11.so')
        self._gl = cdll.LoadLibrary('libGL.so')
        self._glx = cdll.LoadLibrary('libGLU.so')
        self._x_display = self._xlib.XOpenDisplay()
        elements = [
            GLX_X_RENDERABLE, 1,
            GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
            GLX_RENDER_TYPE, GLX_RGBA_BIT,
            GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
            GLX_DOUBLEBUFFER, 1,
            GLX_RED_SIZE, 8,
            GLX_GREEN_SIZE, 8,
            GLX_BLUE_SIZE, 8,
            GLX_ALPHA_SIZE, 8,
            GLX_DEPTH_SIZE, 24,
            GLX_STENCIL_SIZE, 8,
            0
        ]
        elements = (ctypes.c_int * len(elements))(*elements)
        gl_configs = self._glx.glXChooseFBConfig(self._x_display, 0, ctypes.byref(elements), ctypes.sizeof(elements))
        context = self._glx.glXCreateNewContext(self._x_display, gl_configs[0], self._glx.GLX_RGBA_TYPE, None, True)
        self._glx.glXMakeContextCurrent(self._x_display, self._x_window_id, self._x_window_id, context)

tk = tkinter.Tk()
v = OpenGLView(tk)
v.pack(fill=tkinter.BOTH, expand=True)
tk.mainloop()

Why this piece of code generates this error?

libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
Segmentation fault (core dumped)
python
opengl
ctypes
glx
asked on Stack Overflow Aug 29, 2016 by BPS

2 Answers

1

I don't have a linux distribution at hand right now to test it, but what about this mcve?

from ctypes import c_char_p, c_void_p, cdll
from ctypes import c_bool, c_char_p, c_double, c_float, c_int, c_uint, c_ulong, c_void_p, POINTER
from tkinter import Tk, YES, BOTH

_xlib = cdll.LoadLibrary('libX11.so')
_gl = cdll.LoadLibrary('libGL.so')


# Data types for Linux Platform
XID = c_ulong
GLXDrawable = XID

# Data types for OpenGL
GLbitfield = c_uint
GLubyte = c_char_p
GLclampf = c_float
GLclampd = c_double
GLdouble = c_double
GLenum = c_uint
GLfloat = c_float
GLint = c_int

GL_BLEND = 0x0BE2
GL_COLOR_BUFFER_BIT = 0x00004000
GL_DEPTH_BUFFER_BIT = 0x00000100
GL_DEPTH_TEST = 0x0B71
GL_MODELVIEW = 0x1700
GL_ONE_MINUS_SRC_ALPHA = 0x0303
GL_PROJECTION = 0x1701
GL_QUADS = 0x0007
GL_RENDERER = 0x1F01
GL_SRC_ALPHA = 0x0302
GL_VENDOR = 0x1F00
GL_VERSION = 0x1F02
GL_TRUE = 1

# Constants for Linux Platform
PGLint = GLint * 11

GLX_RGBA = 4
GLX_RED_SIZE = 8
GLX_GREEN_SIZE = 9
GLX_BLUE_SIZE = 10
GLX_DEPTH_SIZE = 12
GLX_DOUBLEBUFFER = 5


# OpenGL Function Definitions
glBegin = _gl.glBegin
glBegin.restype = None
glBegin.argtypes = [GLenum]

glClear = _gl.glClear
glClear.restype = None
glClear.argtypes = [GLbitfield]

glBlendFunc = _gl.glBlendFunc
glBlendFunc.restype = None
glBlendFunc.argtypes = [GLenum, GLenum]

glClearColor = _gl.glClearColor
glClearColor.restype = None
glClearColor.argtypes = [GLclampf, GLclampf, GLclampf, GLclampf]

glClearDepth = _gl.glClearDepth
glClearDepth.restype = None
glClearDepth.argtypes = [GLclampd]

glColor3f = _gl.glColor3f
glColor3f.restype = None
glColor3f.argtypes = [GLfloat, GLfloat, GLfloat]

glEnable = _gl.glEnable
glEnable.restype = None
glEnable.argtypes = [GLenum]

glEnd = _gl.glEnd
glEnd.restype = None
glEnd.argtypes = None

glFlush = _gl.glFlush
glFlush.restype = None
glFlush.argtypes = None

glGetString = _gl.glGetString
glGetString.restype = GLubyte
glGetString.argtypes = [GLenum]

glLoadIdentity = _gl.glLoadIdentity
glLoadIdentity.restype = None
glLoadIdentity.argtypes = None

glMatrixMode = _gl.glMatrixMode
glMatrixMode.restype = None
glMatrixMode.argtypes = None

glOrtho = _gl.glOrtho
glOrtho.restype = None
glOrtho.argtypes = [GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble]

glRotatef = _gl.glRotatef
glRotatef.restype = None
glRotatef.argtypes = [GLfloat, GLfloat, GLfloat, GLfloat]

glVertex3f = _gl.glVertex3f
glVertex3f.restype = None
glVertex3f.argtypes = [GLfloat, GLfloat, GLfloat]

glViewport = _gl.glViewport
glViewport.restype = None
glViewport.argtypes = [GLint, GLint, GLint, GLint]

glXChooseVisual = _gl.glXChooseVisual
glXChooseVisual.argtypes = [c_void_p, c_int, POINTER(c_int)]
glXChooseVisual.restype = c_void_p

glXCreateContext = _gl.glXCreateContext
glXCreateContext.argtypes = [c_void_p, c_void_p, c_void_p, c_bool]
glXCreateContext.restype = c_void_p

glXMakeCurrent = _gl.glXMakeCurrent
glXMakeCurrent.argtypes = [c_void_p, GLXDrawable, c_void_p]
glXMakeCurrent.restype = c_bool

glXSwapBuffers = _gl.glXSwapBuffers
glXSwapBuffers.argtypes = [c_void_p, GLXDrawable]
glXSwapBuffers.resttype = None

X11_None = 0

x_open_display = _xlib.XOpenDisplay
x_open_display.argtypes = [c_char_p]
x_open_display.restype = c_void_p


class TkOglWin(Frame):

    def __init__(self, parent, *args, **kwargs):
        Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.parent.title(kwargs.get('app_title', 'Opengl Test'))
        self.bind('<Configure>', self.on_resize)
        self.parent.after(100, self._cfg_tkogl)

    def _cfg_tkogl(self):
        att = PGLint(
            GLX_RGBA, GLX_DOUBLEBUFFER,
            GLX_RED_SIZE, 4,
            GLX_GREEN_SIZE, 4,
            GLX_BLUE_SIZE, 4,
            GLX_DEPTH_SIZE, 16,
            X11_None
        )

        self.dpy = x_open_display(None)
        vi = glXChooseVisual(self.dpy, 0, att)
        glc = glXCreateContext(self.dpy, vi, None, GL_TRUE)
        glXMakeCurrent(self.dpy, self.winfo_id(), glc)
        self.set_ortho_view()
        self.parent.after(10, self._render_loop)

    def on_resize(self, event, arg=None):
        raise NotImplementedError

    def _render_loop(self):
        self.render_scene()
        glXSwapBuffers(self.dpy, self.winfo_id())
        self.parent.after(5, self._render_loop)

    def render_scene(self):
        raise NotImplementedError

    def set_ortho_view(self):
        raise NotImplementedError


class AppOgl(TkOglWin):
    rot = 0

    def on_resize(self, event, arg=None):
        if event:
            w = event.width
            h = event.height
        else:
            if arg:
                w = arg['w']
                h = arg['h']
            else:
                raise Exception

        dx = w / h
        glViewport(0, 0, w, h)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(-2 * dx, 2 * dx, -2, 2, -2, 2)

    def set_ortho_view(self):
        glEnable(GL_BLEND)
        glEnable(GL_DEPTH_TEST)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glClearColor(0, 0, 0, 0)
        glClearDepth(1)
        glMatrixMode(GL_PROJECTION)

        self.on_resize(None, arg={
            'w': self.winfo_width(),
            'h': self.winfo_height()
        })

        print('%s - %s - %s' % (
            glGetString(GL_VENDOR),
            glGetString(GL_VERSION),
            glGetString(GL_RENDERER)
        ))

    def render_scene(self):
        self.rot += .5

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glRotatef(self.rot, 1, 1, 0.5)

        # Draw a simple cube.
        glBegin(GL_QUADS)
        glColor3f(0, 1, 0)
        glVertex3f(1, 1, -1)
        glVertex3f(-1, 1, -1)
        glVertex3f(-1, 1, 1)
        glVertex3f(1, 1, 1)
        glColor3f(1, 0.5, 0)
        glVertex3f(1, -1, 1)
        glVertex3f(-1, -1, 1)
        glVertex3f(-1, -1, -1)
        glVertex3f(1, -1, -1)
        glColor3f(1, 0, 0)
        glVertex3f(1, 1, 1)
        glVertex3f(-1, 1, 1)
        glVertex3f(-1, -1, 1)
        glVertex3f(1, -1, 1)
        glColor3f(1, 1, 0)
        glVertex3f(1, -1, -1)
        glVertex3f(-1, -1, -1)
        glVertex3f(-1, 1, -1)
        glVertex3f(1, 1, -1)
        glColor3f(0, 0, 1)
        glVertex3f(-1, 1, 1)
        glVertex3f(-1, 1, -1)
        glVertex3f(-1, -1, -1)
        glVertex3f(-1, -1, 1)
        glColor3f(1, 0, 1)
        glVertex3f(1, 1, -1)
        glVertex3f(1, 1, 1)
        glVertex3f(1, -1, 1)
        glVertex3f(1, -1, -1)
        glEnd()
        glFlush()

if __name__ == '__main__':
    root = Tk()
    app = AppOgl(root, width=320, height=200)
    app.pack(fill=BOTH, expand=YES)
    app.mainloop()
answered on Stack Overflow Aug 29, 2016 by BPL • edited May 23, 2017 by Community
0

After selecting a FBConfig you also have to select a window visual that matches that FBConfig to make sure that this FBConfig can in fact be used for the window you're trying to attach do. Also the Window must have set its visual/FBConfig to that format so that you can make current the OpenGL context on it.

I have an example on how to do this in C in my codesamples repository over at GitHub. Feel free to use that as a template. There's also an alongside example that shows how to use GLX alongside Xcb (the example you can find over at the Xcb homepage is broken).

answered on Stack Overflow Aug 29, 2016 by datenwolf

User contributions licensed under CC BY-SA 3.0