/////////////////////////////////////////////////////////////////////////////
// Name:        wx/msw/private/dpiaware.h
// Purpose:     AutoSystemDpiAware class
// Author:      Maarten Bent
// Created:     2016-10-06
// Copyright:   (c) Maarten Bent
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _WX_MSW_DPI_AWARE_H_
#define _WX_MSW_DPI_AWARE_H_

#ifndef WX_PRECOMP
    #include "wx/msw/missing.h"
#endif

#include "wx/dynlib.h"
#include "wx/display.h"
#include "wx/sysopt.h"

#if wxUSE_DYNLIB_CLASS

namespace wxMSWImpl
{

// ----------------------------------------------------------------------------
// Temporarily change the DPI Awareness context to GDIScaled or System
// ----------------------------------------------------------------------------

class AutoSystemDpiAware
{
    #define WXDPI_AWARENESS_CONTEXT_UNAWARE           ((WXDPI_AWARENESS_CONTEXT)-1)
    #define WXDPI_AWARENESS_CONTEXT_SYSTEM_AWARE      ((WXDPI_AWARENESS_CONTEXT)-2)
    #define WXDPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((WXDPI_AWARENESS_CONTEXT)-5)
    typedef WXDPI_AWARENESS_CONTEXT
            (WINAPI *SetThreadDpiAwarenessContext_t)(WXDPI_AWARENESS_CONTEXT);

public:
    AutoSystemDpiAware()
        : m_prevContext(WXDPI_AWARENESS_CONTEXT_UNAWARE)
    {
        if ( !Needed() )
            return;

        if ( ms_pfnSetThreadDpiAwarenessContext == (SetThreadDpiAwarenessContext_t)-1)
        {
            wxLoadedDLL dllUser32("user32.dll");
            wxDL_INIT_FUNC(ms_pfn, SetThreadDpiAwarenessContext, dllUser32);
        }

        if ( ms_pfnSetThreadDpiAwarenessContext )
        {
            m_prevContext = ms_pfnSetThreadDpiAwarenessContext(
                                    WXDPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
            if ( !m_prevContext )
            {
                m_prevContext = ms_pfnSetThreadDpiAwarenessContext(
                                    WXDPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
            }
        }

    }

    ~AutoSystemDpiAware()
    {
        if ( ms_pfnSetThreadDpiAwarenessContext &&
                ms_pfnSetThreadDpiAwarenessContext != (SetThreadDpiAwarenessContext_t)-1 )
        {
            ms_pfnSetThreadDpiAwarenessContext(m_prevContext);
        }
    }

    static bool Needed()
    {
        // use system-dpi-aware context when:
        // - the user did not set an option to force per-monitor context
        // - there are displays with different DPI
        if ( wxSystemOptions::GetOptionInt("msw.native-dialogs-pmdpi") == 1 )
            return false;

        bool diferentDPI = false;
        for ( unsigned i = 1; i < wxDisplay::GetCount() && !diferentDPI; ++i )
            diferentDPI = wxDisplay(0u).GetPPI() != wxDisplay(i).GetPPI();
        return diferentDPI;
    }

private:
    WXDPI_AWARENESS_CONTEXT m_prevContext;

    // This static member is defined in src/msw/window.cpp.
    static SetThreadDpiAwarenessContext_t ms_pfnSetThreadDpiAwarenessContext;
};

#else // !wxUSE_DYNLIB_CLASS

// Just a stub to avoid littering the code with wxUSE_DYNLIB_CLASS checks.
class AutoSystemDpiAware { };

#endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS

} // namespace wxMSWImpl

#endif // _WX_MSW_DPI_AWARE_H_
