Search code examples
c++openglxcbglx

BadValue on X_ChangeProperty when calling glXChooseFBConfig() on XCB connection?


I'm trying to use my XCB implementation with OpenGL but I encountered an error.

The problem is in the code below:

#include <dlfcn.h>
#include <iostream>

#include <xcb/xcb.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <sys/time.h>
#include <time.h>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <memory>

#include <GL/glx.h>
#include <GL/gl.h>

struct LinuxPlatformInternalState
{
    Display *display;
    xcb_connection_t *connection;
    xcb_window_t window;
    xcb_screen_t *screen;
    xcb_atom_t wmDeleteProtocols;
    xcb_atom_t wmDestroyWindow;
    int screenCount;
};

static const int visualAttribs[] =
    {
        GLX_X_RENDERABLE, True,
        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
        GLX_RENDER_TYPE, GLX_RGBA_BIT,
        GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
        GLX_RED_SIZE, 8,
        GLX_GREEN_SIZE, 8,
        GLX_BLUE_SIZE, 8,
        GLX_ALPHA_SIZE, 8,
        GLX_DEPTH_SIZE, 24,
        GLX_STENCIL_SIZE, 8,
        GLX_DOUBLEBUFFER, True,
        None};

int _getEventsMask()
{
    return XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
}

unsigned int _getEventsValue()
{
    return XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
}

int main(int argc, char **argv)
{
    std::shared_ptr<LinuxPlatformInternalState> m_pstate = std::make_shared<LinuxPlatformInternalState>();

    // connect to X Server
    m_pstate->display = XOpenDisplay(0);

    if (m_pstate->display == nullptr)
    {
        return false;
    }

    XAutoRepeatOff(m_pstate->display);
    m_pstate->screenCount = XScreenCount(m_pstate->display);

    // init XCB
    m_pstate->connection = XGetXCBConnection(m_pstate->display);

    if (m_pstate->connection == nullptr || xcb_connection_has_error(m_pstate->connection))
    {
        return false;
    }

    const xcb_setup_t *setup = xcb_get_setup(m_pstate->connection);

    m_pstate->screen = xcb_setup_roots_iterator(setup).data;
    m_pstate->window = xcb_generate_id(m_pstate->connection);

    int eventsMask = _getEventsMask();
    u_int32_t eventValues = _getEventsValue();
    u_int32_t valueList[] = {m_pstate->screen->black_pixel, eventValues};

    xcb_void_cookie_t cookie = xcb_create_window(
        m_pstate->connection,
        XCB_COPY_FROM_PARENT,
        m_pstate->window,
        m_pstate->screen->root,
        0,
        0,
        800,
        600,
        0,
        XCB_WINDOW_CLASS_INPUT_OUTPUT,
        m_pstate->screen->root_visual,
        eventsMask,
        valueList);

    if (cookie.sequence == 0)
    {
        return false;
    }

    std::string appName = "Test";

    // change window title
    xcb_change_property(
        m_pstate->connection,
        XCB_PROP_MODE_REPLACE,
        m_pstate->window,
        XCB_ATOM_WM_NAME,
        XCB_ATOM_STRING,
        0,
        appName.length(),
        appName.c_str());

    xcb_void_cookie_t mappingCookie = xcb_map_window(m_pstate->connection, m_pstate->window);

    if (mappingCookie.sequence == 0)
    {
        return -1;
    }

    // flush the stream
    int flushResult = xcb_flush(m_pstate->connection);

    if (flushResult <= 0)
    {
        return -1;
    }

    int numFbConfigs = 0;
    int visualId = 0;
    int defaultScreen = DefaultScreen(m_pstate->display);

    GLXFBConfig *frameBufferConfigs = glXChooseFBConfig(m_pstate->display, defaultScreen, visualAttribs, &numFbConfigs);

    if (!frameBufferConfigs || numFbConfigs <= 0)
    {

        if (frameBufferConfigs)
        {
            XFree(frameBufferConfigs);
        }
        return -1;
    }

    bool configFound = false;
    for (int i = 0; i < numFbConfigs; ++i)
    {
        if (!glXGetFBConfigAttrib(m_pstate->display, frameBufferConfigs[i], GLX_VISUAL_ID, &visualId))
        {
            continue;
        }

        GLXContext context = glXCreateNewContext(m_pstate->display, frameBufferConfigs[i], GLX_RGBA_TYPE, 0, True);

        if (!context)
        {
            continue;
        }

        GLXDrawable drawable = 0;

        if (!m_pstate->window)
        {
            glXDestroyContext(m_pstate->display, context);
            XFree(frameBufferConfigs);
            continue;
        }

        GLXWindow glxwindow = glXCreateWindow(m_pstate->display, frameBufferConfigs[i], m_pstate->window, 0);

        if (!glxwindow)
        {
            glXDestroyContext(m_pstate->display, context);
            XFree(frameBufferConfigs);
            continue;
        }

        drawable = glxwindow;

        bool glxMakeCtxCurrentRes = glXMakeContextCurrent(m_pstate->display, drawable, drawable, context);

        if (!glxMakeCtxCurrentRes)
        {
            glXDestroyContext(m_pstate->display, context);
            glXDestroyWindow(m_pstate->display, glxwindow);
            XFree(frameBufferConfigs);
            continue;
        }

        configFound = true;
        break;
    }

    if (!configFound)
    {
        XFree(frameBufferConfigs);
        return -1;
    }

    XFree(frameBufferConfigs);

    return 0;
}

When I'm trying to execute it, it gives the following output:

X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  18 (X_ChangeProperty)
  Value in failed request:  0x0
  Serial number of failed request:  9
  Current serial number in output stream:  13

The problem seems to be related to the integer parameter defaultScreen. I'm pretty confident m_pstate->display is not the malformed or anything because I used it on another implementation of Vulkan in the same project without any issue, Of course the xcb_open_window call worked fine.

Anyone has some kind of idea of what is the problem?


Solution

  • The problem is in xcb_change_property call. The permissible values for the format parameter are 8, 16 and 32. You are passing 0.

    Since your data is a byte-string, you need to pass 8. This at least makes the program open a window in my experiment, and its title is Test as expected.