mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
446 lines
17 KiB
C++
446 lines
17 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE framework.
|
|
Copyright (c) Raw Material Software Limited
|
|
|
|
JUCE is an open source framework subject to commercial or open source
|
|
licensing.
|
|
|
|
By downloading, installing, or using the JUCE framework, or combining the
|
|
JUCE framework with any other source code, object code, content or any other
|
|
copyrightable work, you agree to the terms of the JUCE End User Licence
|
|
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
|
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
|
do not agree to the terms of these agreements, we will not license the JUCE
|
|
framework to you, and you must discontinue the installation or download
|
|
process and cease use of the JUCE framework.
|
|
|
|
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
|
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
|
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
|
|
|
Or:
|
|
|
|
You may also use this code under the terms of the AGPLv3:
|
|
https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
|
|
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
|
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
|
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, Component* parent);
|
|
|
|
//==============================================================================
|
|
class OpenGLContext::NativeContext : private ComponentPeer::ScaleFactorListener,
|
|
private AsyncUpdater
|
|
{
|
|
public:
|
|
NativeContext (Component& component,
|
|
const OpenGLPixelFormat& pixelFormat,
|
|
void* contextToShareWithIn,
|
|
bool /*useMultisampling*/,
|
|
OpenGLVersion version)
|
|
: sharedContext (contextToShareWithIn)
|
|
{
|
|
placeholderComponent.reset (new PlaceholderComponent (*this));
|
|
createNativeWindow (component);
|
|
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
initialisePixelFormatDescriptor (pfd, pixelFormat);
|
|
|
|
auto pixFormat = ChoosePixelFormat (dc.get(), &pfd);
|
|
|
|
if (pixFormat != 0)
|
|
SetPixelFormat (dc.get(), pixFormat, &pfd);
|
|
|
|
initialiseWGLExtensions (dc.get());
|
|
renderContext.reset (createRenderContext (version, dc.get()));
|
|
|
|
if (renderContext != nullptr)
|
|
{
|
|
makeActive();
|
|
|
|
auto wglFormat = wglChoosePixelFormatExtension (pixelFormat);
|
|
deactivateCurrentContext();
|
|
|
|
if (wglFormat != pixFormat && wglFormat != 0)
|
|
{
|
|
// can't change the pixel format of a window, so need to delete the
|
|
// old one and create a new one.
|
|
dc.reset();
|
|
nativeWindow = nullptr;
|
|
createNativeWindow (component);
|
|
|
|
if (SetPixelFormat (dc.get(), wglFormat, &pfd))
|
|
{
|
|
renderContext.reset();
|
|
renderContext.reset (createRenderContext (version, dc.get()));
|
|
}
|
|
}
|
|
|
|
component.getTopLevelComponent()->repaint();
|
|
component.repaint();
|
|
}
|
|
}
|
|
|
|
~NativeContext() override
|
|
{
|
|
cancelPendingUpdate();
|
|
renderContext.reset();
|
|
dc.reset();
|
|
|
|
if (safeComponent != nullptr)
|
|
if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
|
|
peer->removeScaleFactorListener (this);
|
|
}
|
|
|
|
InitResult initialiseOnRenderThread (OpenGLContext& c)
|
|
{
|
|
threadAwarenessSetter = std::make_unique<ScopedThreadDPIAwarenessSetter> (nativeWindow->getNativeHandle());
|
|
context = &c;
|
|
|
|
if (sharedContext != nullptr)
|
|
{
|
|
if (! wglShareLists ((HGLRC) sharedContext, renderContext.get()))
|
|
{
|
|
TCHAR messageBuffer[256] = {};
|
|
|
|
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
nullptr,
|
|
GetLastError(),
|
|
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
messageBuffer,
|
|
(DWORD) numElementsInArray (messageBuffer) - 1,
|
|
nullptr);
|
|
|
|
DBG (messageBuffer);
|
|
jassertfalse;
|
|
}
|
|
}
|
|
|
|
return InitResult::success;
|
|
}
|
|
|
|
void shutdownOnRenderThread()
|
|
{
|
|
deactivateCurrentContext();
|
|
context = nullptr;
|
|
threadAwarenessSetter = nullptr;
|
|
}
|
|
|
|
static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); }
|
|
bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc.get(), renderContext.get()) != FALSE; }
|
|
bool isActive() const noexcept { return wglGetCurrentContext() == renderContext.get(); }
|
|
|
|
void swapBuffers() noexcept
|
|
{
|
|
SwapBuffers (dc.get());
|
|
|
|
if (! std::exchange (haveBuffersBeenSwapped, true))
|
|
triggerAsyncUpdate();
|
|
}
|
|
|
|
bool setSwapInterval (int numFramesPerSwap)
|
|
{
|
|
jassert (isActive()); // this can only be called when the context is active
|
|
return wglSwapIntervalEXT != nullptr && wglSwapIntervalEXT (numFramesPerSwap) != FALSE;
|
|
}
|
|
|
|
int getSwapInterval() const
|
|
{
|
|
jassert (isActive()); // this can only be called when the context is active
|
|
return wglGetSwapIntervalEXT != nullptr ? wglGetSwapIntervalEXT() : 0;
|
|
}
|
|
|
|
void updateWindowPosition (Rectangle<int> bounds)
|
|
{
|
|
if (nativeWindow != nullptr)
|
|
{
|
|
if (! approximatelyEqual (nativeScaleFactor, 1.0))
|
|
bounds = (bounds.toDouble() * nativeScaleFactor).toNearestInt();
|
|
|
|
SetWindowPos ((HWND) nativeWindow->getNativeHandle(), nullptr,
|
|
bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
}
|
|
}
|
|
|
|
bool createdOk() const noexcept { return getRawContext() != nullptr; }
|
|
void* getRawContext() const noexcept { return renderContext.get(); }
|
|
unsigned int getFrameBufferID() const noexcept { return 0; }
|
|
|
|
void triggerRepaint()
|
|
{
|
|
if (context != nullptr)
|
|
context->triggerRepaint();
|
|
}
|
|
|
|
struct Locker
|
|
{
|
|
explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
|
|
const ScopedLock lock;
|
|
};
|
|
|
|
HWND getNativeHandle()
|
|
{
|
|
if (nativeWindow != nullptr)
|
|
return (HWND) nativeWindow->getNativeHandle();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void addListener (NativeContextListener&) {}
|
|
void removeListener (NativeContextListener&) {}
|
|
|
|
private:
|
|
//==============================================================================
|
|
void handleAsyncUpdate() override
|
|
{
|
|
nativeWindow->setVisible (true);
|
|
}
|
|
|
|
static void initialiseWGLExtensions (HDC dcIn)
|
|
{
|
|
static bool initialised = false;
|
|
|
|
if (initialised)
|
|
return;
|
|
|
|
initialised = true;
|
|
|
|
const auto dummyContext = wglCreateContext (dcIn);
|
|
wglMakeCurrent (dcIn, dummyContext);
|
|
|
|
#define JUCE_INIT_WGL_FUNCTION(name) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name);
|
|
JUCE_INIT_WGL_FUNCTION (wglChoosePixelFormatARB)
|
|
JUCE_INIT_WGL_FUNCTION (wglSwapIntervalEXT)
|
|
JUCE_INIT_WGL_FUNCTION (wglGetSwapIntervalEXT)
|
|
JUCE_INIT_WGL_FUNCTION (wglCreateContextAttribsARB)
|
|
#undef JUCE_INIT_WGL_FUNCTION
|
|
|
|
wglMakeCurrent (nullptr, nullptr);
|
|
wglDeleteContext (dummyContext);
|
|
}
|
|
|
|
static void initialisePixelFormatDescriptor (PIXELFORMATDESCRIPTOR& pfd, const OpenGLPixelFormat& pixelFormat)
|
|
{
|
|
zerostruct (pfd);
|
|
pfd.nSize = sizeof (pfd);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.iLayerType = PFD_MAIN_PLANE;
|
|
pfd.cColorBits = (BYTE) (pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits);
|
|
pfd.cRedBits = (BYTE) pixelFormat.redBits;
|
|
pfd.cGreenBits = (BYTE) pixelFormat.greenBits;
|
|
pfd.cBlueBits = (BYTE) pixelFormat.blueBits;
|
|
pfd.cAlphaBits = (BYTE) pixelFormat.alphaBits;
|
|
pfd.cDepthBits = (BYTE) pixelFormat.depthBufferBits;
|
|
pfd.cStencilBits = (BYTE) pixelFormat.stencilBufferBits;
|
|
pfd.cAccumBits = (BYTE) (pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits
|
|
+ pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits);
|
|
pfd.cAccumRedBits = (BYTE) pixelFormat.accumulationBufferRedBits;
|
|
pfd.cAccumGreenBits = (BYTE) pixelFormat.accumulationBufferGreenBits;
|
|
pfd.cAccumBlueBits = (BYTE) pixelFormat.accumulationBufferBlueBits;
|
|
pfd.cAccumAlphaBits = (BYTE) pixelFormat.accumulationBufferAlphaBits;
|
|
}
|
|
|
|
static HGLRC createRenderContext (OpenGLVersion version, HDC dcIn)
|
|
{
|
|
const auto components = [&]() -> Optional<Version>
|
|
{
|
|
switch (version)
|
|
{
|
|
case OpenGLVersion::openGL3_2: return Version { 3, 2 };
|
|
case OpenGLVersion::openGL4_1: return Version { 4, 1 };
|
|
case OpenGLVersion::openGL4_3: return Version { 4, 3 };
|
|
|
|
case OpenGLVersion::defaultGLVersion: break;
|
|
}
|
|
|
|
return {};
|
|
}();
|
|
|
|
if (components.hasValue() && wglCreateContextAttribsARB != nullptr)
|
|
{
|
|
#if JUCE_DEBUG
|
|
constexpr auto contextFlags = WGL_CONTEXT_DEBUG_BIT_ARB;
|
|
constexpr auto noErrorChecking = GL_FALSE;
|
|
#else
|
|
constexpr auto contextFlags = 0;
|
|
constexpr auto noErrorChecking = GL_TRUE;
|
|
#endif
|
|
|
|
const int attribs[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, components->major,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, components->minor,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
WGL_CONTEXT_FLAGS_ARB, contextFlags,
|
|
WGL_CONTEXT_OPENGL_NO_ERROR_ARB, noErrorChecking,
|
|
0
|
|
};
|
|
|
|
const auto c = wglCreateContextAttribsARB (dcIn, nullptr, attribs);
|
|
|
|
if (c != nullptr)
|
|
return c;
|
|
}
|
|
|
|
return wglCreateContext (dcIn);
|
|
}
|
|
|
|
//==============================================================================
|
|
struct PlaceholderComponent : public Component
|
|
{
|
|
explicit PlaceholderComponent (NativeContext& c)
|
|
: context (c)
|
|
{
|
|
setOpaque (true);
|
|
}
|
|
|
|
// The windowing code will call this when a paint callback happens
|
|
void handleCommandMessage (int) override { context.triggerRepaint(); }
|
|
|
|
NativeContext& context;
|
|
};
|
|
|
|
//==============================================================================
|
|
void nativeScaleFactorChanged (double newScaleFactor) override
|
|
{
|
|
if (approximatelyEqual (newScaleFactor, nativeScaleFactor)
|
|
|| safeComponent == nullptr)
|
|
return;
|
|
|
|
if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
|
|
{
|
|
nativeScaleFactor = newScaleFactor;
|
|
updateWindowPosition (peer->getAreaCoveredBy (*safeComponent));
|
|
}
|
|
}
|
|
|
|
void createNativeWindow (Component& component)
|
|
{
|
|
auto* topComp = component.getTopLevelComponent();
|
|
|
|
{
|
|
auto* parentHWND = topComp->getWindowHandle();
|
|
|
|
ScopedThreadDPIAwarenessSetter setter { parentHWND };
|
|
nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*placeholderComponent, topComp));
|
|
}
|
|
|
|
if (auto* peer = topComp->getPeer())
|
|
{
|
|
safeComponent = Component::SafePointer<Component> (&component);
|
|
|
|
nativeScaleFactor = peer->getPlatformScaleFactor();
|
|
updateWindowPosition (peer->getAreaCoveredBy (component));
|
|
peer->addScaleFactorListener (this);
|
|
}
|
|
|
|
dc = std::unique_ptr<std::remove_pointer_t<HDC>, DeviceContextDeleter> { GetDC ((HWND) nativeWindow->getNativeHandle()),
|
|
DeviceContextDeleter { (HWND) nativeWindow->getNativeHandle() } };
|
|
}
|
|
|
|
int wglChoosePixelFormatExtension (const OpenGLPixelFormat& pixelFormat) const
|
|
{
|
|
int format = 0;
|
|
|
|
if (wglChoosePixelFormatARB != nullptr)
|
|
{
|
|
int atts[64];
|
|
int n = 0;
|
|
|
|
atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE;
|
|
atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE;
|
|
atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE;
|
|
atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB;
|
|
atts[n++] = WGL_ACCELERATION_ARB;
|
|
atts[n++] = WGL_FULL_ACCELERATION_ARB;
|
|
|
|
atts[n++] = WGL_COLOR_BITS_ARB; atts[n++] = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits;
|
|
atts[n++] = WGL_RED_BITS_ARB; atts[n++] = pixelFormat.redBits;
|
|
atts[n++] = WGL_GREEN_BITS_ARB; atts[n++] = pixelFormat.greenBits;
|
|
atts[n++] = WGL_BLUE_BITS_ARB; atts[n++] = pixelFormat.blueBits;
|
|
atts[n++] = WGL_ALPHA_BITS_ARB; atts[n++] = pixelFormat.alphaBits;
|
|
atts[n++] = WGL_DEPTH_BITS_ARB; atts[n++] = pixelFormat.depthBufferBits;
|
|
|
|
atts[n++] = WGL_STENCIL_BITS_ARB; atts[n++] = pixelFormat.stencilBufferBits;
|
|
atts[n++] = WGL_ACCUM_RED_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferRedBits;
|
|
atts[n++] = WGL_ACCUM_GREEN_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferGreenBits;
|
|
atts[n++] = WGL_ACCUM_BLUE_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferBlueBits;
|
|
atts[n++] = WGL_ACCUM_ALPHA_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferAlphaBits;
|
|
|
|
if (pixelFormat.multisamplingLevel > 0
|
|
&& OpenGLHelpers::isExtensionSupported ("GL_ARB_multisample"))
|
|
{
|
|
atts[n++] = WGL_SAMPLE_BUFFERS_ARB;
|
|
atts[n++] = 1;
|
|
atts[n++] = WGL_SAMPLES_ARB;
|
|
atts[n++] = pixelFormat.multisamplingLevel;
|
|
}
|
|
|
|
atts[n++] = 0;
|
|
jassert (n <= numElementsInArray (atts));
|
|
|
|
UINT formatsCount = 0;
|
|
wglChoosePixelFormatARB (dc.get(), atts, nullptr, 1, &format, &formatsCount);
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
//==============================================================================
|
|
#define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \
|
|
typedef returnType (__stdcall *type_ ## name) params; static type_ ## name name;
|
|
|
|
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglChoosePixelFormatARB, BOOL, (HDC, const int*, const FLOAT*, UINT, int*, UINT*))
|
|
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglSwapIntervalEXT, BOOL, (int))
|
|
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglGetSwapIntervalEXT, int, ())
|
|
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglCreateContextAttribsARB, HGLRC, (HDC, HGLRC, const int*))
|
|
#undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION
|
|
|
|
//==============================================================================
|
|
struct RenderContextDeleter
|
|
{
|
|
void operator() (HGLRC ptr) const { wglDeleteContext (ptr); }
|
|
};
|
|
|
|
struct DeviceContextDeleter
|
|
{
|
|
void operator() (HDC ptr) const { ReleaseDC (hwnd, ptr); }
|
|
HWND hwnd;
|
|
};
|
|
|
|
CriticalSection mutex;
|
|
std::unique_ptr<PlaceholderComponent> placeholderComponent;
|
|
std::unique_ptr<ComponentPeer> nativeWindow;
|
|
std::unique_ptr<ScopedThreadDPIAwarenessSetter> threadAwarenessSetter;
|
|
Component::SafePointer<Component> safeComponent;
|
|
std::unique_ptr<std::remove_pointer_t<HGLRC>, RenderContextDeleter> renderContext;
|
|
std::unique_ptr<std::remove_pointer_t<HDC>, DeviceContextDeleter> dc;
|
|
OpenGLContext* context = nullptr;
|
|
void* sharedContext = nullptr;
|
|
double nativeScaleFactor = 1.0;
|
|
bool haveBuffersBeenSwapped = false;
|
|
|
|
//==============================================================================
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
|
|
};
|
|
|
|
|
|
//==============================================================================
|
|
bool OpenGLHelpers::isContextActive()
|
|
{
|
|
return wglGetCurrentContext() != nullptr;
|
|
}
|
|
|
|
} // namespace juce
|