Files
wxWidgets/src/msw/glcanvas.cpp

1384 lines
41 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// 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 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
// ----------------------------------------------------------------------------
// For MSVC we can link in the required library explicitly, for the other
// compilers (e.g. MinGW) this needs to be done at makefiles level.
#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
{
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