/////////////////////////////////////////////////////////////////////////////
// Name:        src/msw/glcanvas.cpp
// Purpose:     wxGLCanvas, for using OpenGL with wxWidgets under MS Windows
// Author:      Julian Smart
// Modified by:
// Created:     04/01/98
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

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

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

#include "wx/wxprec.h"

#if defined(__BORLANDC__)
    #pragma hdrstop
#endif

#if wxUSE_GLCANVAS

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

#include "wx/msw/private.h"

#include "wx/glcanvas.h"

// from src/msw/window.cpp
LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

#ifdef GL_EXT_vertex_array
    #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name
#else
    #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name)
#endif

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

#ifndef WGL_ARB_pixel_format
#define WGL_ARB_pixel_format
#define WGL_DRAW_TO_WINDOW_ARB            0x2001
#define WGL_DRAW_TO_BITMAP_ARB            0x2002
#define WGL_ACCELERATION_ARB              0x2003
#define WGL_NEED_PALETTE_ARB              0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB       0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB        0x2006
#define WGL_SWAP_METHOD_ARB               0x2007
#define WGL_NUMBER_OVERLAYS_ARB           0x2008
#define WGL_NUMBER_UNDERLAYS_ARB          0x2009
#define WGL_SUPPORT_GDI_ARB               0x200F
#define WGL_SUPPORT_OPENGL_ARB            0x2010
#define WGL_DOUBLE_BUFFER_ARB             0x2011
#define WGL_STEREO_ARB                    0x2012
#define WGL_PIXEL_TYPE_ARB                0x2013
#define WGL_COLOR_BITS_ARB                0x2014
#define WGL_RED_BITS_ARB                  0x2015
#define WGL_GREEN_BITS_ARB                0x2017
#define WGL_BLUE_BITS_ARB                 0x2019
#define WGL_ALPHA_BITS_ARB                0x201B
#define WGL_ACCUM_BITS_ARB                0x201D
#define WGL_ACCUM_RED_BITS_ARB            0x201E
#define WGL_ACCUM_GREEN_BITS_ARB          0x201F
#define WGL_ACCUM_BLUE_BITS_ARB           0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB          0x2021
#define WGL_DEPTH_BITS_ARB                0x2022
#define WGL_STENCIL_BITS_ARB              0x2023
#define WGL_AUX_BUFFERS_ARB               0x2024
#define WGL_NO_ACCELERATION_ARB           0x2025
#define WGL_GENERIC_ACCELERATION_ARB      0x2026
#define WGL_FULL_ACCELERATION_ARB         0x2027
#define WGL_SWAP_EXCHANGE_ARB             0x2028
#define WGL_SWAP_COPY_ARB                 0x2029
#define WGL_TYPE_RGBA_ARB                 0x202B
#define WGL_TYPE_COLORINDEX_ARB           0x202C
#endif

#ifndef WGL_ARB_multisample
#define WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB            0x2041
#define WGL_SAMPLES_ARB                   0x2042
#endif

#ifndef WGL_ARB_framebuffer_sRGB
#define WGL_ARB_framebuffer_sRGB
#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB  0x20A9
#endif

#ifndef WGL_ARB_create_context
#define WGL_ARB_create_context
#define WGL_CONTEXT_MAJOR_VERSION_ARB   0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB   0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB     0x2093
#define WGL_CONTEXT_FLAGS_ARB           0x2094
#define WGL_CONTEXT_DEBUG_BIT_ARB       0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB  0x0002
#endif

#ifndef WGL_ARB_create_context_profile
#define WGL_ARB_create_context_profile
#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB          0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#endif

#ifndef WGL_ARB_create_context_robustness
#define WGL_ARB_create_context_robustness
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB         0x00000004
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB     0x8256
#define WGL_NO_RESET_NOTIFICATION_ARB                   0x8261
#define WGL_LOSE_CONTEXT_ON_RESET_ARB                   0x8252
#endif

#ifndef WGL_ARB_robustness_application_isolation
#define WGL_ARB_robustness_application_isolation
#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB             0x00000008
#endif
#ifndef WGL_ARB_robustness_share_group_isolation
#define WGL_ARB_robustness_share_group_isolation
#endif

#ifndef WGL_ARB_context_flush_control
#define WGL_ARB_context_flush_control
#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB            0x2097
#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB       0
#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB      0x2098
#endif

#ifndef WGL_EXT_create_context_es2_profile
#define WGL_EXT_create_context_es2_profile
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT           0x00000004
#endif

#ifndef WGL_EXT_create_context_es_profile
#define WGL_EXT_create_context_es_profile
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT            0x00000004
#endif

// This helper function only exists to suppress unavoidable gcc 8 warnings
// about incompatible function casts.
template <typename T>
inline T wxWGLProcCast(PROC proc)
{
    wxGCC_WARNING_SUPPRESS_CAST_FUNCTION_TYPE()

    return reinterpret_cast<T>(proc);

    wxGCC_WARNING_RESTORE_CAST_FUNCTION_TYPE()
}

// this macro defines a variable of type "name_t" called "name" and initializes
// it with the pointer to WGL function "name" (which may be NULL)
//
// NB: type name_t must be defined by the code using the macro
#define wxDEFINE_WGL_FUNC(name) \
    name##_t name = wxWGLProcCast<name##_t>(wglGetProcAddress(#name))

// ----------------------------------------------------------------------------
// libraries
// ----------------------------------------------------------------------------

/*
  The following two compiler directives are specific to the Microsoft Visual
  C++ family of compilers

  Fundementally what they do is instruct the linker to use these two libraries
  for the resolution of symbols. In essence, this is the equivalent of adding
  these two libraries to either the Makefile or project file.

  This is NOT a recommended technique, and certainly is unlikely to be used
  anywhere else in wxWidgets given it is so specific to not only wxMSW, but
  also the VC compiler. However, in the case of opengl support, it's an
  applicable technique as opengl is optional in setup.h This code (wrapped by
  wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
  setup.h rather than by having to modify either the project or DSP fle.

  See MSDN for further information on the exact usage of these commands.
*/
#ifdef _MSC_VER
#  pragma comment( lib, "opengl32" )
#  pragma comment( lib, "glu32" )
#endif

//-----------------------------------------------------------------------------
// Documentation on internals
//-----------------------------------------------------------------------------

//  OpenGL has evolved not only adding new features, but also there are new
//  ways of doing things. Among these new ways, we pay special attention to
//  pixel format choosing and context creation.
//
//  The old way of choosing a pixel format is to use a PIXELFORMATDESCRIPTOR
//  struct for setting the wanted attributes and to call ChoosePixelFormat()
//  with that struct.
//  When new attributes came into scene, the MSW struct became inadequate, and
//  a new method was implemented: wglChoosePixelFormatARB().
//
//  For rendering context creation wglCreateContext() was the function to call.
//  Starting with OpenGL 3.0 (2009) there are several attributes for context,
//  specially for wanted version and "core" or "compatibility" profiles.
//  In order to use these new features, wglCreateContextAttribsARB() must be
//  called instead of wglCreateContext().
//
//  wxWidgets handles this OpenGL evolution trying to use the new ways, and
//  falling back to the old ways if neither the latter are available nor the
//  user requires them.
//
//  wxGLAttributes is used for pixel format attributes.
//  wxGLContextAttrs is used for OpenGL rendering context attributes.
//  wxWidgets does not handle all of OpenGL attributes. This is because some of
//  them are platform-dependent, or perhaps too new for wx. To cope with these
//  cases, these two objects allow the user to set his own attributes.
//
//  To keep wxWidgets backwards compatibility, a list of mixed attributes is
//  still allowed at wxGLCanvas constructor.


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

wxGLContextAttrs& wxGLContextAttrs::CoreProfile()
{
    AddAttribBits(WGL_CONTEXT_PROFILE_MASK_ARB,
                  WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

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

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

wxGLContextAttrs& wxGLContextAttrs::CompatibilityProfile()
{
    AddAttribBits(WGL_CONTEXT_PROFILE_MASK_ARB,
                  WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ForwardCompatible()
{
    AddAttribBits(WGL_CONTEXT_FLAGS_ARB,
                  WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ES2()
{
    AddAttribBits(WGL_CONTEXT_PROFILE_MASK_ARB,
                  WGL_CONTEXT_ES2_PROFILE_BIT_EXT);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::DebugCtx()
{
    AddAttribBits(WGL_CONTEXT_FLAGS_ARB,
                  WGL_CONTEXT_DEBUG_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::Robust()
{
    AddAttribBits(WGL_CONTEXT_FLAGS_ARB,
                  WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::NoResetNotify()
{
    AddAttribute(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
    AddAttribute(WGL_NO_RESET_NOTIFICATION_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::LoseOnReset()
{
    AddAttribute(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
    AddAttribute(WGL_LOSE_CONTEXT_ON_RESET_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ResetIsolation()
{
    AddAttribBits(WGL_CONTEXT_FLAGS_ARB,
                  WGL_CONTEXT_RESET_ISOLATION_BIT_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::ReleaseFlush(int val)
{
    AddAttribute(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB);
    if ( val == 1 )
        AddAttribute(WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
    else
        AddAttribute(WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
    SetNeedsARB();
    return *this;
}

wxGLContextAttrs& wxGLContextAttrs::PlatformDefaults()
{
    // No MSW specific defaults
    return *this;
}

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

// ----------------------------------------------------------------------------
// wxGLAttributes: pixel format attributes
// ----------------------------------------------------------------------------
// MSW specific values

wxGLAttributes& wxGLAttributes::RGBA()
{
    AddAttribute(WGL_PIXEL_TYPE_ARB);
    AddAttribute(WGL_TYPE_RGBA_ARB);
    return *this;
}

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

wxGLAttributes& wxGLAttributes::Level(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(WGL_NUMBER_OVERLAYS_ARB);
        AddAttribute(val);
    }
    else if ( val < 0 )
    {
        AddAttribute(WGL_NUMBER_UNDERLAYS_ARB);
        AddAttribute(-val);
    }
    if ( val < -1 || val > 1 )
    {
        SetNeedsARB();
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::DoubleBuffer()
{
    AddAttribute(WGL_DOUBLE_BUFFER_ARB);
    AddAttribute(GL_TRUE);
    return *this;
}

wxGLAttributes& wxGLAttributes::Stereo()
{
    AddAttribute(WGL_STEREO_ARB);
    AddAttribute(GL_TRUE);
    return *this;
}

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

wxGLAttributes& wxGLAttributes::MinRGBA(int mRed, int mGreen, int mBlue, int mAlpha)
{
    int colorBits = 0;
    if ( mRed >= 0)
    {
        AddAttribute(WGL_RED_BITS_ARB);
        AddAttribute(mRed);
        colorBits += mRed;
    }
    if ( mGreen >= 0)
    {
        AddAttribute(WGL_GREEN_BITS_ARB);
        AddAttribute(mGreen);
        colorBits += mGreen;
    }
    if ( mBlue >= 0)
    {
        AddAttribute(WGL_BLUE_BITS_ARB);
        AddAttribute(mBlue);
        colorBits += mBlue;
    }
    if ( mAlpha >= 0)
    {
        AddAttribute(WGL_ALPHA_BITS_ARB);
        AddAttribute(mAlpha);
        // doesn't count in colorBits
    }
    if ( colorBits )
    {
        AddAttribute(WGL_COLOR_BITS_ARB);
        AddAttribute(colorBits);
    }
    return *this;
}

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

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

wxGLAttributes& wxGLAttributes::MinAcumRGBA(int mRed, int mGreen, int mBlue, int mAlpha)
{
    int acumBits = 0;
    if ( mRed >= 0)
    {
        AddAttribute(WGL_ACCUM_RED_BITS_ARB);
        AddAttribute(mRed);
        acumBits += mRed;
    }
    if ( mGreen >= 0)
    {
        AddAttribute(WGL_ACCUM_GREEN_BITS_ARB);
        AddAttribute(mGreen);
        acumBits += mGreen;
    }
    if ( mBlue >= 0)
    {
        AddAttribute(WGL_ACCUM_BLUE_BITS_ARB);
        AddAttribute(mBlue);
        acumBits += mBlue;
    }
    if ( mAlpha >= 0)
    {
        AddAttribute(WGL_ACCUM_ALPHA_BITS_ARB);
        AddAttribute(mAlpha);
        acumBits += mAlpha;
    }
    if ( acumBits )
    {
        AddAttribute(WGL_ACCUM_BITS_ARB);
        AddAttribute(acumBits);
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::SampleBuffers(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(WGL_SAMPLE_BUFFERS_ARB);
        AddAttribute(val);
        SetNeedsARB();
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::Samplers(int val)
{
    if ( val >= 0 )
    {
        AddAttribute(WGL_SAMPLES_ARB);
        AddAttribute(val);
        SetNeedsARB();
    }
    return *this;
}

wxGLAttributes& wxGLAttributes::FrameBuffersRGB()
{
    AddAttribute(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
    AddAttribute(GL_TRUE);
    SetNeedsARB();
    return *this;
}

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

wxGLAttributes& wxGLAttributes::PlatformDefaults()
{
    AddAttribute(WGL_DRAW_TO_WINDOW_ARB);
    AddAttribute(GL_TRUE);
    AddAttribute(WGL_SUPPORT_OPENGL_ARB);
    AddAttribute(GL_TRUE);
    AddAttribute(WGL_ACCELERATION_ARB);
    AddAttribute(WGL_FULL_ACCELERATION_ARB);
    return *this;
}

wxGLAttributes& wxGLAttributes::Defaults()
{
    RGBA().Depth(16).DoubleBuffer().SampleBuffers(1).Samplers(4);
    SetNeedsARB();
    return *this;
}

void wxGLAttributes::AddDefaultsForWXBefore31()
{
    // ParseAttribList() will add EndList(), don't do it now
    RGBA().DoubleBuffer().Depth(16);
}

// ----------------------------------------------------------------------------
// wxGLContext
// ----------------------------------------------------------------------------

wxIMPLEMENT_CLASS(wxGLContext, wxObject);

wxGLContext::wxGLContext(wxGLCanvas *win,
                         const wxGLContext *other,
                         const wxGLContextAttrs *ctxAttrs)
    : m_glContext(NULL)
{
    const int* contextAttribs = NULL;
    bool needsARB = false;

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

    m_isOk = false;

    // We need to create a temporary context to get the pointer to
    // wglCreateContextAttribsARB function
    HGLRC tempContext = wglCreateContext(win->GetHDC());
    wxCHECK_RET( tempContext, "wglCreateContext failed!" );

    wglMakeCurrent(win->GetHDC(), tempContext);

    typedef HGLRC(WINAPI * wglCreateContextAttribsARB_t)
        (HDC hDC, HGLRC hShareContext, const int *attribList);

    wxDEFINE_WGL_FUNC(wglCreateContextAttribsARB);
    wglMakeCurrent(win->GetHDC(), NULL);
    wglDeleteContext(tempContext);

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

    if ( wglCreateContextAttribsARB )
    {
        m_glContext = wglCreateContextAttribsARB(win->GetHDC(),
                                                other ? other->m_glContext : 0,
                                                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.
    if ( !m_glContext && (!contextAttribs || !needsARB) )
    {
        // Create legacy context
        m_glContext = wglCreateContext(win->GetHDC());
        // Set shared context
        if ( other && !wglShareLists(other->m_glContext, m_glContext) )
            wxLogLastError("wglShareLists");
     }

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

wxGLContext::~wxGLContext()
{
    // note that it's ok to delete the context even if it's the current one
    if ( m_glContext )
    {
        wglDeleteContext(m_glContext);
    }
}

bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
{
    if ( !wglMakeCurrent(win.GetHDC(), m_glContext) )
    {
        wxLogLastError(wxT("wglMakeCurrent"));
        return false;
    }
    return true;
}

// ============================================================================
// wxGLCanvas
// ============================================================================

wxIMPLEMENT_CLASS(wxGLCanvas, wxWindow);

wxBEGIN_EVENT_TABLE(wxGLCanvas, wxWindow)
#if wxUSE_PALETTE
    EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
    EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
#endif
wxEND_EVENT_TABLE()

// ----------------------------------------------------------------------------
// wxGLCanvas construction
// ----------------------------------------------------------------------------

void wxGLCanvas::Init()
{
#if WXWIN_COMPATIBILITY_2_8
    m_glContext = NULL;
#endif
    m_hDC = NULL;
}

wxGLCanvas::wxGLCanvas(wxWindow *parent,
                       const wxGLAttributes& dispAttrs,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       long style,
                       const wxString& name,
                       const wxPalette& palette)
{
    Init();

    (void)Create(parent, dispAttrs, id, pos, size, style, name, palette);
}

wxGLCanvas::wxGLCanvas(wxWindow *parent,
                       wxWindowID id,
                       const int *attribList,
                       const wxPoint& pos,
                       const wxSize& size,
                       long style,
                       const wxString& name,
                       const wxPalette& palette)
{
    Init();

    (void)Create(parent, id, pos, size, style, name, attribList, palette);
}

wxGLCanvas::~wxGLCanvas()
{
    ::ReleaseDC(GetHwnd(), m_hDC);
}

// Replaces wxWindow::Create functionality, since we need to use a different
// window class
bool wxGLCanvas::CreateWindow(wxWindow *parent,
                              wxWindowID id,
                              const wxPoint& pos,
                              const wxSize& size,
                              long style,
                              const wxString& name)
{
    wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );

    if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
        return false;

    parent->AddChild(this);

    /*
       A general rule with OpenGL and Win32 is that any window that will have a
       HGLRC built for it must have two flags:  WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
       You can find references about this within the knowledge base and most OpenGL
       books that contain the wgl function descriptions.
     */
    WXDWORD exStyle = 0;
    DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    msflags |= MSWGetStyle(style, &exStyle);

    if ( !MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC),
                    NULL, pos, size, msflags, exStyle) )
        return false;

    m_hDC = ::GetDC(GetHwnd());
    if ( !m_hDC )
        return false;

    return true;
}

bool wxGLCanvas::Create(wxWindow *parent,
                        wxWindowID id,
                        const wxPoint& pos,
                        const wxSize& size,
                        long style,
                        const wxString& name,
                        const int *attribList,
                        const wxPalette& palette)
{
    // Separate 'pixel format' attributes and add platform-defaults.
    // Also store context attributes for wxGLContext ctor
    wxGLAttributes dispAttrs;
    if ( ! ParseAttribList(attribList, dispAttrs, &m_GLCTXAttrs) )
        return false;

    return Create(parent, dispAttrs, id, pos, size, style, name, palette);
}

bool wxGLCanvas::Create(wxWindow *parent,
                        const wxGLAttributes& dispAttrs,
                        wxWindowID id,
                        const wxPoint& pos,
                        const wxSize& size,
                        long style,
                        const wxString& name,
                        const wxPalette& palette)
{
    if ( !CreateWindow(parent, id, pos, size, style, name) )
        return false;

    // Choose a matching pixel format.
    // Need a PIXELFORMATDESCRIPTOR for SetPixelFormat()
    PIXELFORMATDESCRIPTOR pfd;
    int pixelFormat = FindMatchingPixelFormat(dispAttrs, &pfd);
    if ( !pixelFormat )
    {
        wxFAIL_MSG("Can't find a pixel format for the requested attributes");
        return false;
    }

    // From SetPixelFormat() docs, relating pfd parameter:
    // https://msdn.microsoft.com/en-us/library/dd369049%28v=vs.85%29.aspx
    //   "The system's metafile component uses this structure to record the
    //   logical pixel format specification."
    // If anybody understands this sentence, please explain.
    // Pass pfd just in case it's somehow needed. Passing NULL also works here.
    if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
    {
        wxLogLastError("SetPixelFormat");
        return false;
    }

#if wxUSE_PALETTE
    if ( !SetupPalette(palette) )
        return false;
#else // !wxUSE_PALETTE
    wxUnusedVar(palette);
#endif // wxUSE_PALETTE/!wxUSE_PALETTE

    return true;
}

// ----------------------------------------------------------------------------
// operations
// ----------------------------------------------------------------------------

bool wxGLCanvas::SwapBuffers()
{
    if ( !::SwapBuffers(m_hDC) )
    {
        wxLogLastError(wxT("SwapBuffers"));
        return false;
    }

    return true;
}


/* static */
bool wxGLCanvasBase::IsExtensionSupported(const char *extension)
{
    static const char *s_extensionsList = (char *)wxUIntPtr(-1);
    if ( s_extensionsList == (char *)wxUIntPtr(-1) )
    {
        typedef const char * (WINAPI *wglGetExtensionsStringARB_t)(HDC hdc);

        wxDEFINE_WGL_FUNC(wglGetExtensionsStringARB);
        if ( wglGetExtensionsStringARB )
        {
            s_extensionsList = wglGetExtensionsStringARB(wglGetCurrentDC());
        }
        else
        {
            typedef const char * (WINAPI * wglGetExtensionsStringEXT_t)();

            wxDEFINE_WGL_FUNC(wglGetExtensionsStringEXT);
            if ( wglGetExtensionsStringEXT )
            {
                s_extensionsList = wglGetExtensionsStringEXT();
            }
            else
            {
                s_extensionsList = NULL;
            }
        }
    }

    return s_extensionsList && IsExtensionInList(s_extensionsList, extension);
}

// ----------------------------------------------------------------------------
// pixel format stuff
// ----------------------------------------------------------------------------

// A dummy window, needed at FindMatchingPixelFormat()
class WXDLLIMPEXP_GL wxGLdummyWin : public wxWindow
{
public:
    wxGLdummyWin()
    {
        hdc = 0;
        CreateBase(NULL, wxID_ANY);
        DWORD msflags = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
        if( MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC),
                      NULL, wxDefaultPosition, wxDefaultSize, msflags, 0) )
        {
            hdc = ::GetDC(GetHwnd());
        }
    }
    ~wxGLdummyWin()
    {
        if ( hdc )
            ::ReleaseDC(GetHwnd(), hdc);
    }
    HDC hdc;
};

// Fills PIXELFORMATDESCRIPTOR struct
static void SetPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int* attrsListWGL)
{
    // Some defaults
    pfd.nSize =  sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.iPixelType = PFD_TYPE_COLORINDEX; // If no RGBA is specified
    pfd.iLayerType = PFD_MAIN_PLANE; // For very early MSW OpenGL
    // Initialize rest of fields
    pfd.dwFlags = 0;
    pfd.cColorBits = 0;
    pfd.cRedBits = pfd.cRedShift = pfd.cGreenBits = pfd.cGreenShift = pfd.cBlueBits = pfd.cBlueShift = 0;
    pfd.cAlphaBits = pfd.cAlphaShift = 0;
    pfd.cAccumBits = 0;
    pfd.cAccumRedBits = pfd.cAccumGreenBits = pfd.cAccumBlueBits = pfd.cAccumAlphaBits = 0;
    pfd.cDepthBits = pfd.cStencilBits = pfd.cAuxBuffers = 0;
    pfd.bReserved = 0;
    pfd.dwLayerMask = pfd.dwVisibleMask = pfd.dwDamageMask = 0;

    // We can meet some WGL_XX values not managed by wx. But the user
    // may require them. Allow here those that are also used for pfd.
    // Notice that the user can't require PFD values, only WGL ones.
    // Color shift and transparency are not handled.
    for ( int arg = 0; attrsListWGL[arg]; )
    {
        switch ( attrsListWGL[arg++] )
        {
            case WGL_DRAW_TO_WINDOW_ARB:
                if ( attrsListWGL[arg++] ) //arg++ is for skipping 'true' attribute
                    pfd.dwFlags |= PFD_DRAW_TO_WINDOW;
                break;

            case WGL_DRAW_TO_BITMAP_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_DRAW_TO_BITMAP;
                break;

            case WGL_ACCELERATION_ARB:
                if ( attrsListWGL[arg++] == WGL_GENERIC_ACCELERATION_ARB )
                    pfd.dwFlags |= PFD_GENERIC_ACCELERATED;
                break;

            case WGL_NEED_PALETTE_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_NEED_PALETTE;
                break;

            case WGL_NEED_SYSTEM_PALETTE_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_NEED_SYSTEM_PALETTE;
                break;

            case WGL_SWAP_LAYER_BUFFERS_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_SWAP_LAYER_BUFFERS;
                break;

            case WGL_SWAP_METHOD_ARB:
                if ( attrsListWGL[arg++] == WGL_SWAP_EXCHANGE_ARB )
                    pfd.dwFlags |= PFD_SWAP_EXCHANGE;
                else if ( attrsListWGL[arg] == WGL_SWAP_COPY_ARB )
                    pfd.dwFlags |= PFD_SWAP_COPY;
                break;

            case WGL_NUMBER_OVERLAYS_ARB:
                // Bits 0-3
                pfd.bReserved |= attrsListWGL[arg++] & 15;
                break;

            case WGL_NUMBER_UNDERLAYS_ARB:
                // Bits 4-7
                pfd.bReserved |= attrsListWGL[arg++] & 240;
                break;

            case WGL_SUPPORT_GDI_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_SUPPORT_GDI;
                break;

            case WGL_SUPPORT_OPENGL_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_SUPPORT_OPENGL;
                break;

            case WGL_DOUBLE_BUFFER_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_DOUBLEBUFFER;
                break;

            case WGL_STEREO_ARB:
                if ( attrsListWGL[arg++] )
                    pfd.dwFlags |= PFD_STEREO;
                break;

            case WGL_PIXEL_TYPE_ARB:
                if ( attrsListWGL[arg++] == WGL_TYPE_RGBA_ARB )
                    pfd.iPixelType = PFD_TYPE_RGBA;
                else
                    pfd.iPixelType = PFD_TYPE_COLORINDEX;
                break;

            case WGL_COLOR_BITS_ARB:
                pfd.cColorBits = attrsListWGL[arg++];
                break;

            case WGL_RED_BITS_ARB:
                pfd.cRedBits = attrsListWGL[arg++];
                break;

            case WGL_GREEN_BITS_ARB:
                pfd.cGreenBits = attrsListWGL[arg++];
                break;

            case WGL_BLUE_BITS_ARB:
                pfd.cBlueBits = attrsListWGL[arg++];
                break;

            case WGL_ALPHA_BITS_ARB:
                pfd.cAlphaBits = attrsListWGL[arg++];
                break;

            case WGL_ACCUM_BITS_ARB:
                pfd.cAccumBits = attrsListWGL[arg++];
                break;

            case WGL_ACCUM_RED_BITS_ARB:
                pfd.cAccumRedBits = attrsListWGL[arg++];
                break;

            case WGL_ACCUM_GREEN_BITS_ARB:
                pfd.cAccumGreenBits = attrsListWGL[arg++];
                break;

            case WGL_ACCUM_BLUE_BITS_ARB:
                pfd.cAccumBlueBits = attrsListWGL[arg++];
                break;

            case WGL_ACCUM_ALPHA_BITS_ARB:
                pfd.cAccumAlphaBits = attrsListWGL[arg++];
                break;

            case WGL_DEPTH_BITS_ARB:
                pfd.cDepthBits = attrsListWGL[arg++];
                break;

            case WGL_STENCIL_BITS_ARB:
                pfd.cStencilBits = attrsListWGL[arg++];
                break;

            case WGL_AUX_BUFFERS_ARB:
                pfd.cAuxBuffers = attrsListWGL[arg++];
                break;

            default:
                // ignore
                break;
        }
    }
}

/* static */
int wxGLCanvas::FindMatchingPixelFormat(const wxGLAttributes& dispAttrs,
                                        PIXELFORMATDESCRIPTOR* ppfd)
{
    // WGL_XX attributes
    const int* attrsListWGL = dispAttrs.GetGLAttrs();
    if ( !attrsListWGL )
    {
        wxFAIL_MSG("wxGLAttributes object is empty.");
        return 0;
    }

    // The preferred way is using wglChoosePixelFormatARB. This is not a MSW
    // function, we must ask the GPU driver for a pointer to it. We need a
    // rendering context for this operation. Create a dummy one.
    // Notice that we don't create a wxGLContext on purpose.
    // We meet another issue:
    // Before creating any context, we must set a pixel format for its hdc:
    //   https://msdn.microsoft.com/en-us/library/dd374379%28v=vs.85%29.aspx
    // but we can't set a pixel format more than once:
    //   https://msdn.microsoft.com/en-us/library/dd369049%28v=vs.85%29.aspx
    // To cope with this we need a dummy hidden window.
    //
    // Having this dummy window allows also calling IsDisplaySupported()
    // without creating a wxGLCanvas.
    wxGLdummyWin* dummyWin = new wxGLdummyWin();
    HDC dummyHDC = dummyWin->hdc;
    if ( !dummyHDC )
    {
        dummyWin->Destroy();
        wxFAIL_MSG("Can't create dummy window");
        return 0;
    }
    // Dummy context
    PIXELFORMATDESCRIPTOR dpfd; //any one is valid
    ::SetPixelFormat(dummyHDC, 1, &dpfd); // pixelformat=1, any one is valid
    HGLRC dumctx = ::wglCreateContext(dummyHDC);
    if ( !dumctx )
    {
        dummyWin->Destroy();
        // A fatal error!
        wxFAIL_MSG("wglCreateContext failed!");
        return 0;
    }

    ::wglMakeCurrent(dummyHDC, dumctx);

    typedef BOOL (WINAPI * wglChoosePixelFormatARB_t)
                 (HDC hdc,
                  const int *piAttribIList,
                  const FLOAT *pfAttribFList,
                  UINT nMaxFormats,
                  int *piFormats,
                  UINT *nNumFormats
                 );

    wxDEFINE_WGL_FUNC(wglChoosePixelFormatARB); // get a pointer to it

    // If wglChoosePixelFormatARB is not supported but  the attributes require
    // it, then fail.
    if ( !wglChoosePixelFormatARB && dispAttrs.NeedsARB() )
    {
        wxLogLastError("wglChoosePixelFormatARB unavailable");
        // Delete the dummy objects
        ::wglMakeCurrent(NULL, NULL);
        ::wglDeleteContext(dumctx);
        dummyWin->Destroy();
        return 0;
    }

    int pixelFormat = 0;

    if ( !wglChoosePixelFormatARB && !dispAttrs.NeedsARB() )
    {
        // Old way
        if ( !ppfd )
        {
            // We have been called from IsDisplaySupported()
            PIXELFORMATDESCRIPTOR pfd;
            SetPFDForAttributes(pfd, attrsListWGL);
            pixelFormat = ::ChoosePixelFormat(dummyHDC, &pfd);
        }
        else
        {
            SetPFDForAttributes(*ppfd, attrsListWGL);
            pixelFormat = ::ChoosePixelFormat(dummyHDC, ppfd);
        }
        // We should ensure that we have got what we have asked for. This can
        // be done using DescribePixelFormat() and comparing attributes.
        // Nevertheless wglChoosePixelFormatARB exists since 2001, so it's
        // very unlikely that ChoosePixelFormat() is used. So, do nothing.
    }
    else
    {
        // New way, using wglChoosePixelFormatARB
        // 'ppfd' is used at wxGLCanvas::Create(). See explanations there.
        if ( ppfd )
            SetPFDForAttributes(*ppfd, attrsListWGL);

        UINT numFormats = 0;

        // Get the first good match
        if ( !wglChoosePixelFormatARB(dummyHDC, attrsListWGL, NULL,
                                      1, &pixelFormat, &numFormats) )
        {
            wxLogLastError("wglChoosePixelFormatARB. Is the list zero-terminated?");
            numFormats = 0;
        }

        // Although TRUE is returned if no matching formats are found (see
        // https://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt),
        // pixelFormat is not initialized in this case so we need to check
        // for numFormats being not 0 explicitly (however this is not an error
        // so don't call wxLogLastError() here).
        if ( !numFormats )
            pixelFormat = 0;
    }

    // Delete the dummy objects
    ::wglMakeCurrent(NULL, NULL);
    ::wglDeleteContext(dumctx);
    dummyWin->Destroy();

    return pixelFormat;
}

/* static */
int
wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc,
                                      const int *attribList,
                                      PIXELFORMATDESCRIPTOR *ppfd)
{
    wxGLAttributes dispAttrs;
    ParseAttribList(attribList, dispAttrs);
    wxUnusedVar(hdc);
    return FindMatchingPixelFormat(dispAttrs, ppfd);
}

/* static */
bool wxGLCanvasBase::IsDisplaySupported(const wxGLAttributes& dispAttrs)
{
    return wxGLCanvas::FindMatchingPixelFormat(dispAttrs) > 0;
}

/* static */
bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
{
    // We need a device context to test the pixel format, so get one
    // for the root window.
    // Not true anymore. Keep it just in case somebody uses this undocumented function
    return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0;
}

int wxGLCanvas::DoSetup(PIXELFORMATDESCRIPTOR &pfd, const int *attribList)
{
    // Keep this member is case somebody is overriding it
    wxUnusedVar(pfd);
    wxUnusedVar(attribList);
    return -1;
}

// ----------------------------------------------------------------------------
// palette stuff
// ----------------------------------------------------------------------------

#if wxUSE_PALETTE

bool wxGLCanvas::SetupPalette(const wxPalette& palette)
{
    const int pixelFormat = ::GetPixelFormat(m_hDC);
    if ( !pixelFormat )
    {
        wxLogLastError(wxT("GetPixelFormat"));
        return false;
    }

    PIXELFORMATDESCRIPTOR pfd;
    if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) )
    {
        wxLogLastError(wxT("DescribePixelFormat"));
        return false;
    }

    if ( !(pfd.dwFlags & PFD_NEED_PALETTE) )
        return true;

    m_palette = palette;

    if ( !m_palette.IsOk() )
    {
        m_palette = CreateDefaultPalette();
        if ( !m_palette.IsOk() )
            return false;
    }

    if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) )
    {
        wxLogLastError(wxT("SelectPalette"));
        return false;
    }

    if ( ::RealizePalette(m_hDC) == GDI_ERROR )
    {
        wxLogLastError(wxT("RealizePalette"));
        return false;
    }

    return true;
}

wxPalette wxGLCanvas::CreateDefaultPalette()
{
    PIXELFORMATDESCRIPTOR pfd;
    int paletteSize;
    int pixelFormat = GetPixelFormat(m_hDC);

    DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    paletteSize = 1 << pfd.cColorBits;

    LOGPALETTE* pPal =
     (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
    pPal->palVersion = 0x300;
    pPal->palNumEntries = (WORD)paletteSize;

    /* build a simple RGB color palette */
    int redMask = (1 << pfd.cRedBits) - 1;
    int greenMask = (1 << pfd.cGreenBits) - 1;
    int blueMask = (1 << pfd.cBlueBits) - 1;

    for (int i=0; i<paletteSize; ++i)
    {
        pPal->palPalEntry[i].peRed =
            (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask);
        pPal->palPalEntry[i].peGreen =
            (BYTE)((((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask);
        pPal->palPalEntry[i].peBlue =
            (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask);
        pPal->palPalEntry[i].peFlags = 0;
    }

    HPALETTE hPalette = CreatePalette(pPal);
    free(pPal);

    wxPalette palette;
    palette.SetHPALETTE((WXHPALETTE) hPalette);

    return palette;
}

void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
{
  /* realize palette if this is the current window */
  if ( GetPalette()->IsOk() ) {
    ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
    ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
    ::RealizePalette(GetHDC());
    Refresh();
    event.SetPaletteRealized(true);
  }
  else
    event.SetPaletteRealized(false);
}

void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
{
  /* realize palette if this is *not* the current window */
  if ( GetPalette() &&
       GetPalette()->IsOk() && (this != event.GetChangedWindow()) )
  {
    ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
    ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
    ::RealizePalette(GetHDC());
    Refresh();
  }
}

#endif // wxUSE_PALETTE

// ----------------------------------------------------------------------------
// deprecated wxGLCanvas methods using implicit wxGLContext
// ----------------------------------------------------------------------------

// deprecated constructors creating an implicit m_glContext
#if WXWIN_COMPATIBILITY_2_8

wxGLCanvas::wxGLCanvas(wxWindow *parent,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       long style,
                       const wxString& name,
                       const int *attribList,
                       const wxPalette& palette)
{
    Init();

    if ( Create(parent, id, pos, size, style, name, attribList, palette) )
        m_glContext = new wxGLContext(this);
}

wxGLCanvas::wxGLCanvas(wxWindow *parent,
                       const wxGLContext *shared,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       long style,
                       const wxString& name,
                       const int *attribList,
                       const wxPalette& palette)
{
    Init();

    if ( Create(parent, id, pos, size, style, name, attribList, palette) )
        m_glContext = new wxGLContext(this, shared);
}

wxGLCanvas::wxGLCanvas(wxWindow *parent,
                       const wxGLCanvas *shared,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       long style,
                       const wxString& name,
                       const int *attribList,
                       const wxPalette& palette)
{
    Init();

    if ( Create(parent, id, pos, size, style, name, attribList, palette) )
        m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
}

#endif // WXWIN_COMPATIBILITY_2_8


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

bool wxGLApp::InitGLVisual(const int *attribList)
{
    if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) )
    {
        wxLogError(_("Failed to initialize OpenGL"));
        return false;
    }

    return true;
}

#endif // wxUSE_GLCANVAS
