///////////////////////////////////////////////////////////////////////////////
// Name:        src/unix/glx11.cpp
// Purpose:     code common to all X11-based wxGLCanvas implementations
// Author:      Vadim Zeitlin
// Created:     2007-04-15
// Copyright:   (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#if wxUSE_GLCANVAS && !wxUSE_GLCANVAS_EGL

#ifndef WX_PRECOMP
    #include "wx/log.h"
#endif //WX_PRECOMP

#include "wx/glcanvas.h"
#include <GL/glx.h>

// IRIX headers call this differently
#ifdef __SGI__
    #ifndef GLX_SAMPLE_BUFFERS_ARB
        #define GLX_SAMPLE_BUFFERS_ARB GLX_SAMPLE_BUFFERS_SGIS
    #endif
    #ifndef GLX_SAMPLES_ARB
        #define GLX_SAMPLES_ARB GLX_SAMPLES_SGIS
    #endif
#endif // __SGI__

// ----------------------------------------------------------------------------
// define possibly missing XGL constants and types
// ----------------------------------------------------------------------------

#ifndef GLX_NONE_EXT
#define GLX_NONE_EXT                       0x8000
#endif

#ifndef GLX_ARB_multisample
#define GLX_ARB_multisample
#define GLX_SAMPLE_BUFFERS_ARB             100000
#define GLX_SAMPLES_ARB                    100001
#endif

#ifndef GLX_EXT_visual_rating
#define GLX_EXT_visual_rating
#define GLX_VISUAL_CAVEAT_EXT              0x20
#define GLX_NONE_EXT                       0x8000
#define GLX_SLOW_VISUAL_EXT                0x8001
#define GLX_NON_CONFORMANT_VISUAL_EXT      0x800D
#endif

#ifndef GLX_EXT_visual_info
#define GLX_EXT_visual_info
#define GLX_X_VISUAL_TYPE_EXT              0x22
#define GLX_DIRECT_COLOR_EXT               0x8003
#endif

#ifndef GLX_ARB_framebuffer_sRGB
#define GLX_ARB_framebuffer_sRGB
#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB   0x20B2
#endif

#ifndef GLX_ARB_create_context
#define GLX_ARB_create_context
#define GLX_CONTEXT_MAJOR_VERSION_ARB      0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
#define GLX_CONTEXT_FLAGS_ARB              0x2094
#define GLX_CONTEXT_DEBUG_BIT_ARB          0x0001
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002

/* Typedef for the GL 3.0 context creation function */
typedef GLXContext(*PFNGLXCREATECONTEXTATTRIBSARBPROC)
    (Display * dpy, GLXFBConfig config, GLXContext share_context,
    Bool direct, const int *attrib_list);
#endif

#ifndef GLX_ARB_create_context_profile
#define GLX_ARB_create_context_profile
#define GLX_CONTEXT_PROFILE_MASK_ARB       0x9126
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB   0x00000001
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#endif

#ifndef GLX_ARB_create_context_robustness
#define GLX_ARB_create_context_robustness
#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB  0x00000004
#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB     0x8256
#define GLX_NO_RESET_NOTIFICATION_ARB                   0x8261
#define GLX_LOSE_CONTEXT_ON_RESET_ARB                   0x8252
#endif

#ifndef GLX_ARB_robustness_application_isolation
#define GLX_ARB_robustness_application_isolation
#define GLX_CONTEXT_RESET_ISOLATION_BIT_ARB             0x00000008
#endif
#ifndef GLX_ARB_robustness_share_group_isolation
#define GLX_ARB_robustness_share_group_isolation
#endif

#ifndef GLX_ARB_context_flush_control
#define GLX_ARB_context_flush_control
#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB            0x2097
#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB       0
#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB      0x2098
#endif

#ifndef GLX_EXT_create_context_es2_profile
#define GLX_EXT_create_context_es2_profile
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT    0x00000004
#endif

#ifndef GLX_EXT_create_context_es_profile
#define GLX_EXT_create_context_es_profile
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT    0x00000004
#endif

namespace
{

constexpr const char* TRACE_GLX = "glx";

} // anonymous namespace

// ----------------------------------------------------------------------------
// wxGLContextAttrs: OpenGL rendering context attributes
// ----------------------------------------------------------------------------
// GLX specific values

wxGLContextAttrs& wxGLContextAttrs::CoreProfile()
{
    AddAttribBits(GLX_CONTEXT_PROFILE_MASK_ARB,
                  GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::MajorVersion(int val)
{
    if ( val > 0 )
    {
        AddAttribute(GLX_CONTEXT_MAJOR_VERSION_ARB);
        AddAttribute(val);
        if ( val >= 3 )
            SetNeedsARB();
    }
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::MinorVersion(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(GLX_CONTEXT_MINOR_VERSION_ARB);
        AddAttribute(val);
    }
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::CompatibilityProfile()
{
    AddAttribBits(GLX_CONTEXT_PROFILE_MASK_ARB,
                  GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ForwardCompatible()
{
    AddAttribBits(GLX_CONTEXT_FLAGS_ARB,
                  GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ES2()
{
    AddAttribBits(GLX_CONTEXT_PROFILE_MASK_ARB,
                  GLX_CONTEXT_ES2_PROFILE_BIT_EXT);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::DebugCtx()
{
    AddAttribBits(GLX_CONTEXT_FLAGS_ARB,
                  GLX_CONTEXT_DEBUG_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::Robust()
{
    AddAttribBits(GLX_CONTEXT_FLAGS_ARB,
                  GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::NoResetNotify()
{
    AddAttribute(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
    AddAttribute(GLX_NO_RESET_NOTIFICATION_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::LoseOnReset()
{
    AddAttribute(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
    AddAttribute(GLX_LOSE_CONTEXT_ON_RESET_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ResetIsolation()
{
    AddAttribBits(GLX_CONTEXT_FLAGS_ARB,
                  GLX_CONTEXT_RESET_ISOLATION_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ReleaseFlush(int val)
{
    AddAttribute(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB);
    if ( val == 1 )
        AddAttribute(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
    else
        AddAttribute(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::PlatformDefaults()
{
    renderTypeRGBA = true;
    x11Direct = true;
    return *this;
}

void wxGLContextAttrs::EndList()
{
    AddAttribute(None);
}

// ----------------------------------------------------------------------------
// wxGLAttributes: Visual/FBconfig attributes
// ----------------------------------------------------------------------------
// GLX specific values

//   Different versions of GLX API use rather different attributes lists, see
//   the following URLs:
//
//   - <= 1.2: http://www.opengl.org/sdk/docs/man/xhtml/glXChooseVisual.xml
//   - >= 1.3: http://www.opengl.org/sdk/docs/man/xhtml/glXChooseFBConfig.xml
//
//   Notice in particular that
//   - GLX_RGBA is boolean attribute in the old version of the API but a
//     value of GLX_RENDER_TYPE in the new one
//   - Boolean attributes such as GLX_DOUBLEBUFFER don't take values in the
//     old version but must be followed by True or False in the new one.

wxGLAttributes& wxGLAttributes::RGBA()
{
    if ( wxGLCanvasX11::GetGLXVersion() >= 13 )
        AddAttribBits(GLX_RENDER_TYPE, GLX_RGBA_BIT);
    else
        AddAttribute(GLX_RGBA);
    return *this;
}

wxGLAttributes& wxGLAttributes::BufferSize(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(GLX_BUFFER_SIZE);
        AddAttribute(val);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::Level(int val)
{
    AddAttribute(GLX_LEVEL);
    AddAttribute(val);
    return *this;
}

wxGLAttributes& wxGLAttributes::DoubleBuffer()
{
    AddAttribute(GLX_DOUBLEBUFFER);
    if ( wxGLCanvasX11::GetGLXVersion() >= 13 )
        AddAttribute(True);
    return *this;
}

wxGLAttributes& wxGLAttributes::Stereo()
{
    AddAttribute(GLX_STEREO);
    if ( wxGLCanvasX11::GetGLXVersion() >= 13 )
        AddAttribute(True);
    return *this;
}

wxGLAttributes& wxGLAttributes::AuxBuffers(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(GLX_AUX_BUFFERS);
        AddAttribute(val);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::MinRGBA(int mRed, int mGreen, int mBlue, int mAlpha)
{
    if ( mRed >= 0)
    {
        AddAttribute(GLX_RED_SIZE);
        AddAttribute(mRed);
    }
    if ( mGreen >= 0)
    {
        AddAttribute(GLX_GREEN_SIZE);
        AddAttribute(mGreen);
    }
    if ( mBlue >= 0)
    {
        AddAttribute(GLX_BLUE_SIZE);
        AddAttribute(mBlue);
    }
    if ( mAlpha >= 0)
    {
        AddAttribute(GLX_ALPHA_SIZE);
        AddAttribute(mAlpha);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::Depth(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(GLX_DEPTH_SIZE);
        AddAttribute(val);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::Stencil(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(GLX_STENCIL_SIZE);
        AddAttribute(val);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::MinAcumRGBA(int mRed, int mGreen, int mBlue, int mAlpha)
{
    if ( mRed >= 0)
    {
        AddAttribute(GLX_ACCUM_RED_SIZE);
        AddAttribute(mRed);
    }
    if ( mGreen >= 0)
    {
        AddAttribute(GLX_ACCUM_GREEN_SIZE);
        AddAttribute(mGreen);
    }
    if ( mBlue >= 0)
    {
        AddAttribute(GLX_ACCUM_BLUE_SIZE);
        AddAttribute(mBlue);
    }
    if ( mAlpha >= 0)
    {
        AddAttribute(GLX_ACCUM_ALPHA_SIZE);
        AddAttribute(mAlpha);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::SampleBuffers(int val)
{
#ifdef GLX_SAMPLE_BUFFERS_ARB
    if ( val >= 0 && wxGLCanvasX11::IsGLXMultiSampleAvailable() )
    {
        AddAttribute(GLX_SAMPLE_BUFFERS_ARB);
        AddAttribute(val);
    }
#endif
    return *this;
}

wxGLAttributes& wxGLAttributes::Samplers(int val)
{
#ifdef GLX_SAMPLES_ARB
    if ( val >= 0 && wxGLCanvasX11::IsGLXMultiSampleAvailable() )
    {
        AddAttribute(GLX_SAMPLES_ARB);
        AddAttribute(val);
    }
#endif
    return *this;
}

wxGLAttributes& wxGLAttributes::FrameBuffersRGB()
{
    AddAttribute(GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
    AddAttribute(True);
    return *this;
}

void wxGLAttributes::EndList()
{
    AddAttribute(None);
}

wxGLAttributes& wxGLAttributes::PlatformDefaults()
{
    // No GLX specific values
    return *this;
}


// ============================================================================
// wxGLContext implementation
// ============================================================================

static bool MakeCurrent(GLXDrawable drawable, GLXContext context);

// Need this X error handler for the case context creation fails
static bool g_ctxErrorOccurred = false;
static int CTXErrorHandler( Display* WXUNUSED(dpy), XErrorEvent* WXUNUSED(ev) )
{
    g_ctxErrorOccurred = true;
    return 0;
}

wxIMPLEMENT_CLASS(wxGLContext, wxObject);

wxGLContext::wxGLContext(wxGLCanvas *win,
                         const wxGLContext *other,
                         const wxGLContextAttrs *ctxAttrs)
    : m_glContext(nullptr)
{
    const int* contextAttribs = nullptr;
    Bool x11Direct = True;
    int renderType = GLX_RGBA_TYPE;
    bool needsARB = false;

    if ( ctxAttrs )
    {
        contextAttribs = ctxAttrs->GetGLAttrs();
        x11Direct = ctxAttrs->x11Direct;
        renderType = ctxAttrs->renderTypeRGBA ? GLX_RGBA_TYPE : GLX_COLOR_INDEX_TYPE;
        needsARB = ctxAttrs->NeedsARB();
    }
    else if ( win->GetGLCTXAttrs().GetGLAttrs() )
    {
        // If OpenGL context parameters were set at wxGLCanvas ctor, get them now
        contextAttribs = win->GetGLCTXAttrs().GetGLAttrs();
        x11Direct = win->GetGLCTXAttrs().x11Direct;
        renderType = win->GetGLCTXAttrs().renderTypeRGBA ? GLX_RGBA_TYPE : GLX_COLOR_INDEX_TYPE;
        needsARB = win->GetGLCTXAttrs().NeedsARB();
    }
    // else use GPU driver defaults and x11Direct renderType ones

    m_isOk = false;

    Display* dpy = wxGetX11Display();
    XVisualInfo* vi = static_cast<XVisualInfo*>(win->GetXVisualInfo());
    wxCHECK_RET( vi, "invalid visual for OpenGL" );

    // We need to create a temporary context to get the
    // glXCreateContextAttribsARB function
    GLXContext tempContext = glXCreateContext(dpy, vi, nullptr, x11Direct);
    wxCHECK_RET(tempContext, "glXCreateContext failed" );

    GLXFBConfig* const fbc = win->GetGLXFBConfig();
    PFNGLXCREATECONTEXTATTRIBSARBPROC wx_glXCreateContextAttribsARB = 0;
    if (fbc)
    {
        wx_glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
            glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB"));
    }

    glXDestroyContext( dpy, tempContext );

    // The preferred way is using glXCreateContextAttribsARB, even for old context
    if ( !wx_glXCreateContextAttribsARB && needsARB ) // OpenGL 3 context creation
    {
        wxLogMessage(_("OpenGL 3.0 or later is not supported by the OpenGL driver."));
        return;
    }

    // Install a X error handler, so as to the app doesn't exit (without
    // even a warning) if GL >= 3.0 context creation fails
    g_ctxErrorOccurred = false;
    int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&CTXErrorHandler);

    if ( wx_glXCreateContextAttribsARB )
    {
        m_glContext = wx_glXCreateContextAttribsARB( dpy, fbc[0],
                                other ? other->m_glContext : None,
                                x11Direct, contextAttribs );

        // Some old hardware may accept the use of this ARB, but may fail.
        // In case of null attributes we'll try creating the context old-way.
        XSync( dpy, False );
        if ( g_ctxErrorOccurred && (!contextAttribs || !needsARB) )
        {
            g_ctxErrorOccurred = false; //Reset
            m_glContext = nullptr;
        }
    }

    if ( !g_ctxErrorOccurred && !m_glContext )
    {
        // Old-way, without context atributes. Up to GL 2.1
        if (fbc)
        {
            m_glContext = glXCreateNewContext( dpy, fbc[0], renderType,
                                               other ? other->m_glContext : None,
                                               x11Direct );
        }
        else // GLX <= 1.2
        {
            m_glContext = glXCreateContext( dpy, vi,
                                            other ? other->m_glContext : None,
                                            x11Direct );
        }
    }

    // Sync to ensure any errors generated are processed.
    XSync( dpy, False );

    if ( g_ctxErrorOccurred || !m_glContext )
        wxLogMessage(_("Couldn't create OpenGL context"));
    else
        m_isOk = true;

    // Restore old error handler
    XSetErrorHandler( oldHandler );
}

wxGLContext::~wxGLContext()
{
    if ( !m_glContext )
        return;

    if ( m_glContext == glXGetCurrentContext() )
        MakeCurrent(None, nullptr);

    glXDestroyContext( wxGetX11Display(), m_glContext );
}

bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
{
    if ( !m_glContext )
        return false;

    const Window xid = win.GetXWindow();
    wxCHECK2_MSG( xid, return false, wxT("window must be shown") );

    return MakeCurrent(xid, m_glContext);
}

// wrapper around glXMakeContextCurrent/glXMakeCurrent depending on GLX
// version
static bool MakeCurrent(GLXDrawable drawable, GLXContext context)
{
    if (wxGLCanvas::GetGLXVersion() >= 13)
        return glXMakeContextCurrent( wxGetX11Display(), drawable, drawable, context);
    else // GLX <= 1.2 doesn't have glXMakeContextCurrent()
        return glXMakeCurrent( wxGetX11Display(), drawable, context);
}

// ============================================================================
// wxGLCanvasX11 implementation
// ============================================================================

static GLXFBConfig* gs_glFBCInfo;
static XVisualInfo* gs_glVisualInfo;

static bool InitXVisualInfo(
    const wxGLAttributes& dispAttrs, GLXFBConfig** pFBC, XVisualInfo** pXVisual);

// ----------------------------------------------------------------------------
// initialization methods and dtor
// ----------------------------------------------------------------------------

wxGLCanvasX11::wxGLCanvasX11()
{
    m_fbc = nullptr;
    m_vi = nullptr;
}

bool wxGLCanvasX11::InitVisual(const wxGLAttributes& dispAttrs)
{
    XVisualInfo* vi = nullptr;
    bool ret = InitXVisualInfo(dispAttrs, &m_fbc, &vi);
    m_vi = vi;
    if ( !ret )
    {
        wxFAIL_MSG("Failed to get a XVisualInfo for the requested attributes.");
    }
    return ret;
}

wxGLCanvasX11::~wxGLCanvasX11()
{
    if (m_fbc && m_fbc != gs_glFBCInfo)
        XFree(m_fbc);

    if (m_vi && m_vi != gs_glVisualInfo)
        XFree(m_vi);
}

// ----------------------------------------------------------------------------
// working with GL attributes
// ----------------------------------------------------------------------------

/* static */
bool wxGLCanvasBase::IsExtensionSupported(const char *extension)
{
    Display * const dpy = wxGetX11Display();

    return IsExtensionInList(glXQueryExtensionsString(dpy, DefaultScreen(dpy)),
                             extension);
}


/* static */
bool wxGLCanvasX11::IsGLXMultiSampleAvailable()
{
    static int s_isMultiSampleAvailable = -1;
    if ( s_isMultiSampleAvailable == -1 )
        s_isMultiSampleAvailable = IsExtensionSupported("GLX_ARB_multisample");

    return s_isMultiSampleAvailable != 0;
}

static bool InitXVisualInfo(const wxGLAttributes& dispAttrs,
                                    GLXFBConfig** pFBC,
                                    XVisualInfo** pXVisual)
{
    // GLX_XX attributes
    const int* attrsListGLX = dispAttrs.GetGLAttrs();
    if ( !attrsListGLX )
    {
        wxFAIL_MSG("wxGLAttributes object is empty.");
        return false;
    }

    Display* dpy = wxGetX11Display();

    if (wxGLCanvasX11::GetGLXVersion() >= 13)
    {
        int returned;
        *pFBC = glXChooseFBConfig(dpy, DefaultScreen(dpy), attrsListGLX, &returned);

        if ( *pFBC )
        {
            // Use the first good match
            *pXVisual = glXGetVisualFromFBConfig(wxGetX11Display(), **pFBC);
            if ( !*pXVisual )
            {
                XFree(*pFBC);
                *pFBC = nullptr;
            }
        }
    }
    else // GLX <= 1.2
    {
        *pFBC = nullptr;
        *pXVisual = glXChooseVisual(dpy, DefaultScreen(dpy),
                                    const_cast<int*>(attrsListGLX) );
    }

    return *pXVisual != nullptr;
}

/* static */
bool wxGLCanvasBase::IsDisplaySupported(const wxGLAttributes& dispAttrs)
{
    GLXFBConfig *fbc = nullptr;
    XVisualInfo *vi = nullptr;

    bool isSupported = InitXVisualInfo(dispAttrs, &fbc, &vi);

    if ( fbc )
        XFree(fbc);
    if ( vi )
        XFree(vi);

    return isSupported;
}

/* static */
bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
{
    wxGLAttributes dispAttrs;
    ParseAttribList(attribList, dispAttrs);

    return IsDisplaySupported(dispAttrs);
}

// ----------------------------------------------------------------------------
// default visual management
// ----------------------------------------------------------------------------

static void FreeDefaultVisualInfo()
{
    if (gs_glFBCInfo)
    {
        XFree(gs_glFBCInfo);
        gs_glFBCInfo = nullptr;
    }
    if (gs_glVisualInfo)
    {
        XFree(gs_glVisualInfo);
        gs_glVisualInfo = nullptr;
    }
}

/* static */
bool wxGLCanvasX11::InitDefaultVisualInfo(const int *attribList)
{
    FreeDefaultVisualInfo();
    wxGLAttributes dispAttrs;
    ParseAttribList(attribList, dispAttrs);

    return InitXVisualInfo(dispAttrs, &gs_glFBCInfo, &gs_glVisualInfo);
}

// ----------------------------------------------------------------------------
// other GL methods
// ----------------------------------------------------------------------------

/* static */
int wxGLCanvasX11::GetGLXVersion()
{
    static int s_glxVersion = 0;
    if ( s_glxVersion == 0 )
    {
        // check the GLX version
        int glxMajorVer, glxMinorVer;
        bool ok = glXQueryVersion(wxGetX11Display(), &glxMajorVer, &glxMinorVer);
        wxASSERT_MSG( ok, wxT("GLX version not found") );
        if (!ok)
            s_glxVersion = 10; // 1.0 by default
        else
            s_glxVersion = glxMajorVer*10 + glxMinorVer;
    }

    return s_glxVersion;
}

namespace
{

// Call glXSwapIntervalEXT() if present.
//
// For now just try using EXT_swap_control extension, in principle there is
// also a MESA one, but it's not clear if it's worth falling back on it (or
// preferring to use it?).
void wxGLSetSwapInterval(Display* dpy, GLXDrawable drawable, int interval)
{
    typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display *dpy,
                                              GLXDrawable drawable,
                                              int interval);

    static PFNGLXSWAPINTERVALEXTPROC s_glXSwapIntervalEXT = nullptr;
    static bool s_glXSwapIntervalEXTInit = false;
    if ( !s_glXSwapIntervalEXTInit )
    {
        s_glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
            glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");

        s_glXSwapIntervalEXTInit = true;
    }

    if ( s_glXSwapIntervalEXT )
    {
        wxLogTrace(TRACE_GLX, "Setting GLX swap interval to %d", interval);

        s_glXSwapIntervalEXT(dpy, drawable, interval);
    }
}

} // anonymous namespace

bool wxGLCanvasX11::SwapBuffers()
{
    const Window xid = GetXWindow();
    wxCHECK2_MSG( xid, return false, wxT("window must be shown") );

    const auto dpy = wxGetX11Display();

    // Disable blocking in glXSwapBuffers, as this is needed under XWayland for
    // the reasons explained in wxGLCanvasEGL::SwapBuffers().
    if ( !m_swapIntervalSet )
    {
        wxGLSetSwapInterval(dpy, xid, 0);

        // Don't try again in any case, if we failed this time, we'll fail the
        // next one anyhow.
        m_swapIntervalSet = true;
    }

    glXSwapBuffers(dpy, xid);
    return true;
}

bool wxGLCanvasX11::IsShownOnScreen() const
{
    return GetXWindow() && wxGLCanvasBase::IsShownOnScreen();
}

// ----------------------------------------------------------------------------
// wxGLApp
// ----------------------------------------------------------------------------

bool wxGLApp::InitGLVisual(const int* attribList)
{
    return wxGLCanvasX11::InitDefaultVisualInfo(attribList);
}

void* wxGLApp::GetXVisualInfo()
{
    return gs_glVisualInfo;
}

int wxGLApp::OnExit()
{
    FreeDefaultVisualInfo();

    return wxGLAppBase::OnExit();
}

#endif // wxUSE_GLCANVAS
