diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
index 5e824a2cd6..d3ab2b3b2f 100644
--- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
+++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
@@ -2249,6 +2249,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
@@ -4731,6 +4732,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
index 6d4e05075f..4472af4312 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
@@ -2726,6 +2726,9 @@
true
+
+ true
+
true
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
index d6cd37694c..927e467c9d 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
@@ -3517,6 +3517,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
index d175f3c9a1..386672621c 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
@@ -2726,6 +2726,9 @@
true
+
+ true
+
true
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
index 2494e098b2..719d35ca9c 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
@@ -3517,6 +3517,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
index d6f3c4d2d6..bcc8892132 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
@@ -2726,6 +2726,9 @@
true
+
+ true
+
true
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
index 6ea430c022..f2bd15238a 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
@@ -3517,6 +3517,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
index 23c61b6b6d..258cb171a3 100644
--- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
@@ -2011,6 +2011,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
@@ -4175,6 +4176,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
index 8e36d440a7..b3f26783c9 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
@@ -2419,6 +2419,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
index 8e3afbff5b..a17006751e 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
@@ -3061,6 +3061,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
index 4b968d1d94..876c932b7e 100644
--- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
@@ -2141,6 +2141,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
@@ -4458,6 +4459,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
index 7e8415bafb..efe24d5370 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
@@ -2553,6 +2553,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
index 2ab97b9806..2c14ce6efc 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
@@ -3268,6 +3268,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
index 8a57911d7c..01f090e1f6 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
@@ -2553,6 +2553,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
index 20c37f8dbb..a9ec14bd1f 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
@@ -3268,6 +3268,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
index 25dd897090..531ae00fa6 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
@@ -2553,6 +2553,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
index 681604d133..2bfc7ee7fd 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
@@ -3268,6 +3268,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
index 616be1d213..5466961251 100644
--- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
+++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
@@ -2030,6 +2030,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
@@ -4274,6 +4275,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/native/juce_ScopedThreadDPIAwarenessSetter_windows.h"
"../../../../../modules/juce_gui_basics/native/juce_ScopedWindowAssociation_linux.h"
"../../../../../modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm"
+ "../../../../../modules/juce_gui_basics/native/juce_VBlank_windows.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_android.cpp"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_ios.mm"
"../../../../../modules/juce_gui_basics/native/juce_Windowing_linux.cpp"
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
index de43aeac77..6dd4b3b37b 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
@@ -2440,6 +2440,9 @@
true
+
+ true
+
true
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
index 69f901da23..813d5906a9 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
@@ -3115,6 +3115,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
index d03d9d66f4..9805bc0059 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
@@ -1622,6 +1622,9 @@
true
+
+ true
+
true
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
index 3653d9643a..501a5f7948 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
@@ -2059,6 +2059,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
index c180c64f95..cf7dd55d5b 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
@@ -1622,6 +1622,9 @@
true
+
+ true
+
true
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
index 836cb641ab..6c47970b38 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
@@ -2059,6 +2059,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
index 74468a19f7..ba9ca29d0d 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
@@ -1622,6 +1622,9 @@
true
+
+ true
+
true
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
index 90d045252b..6a0a19264c 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
@@ -2059,6 +2059,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
index 71f9461e47..90eb689173 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
@@ -2561,6 +2561,9 @@
true
+
+ true
+
true
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
index 0c3f094de4..eec19e5c28 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -3289,6 +3289,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
index f8682e5515..95766d8b0b 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
@@ -2561,6 +2561,9 @@
true
+
+ true
+
true
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
index 7b2ecd12e5..f0d8c5d661 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -3289,6 +3289,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
index 8698ac2dc4..0bc775c155 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
@@ -2561,6 +2561,9 @@
true
+
+ true
+
true
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
index f92610bbe6..03dd29757d 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -3289,6 +3289,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
index 4fddd50959..3487f33952 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
@@ -2439,6 +2439,9 @@
true
+
+ true
+
true
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
index a6758606de..37f34aedc0 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
@@ -3112,6 +3112,9 @@
JUCE Modules\juce_gui_basics\native
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp
index f7786f6125..c60dc69f7e 100644
--- a/modules/juce_gui_basics/juce_gui_basics.cpp
+++ b/modules/juce_gui_basics/juce_gui_basics.cpp
@@ -197,6 +197,7 @@
#include "native/accessibility/juce_Accessibility_windows.cpp"
#include "native/juce_WindowsHooks_windows.h"
#include "native/juce_WindowUtils_windows.cpp"
+ #include "native/juce_VBlank_windows.cpp"
#include "native/juce_Windowing_windows.cpp"
#include "native/juce_WindowsHooks_windows.cpp"
#include "native/juce_NativeMessageBox_windows.cpp"
diff --git a/modules/juce_gui_basics/native/juce_VBlank_windows.cpp b/modules/juce_gui_basics/native/juce_VBlank_windows.cpp
new file mode 100644
index 0000000000..08be92714f
--- /dev/null
+++ b/modules/juce_gui_basics/native/juce_VBlank_windows.cpp
@@ -0,0 +1,316 @@
+/*
+ ==============================================================================
+
+ 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
+{
+
+class VBlankThread : private Thread,
+ private AsyncUpdater
+{
+public:
+ using VBlankListener = ComponentPeer::VBlankListener;
+
+ VBlankThread (ComSmartPtr out,
+ HMONITOR mon,
+ VBlankListener& listener)
+ : Thread ("VBlankThread"),
+ output (out),
+ monitor (mon)
+ {
+ listeners.push_back (listener);
+ startThread (Priority::highest);
+ }
+
+ ~VBlankThread() override
+ {
+ cancelPendingUpdate();
+
+ {
+ const std::scoped_lock lock { mutex };
+ threadState = ThreadState::exit;
+ }
+
+ condvar.notify_one();
+
+ stopThread (-1);
+ }
+
+ 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; });
+ }
+
+ //==============================================================================
+ static HMONITOR getMonitorFromOutput (ComSmartPtr output)
+ {
+ DXGI_OUTPUT_DESC desc = {};
+ return (FAILED (output->GetDesc (&desc)) || ! desc.AttachedToDesktop)
+ ? nullptr
+ : desc.Monitor;
+ }
+
+private:
+ //==============================================================================
+ void run() override
+ {
+ for (;;)
+ {
+ if (output->WaitForVBlank() == S_OK)
+ {
+ std::unique_lock lock { mutex };
+ condvar.wait (lock, [this] { return threadState != ThreadState::sleep; });
+
+ if (threadState == ThreadState::exit)
+ return;
+
+ threadState = ThreadState::sleep;
+ triggerAsyncUpdate();
+ }
+ else
+ {
+ Thread::sleep (1);
+ }
+ }
+ }
+
+ void handleAsyncUpdate() override
+ {
+ for (auto& listener : listeners)
+ listener.get().onVBlank();
+
+ {
+ const std::scoped_lock lock { mutex };
+
+ if (threadState == ThreadState::sleep)
+ threadState = ThreadState::paint;
+ }
+
+ condvar.notify_one();
+ }
+
+ //==============================================================================
+ ComSmartPtr output;
+ HMONITOR monitor = nullptr;
+ std::vector> listeners;
+
+ enum class ThreadState
+ {
+ sleep,
+ paint,
+ exit,
+ };
+
+ ThreadState threadState = ThreadState::paint;
+ std::condition_variable condvar;
+ std::mutex mutex;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankThread)
+ JUCE_DECLARE_NON_MOVEABLE (VBlankThread)
+};
+
+//==============================================================================
+class VBlankDispatcher final : public DeletedAtShutdown
+{
+public:
+ void updateDisplay (ComponentPeer::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 output;
+
+ while (adapter->EnumOutputs (i, output.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
+ {
+ if (VBlankThread::getMonitorFromOutput (output) == monitor)
+ {
+ threads.emplace_back (std::make_unique (output, monitor, listener));
+ return;
+ }
+
+ ++i;
+ }
+ }
+ }
+
+ void removeListener (const ComponentPeer::VBlankListener& listener)
+ {
+ for (auto it = threads.begin(); it != threads.end(); ++it)
+ if (removeListener (it, listener))
+ return;
+ }
+
+ void reconfigureDisplays()
+ {
+ adapters.clear();
+
+ ComSmartPtr 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 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, false)
+
+private:
+ //==============================================================================
+ using Threads = std::vector>;
+
+ 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 ComponentPeer::VBlankListener& listener)
+ {
+ if ((*it)->removeListener (listener))
+ {
+ if ((*it)->hasNoListeners())
+ threads.erase (it);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ //==============================================================================
+ std::vector> adapters;
+ Threads threads;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher)
+ JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher)
+};
+
+JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher)
+
+} // namespace juce
diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp
index 6ac0944c48..c094cd2781 100644
--- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp
+++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp
@@ -1422,249 +1422,9 @@ private:
WindowsDeleteStringFuncPtr deleteHString;
};
-//==============================================================================
-static HMONITOR getMonitorFromOutput (ComSmartPtr output)
-{
- DXGI_OUTPUT_DESC desc = {};
- return (FAILED (output->GetDesc (&desc)) || ! desc.AttachedToDesktop)
- ? nullptr
- : desc.Monitor;
-}
-
-using VBlankListener = ComponentPeer::VBlankListener;
-
-//==============================================================================
-class VSyncThread final : private Thread,
- private AsyncUpdater
-{
-public:
- VSyncThread (ComSmartPtr out,
- HMONITOR mon,
- VBlankListener& listener)
- : Thread ("VSyncThread"),
- output (out),
- monitor (mon)
- {
- listeners.push_back (listener);
- startThread (Priority::highest);
- }
-
- ~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 output;
- HMONITOR monitor = nullptr;
- std::vector> listeners;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSyncThread)
- JUCE_DECLARE_NON_MOVEABLE (VSyncThread)
-};
-
-//==============================================================================
-class VBlankDispatcher final : 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 output;
-
- while (adapter->EnumOutputs (i, output.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
- {
- if (getMonitorFromOutput (output) == monitor)
- {
- threads.emplace_back (std::make_unique (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 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 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, false)
-
-private:
- //==============================================================================
- using Threads = std::vector>;
-
- 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> adapters;
- Threads threads;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher)
- JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher)
-};
-
-JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher)
-
//==============================================================================
class HWNDComponentPeer final : public ComponentPeer,
- private VBlankListener,
+ private ComponentPeer::VBlankListener,
private Timer
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
, public ModifierKeyReceiver