|
|
@@ -1476,8 +1476,252 @@ private: |
|
|
|
WindowsDeleteStringFuncPtr deleteHString;
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
static HMONITOR getMonitorFromOutput (ComSmartPtr<IDXGIOutput> output)
|
|
|
|
{
|
|
|
|
DXGI_OUTPUT_DESC desc = {};
|
|
|
|
return (FAILED (output->GetDesc (&desc)) || ! desc.AttachedToDesktop)
|
|
|
|
? nullptr
|
|
|
|
: desc.Monitor;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VBlankListener
|
|
|
|
{
|
|
|
|
virtual void onVBlank() = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class VSyncThread : private Thread,
|
|
|
|
private AsyncUpdater
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
VSyncThread (ComSmartPtr<IDXGIOutput> out,
|
|
|
|
HMONITOR mon,
|
|
|
|
VBlankListener& listener)
|
|
|
|
: Thread ("VSyncThread"),
|
|
|
|
output (out),
|
|
|
|
monitor (mon)
|
|
|
|
{
|
|
|
|
listeners.push_back (listener);
|
|
|
|
startThread (10);
|
|
|
|
}
|
|
|
|
|
|
|
|
~VSyncThread() override
|
|
|
|
{
|
|
|
|
stopThread (-1);
|
|
|
|
cancelPendingUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateMonitor()
|
|
|
|
{
|
|
|
|
monitor = getMonitorFromOutput (output);
|
|
|
|
}
|
|
|
|
|
|
|
|
HMONITOR getMonitor() const noexcept { return monitor; }
|
|
|
|
|
|
|
|
void addListener (VBlankListener& listener)
|
|
|
|
{
|
|
|
|
listeners.push_back (listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool removeListener (const VBlankListener& listener)
|
|
|
|
{
|
|
|
|
auto it = std::find_if (listeners.cbegin(),
|
|
|
|
listeners.cend(),
|
|
|
|
[&listener] (const auto& l) { return &(l.get()) == &listener; });
|
|
|
|
|
|
|
|
if (it != listeners.cend())
|
|
|
|
{
|
|
|
|
listeners.erase (it);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasNoListeners() const noexcept
|
|
|
|
{
|
|
|
|
return listeners.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasListener (const VBlankListener& listener) const noexcept
|
|
|
|
{
|
|
|
|
return std::any_of (listeners.cbegin(),
|
|
|
|
listeners.cend(),
|
|
|
|
[&listener] (const auto& l) { return &(l.get()) == &listener; });
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
//==============================================================================
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
while (! threadShouldExit())
|
|
|
|
{
|
|
|
|
if (output->WaitForVBlank() == S_OK)
|
|
|
|
triggerAsyncUpdate();
|
|
|
|
else
|
|
|
|
Thread::sleep (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleAsyncUpdate() override
|
|
|
|
{
|
|
|
|
for (auto& listener : listeners)
|
|
|
|
listener.get().onVBlank();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
ComSmartPtr<IDXGIOutput> output;
|
|
|
|
HMONITOR monitor = nullptr;
|
|
|
|
std::vector<std::reference_wrapper<VBlankListener>> listeners;
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSyncThread)
|
|
|
|
JUCE_DECLARE_NON_MOVEABLE (VSyncThread)
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class VBlankDispatcher : public DeletedAtShutdown
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void updateDisplay (VBlankListener& listener, HMONITOR monitor)
|
|
|
|
{
|
|
|
|
if (monitor == nullptr)
|
|
|
|
{
|
|
|
|
removeListener (listener);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto threadWithListener = threads.end();
|
|
|
|
auto threadWithMonitor = threads.end();
|
|
|
|
|
|
|
|
for (auto it = threads.begin(); it != threads.end(); ++it)
|
|
|
|
{
|
|
|
|
if ((*it)->hasListener (listener))
|
|
|
|
threadWithListener = it;
|
|
|
|
|
|
|
|
if ((*it)->getMonitor() == monitor)
|
|
|
|
threadWithMonitor = it;
|
|
|
|
|
|
|
|
if (threadWithListener != threads.end()
|
|
|
|
&& threadWithMonitor != threads.end())
|
|
|
|
{
|
|
|
|
if (threadWithListener == threadWithMonitor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
(*threadWithMonitor)->addListener (listener);
|
|
|
|
|
|
|
|
// This may invalidate iterators, so be careful!
|
|
|
|
removeListener (threadWithListener, listener);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threadWithMonitor != threads.end())
|
|
|
|
{
|
|
|
|
(*threadWithMonitor)->addListener (listener);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threadWithListener != threads.end())
|
|
|
|
removeListener (threadWithListener, listener);
|
|
|
|
|
|
|
|
for (auto adapter : adapters)
|
|
|
|
{
|
|
|
|
UINT i = 0;
|
|
|
|
ComSmartPtr<IDXGIOutput> output;
|
|
|
|
|
|
|
|
while (adapter->EnumOutputs (i, output.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
|
|
|
|
{
|
|
|
|
if (getMonitorFromOutput (output) == monitor)
|
|
|
|
{
|
|
|
|
threads.emplace_back (std::make_unique<VSyncThread> (output, monitor, listener));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeListener (const VBlankListener& listener)
|
|
|
|
{
|
|
|
|
for (auto it = threads.begin(); it != threads.end(); ++it)
|
|
|
|
if (removeListener (it, listener))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reconfigureDisplays()
|
|
|
|
{
|
|
|
|
adapters.clear();
|
|
|
|
|
|
|
|
ComSmartPtr<IDXGIFactory> factory;
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
|
|
|
CreateDXGIFactory (__uuidof (IDXGIFactory), (void**)factory.resetAndGetPointerAddress());
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
UINT i = 0;
|
|
|
|
ComSmartPtr<IDXGIAdapter> adapter;
|
|
|
|
|
|
|
|
while (factory->EnumAdapters (i, adapter.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
|
|
|
|
{
|
|
|
|
adapters.push_back (adapter);
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& thread : threads)
|
|
|
|
thread->updateMonitor();
|
|
|
|
|
|
|
|
threads.erase (std::remove_if (threads.begin(),
|
|
|
|
threads.end(),
|
|
|
|
[] (const auto& thread) { return thread->getMonitor() == nullptr; }),
|
|
|
|
threads.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
JUCE_DECLARE_SINGLETON_SINGLETHREADED (VBlankDispatcher, true)
|
|
|
|
|
|
|
|
private:
|
|
|
|
//==============================================================================
|
|
|
|
using Threads = std::vector<std::unique_ptr<VSyncThread>>;
|
|
|
|
|
|
|
|
VBlankDispatcher()
|
|
|
|
{
|
|
|
|
reconfigureDisplays();
|
|
|
|
}
|
|
|
|
|
|
|
|
~VBlankDispatcher() override
|
|
|
|
{
|
|
|
|
threads.clear();
|
|
|
|
clearSingletonInstance();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This may delete the corresponding thread and invalidate iterators,
|
|
|
|
// so be careful!
|
|
|
|
bool removeListener (Threads::iterator it, const VBlankListener& listener)
|
|
|
|
{
|
|
|
|
if ((*it)->removeListener (listener))
|
|
|
|
{
|
|
|
|
if ((*it)->hasNoListeners())
|
|
|
|
threads.erase (it);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
std::vector<ComSmartPtr<IDXGIAdapter>> adapters;
|
|
|
|
Threads threads;
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher)
|
|
|
|
JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher)
|
|
|
|
};
|
|
|
|
|
|
|
|
JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher)
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class HWNDComponentPeer : public ComponentPeer,
|
|
|
|
private VBlankListener,
|
|
|
|
private Timer
|
|
|
|
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
|
|
|
|
, public ModifierKeyReceiver
|
|
|
@@ -1517,10 +1761,15 @@ public: |
|
|
|
|
|
|
|
return ModifierKeys::currentModifiers;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (updateCurrentMonitor())
|
|
|
|
VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
~HWNDComponentPeer() override
|
|
|
|
{
|
|
|
|
VBlankDispatcher::getInstance()->removeListener (*this);
|
|
|
|
|
|
|
|
// do this first to avoid messages arriving for this window before it's destroyed
|
|
|
|
JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
|
|
|
|
|
|
|
@@ -1889,14 +2138,26 @@ public: |
|
|
|
|
|
|
|
void repaint (const Rectangle<int>& area) override
|
|
|
|
{
|
|
|
|
auto r = RECTFromRectangle ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
|
|
|
|
InvalidateRect (hwnd, &r, FALSE);
|
|
|
|
deferredRepaints.add ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
|
|
|
|
}
|
|
|
|
|
|
|
|
void dispatchDeferredRepaints()
|
|
|
|
{
|
|
|
|
for (auto deferredRect : deferredRepaints)
|
|
|
|
{
|
|
|
|
auto r = RECTFromRectangle (deferredRect);
|
|
|
|
InvalidateRect (hwnd, &r, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
deferredRepaints.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void performAnyPendingRepaintsNow() override
|
|
|
|
{
|
|
|
|
if (component.isVisible())
|
|
|
|
{
|
|
|
|
dispatchDeferredRepaints();
|
|
|
|
|
|
|
|
WeakReference<Component> localRef (&component);
|
|
|
|
MSG m;
|
|
|
|
|
|
|
@@ -1906,6 +2167,12 @@ public: |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void onVBlank() override
|
|
|
|
{
|
|
|
|
dispatchDeferredRepaints();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
|
|
|
|
{
|
|
|
@@ -2156,6 +2423,7 @@ private: |
|
|
|
|
|
|
|
double scaleFactor = 1.0;
|
|
|
|
bool inDpiChange = 0, inHandlePositionChanged = 0;
|
|
|
|
HMONITOR currentMonitor = nullptr;
|
|
|
|
|
|
|
|
bool isAccessibilityActive = false;
|
|
|
|
|
|
|
@@ -3479,6 +3747,12 @@ private: |
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool updateCurrentMonitor()
|
|
|
|
{
|
|
|
|
auto monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONULL);
|
|
|
|
return std::exchange (currentMonitor, monitor) != monitor;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool handlePositionChanged()
|
|
|
|
{
|
|
|
|
auto pos = getCurrentMousePos();
|
|
|
@@ -3496,6 +3770,9 @@ private: |
|
|
|
|
|
|
|
handleMovedOrResized();
|
|
|
|
|
|
|
|
if (updateCurrentMonitor())
|
|
|
|
VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
|
|
|
|
|
|
|
|
return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly.
|
|
|
|
}
|
|
|
|
|
|
|
@@ -3649,6 +3926,11 @@ private: |
|
|
|
setWindowPos (hwnd, ScalingHelpers::scaledScreenPosToUnscaled (component, Desktop::getInstance().getDisplays()
|
|
|
|
.getDisplayForRect (component.getScreenBounds())->userArea),
|
|
|
|
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
|
|
|
|
|
|
|
|
auto* dispatcher = VBlankDispatcher::getInstance();
|
|
|
|
dispatcher->reconfigureDisplays();
|
|
|
|
updateCurrentMonitor();
|
|
|
|
dispatcher->updateDisplay (*this, currentMonitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
@@ -4408,6 +4690,8 @@ private: |
|
|
|
IMEHandler imeHandler;
|
|
|
|
bool shouldIgnoreModalDismiss = false;
|
|
|
|
|
|
|
|
RectangleList<int> deferredRepaints;
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer)
|
|
|
|
};
|
|
|
|