|  | /*
  ==============================================================================
   This file is part of the JUCE 6 technical preview.
   Copyright (c) 2020 - Raw Material Software Limited
   You may use this code under the terms of the GPL v3
   (see www.gnu.org/licenses).
   For this technical preview, this file is not subject to commercial licensing.
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.
  ==============================================================================
*/
namespace juce
{
extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent);
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
 extern void setThreadDPIAwarenessForWindow (HWND);
#endif
//==============================================================================
class OpenGLContext::NativeContext
   #if JUCE_WIN_PER_MONITOR_DPI_AWARE
    : private Timer
   #endif
{
public:
    NativeContext (Component& component,
                   const OpenGLPixelFormat& pixelFormat,
                   void* contextToShareWith,
                   bool /*useMultisampling*/,
                   OpenGLVersion)
    {
        dummyComponent.reset (new DummyComponent (*this));
        createNativeWindow (component);
        PIXELFORMATDESCRIPTOR pfd;
        initialisePixelFormatDescriptor (pfd, pixelFormat);
        auto pixFormat = ChoosePixelFormat (dc, &pfd);
        if (pixFormat != 0)
            SetPixelFormat (dc, pixFormat, &pfd);
        renderContext = wglCreateContext (dc);
        if (renderContext != nullptr)
        {
            makeActive();
            initialiseGLExtensions();
            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..
                releaseDC();
                nativeWindow = nullptr;
                createNativeWindow (component);
                if (SetPixelFormat (dc, wglFormat, &pfd))
                {
                    deleteRenderContext();
                    renderContext = wglCreateContext (dc);
                }
            }
            if (contextToShareWith != nullptr)
                wglShareLists ((HGLRC) contextToShareWith, renderContext);
            component.getTopLevelComponent()->repaint();
            component.repaint();
        }
    }
    ~NativeContext()
    {
        deleteRenderContext();
        releaseDC();
    }
    bool initialiseOnRenderThread (OpenGLContext& c)
    {
       #if JUCE_WIN_PER_MONITOR_DPI_AWARE
        setThreadDPIAwarenessForWindow ((HWND) nativeWindow->getNativeHandle());
       #endif
        context = &c;
        return true;
    }
    void shutdownOnRenderThread()           { deactivateCurrentContext(); context = nullptr; }
    static void deactivateCurrentContext()  { wglMakeCurrent (nullptr, nullptr); }
    bool makeActive() const noexcept        { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; }
    bool isActive() const noexcept          { return wglGetCurrentContext() == renderContext; }
    void swapBuffers() const noexcept       { SwapBuffers (dc); }
    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; }
    unsigned int getFrameBufferID() const noexcept  { return 0; }
    void triggerRepaint()
    {
        if (context != nullptr)
            context->triggerRepaint();
    }
    struct Locker { Locker (NativeContext&) {} };
    HWND getNativeHandle()
    {
        if (nativeWindow != nullptr)
            return (HWND) nativeWindow->getNativeHandle();
        return nullptr;
    }
private:
    struct DummyComponent  : public Component
    {
        DummyComponent (NativeContext& c) : context (c) {}
        // The windowing code will call this when a paint callback happens
        void handleCommandMessage (int) override   { context.triggerRepaint(); }
        NativeContext& context;
    };
    std::unique_ptr<DummyComponent> dummyComponent;
    std::unique_ptr<ComponentPeer> nativeWindow;
    HGLRC renderContext;
    HDC dc;
    OpenGLContext* context = {};
    double nativeScaleFactor = 1.0;
   #if JUCE_WIN_PER_MONITOR_DPI_AWARE
    Component::SafePointer<Component> safeComponent;
   #endif
    #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \
        typedef returnType (__stdcall *type_ ## name) params; 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, ())
    #undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION
   #if JUCE_WIN_PER_MONITOR_DPI_AWARE
    void timerCallback() override
    {
        if (safeComponent != nullptr)
        {
            if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
            {
                auto newScale = peer->getPlatformScaleFactor();
                if (! approximatelyEqual (newScale, nativeScaleFactor))
                {
                    nativeScaleFactor = newScale;
                    updateWindowPosition (peer->getAreaCoveredBy (*safeComponent));
                }
            }
        }
    }
   #endif
    void initialiseGLExtensions()
    {
        #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);
        #undef JUCE_INIT_WGL_FUNCTION
    }
    void createNativeWindow (Component& component)
    {
        auto* topComp = component.getTopLevelComponent();
        nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle()));
        if (auto* peer = topComp->getPeer())
        {
           #if JUCE_WIN_PER_MONITOR_DPI_AWARE
            safeComponent = Component::SafePointer<Component> (&component);
            nativeScaleFactor = peer->getPlatformScaleFactor();
            startTimer (50);
           #endif
            updateWindowPosition (peer->getAreaCoveredBy (component));
        }
        nativeWindow->setVisible (true);
        dc = GetDC ((HWND) nativeWindow->getNativeHandle());
    }
    void deleteRenderContext()
    {
        if (renderContext != nullptr)
        {
            wglDeleteContext (renderContext);
            renderContext = nullptr;
        }
    }
    void releaseDC()
    {
        ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc);
    }
    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;
    }
    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, atts, nullptr, 1, &format, &formatsCount);
        }
        return format;
    }
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
    return wglGetCurrentContext() != nullptr;
}
} // namespace juce
 |