2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
26 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
27 #include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h>
33 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type")
35 #undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
37 // these are in the windows SDK, but need to be repeated here for GCC..
38 #ifndef GET_APPCOMMAND_LPARAM
39 #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
41 #define FAPPCOMMAND_MASK 0xF000
42 #define APPCOMMAND_MEDIA_NEXTTRACK 11
43 #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
44 #define APPCOMMAND_MEDIA_STOP 13
45 #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
49 #define WM_APPCOMMAND 0x0319
52 void juce_repeatLastProcessPriority();
53 bool juce_isRunningInWine();
55 using CheckEventBlockedByModalComps
= bool (*) (const MSG
&);
56 extern CheckEventBlockedByModalComps isEventBlockedByModalComps
;
58 static bool shouldDeactivateTitleBar
= true;
60 void* getUser32Function (const char*);
63 int numActiveScopedDpiAwarenessDisablers
= 0;
64 static bool isInScopedDPIAwarenessDisabler() { return numActiveScopedDpiAwarenessDisablers
> 0; }
65 extern HWND juce_messageWindowHandle
;
68 struct ScopedDeviceContext
70 explicit ScopedDeviceContext (HWND h
)
71 : hwnd (h
), dc (GetDC (hwnd
))
75 ~ScopedDeviceContext()
83 JUCE_DECLARE_NON_COPYABLE (ScopedDeviceContext
)
84 JUCE_DECLARE_NON_MOVEABLE (ScopedDeviceContext
)
87 //==============================================================================
92 TOUCHEVENTF_MOVE
= 0x0001,
93 TOUCHEVENTF_DOWN
= 0x0002,
94 TOUCHEVENTF_UP
= 0x0004
97 typedef HANDLE HTOUCHINPUT
;
98 typedef HANDLE HGESTUREINFO
;
109 ULONG_PTR dwExtraInfo
;
123 ULONGLONG ullArguments
;
128 #ifndef WM_NCPOINTERUPDATE
131 WM_NCPOINTERUPDATE
= 0x241,
132 WM_NCPOINTERDOWN
= 0x242,
133 WM_NCPOINTERUP
= 0x243,
134 WM_POINTERUPDATE
= 0x245,
135 WM_POINTERDOWN
= 0x246,
136 WM_POINTERUP
= 0x247,
137 WM_POINTERENTER
= 0x249,
138 WM_POINTERLEAVE
= 0x24A,
139 WM_POINTERACTIVATE
= 0x24B,
140 WM_POINTERCAPTURECHANGED
= 0x24C,
141 WM_TOUCHHITTESTING
= 0x24D,
142 WM_POINTERWHEEL
= 0x24E,
143 WM_POINTERHWHEEL
= 0x24F,
144 WM_POINTERHITTEST
= 0x250
149 PT_TOUCH
= 0x00000002,
153 enum POINTER_BUTTON_CHANGE_TYPE
156 POINTER_CHANGE_FIRSTBUTTON_DOWN
,
157 POINTER_CHANGE_FIRSTBUTTON_UP
,
158 POINTER_CHANGE_SECONDBUTTON_DOWN
,
159 POINTER_CHANGE_SECONDBUTTON_UP
,
160 POINTER_CHANGE_THIRDBUTTON_DOWN
,
161 POINTER_CHANGE_THIRDBUTTON_UP
,
162 POINTER_CHANGE_FOURTHBUTTON_DOWN
,
163 POINTER_CHANGE_FOURTHBUTTON_UP
,
164 POINTER_CHANGE_FIFTHBUTTON_DOWN
,
165 POINTER_CHANGE_FIFTHBUTTON_UP
170 PEN_MASK_NONE
= 0x00000000,
171 PEN_MASK_PRESSURE
= 0x00000001,
172 PEN_MASK_ROTATION
= 0x00000002,
173 PEN_MASK_TILT_X
= 0x00000004,
174 PEN_MASK_TILT_Y
= 0x00000008
179 TOUCH_MASK_NONE
= 0x00000000,
180 TOUCH_MASK_CONTACTAREA
= 0x00000001,
181 TOUCH_MASK_ORIENTATION
= 0x00000002,
182 TOUCH_MASK_PRESSURE
= 0x00000004
187 POINTER_FLAG_NONE
= 0x00000000,
188 POINTER_FLAG_NEW
= 0x00000001,
189 POINTER_FLAG_INRANGE
= 0x00000002,
190 POINTER_FLAG_INCONTACT
= 0x00000004,
191 POINTER_FLAG_FIRSTBUTTON
= 0x00000010,
192 POINTER_FLAG_SECONDBUTTON
= 0x00000020,
193 POINTER_FLAG_THIRDBUTTON
= 0x00000040,
194 POINTER_FLAG_FOURTHBUTTON
= 0x00000080,
195 POINTER_FLAG_FIFTHBUTTON
= 0x00000100,
196 POINTER_FLAG_PRIMARY
= 0x00002000,
197 POINTER_FLAG_CONFIDENCE
= 0x00004000,
198 POINTER_FLAG_CANCELED
= 0x00008000,
199 POINTER_FLAG_DOWN
= 0x00010000,
200 POINTER_FLAG_UPDATE
= 0x00020000,
201 POINTER_FLAG_UP
= 0x00040000,
202 POINTER_FLAG_WHEEL
= 0x00080000,
203 POINTER_FLAG_HWHEEL
= 0x00100000,
204 POINTER_FLAG_CAPTURECHANGED
= 0x00200000,
205 POINTER_FLAG_HASTRANSFORM
= 0x00400000
208 typedef DWORD POINTER_INPUT_TYPE
;
209 typedef UINT32 POINTER_FLAGS
;
210 typedef UINT32 PEN_FLAGS
;
211 typedef UINT32 PEN_MASK
;
212 typedef UINT32 TOUCH_FLAGS
;
213 typedef UINT32 TOUCH_MASK
;
217 POINTER_INPUT_TYPE pointerType
;
220 POINTER_FLAGS pointerFlags
;
223 POINT ptPixelLocation
;
224 POINT ptHimetricLocation
;
225 POINT ptPixelLocationRaw
;
226 POINT ptHimetricLocationRaw
;
231 UINT64 PerformanceCount
;
232 POINTER_BUTTON_CHANGE_TYPE ButtonChangeType
;
235 struct POINTER_TOUCH_INFO
237 POINTER_INFO pointerInfo
;
238 TOUCH_FLAGS touchFlags
;
239 TOUCH_MASK touchMask
;
246 struct POINTER_PEN_INFO
248 POINTER_INFO pointerInfo
;
257 #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
260 #ifndef MONITOR_DPI_TYPE
261 enum Monitor_DPI_Type
263 MDT_Effective_DPI
= 0,
266 MDT_Default
= MDT_Effective_DPI
270 #ifndef DPI_AWARENESS
273 DPI_Awareness_Invalid
= -1,
274 DPI_Awareness_Unaware
= 0,
275 DPI_Awareness_System_Aware
= 1,
276 DPI_Awareness_Per_Monitor_Aware
= 2
280 #ifndef USER_DEFAULT_SCREEN_DPI
281 #define USER_DEFAULT_SCREEN_DPI 96
284 #ifndef _DPI_AWARENESS_CONTEXTS_
285 typedef HANDLE DPI_AWARENESS_CONTEXT
;
287 #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT) - 1)
288 #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT) - 2)
289 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT) - 3)
290 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
293 // Some versions of the Windows 10 SDK define _DPI_AWARENESS_CONTEXTS_ but not
294 // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
295 #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
296 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
299 //==============================================================================
300 using RegisterTouchWindowFunc
= BOOL (WINAPI
*) (HWND
, ULONG
);
301 using GetTouchInputInfoFunc
= BOOL (WINAPI
*) (HTOUCHINPUT
, UINT
, TOUCHINPUT
*, int);
302 using CloseTouchInputHandleFunc
= BOOL (WINAPI
*) (HTOUCHINPUT
);
303 using GetGestureInfoFunc
= BOOL (WINAPI
*) (HGESTUREINFO
, GESTUREINFO
*);
305 static RegisterTouchWindowFunc registerTouchWindow
= nullptr;
306 static GetTouchInputInfoFunc getTouchInputInfo
= nullptr;
307 static CloseTouchInputHandleFunc closeTouchInputHandle
= nullptr;
308 static GetGestureInfoFunc getGestureInfo
= nullptr;
310 static bool hasCheckedForMultiTouch
= false;
312 static bool canUseMultiTouch()
314 if (registerTouchWindow
== nullptr && ! hasCheckedForMultiTouch
)
316 hasCheckedForMultiTouch
= true;
318 registerTouchWindow
= (RegisterTouchWindowFunc
) getUser32Function ("RegisterTouchWindow");
319 getTouchInputInfo
= (GetTouchInputInfoFunc
) getUser32Function ("GetTouchInputInfo");
320 closeTouchInputHandle
= (CloseTouchInputHandleFunc
) getUser32Function ("CloseTouchInputHandle");
321 getGestureInfo
= (GetGestureInfoFunc
) getUser32Function ("GetGestureInfo");
324 return registerTouchWindow
!= nullptr;
327 //==============================================================================
328 using GetPointerTypeFunc
= BOOL (WINAPI
*) (UINT32
, POINTER_INPUT_TYPE
*);
329 using GetPointerTouchInfoFunc
= BOOL (WINAPI
*) (UINT32
, POINTER_TOUCH_INFO
*);
330 using GetPointerPenInfoFunc
= BOOL (WINAPI
*) (UINT32
, POINTER_PEN_INFO
*);
332 static GetPointerTypeFunc getPointerTypeFunction
= nullptr;
333 static GetPointerTouchInfoFunc getPointerTouchInfo
= nullptr;
334 static GetPointerPenInfoFunc getPointerPenInfo
= nullptr;
336 static bool canUsePointerAPI
= false;
338 static void checkForPointerAPI()
340 getPointerTypeFunction
= (GetPointerTypeFunc
) getUser32Function ("GetPointerType");
341 getPointerTouchInfo
= (GetPointerTouchInfoFunc
) getUser32Function ("GetPointerTouchInfo");
342 getPointerPenInfo
= (GetPointerPenInfoFunc
) getUser32Function ("GetPointerPenInfo");
344 canUsePointerAPI
= (getPointerTypeFunction
!= nullptr
345 && getPointerTouchInfo
!= nullptr
346 && getPointerPenInfo
!= nullptr);
349 //==============================================================================
350 using SetProcessDPIAwareFunc
= BOOL (WINAPI
*) ();
351 using SetProcessDPIAwarenessContextFunc
= BOOL (WINAPI
*) (DPI_AWARENESS_CONTEXT
);
352 using SetProcessDPIAwarenessFunc
= HRESULT (WINAPI
*) (DPI_Awareness
);
353 using SetThreadDPIAwarenessContextFunc
= DPI_AWARENESS_CONTEXT (WINAPI
*) (DPI_AWARENESS_CONTEXT
);
354 using GetDPIForWindowFunc
= UINT (WINAPI
*) (HWND
);
355 using GetDPIForMonitorFunc
= HRESULT (WINAPI
*) (HMONITOR
, Monitor_DPI_Type
, UINT
*, UINT
*);
356 using GetSystemMetricsForDpiFunc
= int (WINAPI
*) (int, UINT
);
357 using GetProcessDPIAwarenessFunc
= HRESULT (WINAPI
*) (HANDLE
, DPI_Awareness
*);
358 using GetWindowDPIAwarenessContextFunc
= DPI_AWARENESS_CONTEXT (WINAPI
*) (HWND
);
359 using GetThreadDPIAwarenessContextFunc
= DPI_AWARENESS_CONTEXT (WINAPI
*) ();
360 using GetAwarenessFromDpiAwarenessContextFunc
= DPI_Awareness (WINAPI
*) (DPI_AWARENESS_CONTEXT
);
361 using EnableNonClientDPIScalingFunc
= BOOL (WINAPI
*) (HWND
);
363 static SetProcessDPIAwareFunc setProcessDPIAware
= nullptr;
364 static SetProcessDPIAwarenessContextFunc setProcessDPIAwarenessContext
= nullptr;
365 static SetProcessDPIAwarenessFunc setProcessDPIAwareness
= nullptr;
366 static SetThreadDPIAwarenessContextFunc setThreadDPIAwarenessContext
= nullptr;
367 static GetDPIForMonitorFunc getDPIForMonitor
= nullptr;
368 static GetDPIForWindowFunc getDPIForWindow
= nullptr;
369 static GetProcessDPIAwarenessFunc getProcessDPIAwareness
= nullptr;
370 static GetWindowDPIAwarenessContextFunc getWindowDPIAwarenessContext
= nullptr;
371 static GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContext
= nullptr;
372 static GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContext
= nullptr;
373 static EnableNonClientDPIScalingFunc enableNonClientDPIScaling
= nullptr;
375 static bool hasCheckedForDPIAwareness
= false;
377 static void loadDPIAwarenessFunctions()
379 setProcessDPIAware
= (SetProcessDPIAwareFunc
) getUser32Function ("SetProcessDPIAware");
381 constexpr auto shcore
= "SHCore.dll";
382 LoadLibraryA (shcore
);
383 const auto shcoreModule
= GetModuleHandleA (shcore
);
385 if (shcoreModule
== nullptr)
388 getDPIForMonitor
= (GetDPIForMonitorFunc
) GetProcAddress (shcoreModule
, "GetDpiForMonitor");
389 setProcessDPIAwareness
= (SetProcessDPIAwarenessFunc
) GetProcAddress (shcoreModule
, "SetProcessDpiAwareness");
391 #if JUCE_WIN_PER_MONITOR_DPI_AWARE
392 getDPIForWindow
= (GetDPIForWindowFunc
) getUser32Function ("GetDpiForWindow");
393 getProcessDPIAwareness
= (GetProcessDPIAwarenessFunc
) GetProcAddress (shcoreModule
, "GetProcessDpiAwareness");
394 getWindowDPIAwarenessContext
= (GetWindowDPIAwarenessContextFunc
) getUser32Function ("GetWindowDpiAwarenessContext");
395 setThreadDPIAwarenessContext
= (SetThreadDPIAwarenessContextFunc
) getUser32Function ("SetThreadDpiAwarenessContext");
396 getThreadDPIAwarenessContext
= (GetThreadDPIAwarenessContextFunc
) getUser32Function ("GetThreadDpiAwarenessContext");
397 getAwarenessFromDPIAwarenessContext
= (GetAwarenessFromDpiAwarenessContextFunc
) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
398 setProcessDPIAwarenessContext
= (SetProcessDPIAwarenessContextFunc
) getUser32Function ("SetProcessDpiAwarenessContext");
399 enableNonClientDPIScaling
= (EnableNonClientDPIScalingFunc
) getUser32Function ("EnableNonClientDpiScaling");
403 static void setDPIAwareness()
405 if (hasCheckedForDPIAwareness
)
408 hasCheckedForDPIAwareness
= true;
410 if (! JUCEApplicationBase::isStandaloneApp())
413 loadDPIAwarenessFunctions();
415 if (setProcessDPIAwarenessContext
!= nullptr
416 && setProcessDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
))
419 if (setProcessDPIAwareness
!= nullptr && enableNonClientDPIScaling
!= nullptr
420 && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
)))
423 if (setProcessDPIAwareness
!= nullptr && getDPIForMonitor
!= nullptr
424 && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_System_Aware
)))
427 if (setProcessDPIAware
!= nullptr)
428 setProcessDPIAware();
431 static bool isPerMonitorDPIAwareProcess()
433 #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
436 static bool dpiAware
= []() -> bool
440 if (! JUCEApplication::isStandaloneApp())
443 if (getProcessDPIAwareness
== nullptr)
446 DPI_Awareness context
;
447 getProcessDPIAwareness (nullptr, &context
);
449 return context
== DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
;
456 static bool isPerMonitorDPIAwareWindow (HWND nativeWindow
)
458 #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
459 ignoreUnused (nativeWindow
);
464 if (getWindowDPIAwarenessContext
!= nullptr
465 && getAwarenessFromDPIAwarenessContext
!= nullptr)
467 return (getAwarenessFromDPIAwarenessContext (getWindowDPIAwarenessContext (nativeWindow
))
468 == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
);
471 return isPerMonitorDPIAwareProcess();
475 static bool isPerMonitorDPIAwareThread (GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContextIn
= getThreadDPIAwarenessContext
,
476 GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContextIn
= getAwarenessFromDPIAwarenessContext
)
478 #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
483 if (getThreadDPIAwarenessContextIn
!= nullptr
484 && getAwarenessFromDPIAwarenessContextIn
!= nullptr)
486 return (getAwarenessFromDPIAwarenessContextIn (getThreadDPIAwarenessContextIn())
487 == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
);
490 return isPerMonitorDPIAwareProcess();
494 static double getGlobalDPI()
498 ScopedDeviceContext deviceContext
{ nullptr };
499 return (GetDeviceCaps (deviceContext
.dc
, LOGPIXELSX
) + GetDeviceCaps (deviceContext
.dc
, LOGPIXELSY
)) / 2.0;
502 //==============================================================================
503 class ScopedThreadDPIAwarenessSetter::NativeImpl
506 explicit NativeImpl (HWND nativeWindow
)
508 ignoreUnused (nativeWindow
);
510 #if JUCE_WIN_PER_MONITOR_DPI_AWARE
511 if (auto* functionSingleton
= FunctionSingleton::getInstance())
513 if (! functionSingleton
->isLoaded())
516 auto dpiAwareWindow
= (functionSingleton
->getAwarenessFromContext (functionSingleton
->getWindowAwareness (nativeWindow
))
517 == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
);
519 auto dpiAwareThread
= (functionSingleton
->getAwarenessFromContext (functionSingleton
->getThreadAwareness())
520 == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware
);
522 if (dpiAwareWindow
&& ! dpiAwareThread
)
523 oldContext
= functionSingleton
->setThreadAwareness (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
);
524 else if (! dpiAwareWindow
&& dpiAwareThread
)
525 oldContext
= functionSingleton
->setThreadAwareness (DPI_AWARENESS_CONTEXT_UNAWARE
);
532 if (oldContext
!= nullptr)
533 if (auto* functionSingleton
= FunctionSingleton::getInstance())
534 functionSingleton
->setThreadAwareness (oldContext
);
538 struct FunctionSingleton
: public DeletedAtShutdown
540 FunctionSingleton() = default;
541 ~FunctionSingleton() override
{ clearSingletonInstance(); }
543 SetThreadDPIAwarenessContextFunc setThreadAwareness
= (SetThreadDPIAwarenessContextFunc
) getUser32Function ("SetThreadDpiAwarenessContext");
544 GetWindowDPIAwarenessContextFunc getWindowAwareness
= (GetWindowDPIAwarenessContextFunc
) getUser32Function ("GetWindowDpiAwarenessContext");
545 GetThreadDPIAwarenessContextFunc getThreadAwareness
= (GetThreadDPIAwarenessContextFunc
) getUser32Function ("GetThreadDpiAwarenessContext");
546 GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromContext
= (GetAwarenessFromDpiAwarenessContextFunc
) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
548 bool isLoaded() const noexcept
550 return setThreadAwareness
!= nullptr
551 && getWindowAwareness
!= nullptr
552 && getThreadAwareness
!= nullptr
553 && getAwarenessFromContext
!= nullptr;
556 JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FunctionSingleton
)
558 JUCE_DECLARE_NON_COPYABLE (FunctionSingleton
)
559 JUCE_DECLARE_NON_MOVEABLE (FunctionSingleton
)
562 DPI_AWARENESS_CONTEXT oldContext
= nullptr;
564 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeImpl
)
565 JUCE_DECLARE_NON_MOVEABLE (NativeImpl
)
569 JUCE_IMPLEMENT_SINGLETON (ScopedThreadDPIAwarenessSetter::NativeImpl::FunctionSingleton
)
571 ScopedThreadDPIAwarenessSetter::ScopedThreadDPIAwarenessSetter (void* nativeWindow
)
573 pimpl
= std::make_unique
<NativeImpl
> ((HWND
) nativeWindow
);
576 ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() = default;
578 ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler()
580 static auto localGetThreadDpiAwarenessContext
= (GetThreadDPIAwarenessContextFunc
) getUser32Function ("GetThreadDpiAwarenessContext");
581 static auto localGetAwarenessFromDpiAwarenessContextFunc
= (GetAwarenessFromDpiAwarenessContextFunc
) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
583 if (! isPerMonitorDPIAwareThread (localGetThreadDpiAwarenessContext
, localGetAwarenessFromDpiAwarenessContextFunc
))
586 static auto localSetThreadDPIAwarenessContext
= (SetThreadDPIAwarenessContextFunc
) getUser32Function ("SetThreadDpiAwarenessContext");
588 if (localSetThreadDPIAwarenessContext
!= nullptr)
590 previousContext
= localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE
);
593 ++numActiveScopedDpiAwarenessDisablers
;
598 ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler()
600 if (previousContext
!= nullptr)
602 static auto localSetThreadDPIAwarenessContext
= (SetThreadDPIAwarenessContextFunc
) getUser32Function ("SetThreadDpiAwarenessContext");
604 if (localSetThreadDPIAwarenessContext
!= nullptr)
605 localSetThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT
) previousContext
);
608 --numActiveScopedDpiAwarenessDisablers
;
613 //==============================================================================
614 using SettingChangeCallbackFunc
= void (*)(void);
615 extern SettingChangeCallbackFunc settingChangeCallback
;
617 //==============================================================================
618 static Rectangle
<int> rectangleFromRECT (RECT r
) noexcept
{ return { r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
}; }
619 static RECT
RECTFromRectangle (Rectangle
<int> r
) noexcept
{ return { r
.getX(), r
.getY(), r
.getRight(), r
.getBottom() }; }
621 static Point
<int> pointFromPOINT (POINT p
) noexcept
{ return { p
.x
, p
.y
}; }
622 static POINT
POINTFromPoint (Point
<int> p
) noexcept
{ return { p
.x
, p
.y
}; }
624 //==============================================================================
625 static const Displays::Display
* getCurrentDisplayFromScaleFactor (HWND hwnd
);
627 template <typename ValueType
>
628 static Rectangle
<ValueType
> convertPhysicalScreenRectangleToLogical (Rectangle
<ValueType
> r
, HWND h
) noexcept
630 if (isPerMonitorDPIAwareWindow (h
))
631 return Desktop::getInstance().getDisplays().physicalToLogical (r
, getCurrentDisplayFromScaleFactor (h
));
636 template <typename ValueType
>
637 static Rectangle
<ValueType
> convertLogicalScreenRectangleToPhysical (Rectangle
<ValueType
> r
, HWND h
) noexcept
639 if (isPerMonitorDPIAwareWindow (h
))
640 return Desktop::getInstance().getDisplays().logicalToPhysical (r
, getCurrentDisplayFromScaleFactor (h
));
645 static Point
<int> convertPhysicalScreenPointToLogical (Point
<int> p
, HWND h
) noexcept
647 if (isPerMonitorDPIAwareWindow (h
))
648 return Desktop::getInstance().getDisplays().physicalToLogical (p
, getCurrentDisplayFromScaleFactor (h
));
653 static Point
<int> convertLogicalScreenPointToPhysical (Point
<int> p
, HWND h
) noexcept
655 if (isPerMonitorDPIAwareWindow (h
))
656 return Desktop::getInstance().getDisplays().logicalToPhysical (p
, getCurrentDisplayFromScaleFactor (h
));
661 JUCE_API
double getScaleFactorForWindow (HWND h
);
662 JUCE_API
double getScaleFactorForWindow (HWND h
)
664 // NB. Using a local function here because we need to call this method from the plug-in wrappers
665 // which don't load the DPI-awareness functions on startup
666 static GetDPIForWindowFunc localGetDPIForWindow
= nullptr;
668 static bool hasChecked
= false;
674 if (localGetDPIForWindow
== nullptr)
675 localGetDPIForWindow
= (GetDPIForWindowFunc
) getUser32Function ("GetDpiForWindow");
678 if (localGetDPIForWindow
!= nullptr)
679 return (double) localGetDPIForWindow (h
) / USER_DEFAULT_SCREEN_DPI
;
684 //==============================================================================
685 static void setWindowPos (HWND hwnd
, Rectangle
<int> bounds
, UINT flags
, bool adjustTopLeft
= false)
687 ScopedThreadDPIAwarenessSetter setter
{ hwnd
};
689 if (isPerMonitorDPIAwareWindow (hwnd
))
692 bounds
= convertLogicalScreenRectangleToPhysical (bounds
, hwnd
)
693 .withPosition (Desktop::getInstance().getDisplays().logicalToPhysical (bounds
.getTopLeft()));
695 bounds
= convertLogicalScreenRectangleToPhysical (bounds
, hwnd
);
698 SetWindowPos (hwnd
, nullptr, bounds
.getX(), bounds
.getY(), bounds
.getWidth(), bounds
.getHeight(), flags
);
701 static RECT
getWindowScreenRect (HWND hwnd
)
703 ScopedThreadDPIAwarenessSetter setter
{ hwnd
};
706 GetWindowRect (hwnd
, &rect
);
710 static RECT
getWindowClientRect (HWND hwnd
)
712 auto rect
= getWindowScreenRect (hwnd
);
714 if (auto parentH
= GetParent (hwnd
))
716 ScopedThreadDPIAwarenessSetter setter
{ hwnd
};
717 MapWindowPoints (HWND_DESKTOP
, parentH
, (LPPOINT
) &rect
, 2);
723 static void setWindowZOrder (HWND hwnd
, HWND insertAfter
)
725 SetWindowPos (hwnd
, insertAfter
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOSENDCHANGING
);
728 //==============================================================================
730 extern RTL_OSVERSIONINFOW
getWindowsVersionInfo();
733 double Desktop::getDefaultMasterScale()
735 if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess())
738 return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI
;
741 bool Desktop::canUseSemiTransparentWindows() noexcept
746 class Desktop::NativeDarkModeChangeDetectorImpl
749 NativeDarkModeChangeDetectorImpl()
752 const auto winVer
= getWindowsVersionInfo();
754 if (winVer
.dwMajorVersion
>= 10 && winVer
.dwBuildNumber
>= 17763)
756 const auto uxtheme
= "uxtheme.dll";
757 LoadLibraryA (uxtheme
);
758 const auto uxthemeModule
= GetModuleHandleA (uxtheme
);
760 if (uxthemeModule
!= nullptr)
762 shouldAppsUseDarkMode
= (ShouldAppsUseDarkModeFunc
) GetProcAddress (uxthemeModule
, MAKEINTRESOURCEA (132));
764 if (shouldAppsUseDarkMode
!= nullptr)
765 darkModeEnabled
= shouldAppsUseDarkMode() && ! isHighContrast();
771 ~NativeDarkModeChangeDetectorImpl()
773 UnhookWindowsHookEx (hook
);
776 bool isDarkModeEnabled() const noexcept
{ return darkModeEnabled
; }
779 static bool isHighContrast()
781 HIGHCONTRASTW highContrast
{};
783 if (SystemParametersInfoW (SPI_GETHIGHCONTRAST
, sizeof (highContrast
), &highContrast
, false))
784 return highContrast
.dwFlags
& HCF_HIGHCONTRASTON
;
789 static LRESULT CALLBACK
callWndProc (int nCode
, WPARAM wParam
, LPARAM lParam
)
791 auto* params
= reinterpret_cast<CWPSTRUCT
*> (lParam
);
795 && params
->message
== WM_SETTINGCHANGE
796 && params
->lParam
!= 0
797 && CompareStringOrdinal (reinterpret_cast<LPWCH
> (params
->lParam
), -1, L
"ImmersiveColorSet", -1, true) == CSTR_EQUAL
)
799 Desktop::getInstance().nativeDarkModeChangeDetectorImpl
->colourSetChanged();
802 return CallNextHookEx ({}, nCode
, wParam
, lParam
);
805 void colourSetChanged()
807 if (shouldAppsUseDarkMode
!= nullptr)
809 const auto wasDarkModeEnabled
= std::exchange (darkModeEnabled
, shouldAppsUseDarkMode() && ! isHighContrast());
811 if (darkModeEnabled
!= wasDarkModeEnabled
)
812 Desktop::getInstance().darkModeChanged();
816 using ShouldAppsUseDarkModeFunc
= bool (WINAPI
*)();
817 ShouldAppsUseDarkModeFunc shouldAppsUseDarkMode
= nullptr;
819 bool darkModeEnabled
= false;
820 HHOOK hook
{ SetWindowsHookEx (WH_CALLWNDPROC
,
822 (HINSTANCE
) juce::Process::getCurrentModuleInstanceHandle(),
823 GetCurrentThreadId()) };
825 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl
)
828 std::unique_ptr
<Desktop::NativeDarkModeChangeDetectorImpl
> Desktop::createNativeDarkModeChangeDetectorImpl()
830 return std::make_unique
<NativeDarkModeChangeDetectorImpl
>();
833 bool Desktop::isDarkModeActive() const
835 return nativeDarkModeChangeDetectorImpl
->isDarkModeEnabled();
838 Desktop::DisplayOrientation
Desktop::getCurrentOrientation() const
843 int64
getMouseEventTime();
844 int64
getMouseEventTime()
846 static int64 eventTimeOffset
= 0;
847 static LONG lastMessageTime
= 0;
848 const LONG thisMessageTime
= GetMessageTime();
850 if (thisMessageTime
< lastMessageTime
|| lastMessageTime
== 0)
852 lastMessageTime
= thisMessageTime
;
853 eventTimeOffset
= Time::currentTimeMillis() - thisMessageTime
;
856 return eventTimeOffset
+ thisMessageTime
;
859 //==============================================================================
860 const int extendedKeyModifier
= 0x10000;
862 const int KeyPress::spaceKey
= VK_SPACE
;
863 const int KeyPress::returnKey
= VK_RETURN
;
864 const int KeyPress::escapeKey
= VK_ESCAPE
;
865 const int KeyPress::backspaceKey
= VK_BACK
;
866 const int KeyPress::deleteKey
= VK_DELETE
| extendedKeyModifier
;
867 const int KeyPress::insertKey
= VK_INSERT
| extendedKeyModifier
;
868 const int KeyPress::tabKey
= VK_TAB
;
869 const int KeyPress::leftKey
= VK_LEFT
| extendedKeyModifier
;
870 const int KeyPress::rightKey
= VK_RIGHT
| extendedKeyModifier
;
871 const int KeyPress::upKey
= VK_UP
| extendedKeyModifier
;
872 const int KeyPress::downKey
= VK_DOWN
| extendedKeyModifier
;
873 const int KeyPress::homeKey
= VK_HOME
| extendedKeyModifier
;
874 const int KeyPress::endKey
= VK_END
| extendedKeyModifier
;
875 const int KeyPress::pageUpKey
= VK_PRIOR
| extendedKeyModifier
;
876 const int KeyPress::pageDownKey
= VK_NEXT
| extendedKeyModifier
;
877 const int KeyPress::F1Key
= VK_F1
| extendedKeyModifier
;
878 const int KeyPress::F2Key
= VK_F2
| extendedKeyModifier
;
879 const int KeyPress::F3Key
= VK_F3
| extendedKeyModifier
;
880 const int KeyPress::F4Key
= VK_F4
| extendedKeyModifier
;
881 const int KeyPress::F5Key
= VK_F5
| extendedKeyModifier
;
882 const int KeyPress::F6Key
= VK_F6
| extendedKeyModifier
;
883 const int KeyPress::F7Key
= VK_F7
| extendedKeyModifier
;
884 const int KeyPress::F8Key
= VK_F8
| extendedKeyModifier
;
885 const int KeyPress::F9Key
= VK_F9
| extendedKeyModifier
;
886 const int KeyPress::F10Key
= VK_F10
| extendedKeyModifier
;
887 const int KeyPress::F11Key
= VK_F11
| extendedKeyModifier
;
888 const int KeyPress::F12Key
= VK_F12
| extendedKeyModifier
;
889 const int KeyPress::F13Key
= VK_F13
| extendedKeyModifier
;
890 const int KeyPress::F14Key
= VK_F14
| extendedKeyModifier
;
891 const int KeyPress::F15Key
= VK_F15
| extendedKeyModifier
;
892 const int KeyPress::F16Key
= VK_F16
| extendedKeyModifier
;
893 const int KeyPress::F17Key
= VK_F17
| extendedKeyModifier
;
894 const int KeyPress::F18Key
= VK_F18
| extendedKeyModifier
;
895 const int KeyPress::F19Key
= VK_F19
| extendedKeyModifier
;
896 const int KeyPress::F20Key
= VK_F20
| extendedKeyModifier
;
897 const int KeyPress::F21Key
= VK_F21
| extendedKeyModifier
;
898 const int KeyPress::F22Key
= VK_F22
| extendedKeyModifier
;
899 const int KeyPress::F23Key
= VK_F23
| extendedKeyModifier
;
900 const int KeyPress::F24Key
= VK_F24
| extendedKeyModifier
;
901 const int KeyPress::F25Key
= 0x31000; // Windows doesn't support F-keys 25 or higher
902 const int KeyPress::F26Key
= 0x31001;
903 const int KeyPress::F27Key
= 0x31002;
904 const int KeyPress::F28Key
= 0x31003;
905 const int KeyPress::F29Key
= 0x31004;
906 const int KeyPress::F30Key
= 0x31005;
907 const int KeyPress::F31Key
= 0x31006;
908 const int KeyPress::F32Key
= 0x31007;
909 const int KeyPress::F33Key
= 0x31008;
910 const int KeyPress::F34Key
= 0x31009;
911 const int KeyPress::F35Key
= 0x3100a;
913 const int KeyPress::numberPad0
= VK_NUMPAD0
| extendedKeyModifier
;
914 const int KeyPress::numberPad1
= VK_NUMPAD1
| extendedKeyModifier
;
915 const int KeyPress::numberPad2
= VK_NUMPAD2
| extendedKeyModifier
;
916 const int KeyPress::numberPad3
= VK_NUMPAD3
| extendedKeyModifier
;
917 const int KeyPress::numberPad4
= VK_NUMPAD4
| extendedKeyModifier
;
918 const int KeyPress::numberPad5
= VK_NUMPAD5
| extendedKeyModifier
;
919 const int KeyPress::numberPad6
= VK_NUMPAD6
| extendedKeyModifier
;
920 const int KeyPress::numberPad7
= VK_NUMPAD7
| extendedKeyModifier
;
921 const int KeyPress::numberPad8
= VK_NUMPAD8
| extendedKeyModifier
;
922 const int KeyPress::numberPad9
= VK_NUMPAD9
| extendedKeyModifier
;
923 const int KeyPress::numberPadAdd
= VK_ADD
| extendedKeyModifier
;
924 const int KeyPress::numberPadSubtract
= VK_SUBTRACT
| extendedKeyModifier
;
925 const int KeyPress::numberPadMultiply
= VK_MULTIPLY
| extendedKeyModifier
;
926 const int KeyPress::numberPadDivide
= VK_DIVIDE
| extendedKeyModifier
;
927 const int KeyPress::numberPadSeparator
= VK_SEPARATOR
| extendedKeyModifier
;
928 const int KeyPress::numberPadDecimalPoint
= VK_DECIMAL
| extendedKeyModifier
;
929 const int KeyPress::numberPadEquals
= 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier
;
930 const int KeyPress::numberPadDelete
= VK_DELETE
| extendedKeyModifier
;
931 const int KeyPress::playKey
= 0x30000;
932 const int KeyPress::stopKey
= 0x30001;
933 const int KeyPress::fastForwardKey
= 0x30002;
934 const int KeyPress::rewindKey
= 0x30003;
937 //==============================================================================
938 class WindowsBitmapImage
: public ImagePixelData
941 WindowsBitmapImage (const Image::PixelFormat format
,
942 const int w
, const int h
, const bool clearImage
)
943 : ImagePixelData (format
, w
, h
)
945 jassert (format
== Image::RGB
|| format
== Image::ARGB
);
947 static bool alwaysUse32Bits
= isGraphicsCard32Bit(); // NB: for 32-bit cards, it's faster to use a 32-bit image.
949 pixelStride
= (alwaysUse32Bits
|| format
== Image::ARGB
) ? 4 : 3;
950 lineStride
= -((w
* pixelStride
+ 3) & ~3);
952 zerostruct (bitmapInfo
);
953 bitmapInfo
.bV4Size
= sizeof (BITMAPV4HEADER
);
954 bitmapInfo
.bV4Width
= w
;
955 bitmapInfo
.bV4Height
= h
;
956 bitmapInfo
.bV4Planes
= 1;
957 bitmapInfo
.bV4CSType
= 1;
958 bitmapInfo
.bV4BitCount
= (unsigned short) (pixelStride
* 8);
960 if (format
== Image::ARGB
)
962 bitmapInfo
.bV4AlphaMask
= 0xff000000;
963 bitmapInfo
.bV4RedMask
= 0xff0000;
964 bitmapInfo
.bV4GreenMask
= 0xff00;
965 bitmapInfo
.bV4BlueMask
= 0xff;
966 bitmapInfo
.bV4V4Compression
= BI_BITFIELDS
;
970 bitmapInfo
.bV4V4Compression
= BI_RGB
;
974 ScopedDeviceContext deviceContext
{ nullptr };
975 hdc
= CreateCompatibleDC (deviceContext
.dc
);
978 SetMapMode (hdc
, MM_TEXT
);
980 hBitmap
= CreateDIBSection (hdc
, (BITMAPINFO
*) &(bitmapInfo
), DIB_RGB_COLORS
,
981 (void**) &bitmapData
, nullptr, 0);
983 if (hBitmap
!= nullptr)
984 previousBitmap
= SelectObject (hdc
, hBitmap
);
986 if (format
== Image::ARGB
&& clearImage
)
987 zeromem (bitmapData
, (size_t) std::abs (h
* lineStride
));
989 imageData
= bitmapData
- (lineStride
* (h
- 1));
992 ~WindowsBitmapImage() override
994 SelectObject (hdc
, previousBitmap
); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
996 DeleteObject (hBitmap
);
999 std::unique_ptr
<ImageType
> createType() const override
{ return std::make_unique
<NativeImageType
>(); }
1001 std::unique_ptr
<LowLevelGraphicsContext
> createLowLevelContext() override
1003 sendDataChangeMessage();
1004 return std::make_unique
<LowLevelGraphicsSoftwareRenderer
> (Image (this));
1007 void initialiseBitmapData (Image::BitmapData
& bitmap
, int x
, int y
, Image::BitmapData::ReadWriteMode mode
) override
1009 const auto offset
= (size_t) (x
* pixelStride
+ y
* lineStride
);
1010 bitmap
.data
= imageData
+ offset
;
1011 bitmap
.size
= (size_t) (lineStride
* height
) - offset
;
1012 bitmap
.pixelFormat
= pixelFormat
;
1013 bitmap
.lineStride
= lineStride
;
1014 bitmap
.pixelStride
= pixelStride
;
1016 if (mode
!= Image::BitmapData::readOnly
)
1017 sendDataChangeMessage();
1020 ImagePixelData::Ptr
clone() override
1022 auto im
= new WindowsBitmapImage (pixelFormat
, width
, height
, false);
1024 for (int i
= 0; i
< height
; ++i
)
1025 memcpy (im
->imageData
+ i
* lineStride
, imageData
+ i
* lineStride
, (size_t) lineStride
);
1030 void blitToWindow (HWND hwnd
, HDC dc
, bool transparent
, int x
, int y
, uint8 updateLayeredWindowAlpha
) noexcept
1032 SetMapMode (dc
, MM_TEXT
);
1036 auto windowBounds
= getWindowScreenRect (hwnd
);
1038 POINT p
= { -x
, -y
};
1039 POINT pos
= { windowBounds
.left
, windowBounds
.top
};
1040 SIZE size
= { windowBounds
.right
- windowBounds
.left
,
1041 windowBounds
.bottom
- windowBounds
.top
};
1044 bf
.AlphaFormat
= 1 /*AC_SRC_ALPHA*/;
1046 bf
.BlendOp
= AC_SRC_OVER
;
1047 bf
.SourceConstantAlpha
= updateLayeredWindowAlpha
;
1049 UpdateLayeredWindow (hwnd
, nullptr, &pos
, &size
, hdc
, &p
, 0, &bf
, 2 /*ULW_ALPHA*/);
1054 x
, y
, width
, height
,
1055 0, 0, width
, height
,
1056 bitmapData
, (const BITMAPINFO
*) &bitmapInfo
,
1057 DIB_RGB_COLORS
, SRCCOPY
);
1062 HGDIOBJ previousBitmap
;
1063 BITMAPV4HEADER bitmapInfo
;
1066 int pixelStride
, lineStride
;
1070 static bool isGraphicsCard32Bit()
1072 ScopedDeviceContext deviceContext
{ nullptr };
1073 return GetDeviceCaps (deviceContext
.dc
, BITSPIXEL
) > 24;
1076 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage
)
1079 //==============================================================================
1080 Image
createSnapshotOfNativeWindow (void* nativeWindowHandle
)
1082 auto hwnd
= (HWND
) nativeWindowHandle
;
1084 auto r
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd
)), hwnd
);
1085 const auto w
= r
.getWidth();
1086 const auto h
= r
.getHeight();
1088 auto nativeBitmap
= new WindowsBitmapImage (Image::RGB
, w
, h
, true);
1089 Image
bitmap (nativeBitmap
);
1091 ScopedDeviceContext deviceContext
{ hwnd
};
1093 if (isPerMonitorDPIAwareProcess())
1095 auto scale
= getScaleFactorForWindow (hwnd
);
1096 auto prevStretchMode
= SetStretchBltMode (nativeBitmap
->hdc
, HALFTONE
);
1097 SetBrushOrgEx (nativeBitmap
->hdc
, 0, 0, nullptr);
1099 StretchBlt (nativeBitmap
->hdc
, 0, 0, w
, h
,
1100 deviceContext
.dc
, 0, 0, roundToInt (w
* scale
), roundToInt (h
* scale
),
1103 SetStretchBltMode (nativeBitmap
->hdc
, prevStretchMode
);
1107 BitBlt (nativeBitmap
->hdc
, 0, 0, w
, h
, deviceContext
.dc
, 0, 0, SRCCOPY
);
1110 return SoftwareImageType().convert (bitmap
);
1113 //==============================================================================
1114 namespace IconConverters
1116 static Image
createImageFromHICON (HICON icon
)
1118 if (icon
== nullptr)
1121 struct ScopedICONINFO
: public ICONINFO
1131 if (hbmColor
!= nullptr)
1132 ::DeleteObject (hbmColor
);
1134 if (hbmMask
!= nullptr)
1135 ::DeleteObject (hbmMask
);
1139 ScopedICONINFO info
;
1141 if (! ::GetIconInfo (icon
, &info
))
1146 if (! (::GetObject (info
.hbmColor
, sizeof (BITMAP
), &bm
)
1147 && bm
.bmWidth
> 0 && bm
.bmHeight
> 0))
1150 ScopedDeviceContext deviceContext
{ nullptr };
1152 if (auto* dc
= ::CreateCompatibleDC (deviceContext
.dc
))
1154 BITMAPV5HEADER header
= {};
1155 header
.bV5Size
= sizeof (BITMAPV5HEADER
);
1156 header
.bV5Width
= bm
.bmWidth
;
1157 header
.bV5Height
= -bm
.bmHeight
;
1158 header
.bV5Planes
= 1;
1159 header
.bV5Compression
= BI_RGB
;
1160 header
.bV5BitCount
= 32;
1161 header
.bV5RedMask
= 0x00FF0000;
1162 header
.bV5GreenMask
= 0x0000FF00;
1163 header
.bV5BlueMask
= 0x000000FF;
1164 header
.bV5AlphaMask
= 0xFF000000;
1165 header
.bV5CSType
= 0x57696E20; // 'Win '
1166 header
.bV5Intent
= LCS_GM_IMAGES
;
1168 uint32
* bitmapImageData
= nullptr;
1170 if (auto* dib
= ::CreateDIBSection (deviceContext
.dc
, (BITMAPINFO
*) &header
, DIB_RGB_COLORS
,
1171 (void**) &bitmapImageData
, nullptr, 0))
1173 auto oldObject
= ::SelectObject (dc
, dib
);
1175 auto numPixels
= bm
.bmWidth
* bm
.bmHeight
;
1176 auto numColourComponents
= (size_t) numPixels
* 4;
1178 // Windows icon data comes as two layers, an XOR mask which contains the bulk
1179 // of the image data and an AND mask which provides the transparency. Annoyingly
1180 // the XOR mask can also contain an alpha channel, in which case the transparency
1181 // mask should not be applied, but there's no way to find out a priori if the XOR
1182 // mask contains an alpha channel.
1184 HeapBlock
<bool> opacityMask (numPixels
);
1185 memset (bitmapImageData
, 0, numColourComponents
);
1186 ::DrawIconEx (dc
, 0, 0, icon
, bm
.bmWidth
, bm
.bmHeight
, 0, nullptr, DI_MASK
);
1188 for (int i
= 0; i
< numPixels
; ++i
)
1189 opacityMask
[i
] = (bitmapImageData
[i
] == 0);
1191 Image result
= Image (Image::ARGB
, bm
.bmWidth
, bm
.bmHeight
, true);
1192 Image::BitmapData
imageData (result
, Image::BitmapData::readWrite
);
1194 memset (bitmapImageData
, 0, numColourComponents
);
1195 ::DrawIconEx (dc
, 0, 0, icon
, bm
.bmWidth
, bm
.bmHeight
, 0, nullptr, DI_NORMAL
);
1196 memcpy (imageData
.data
, bitmapImageData
, numColourComponents
);
1198 auto imageHasAlphaChannel
= [&imageData
, numPixels
]()
1200 for (int i
= 0; i
< numPixels
; ++i
)
1201 if (imageData
.data
[i
* 4] != 0)
1207 if (! imageHasAlphaChannel())
1208 for (int i
= 0; i
< numPixels
; ++i
)
1209 imageData
.data
[i
* 4] = opacityMask
[i
] ? 0xff : 0x00;
1211 ::SelectObject (dc
, oldObject
);
1212 ::DeleteObject (dib
);
1224 HICON
createHICONFromImage (const Image
& image
, const BOOL isIcon
, int hotspotX
, int hotspotY
);
1225 HICON
createHICONFromImage (const Image
& image
, const BOOL isIcon
, int hotspotX
, int hotspotY
)
1227 auto nativeBitmap
= new WindowsBitmapImage (Image::ARGB
, image
.getWidth(), image
.getHeight(), true);
1228 Image
bitmap (nativeBitmap
);
1231 Graphics
g (bitmap
);
1232 g
.drawImageAt (image
, 0, 0);
1235 auto mask
= CreateBitmap (image
.getWidth(), image
.getHeight(), 1, 1, nullptr);
1238 info
.fIcon
= isIcon
;
1239 info
.xHotspot
= (DWORD
) hotspotX
;
1240 info
.yHotspot
= (DWORD
) hotspotY
;
1241 info
.hbmMask
= mask
;
1242 info
.hbmColor
= nativeBitmap
->hBitmap
;
1244 auto hi
= CreateIconIndirect (&info
);
1245 DeleteObject (mask
);
1248 } // namespace IconConverters
1250 //==============================================================================
1251 JUCE_IUNKNOWNCLASS (ITipInvocation
, "37c994e7-432b-4834-a2f7-dce1f13b834b")
1253 static CLSID
getCLSID() noexcept
{ return { 0x4ce576fa, 0x83dc, 0x4f88, { 0x95, 0x1c, 0x9d, 0x07, 0x82, 0xb4, 0xe3, 0x76 } }; }
1255 JUCE_COMCALL
Toggle (HWND
) = 0;
1260 #ifdef __CRT_UUID_DECL
1261 __CRT_UUID_DECL (juce::ITipInvocation
, 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b)
1267 //==============================================================================
1268 struct HSTRING_PRIVATE
;
1269 typedef HSTRING_PRIVATE
* HSTRING
;
1271 struct IInspectable
: public IUnknown
1273 JUCE_COMCALL
GetIids (ULONG
* ,IID
**) = 0;
1274 JUCE_COMCALL
GetRuntimeClassName (HSTRING
*) = 0;
1275 JUCE_COMCALL
GetTrustLevel (void*) = 0;
1278 JUCE_COMCLASS (IUIViewSettingsInterop
, "3694dbf9-8f68-44be-8ff5-195c98ede8a6") : public IInspectable
1280 JUCE_COMCALL
GetForWindow (HWND
, REFIID
, void**) = 0;
1283 JUCE_COMCLASS (IUIViewSettings
, "c63657f6-8850-470d-88f8-455e16ea2c26") : public IInspectable
1285 enum UserInteractionMode
1291 JUCE_COMCALL
GetUserInteractionMode (UserInteractionMode
*) = 0;
1296 #ifdef __CRT_UUID_DECL
1297 __CRT_UUID_DECL (juce::IUIViewSettingsInterop
, 0x3694dbf9, 0x8f68, 0x44be, 0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6)
1298 __CRT_UUID_DECL (juce::IUIViewSettings
, 0xc63657f6, 0x8850, 0x470d, 0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26)
1303 struct UWPUIViewSettings
1307 ComBaseModule
dll (L
"api-ms-win-core-winrt-l1-1-0");
1309 if (dll
.h
!= nullptr)
1311 roInitialize
= (RoInitializeFuncPtr
) ::GetProcAddress (dll
.h
, "RoInitialize");
1312 roGetActivationFactory
= (RoGetActivationFactoryFuncPtr
) ::GetProcAddress (dll
.h
, "RoGetActivationFactory");
1313 createHString
= (WindowsCreateStringFuncPtr
) ::GetProcAddress (dll
.h
, "WindowsCreateString");
1314 deleteHString
= (WindowsDeleteStringFuncPtr
) ::GetProcAddress (dll
.h
, "WindowsDeleteString");
1316 if (roInitialize
== nullptr || roGetActivationFactory
== nullptr
1317 || createHString
== nullptr || deleteHString
== nullptr)
1320 auto status
= roInitialize (1);
1322 if (status
!= S_OK
&& status
!= S_FALSE
&& (unsigned) status
!= 0x80010106L
)
1325 LPCWSTR uwpClassName
= L
"Windows.UI.ViewManagement.UIViewSettings";
1326 HSTRING uwpClassId
= nullptr;
1328 if (createHString (uwpClassName
, (::UINT32
) wcslen (uwpClassName
), &uwpClassId
) != S_OK
1329 || uwpClassId
== nullptr)
1332 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
1333 status
= roGetActivationFactory (uwpClassId
, __uuidof (IUIViewSettingsInterop
),
1334 (void**) viewSettingsInterop
.resetAndGetPointerAddress());
1335 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
1336 deleteHString (uwpClassId
);
1338 if (status
!= S_OK
|| viewSettingsInterop
== nullptr)
1341 // move dll into member var
1342 comBaseDLL
= std::move (dll
);
1347 //==============================================================================
1348 struct ComBaseModule
1350 ComBaseModule() = default;
1351 ComBaseModule (LPCWSTR libraryName
) : h (::LoadLibrary (libraryName
)) {}
1352 ComBaseModule (ComBaseModule
&& o
) : h (o
.h
) { o
.h
= nullptr; }
1353 ~ComBaseModule() { release(); }
1355 void release() { if (h
!= nullptr) ::FreeLibrary (h
); h
= nullptr; }
1356 ComBaseModule
& operator= (ComBaseModule
&& o
) { release(); h
= o
.h
; o
.h
= nullptr; return *this; }
1361 using RoInitializeFuncPtr
= HRESULT (WINAPI
*) (int);
1362 using RoGetActivationFactoryFuncPtr
= HRESULT (WINAPI
*) (HSTRING
, REFIID
, void**);
1363 using WindowsCreateStringFuncPtr
= HRESULT (WINAPI
*) (LPCWSTR
,UINT32
, HSTRING
*);
1364 using WindowsDeleteStringFuncPtr
= HRESULT (WINAPI
*) (HSTRING
);
1366 ComBaseModule comBaseDLL
;
1367 ComSmartPtr
<IUIViewSettingsInterop
> viewSettingsInterop
;
1369 RoInitializeFuncPtr roInitialize
;
1370 RoGetActivationFactoryFuncPtr roGetActivationFactory
;
1371 WindowsCreateStringFuncPtr createHString
;
1372 WindowsDeleteStringFuncPtr deleteHString
;
1375 //==============================================================================
1377 static HMONITOR
getMonitorFromOutput (ComSmartPtr
<IDXGIOutput
> output
)
1379 DXGI_OUTPUT_DESC desc
= {};
1380 return (FAILED (output
->GetDesc (&desc
)) || ! desc
.AttachedToDesktop
)
1385 struct VBlankListener
1387 virtual void onVBlank() = 0;
1390 //==============================================================================
1391 class VSyncThread
: private Thread
,
1392 private AsyncUpdater
1395 VSyncThread (ComSmartPtr
<IDXGIOutput
> out
,
1397 VBlankListener
& listener
)
1398 : Thread ("VSyncThread"),
1402 listeners
.push_back (listener
);
1406 ~VSyncThread() override
1409 cancelPendingUpdate();
1412 void updateMonitor()
1414 monitor
= getMonitorFromOutput (output
);
1417 HMONITOR
getMonitor() const noexcept
{ return monitor
; }
1419 void addListener (VBlankListener
& listener
)
1421 listeners
.push_back (listener
);
1424 bool removeListener (const VBlankListener
& listener
)
1426 auto it
= std::find_if (listeners
.cbegin(),
1428 [&listener
] (const auto& l
) { return &(l
.get()) == &listener
; });
1430 if (it
!= listeners
.cend())
1432 listeners
.erase (it
);
1439 bool hasNoListeners() const noexcept
1441 return listeners
.empty();
1444 bool hasListener (const VBlankListener
& listener
) const noexcept
1446 return std::any_of (listeners
.cbegin(),
1448 [&listener
] (const auto& l
) { return &(l
.get()) == &listener
; });
1452 //==============================================================================
1455 while (! threadShouldExit())
1457 if (output
->WaitForVBlank() == S_OK
)
1458 triggerAsyncUpdate();
1464 void handleAsyncUpdate() override
1466 for (auto& listener
: listeners
)
1467 listener
.get().onVBlank();
1470 //==============================================================================
1471 ComSmartPtr
<IDXGIOutput
> output
;
1472 HMONITOR monitor
= nullptr;
1473 std::vector
<std::reference_wrapper
<VBlankListener
>> listeners
;
1475 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSyncThread
)
1476 JUCE_DECLARE_NON_MOVEABLE (VSyncThread
)
1479 //==============================================================================
1480 class VBlankDispatcher
: public DeletedAtShutdown
1483 void updateDisplay (VBlankListener
& listener
, HMONITOR monitor
)
1485 if (monitor
== nullptr)
1487 removeListener (listener
);
1491 auto threadWithListener
= threads
.end();
1492 auto threadWithMonitor
= threads
.end();
1494 for (auto it
= threads
.begin(); it
!= threads
.end(); ++it
)
1496 if ((*it
)->hasListener (listener
))
1497 threadWithListener
= it
;
1499 if ((*it
)->getMonitor() == monitor
)
1500 threadWithMonitor
= it
;
1502 if (threadWithListener
!= threads
.end()
1503 && threadWithMonitor
!= threads
.end())
1505 if (threadWithListener
== threadWithMonitor
)
1508 (*threadWithMonitor
)->addListener (listener
);
1510 // This may invalidate iterators, so be careful!
1511 removeListener (threadWithListener
, listener
);
1516 if (threadWithMonitor
!= threads
.end())
1518 (*threadWithMonitor
)->addListener (listener
);
1522 if (threadWithListener
!= threads
.end())
1523 removeListener (threadWithListener
, listener
);
1525 for (auto adapter
: adapters
)
1528 ComSmartPtr
<IDXGIOutput
> output
;
1530 while (adapter
->EnumOutputs (i
, output
.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND
)
1532 if (getMonitorFromOutput (output
) == monitor
)
1534 threads
.emplace_back (std::make_unique
<VSyncThread
> (output
, monitor
, listener
));
1543 void removeListener (const VBlankListener
& listener
)
1545 for (auto it
= threads
.begin(); it
!= threads
.end(); ++it
)
1546 if (removeListener (it
, listener
))
1550 void reconfigureDisplays()
1554 ComSmartPtr
<IDXGIFactory
> factory
;
1555 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
1556 CreateDXGIFactory (__uuidof (IDXGIFactory
), (void**)factory
.resetAndGetPointerAddress());
1557 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
1560 ComSmartPtr
<IDXGIAdapter
> adapter
;
1562 while (factory
->EnumAdapters (i
, adapter
.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND
)
1564 adapters
.push_back (adapter
);
1568 for (auto& thread
: threads
)
1569 thread
->updateMonitor();
1571 threads
.erase (std::remove_if (threads
.begin(),
1573 [] (const auto& thread
) { return thread
->getMonitor() == nullptr; }),
1577 JUCE_DECLARE_SINGLETON_SINGLETHREADED (VBlankDispatcher
, false)
1580 //==============================================================================
1581 using Threads
= std::vector
<std::unique_ptr
<VSyncThread
>>;
1585 reconfigureDisplays();
1588 ~VBlankDispatcher() override
1591 clearSingletonInstance();
1594 // This may delete the corresponding thread and invalidate iterators,
1596 bool removeListener (Threads::iterator it
, const VBlankListener
& listener
)
1598 if ((*it
)->removeListener (listener
))
1600 if ((*it
)->hasNoListeners())
1609 //==============================================================================
1610 std::vector
<ComSmartPtr
<IDXGIAdapter
>> adapters
;
1613 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher
)
1614 JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher
)
1617 JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher
)
1620 //==============================================================================
1621 class HWNDComponentPeer
: public ComponentPeer
,
1623 private VBlankListener
,
1626 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
1627 , public ModifierKeyReceiver
1631 enum RenderingEngineType
1633 softwareRenderingEngine
= 0,
1634 direct2DRenderingEngine
1637 //==============================================================================
1638 HWNDComponentPeer (Component
& comp
, int windowStyleFlags
, HWND parent
, bool nonRepainting
)
1639 : ComponentPeer (comp
, windowStyleFlags
),
1640 dontRepaint (nonRepainting
),
1641 parentToAddTo (parent
),
1642 currentRenderingEngine (softwareRenderingEngine
)
1644 callFunctionIfNotLocked (&createWindowCallback
, this);
1646 setTitle (component
.getName());
1649 getNativeRealtimeModifiers
= []
1651 HWNDComponentPeer::updateKeyModifiers();
1654 if (HWNDComponentPeer::isKeyDown (VK_LBUTTON
)) mouseMods
|= ModifierKeys::leftButtonModifier
;
1655 if (HWNDComponentPeer::isKeyDown (VK_RBUTTON
)) mouseMods
|= ModifierKeys::rightButtonModifier
;
1656 if (HWNDComponentPeer::isKeyDown (VK_MBUTTON
)) mouseMods
|= ModifierKeys::middleButtonModifier
;
1658 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (mouseMods
);
1660 return ModifierKeys::currentModifiers
;
1664 if (updateCurrentMonitor())
1665 VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor
);
1667 updateCurrentMonitor();
1671 ~HWNDComponentPeer() override
1674 VBlankDispatcher::getInstance()->removeListener (*this);
1677 // do this first to avoid messages arriving for this window before it's destroyed
1678 JuceWindowIdentifier::setAsJUCEWindow (hwnd
, false);
1681 if (isAccessibilityActive
)
1682 WindowsAccessibility::revokeUIAMapEntriesForWindow (hwnd
);
1686 currentTouches
.deleteAllTouchesForPeer (this);
1688 callFunctionIfNotLocked (&destroyWindowCallback
, (void*) hwnd
);
1690 if (currentWindowIcon
!= nullptr)
1691 DestroyIcon (currentWindowIcon
);
1693 if (dropTarget
!= nullptr)
1695 dropTarget
->peerIsDeleted
= true;
1696 dropTarget
->Release();
1697 dropTarget
= nullptr;
1701 direct2DContext
= nullptr;
1705 //==============================================================================
1706 void* getNativeHandle() const override
{ return hwnd
; }
1708 void setVisible (bool shouldBeVisible
) override
1710 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1712 ShowWindow (hwnd
, shouldBeVisible
? SW_SHOWNA
: SW_HIDE
);
1714 if (shouldBeVisible
)
1715 InvalidateRect (hwnd
, nullptr, 0);
1720 void setTitle (const String
& title
) override
1722 // Unfortunately some ancient bits of win32 mean you can only perform this operation from the message thread.
1723 JUCE_ASSERT_MESSAGE_THREAD
1725 SetWindowText (hwnd
, title
.toWideCharPointer());
1728 void repaintNowIfTransparent()
1730 if (isUsingUpdateLayeredWindow() && lastPaintTime
> 0 && Time::getMillisecondCounter() > lastPaintTime
+ 30)
1731 handlePaintMessage();
1734 void updateBorderSize()
1737 info
.cbSize
= sizeof (info
);
1739 if (GetWindowInfo (hwnd
, &info
))
1740 windowBorder
= BorderSize
<int> (roundToInt ((info
.rcClient
.top
- info
.rcWindow
.top
) / scaleFactor
),
1741 roundToInt ((info
.rcClient
.left
- info
.rcWindow
.left
) / scaleFactor
),
1742 roundToInt ((info
.rcWindow
.bottom
- info
.rcClient
.bottom
) / scaleFactor
),
1743 roundToInt ((info
.rcWindow
.right
- info
.rcClient
.right
) / scaleFactor
));
1746 if (direct2DContext
!= nullptr)
1747 direct2DContext
->resized();
1751 void setBounds (const Rectangle
<int>& bounds
, bool isNowFullScreen
) override
1753 // If we try to set new bounds while handling an existing position change,
1754 // Windows may get confused about our current scale and size.
1755 // This can happen when moving a window between displays, because the mouse-move
1756 // generator in handlePositionChanged can cause the window to move again.
1757 if (inHandlePositionChanged
)
1760 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1762 fullScreen
= isNowFullScreen
;
1764 auto newBounds
= windowBorder
.addedTo (bounds
);
1766 if (isUsingUpdateLayeredWindow())
1768 if (auto parentHwnd
= GetParent (hwnd
))
1770 auto parentRect
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (parentHwnd
)), hwnd
);
1771 newBounds
.translate (parentRect
.getX(), parentRect
.getY());
1775 auto oldBounds
= getBounds();
1777 const bool hasMoved
= (oldBounds
.getPosition() != bounds
.getPosition());
1778 const bool hasResized
= (oldBounds
.getWidth() != bounds
.getWidth()
1779 || oldBounds
.getHeight() != bounds
.getHeight());
1781 DWORD flags
= SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
;
1782 if (! hasMoved
) flags
|= SWP_NOMOVE
;
1783 if (! hasResized
) flags
|= SWP_NOSIZE
;
1785 setWindowPos (hwnd
, newBounds
, flags
, ! inDpiChange
);
1787 if (hasResized
&& isValidPeer (this))
1790 repaintNowIfTransparent();
1794 Rectangle
<int> getBounds() const override
1796 auto bounds
= [this]
1798 if (parentToAddTo
== nullptr)
1799 return convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd
)), hwnd
);
1801 auto localBounds
= rectangleFromRECT (getWindowClientRect (hwnd
));
1803 if (isPerMonitorDPIAwareWindow (hwnd
))
1804 return (localBounds
.toDouble() / getPlatformScaleFactor()).toNearestInt();
1809 return windowBorder
.subtractedFrom (bounds
);
1812 Point
<int> getScreenPosition() const
1814 auto r
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd
)), hwnd
);
1816 return { r
.getX() + windowBorder
.getLeft(),
1817 r
.getY() + windowBorder
.getTop() };
1820 Point
<float> localToGlobal (Point
<float> relativePosition
) override
{ return relativePosition
+ getScreenPosition().toFloat(); }
1821 Point
<float> globalToLocal (Point
<float> screenPosition
) override
{ return screenPosition
- getScreenPosition().toFloat(); }
1823 using ComponentPeer::localToGlobal
;
1824 using ComponentPeer::globalToLocal
;
1826 void setAlpha (float newAlpha
) override
1828 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1830 auto intAlpha
= (uint8
) jlimit (0, 255, (int) (newAlpha
* 255.0f
));
1832 if (component
.isOpaque())
1834 if (newAlpha
< 1.0f
)
1836 SetWindowLong (hwnd
, GWL_EXSTYLE
, GetWindowLong (hwnd
, GWL_EXSTYLE
) | WS_EX_LAYERED
);
1837 SetLayeredWindowAttributes (hwnd
, RGB (0, 0, 0), intAlpha
, LWA_ALPHA
);
1841 SetWindowLong (hwnd
, GWL_EXSTYLE
, GetWindowLong (hwnd
, GWL_EXSTYLE
) & ~WS_EX_LAYERED
);
1842 RedrawWindow (hwnd
, nullptr, nullptr, RDW_ERASE
| RDW_INVALIDATE
| RDW_FRAME
| RDW_ALLCHILDREN
);
1847 updateLayeredWindowAlpha
= intAlpha
;
1848 component
.repaint();
1852 void setMinimised (bool shouldBeMinimised
) override
1854 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1856 if (shouldBeMinimised
!= isMinimised())
1857 ShowWindow (hwnd
, shouldBeMinimised
? SW_MINIMIZE
: SW_RESTORE
);
1860 bool isMinimised() const override
1863 wp
.length
= sizeof (WINDOWPLACEMENT
);
1864 GetWindowPlacement (hwnd
, &wp
);
1866 return wp
.showCmd
== SW_SHOWMINIMIZED
;
1869 void setFullScreen (bool shouldBeFullScreen
) override
1871 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1873 setMinimised (false);
1875 if (isFullScreen() != shouldBeFullScreen
)
1877 if (constrainer
!= nullptr)
1878 constrainer
->resizeStart();
1880 fullScreen
= shouldBeFullScreen
;
1881 const WeakReference
<Component
> deletionChecker (&component
);
1885 auto boundsCopy
= lastNonFullscreenBounds
;
1888 ShowWindow (hwnd
, SW_SHOWNORMAL
);
1890 if (! boundsCopy
.isEmpty())
1891 setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component
, boundsCopy
), false);
1896 ShowWindow (hwnd
, SW_SHOWMAXIMIZED
);
1898 SendMessageW (hwnd
, WM_SETTINGCHANGE
, 0, 0);
1901 if (deletionChecker
!= nullptr)
1902 handleMovedOrResized();
1904 if (constrainer
!= nullptr)
1905 constrainer
->resizeEnd();
1909 bool isFullScreen() const override
1911 if (! hasTitleBar())
1915 wp
.length
= sizeof (wp
);
1916 GetWindowPlacement (hwnd
, &wp
);
1918 return wp
.showCmd
== SW_SHOWMAXIMIZED
;
1921 bool contains (Point
<int> localPos
, bool trueIfInAChildWindow
) const override
1923 auto r
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd
)), hwnd
);
1925 if (! r
.withZeroOrigin().contains (localPos
))
1928 auto w
= WindowFromPoint (POINTFromPoint (convertLogicalScreenPointToPhysical (localPos
+ getScreenPosition(),
1931 return w
== hwnd
|| (trueIfInAChildWindow
&& (IsChild (hwnd
, w
) != 0));
1934 OptionalBorderSize
getFrameSizeIfPresent() const override
1936 return ComponentPeer::OptionalBorderSize
{ windowBorder
};
1939 BorderSize
<int> getFrameSize() const override
1941 return windowBorder
;
1944 bool setAlwaysOnTop (bool alwaysOnTop
) override
1946 const bool oldDeactivate
= shouldDeactivateTitleBar
;
1947 shouldDeactivateTitleBar
= ((styleFlags
& windowIsTemporary
) == 0);
1949 setWindowZOrder (hwnd
, alwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
);
1951 shouldDeactivateTitleBar
= oldDeactivate
;
1953 if (shadower
!= nullptr)
1954 handleBroughtToFront();
1959 void toFront (bool makeActive
) override
1961 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1963 setMinimised (false);
1965 const bool oldDeactivate
= shouldDeactivateTitleBar
;
1966 shouldDeactivateTitleBar
= ((styleFlags
& windowIsTemporary
) == 0);
1968 callFunctionIfNotLocked (makeActive
? &toFrontCallback1
: &toFrontCallback2
, hwnd
);
1970 shouldDeactivateTitleBar
= oldDeactivate
;
1974 // in this case a broughttofront call won't have occurred, so do it now..
1975 handleBroughtToFront();
1979 void toBehind (ComponentPeer
* other
) override
1981 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
1983 if (auto* otherPeer
= dynamic_cast<HWNDComponentPeer
*> (other
))
1985 setMinimised (false);
1987 // Must be careful not to try to put a topmost window behind a normal one, or Windows
1988 // promotes the normal one to be topmost!
1989 if (component
.isAlwaysOnTop() == otherPeer
->getComponent().isAlwaysOnTop())
1990 setWindowZOrder (hwnd
, otherPeer
->hwnd
);
1991 else if (otherPeer
->getComponent().isAlwaysOnTop())
1992 setWindowZOrder (hwnd
, HWND_TOP
);
1996 jassertfalse
; // wrong type of window?
2000 bool isFocused() const override
2002 return callFunctionIfNotLocked (&getFocusCallback
, nullptr) == (void*) hwnd
;
2005 void grabFocus() override
2007 const ScopedValueSetter
<bool> scope (shouldIgnoreModalDismiss
, true);
2009 const bool oldDeactivate
= shouldDeactivateTitleBar
;
2010 shouldDeactivateTitleBar
= ((styleFlags
& windowIsTemporary
) == 0);
2012 callFunctionIfNotLocked (&setFocusCallback
, hwnd
);
2014 shouldDeactivateTitleBar
= oldDeactivate
;
2017 void textInputRequired (Point
<int>, TextInputTarget
&) override
2019 if (! hasCreatedCaret
)
2020 hasCreatedCaret
= CreateCaret (hwnd
, (HBITMAP
) 1, 0, 0);
2022 if (hasCreatedCaret
)
2028 ImmAssociateContext (hwnd
, nullptr);
2030 // MSVC complains about the nullptr argument, but the docs for this
2031 // function say that the second argument is ignored when the third
2032 // argument is IACE_DEFAULT.
2033 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387)
2034 ImmAssociateContextEx (hwnd
, nullptr, IACE_DEFAULT
);
2035 JUCE_END_IGNORE_WARNINGS_MSVC
2038 void closeInputMethodContext() override
2040 imeHandler
.handleSetContext (hwnd
, false);
2043 void dismissPendingTextInput() override
2045 closeInputMethodContext();
2047 ImmAssociateContext (hwnd
, nullptr);
2049 if (std::exchange (hasCreatedCaret
, false))
2053 void repaint (const Rectangle
<int>& area
) override
2055 deferredRepaints
.add ((area
.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
2058 void dispatchDeferredRepaints()
2060 for (auto deferredRect
: deferredRepaints
)
2062 auto r
= RECTFromRectangle (deferredRect
);
2063 InvalidateRect (hwnd
, &r
, FALSE
);
2066 deferredRepaints
.clear();
2069 void performAnyPendingRepaintsNow() override
2071 if (component
.isVisible())
2073 dispatchDeferredRepaints();
2075 WeakReference
<Component
> localRef (&component
);
2078 if (isUsingUpdateLayeredWindow() || PeekMessage (&m
, hwnd
, WM_PAINT
, WM_PAINT
, PM_REMOVE
))
2079 if (localRef
!= nullptr) // (the PeekMessage call can dispatch messages, which may delete this comp)
2080 handlePaintMessage();
2084 //==============================================================================
2086 void onVBlank() override
2088 dispatchDeferredRepaints();
2092 //==============================================================================
2093 static HWNDComponentPeer
* getOwnerOfWindow (HWND h
) noexcept
2095 if (h
!= nullptr && JuceWindowIdentifier::isJUCEWindow (h
))
2096 return (HWNDComponentPeer
*) GetWindowLongPtr (h
, 8);
2101 //==============================================================================
2102 bool isInside (HWND h
) const noexcept
2104 return GetAncestor (hwnd
, GA_ROOT
) == h
;
2107 //==============================================================================
2108 static bool isKeyDown (const int key
) noexcept
{ return (GetAsyncKeyState (key
) & 0x8000) != 0; }
2110 static void updateKeyModifiers() noexcept
2113 if (isKeyDown (VK_SHIFT
)) keyMods
|= ModifierKeys::shiftModifier
;
2114 if (isKeyDown (VK_CONTROL
)) keyMods
|= ModifierKeys::ctrlModifier
;
2115 if (isKeyDown (VK_MENU
)) keyMods
|= ModifierKeys::altModifier
;
2117 // workaround: Windows maps AltGr to left-Ctrl + right-Alt.
2118 if (isKeyDown (VK_RMENU
) && !isKeyDown (VK_RCONTROL
))
2120 keyMods
= (keyMods
& ~ModifierKeys::ctrlModifier
) | ModifierKeys::altModifier
;
2123 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withOnlyMouseButtons().withFlags (keyMods
);
2126 static void updateModifiersFromWParam (const WPARAM wParam
)
2129 if (wParam
& MK_LBUTTON
) mouseMods
|= ModifierKeys::leftButtonModifier
;
2130 if (wParam
& MK_RBUTTON
) mouseMods
|= ModifierKeys::rightButtonModifier
;
2131 if (wParam
& MK_MBUTTON
) mouseMods
|= ModifierKeys::middleButtonModifier
;
2133 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (mouseMods
);
2134 updateKeyModifiers();
2137 //==============================================================================
2139 static ModifierKeys modifiersAtLastCallback
;
2141 //==============================================================================
2142 struct FileDropTarget
: public ComBaseClassHelper
<IDropTarget
>
2144 FileDropTarget (HWNDComponentPeer
& p
) : peer (p
) {}
2146 JUCE_COMRESULT
DragEnter (IDataObject
* pDataObject
, DWORD grfKeyState
, POINTL mousePos
, DWORD
* pdwEffect
) override
2148 auto hr
= updateFileList (pDataObject
);
2153 return DragOver (grfKeyState
, mousePos
, pdwEffect
);
2156 JUCE_COMRESULT
DragLeave() override
2161 peer
.handleDragExit (dragInfo
);
2165 JUCE_COMRESULT
DragOver (DWORD
/*grfKeyState*/, POINTL mousePos
, DWORD
* pdwEffect
) override
2170 dragInfo
.position
= getMousePos (mousePos
).roundToInt();
2171 *pdwEffect
= peer
.handleDragMove (dragInfo
) ? (DWORD
) DROPEFFECT_COPY
2172 : (DWORD
) DROPEFFECT_NONE
;
2176 JUCE_COMRESULT
Drop (IDataObject
* pDataObject
, DWORD
/*grfKeyState*/, POINTL mousePos
, DWORD
* pdwEffect
) override
2178 auto hr
= updateFileList (pDataObject
);
2183 dragInfo
.position
= getMousePos (mousePos
).roundToInt();
2184 *pdwEffect
= peer
.handleDragDrop (dragInfo
) ? (DWORD
) DROPEFFECT_COPY
2185 : (DWORD
) DROPEFFECT_NONE
;
2189 HWNDComponentPeer
& peer
;
2190 ComponentPeer::DragInfo dragInfo
;
2191 bool peerIsDeleted
= false;
2194 Point
<float> getMousePos (POINTL mousePos
) const
2196 const auto originalPos
= pointFromPOINT ({ mousePos
.x
, mousePos
.y
});
2197 const auto logicalPos
= convertPhysicalScreenPointToLogical (originalPos
, peer
.hwnd
);
2198 return ScalingHelpers::screenPosToLocalPos (peer
.component
, logicalPos
.toFloat());
2203 DroppedData (IDataObject
* dataObject
, CLIPFORMAT type
)
2205 FORMATETC format
= { type
, nullptr, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
2207 if (SUCCEEDED (error
= dataObject
->GetData (&format
, &medium
)) && medium
.hGlobal
!= nullptr)
2209 dataSize
= GlobalSize (medium
.hGlobal
);
2210 data
= GlobalLock (medium
.hGlobal
);
2216 if (data
!= nullptr && medium
.hGlobal
!= nullptr)
2217 GlobalUnlock (medium
.hGlobal
);
2221 STGMEDIUM medium
{ TYMED_HGLOBAL
, { nullptr }, nullptr };
2226 void parseFileList (HDROP dropFiles
)
2228 dragInfo
.files
.clearQuick();
2230 std::vector
<TCHAR
> nameBuffer
;
2232 const auto numFiles
= DragQueryFile (dropFiles
, ~(UINT
) 0, nullptr, 0);
2234 for (UINT i
= 0; i
< numFiles
; ++i
)
2236 const auto bufferSize
= DragQueryFile (dropFiles
, i
, nullptr, 0);
2238 nameBuffer
.resize (bufferSize
+ 1, 0); // + 1 for the null terminator
2240 const auto readCharacters
= DragQueryFile (dropFiles
, i
, nameBuffer
.data(), (UINT
) nameBuffer
.size());
2241 ignoreUnused (readCharacters
);
2242 jassert (readCharacters
== bufferSize
);
2244 dragInfo
.files
.add (String (nameBuffer
.data()));
2248 HRESULT
updateFileList (IDataObject
* const dataObject
)
2256 DroppedData
fileData (dataObject
, CF_HDROP
);
2258 if (SUCCEEDED (fileData
.error
))
2260 parseFileList (static_cast<HDROP
> (fileData
.data
));
2265 DroppedData
textData (dataObject
, CF_UNICODETEXT
);
2267 if (SUCCEEDED (textData
.error
))
2269 dragInfo
.text
= String (CharPointer_UTF16 ((const WCHAR
*) textData
.data
),
2270 CharPointer_UTF16 ((const WCHAR
*) addBytesToPointer (textData
.data
, textData
.dataSize
)));
2274 return textData
.error
;
2277 JUCE_DECLARE_NON_COPYABLE (FileDropTarget
)
2280 static bool offerKeyMessageToJUCEWindow (MSG
& m
)
2282 if (m
.message
== WM_KEYDOWN
|| m
.message
== WM_KEYUP
)
2284 if (Component::getCurrentlyFocusedComponent() != nullptr)
2286 if (auto* peer
= getOwnerOfWindow (m
.hwnd
))
2288 ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter
{ m
.hwnd
};
2290 return m
.message
== WM_KEYDOWN
? peer
->doKeyDown (m
.wParam
)
2291 : peer
->doKeyUp (m
.wParam
);
2299 double getPlatformScaleFactor() const noexcept override
2301 #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
2304 if (! isPerMonitorDPIAwareWindow (hwnd
))
2307 if (auto* parentHWND
= GetParent (hwnd
))
2309 if (auto* parentPeer
= getOwnerOfWindow (parentHWND
))
2310 return parentPeer
->getPlatformScaleFactor();
2312 if (getDPIForWindow
!= nullptr)
2313 return getScaleFactorForWindow (parentHWND
);
2321 HWND hwnd
, parentToAddTo
;
2322 std::unique_ptr
<DropShadower
> shadower
;
2323 RenderingEngineType currentRenderingEngine
;
2325 std::unique_ptr
<Direct2DLowLevelGraphicsContext
> direct2DContext
;
2327 uint32 lastPaintTime
= 0;
2328 ULONGLONG lastMagnifySize
= 0;
2329 bool fullScreen
= false, isDragging
= false, isMouseOver
= false,
2330 hasCreatedCaret
= false, constrainerIsResizing
= false;
2331 BorderSize
<int> windowBorder
;
2332 HICON currentWindowIcon
= nullptr;
2333 FileDropTarget
* dropTarget
= nullptr;
2334 uint8 updateLayeredWindowAlpha
= 255;
2335 UWPUIViewSettings uwpViewSettings
;
2336 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
2337 ModifierKeyProvider
* modProvider
= nullptr;
2340 double scaleFactor
= 1.0;
2341 bool inDpiChange
= 0, inHandlePositionChanged
= 0;
2342 HMONITOR currentMonitor
= nullptr;
2344 bool isAccessibilityActive
= false;
2346 //==============================================================================
2347 static MultiTouchMapper
<DWORD
> currentTouches
;
2349 //==============================================================================
2350 struct TemporaryImage
: private Timer
2354 Image
& getImage (bool transparent
, int w
, int h
)
2356 auto format
= transparent
? Image::ARGB
: Image::RGB
;
2358 if ((! image
.isValid()) || image
.getWidth() < w
|| image
.getHeight() < h
|| image
.getFormat() != format
)
2359 image
= Image (new WindowsBitmapImage (format
, (w
+ 31) & ~31, (h
+ 31) & ~31, false));
2365 void timerCallback() override
2374 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage
)
2377 TemporaryImage offscreenImageGenerator
;
2379 //==============================================================================
2380 class WindowClassHolder
: private DeletedAtShutdown
2385 // this name has to be different for each app/dll instance because otherwise poor old Windows can
2386 // get a bit confused (even despite it not being a process-global window class).
2387 String
windowClassName ("JUCE_");
2388 windowClassName
<< String::toHexString (Time::currentTimeMillis());
2390 auto moduleHandle
= (HINSTANCE
) Process::getCurrentModuleInstanceHandle();
2392 TCHAR moduleFile
[1024] = {};
2393 GetModuleFileName (moduleHandle
, moduleFile
, 1024);
2396 WNDCLASSEX wcex
= {};
2397 wcex
.cbSize
= sizeof (wcex
);
2398 wcex
.style
= CS_OWNDC
;
2399 wcex
.lpfnWndProc
= (WNDPROC
) windowProc
;
2400 wcex
.lpszClassName
= windowClassName
.toWideCharPointer();
2401 wcex
.cbWndExtra
= 32;
2402 wcex
.hInstance
= moduleHandle
;
2403 wcex
.hIcon
= ExtractAssociatedIcon (moduleHandle
, moduleFile
, &iconNum
);
2405 wcex
.hIconSm
= ExtractAssociatedIcon (moduleHandle
, moduleFile
, &iconNum
);
2407 atom
= RegisterClassEx (&wcex
);
2408 jassert (atom
!= 0);
2410 isEventBlockedByModalComps
= checkEventBlockedByModalComps
;
2413 ~WindowClassHolder()
2415 if (ComponentPeer::getNumPeers() == 0)
2416 UnregisterClass (getWindowClassName(), (HINSTANCE
) Process::getCurrentModuleInstanceHandle());
2418 clearSingletonInstance();
2421 LPCTSTR
getWindowClassName() const noexcept
{ return (LPCTSTR
) (pointer_sized_uint
) atom
; }
2423 JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (WindowClassHolder
)
2428 static bool isHWNDBlockedByModalComponents (HWND h
)
2430 for (int i
= Desktop::getInstance().getNumComponents(); --i
>= 0;)
2431 if (auto* c
= Desktop::getInstance().getComponent (i
))
2432 if ((! c
->isCurrentlyBlockedByAnotherModalComponent())
2433 && IsChild ((HWND
) c
->getWindowHandle(), h
))
2439 static bool checkEventBlockedByModalComps (const MSG
& m
)
2441 if (Component::getNumCurrentlyModalComponents() == 0 || JuceWindowIdentifier::isJUCEWindow (m
.hwnd
))
2447 case WM_NCMOUSEMOVE
:
2448 case 0x020A: /* WM_MOUSEWHEEL */
2449 case 0x020E: /* WM_MOUSEHWHEEL */
2457 case WM_MOUSEACTIVATE
:
2458 case WM_NCMOUSEHOVER
:
2461 case WM_POINTERUPDATE
:
2462 case WM_NCPOINTERUPDATE
:
2463 case WM_POINTERWHEEL
:
2464 case WM_POINTERHWHEEL
:
2466 case WM_POINTERACTIVATE
:
2467 return isHWNDBlockedByModalComponents(m
.hwnd
);
2468 case WM_NCLBUTTONDOWN
:
2469 case WM_NCLBUTTONDBLCLK
:
2470 case WM_NCRBUTTONDOWN
:
2471 case WM_NCRBUTTONDBLCLK
:
2472 case WM_NCMBUTTONDOWN
:
2473 case WM_NCMBUTTONDBLCLK
:
2474 case WM_LBUTTONDOWN
:
2475 case WM_LBUTTONDBLCLK
:
2476 case WM_MBUTTONDOWN
:
2477 case WM_MBUTTONDBLCLK
:
2478 case WM_RBUTTONDOWN
:
2479 case WM_RBUTTONDBLCLK
:
2482 case WM_NCPOINTERDOWN
:
2483 case WM_POINTERDOWN
:
2484 if (isHWNDBlockedByModalComponents (m
.hwnd
))
2486 if (auto* modal
= Component::getCurrentlyModalComponent (0))
2487 modal
->inputAttemptWhenModal();
2500 JUCE_DECLARE_NON_COPYABLE (WindowClassHolder
)
2503 //==============================================================================
2504 static void* createWindowCallback (void* userData
)
2506 static_cast<HWNDComponentPeer
*> (userData
)->createWindow();
2513 DWORD type
= WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
2517 type
|= WS_OVERLAPPED
;
2519 if ((styleFlags
& windowHasCloseButton
) != 0)
2525 // annoyingly, windows won't let you have a min/max button without a close button
2526 jassert ((styleFlags
& (windowHasMinimiseButton
| windowHasMaximiseButton
)) == 0);
2529 if ((styleFlags
& windowIsResizable
) != 0)
2530 type
|= WS_THICKFRAME
;
2532 else if (parentToAddTo
!= nullptr)
2538 type
|= WS_POPUP
| WS_SYSMENU
;
2541 if ((styleFlags
& windowAppearsOnTaskbar
) == 0)
2542 exstyle
|= WS_EX_TOOLWINDOW
;
2544 exstyle
|= WS_EX_APPWINDOW
;
2546 if ((styleFlags
& windowHasMinimiseButton
) != 0) type
|= WS_MINIMIZEBOX
;
2547 if ((styleFlags
& windowHasMaximiseButton
) != 0) type
|= WS_MAXIMIZEBOX
;
2548 if ((styleFlags
& windowIgnoresMouseClicks
) != 0) exstyle
|= WS_EX_TRANSPARENT
;
2549 if ((styleFlags
& windowIsSemiTransparent
) != 0) exstyle
|= WS_EX_LAYERED
;
2551 hwnd
= CreateWindowEx (exstyle
, WindowClassHolder::getInstance()->getWindowClassName(),
2552 L
"", type
, 0, 0, 0, 0, parentToAddTo
, nullptr,
2553 (HINSTANCE
) Process::getCurrentModuleInstanceHandle(), nullptr);
2556 // The DPI-awareness context of this window and JUCE's hidden message window are different.
2557 // You normally want these to match otherwise timer events and async messages will happen
2558 // in a different context to normal HWND messages which can cause issues with UI scaling.
2559 jassert (isPerMonitorDPIAwareWindow (hwnd
) == isPerMonitorDPIAwareWindow (juce_messageWindowHandle
)
2560 || isInScopedDPIAwarenessDisabler());
2563 if (hwnd
!= nullptr)
2565 SetWindowLongPtr (hwnd
, 0, 0);
2566 SetWindowLongPtr (hwnd
, 8, (LONG_PTR
) this);
2567 JuceWindowIdentifier::setAsJUCEWindow (hwnd
, true);
2569 if (dropTarget
== nullptr)
2571 HWNDComponentPeer
* peer
= nullptr;
2574 peer
= getOwnerOfWindow (parentToAddTo
);
2576 if (peer
== nullptr)
2579 dropTarget
= new FileDropTarget (*peer
);
2582 RegisterDragDrop (hwnd
, dropTarget
);
2584 if (canUseMultiTouch())
2585 registerTouchWindow (hwnd
, 0);
2589 if (isPerMonitorDPIAwareThread())
2590 scaleFactor
= getScaleFactorForWindow (hwnd
);
2594 checkForPointerAPI();
2596 // This is needed so that our plugin window gets notified of WM_SETTINGCHANGE messages
2597 // and can respond to display scale changes
2598 if (! JUCEApplication::isStandaloneApp())
2599 settingChangeCallback
= ComponentPeer::forceDisplayUpdate
;
2601 // Calling this function here is (for some reason) necessary to make Windows
2602 // correctly enable the menu items that we specify in the wm_initmenu message.
2603 GetSystemMenu (hwnd
, false);
2605 auto alpha
= component
.getAlpha();
2611 TCHAR messageBuffer
[256] = {};
2613 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
2614 nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
2615 messageBuffer
, (DWORD
) numElementsInArray (messageBuffer
) - 1, nullptr);
2617 DBG (messageBuffer
);
2622 static BOOL CALLBACK
revokeChildDragDropCallback (HWND hwnd
, LPARAM
) { RevokeDragDrop (hwnd
); return TRUE
; }
2624 static void* destroyWindowCallback (void* handle
)
2626 auto hwnd
= reinterpret_cast<HWND
> (handle
);
2628 if (IsWindow (hwnd
))
2630 RevokeDragDrop (hwnd
);
2632 // NB: we need to do this before DestroyWindow() as child HWNDs will be invalid after
2633 EnumChildWindows (hwnd
, revokeChildDragDropCallback
, 0);
2635 DestroyWindow (hwnd
);
2641 static void* toFrontCallback1 (void* h
)
2643 BringWindowToTop ((HWND
) h
);
2647 static void* toFrontCallback2 (void* h
)
2649 setWindowZOrder ((HWND
) h
, HWND_TOP
);
2653 static void* setFocusCallback (void* h
)
2655 SetFocus ((HWND
) h
);
2659 static void* getFocusCallback (void*)
2664 bool isUsingUpdateLayeredWindow() const
2666 return ! component
.isOpaque();
2669 bool hasTitleBar() const noexcept
{ return (styleFlags
& windowHasTitleBar
) != 0; }
2671 void updateShadower()
2673 if (! component
.isCurrentlyModal() && (styleFlags
& windowHasDropShadow
) != 0
2674 && ((! hasTitleBar()) || SystemStats::getOperatingSystemType() < SystemStats::WinVista
))
2676 shadower
= component
.getLookAndFeel().createDropShadowerForComponent (component
);
2678 if (shadower
!= nullptr)
2679 shadower
->setOwner (&component
);
2683 void setIcon (const Image
& newIcon
) override
2685 if (auto hicon
= IconConverters::createHICONFromImage (newIcon
, TRUE
, 0, 0))
2687 SendMessage (hwnd
, WM_SETICON
, ICON_BIG
, (LPARAM
) hicon
);
2688 SendMessage (hwnd
, WM_SETICON
, ICON_SMALL
, (LPARAM
) hicon
);
2690 if (currentWindowIcon
!= nullptr)
2691 DestroyIcon (currentWindowIcon
);
2693 currentWindowIcon
= hicon
;
2697 void setMessageFilter()
2699 using ChangeWindowMessageFilterExFunc
= BOOL (WINAPI
*) (HWND
, UINT
, DWORD
, PVOID
);
2701 if (auto changeMessageFilter
= (ChangeWindowMessageFilterExFunc
) getUser32Function ("ChangeWindowMessageFilterEx"))
2703 changeMessageFilter (hwnd
, WM_DROPFILES
, 1 /*MSGFLT_ALLOW*/, nullptr);
2704 changeMessageFilter (hwnd
, WM_COPYDATA
, 1 /*MSGFLT_ALLOW*/, nullptr);
2705 changeMessageFilter (hwnd
, 0x49, 1 /*MSGFLT_ALLOW*/, nullptr);
2709 struct ChildWindowClippingInfo
2712 HWNDComponentPeer
* peer
;
2713 RectangleList
<int>* clip
;
2718 static BOOL CALLBACK
clipChildWindowCallback (HWND hwnd
, LPARAM context
)
2720 if (IsWindowVisible (hwnd
))
2722 auto& info
= *(ChildWindowClippingInfo
*) context
;
2724 if (GetParent (hwnd
) == info
.peer
->hwnd
)
2726 auto clip
= rectangleFromRECT (getWindowClientRect (hwnd
));
2728 info
.clip
->subtract (clip
- info
.origin
);
2730 if (info
.savedDC
== 0)
2731 info
.savedDC
= SaveDC (info
.dc
);
2733 ExcludeClipRect (info
.dc
, clip
.getX(), clip
.getY(), clip
.getRight(), clip
.getBottom());
2740 //==============================================================================
2741 void handlePaintMessage()
2744 if (direct2DContext
!= nullptr)
2748 if (GetUpdateRect (hwnd
, &r
, false))
2750 direct2DContext
->start();
2751 direct2DContext
->clipToRectangle (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r
), hwnd
));
2752 handlePaint (*direct2DContext
);
2753 direct2DContext
->end();
2754 ValidateRect (hwnd
, &r
);
2760 HRGN rgn
= CreateRectRgn (0, 0, 0, 0);
2761 const int regionType
= GetUpdateRgn (hwnd
, rgn
, false);
2763 PAINTSTRUCT paintStruct
;
2764 HDC dc
= BeginPaint (hwnd
, &paintStruct
); // Note this can immediately generate a WM_NCPAINT
2765 // message and become re-entrant, but that's OK
2767 // if something in a paint handler calls, e.g. a message box, this can become reentrant and
2768 // corrupt the image it's using to paint into, so do a check here.
2769 static bool reentrant
= false;
2773 const ScopedValueSetter
<bool> setter (reentrant
, true, false);
2776 component
.handleCommandMessage (0); // (this triggers a repaint in the openGL context)
2778 performPaint (dc
, rgn
, regionType
, paintStruct
);
2782 EndPaint (hwnd
, &paintStruct
);
2785 _fpreset(); // because some graphics cards can unmask FP exceptions
2790 lastPaintTime
= Time::getMillisecondCounter();
2793 void performPaint (HDC dc
, HRGN rgn
, int regionType
, PAINTSTRUCT
& paintStruct
)
2795 int x
= paintStruct
.rcPaint
.left
;
2796 int y
= paintStruct
.rcPaint
.top
;
2797 int w
= paintStruct
.rcPaint
.right
- x
;
2798 int h
= paintStruct
.rcPaint
.bottom
- y
;
2800 const bool transparent
= isUsingUpdateLayeredWindow();
2804 // it's not possible to have a transparent window with a title bar at the moment!
2805 jassert (! hasTitleBar());
2807 auto r
= getWindowScreenRect (hwnd
);
2809 w
= r
.right
- r
.left
;
2810 h
= r
.bottom
- r
.top
;
2815 Image
& offscreenImage
= offscreenImageGenerator
.getImage (transparent
, w
, h
);
2817 RectangleList
<int> contextClip
;
2818 const Rectangle
<int> clipBounds (w
, h
);
2820 bool needToPaintAll
= true;
2822 if (regionType
== COMPLEXREGION
&& ! transparent
)
2824 HRGN clipRgn
= CreateRectRgnIndirect (&paintStruct
.rcPaint
);
2825 CombineRgn (rgn
, rgn
, clipRgn
, RGN_AND
);
2826 DeleteObject (clipRgn
);
2828 std::aligned_storage
<8192, alignof (RGNDATA
)>::type rgnData
;
2829 const DWORD res
= GetRegionData (rgn
, sizeof (rgnData
), (RGNDATA
*) &rgnData
);
2831 if (res
> 0 && res
<= sizeof (rgnData
))
2833 const RGNDATAHEADER
* const hdr
= &(((const RGNDATA
*) &rgnData
)->rdh
);
2835 if (hdr
->iType
== RDH_RECTANGLES
2836 && hdr
->rcBound
.right
- hdr
->rcBound
.left
>= w
2837 && hdr
->rcBound
.bottom
- hdr
->rcBound
.top
>= h
)
2839 needToPaintAll
= false;
2841 auto rects
= unalignedPointerCast
<const RECT
*> ((char*) &rgnData
+ sizeof (RGNDATAHEADER
));
2843 for (int i
= (int) ((RGNDATA
*) &rgnData
)->rdh
.nCount
; --i
>= 0;)
2845 if (rects
->right
<= x
+ w
&& rects
->bottom
<= y
+ h
)
2847 const int cx
= jmax (x
, (int) rects
->left
);
2848 contextClip
.addWithoutMerging (Rectangle
<int> (cx
- x
, rects
->top
- y
,
2849 rects
->right
- cx
, rects
->bottom
- rects
->top
)
2850 .getIntersection (clipBounds
));
2854 needToPaintAll
= true;
2866 contextClip
.clear();
2867 contextClip
.addWithoutMerging (Rectangle
<int> (w
, h
));
2870 ChildWindowClippingInfo childClipInfo
= { dc
, this, &contextClip
, Point
<int> (x
, y
), 0 };
2871 EnumChildWindows (hwnd
, clipChildWindowCallback
, (LPARAM
) &childClipInfo
);
2873 if (! contextClip
.isEmpty())
2876 for (auto& i
: contextClip
)
2877 offscreenImage
.clear (i
);
2880 auto context
= component
.getLookAndFeel()
2881 .createGraphicsContext (offscreenImage
, { -x
, -y
}, contextClip
);
2883 context
->addTransform (AffineTransform::scale ((float) getPlatformScaleFactor()));
2884 handlePaint (*context
);
2887 static_cast<WindowsBitmapImage
*> (offscreenImage
.getPixelData())
2888 ->blitToWindow (hwnd
, dc
, transparent
, x
, y
, updateLayeredWindowAlpha
);
2891 if (childClipInfo
.savedDC
!= 0)
2892 RestoreDC (dc
, childClipInfo
.savedDC
);
2896 //==============================================================================
2897 void doMouseEvent (Point
<float> position
, float pressure
, float orientation
= 0.0f
, ModifierKeys mods
= ModifierKeys::currentModifiers
)
2899 handleMouseEvent (MouseInputSource::InputSourceType::mouse
, position
, mods
, pressure
, orientation
, getMouseEventTime());
2902 StringArray
getAvailableRenderingEngines() override
2904 StringArray
s ("Software Renderer");
2907 if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7
)
2914 int getCurrentRenderingEngine() const override
{ return currentRenderingEngine
; }
2917 void updateDirect2DContext()
2919 if (currentRenderingEngine
!= direct2DRenderingEngine
)
2920 direct2DContext
= nullptr;
2921 else if (direct2DContext
== nullptr)
2922 direct2DContext
.reset (new Direct2DLowLevelGraphicsContext (hwnd
));
2926 void setCurrentRenderingEngine (int index
) override
2928 ignoreUnused (index
);
2931 if (getAvailableRenderingEngines().size() > 1)
2933 currentRenderingEngine
= index
== 1 ? direct2DRenderingEngine
: softwareRenderingEngine
;
2934 updateDirect2DContext();
2935 repaint (component
.getLocalBounds());
2940 static uint32
getMinTimeBetweenMouseMoves()
2942 if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista
)
2945 return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP..
2948 bool isTouchEvent() noexcept
2950 if (registerTouchWindow
== nullptr)
2953 // Relevant info about touch/pen detection flags:
2954 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
2955 // http://www.petertissen.de/?p=4
2957 return ((uint32_t) GetMessageExtraInfo() & 0xFFFFFF80 /*SIGNATURE_MASK*/) == 0xFF515780 /*MI_WP_SIGNATURE*/;
2960 static bool areOtherTouchSourcesActive()
2962 for (auto& ms
: Desktop::getInstance().getMouseSources())
2963 if (ms
.isDragging() && (ms
.getType() == MouseInputSource::InputSourceType::touch
2964 || ms
.getType() == MouseInputSource::InputSourceType::pen
))
2970 void doMouseMove (Point
<float> position
, bool isMouseDownEvent
)
2972 ModifierKeys
modsToSend (ModifierKeys::currentModifiers
);
2974 // this will be handled by WM_TOUCH
2975 if (isTouchEvent() || areOtherTouchSourcesActive())
2982 // This avoids a rare stuck-button problem when focus is lost unexpectedly, but must
2983 // not be called as part of a move, in case it's actually a mouse-drag from another
2984 // app which ends up here when we get focus before the mouse is released..
2985 if (isMouseDownEvent
&& getNativeRealtimeModifiers
!= nullptr)
2986 getNativeRealtimeModifiers();
2988 updateKeyModifiers();
2990 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
2991 if (modProvider
!= nullptr)
2992 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withFlags (modProvider
->getWin32Modifiers());
2995 TRACKMOUSEEVENT tme
;
2996 tme
.cbSize
= sizeof (tme
);
2997 tme
.dwFlags
= TME_LEAVE
;
2998 tme
.hwndTrack
= hwnd
;
2999 tme
.dwHoverTime
= 0;
3001 if (! TrackMouseEvent (&tme
))
3004 Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
3006 else if (! isDragging
)
3008 if (! contains (position
.roundToInt(), false))
3012 static uint32 lastMouseTime
= 0;
3013 static auto minTimeBetweenMouses
= getMinTimeBetweenMouseMoves();
3014 auto now
= Time::getMillisecondCounter();
3016 if (! Desktop::getInstance().getMainMouseSource().isDragging())
3017 modsToSend
= modsToSend
.withoutMouseButtons();
3019 if (now
>= lastMouseTime
+ minTimeBetweenMouses
)
3021 lastMouseTime
= now
;
3022 doMouseEvent (position
, MouseInputSource::defaultPressure
,
3023 MouseInputSource::defaultOrientation
, modsToSend
);
3027 void doMouseDown (Point
<float> position
, const WPARAM wParam
)
3029 // this will be handled by WM_TOUCH
3030 if (isTouchEvent() || areOtherTouchSourcesActive())
3033 if (GetCapture() != hwnd
)
3036 doMouseMove (position
, true);
3038 if (isValidPeer (this))
3040 updateModifiersFromWParam (wParam
);
3042 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
3043 if (modProvider
!= nullptr)
3044 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withFlags (modProvider
->getWin32Modifiers());
3049 doMouseEvent (position
, MouseInputSource::defaultPressure
);
3053 void doMouseUp (Point
<float> position
, const WPARAM wParam
)
3055 // this will be handled by WM_TOUCH
3056 if (isTouchEvent() || areOtherTouchSourcesActive())
3059 updateModifiersFromWParam (wParam
);
3061 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
3062 if (modProvider
!= nullptr)
3063 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withFlags (modProvider
->getWin32Modifiers());
3066 const bool wasDragging
= isDragging
;
3069 // release the mouse capture if the user has released all buttons
3070 if ((wParam
& (MK_LBUTTON
| MK_RBUTTON
| MK_MBUTTON
)) == 0 && hwnd
== GetCapture())
3073 // NB: under some circumstances (e.g. double-clicking a native title bar), a mouse-up can
3074 // arrive without a mouse-down, so in that case we need to avoid sending a message.
3076 doMouseEvent (position
, MouseInputSource::defaultPressure
);
3079 void doCaptureChanged()
3081 if (constrainerIsResizing
)
3083 if (constrainer
!= nullptr)
3084 constrainer
->resizeEnd();
3086 constrainerIsResizing
= false;
3090 doMouseUp (getCurrentMousePos(), (WPARAM
) 0);
3095 isMouseOver
= false;
3097 if (! areOtherTouchSourcesActive())
3098 doMouseEvent (getCurrentMousePos(), MouseInputSource::defaultPressure
);
3101 ComponentPeer
* findPeerUnderMouse (Point
<float>& localPos
)
3103 auto currentMousePos
= getPOINTFromLParam ((LPARAM
) GetMessagePos());
3105 // Because Windows stupidly sends all wheel events to the window with the keyboard
3106 // focus, we have to redirect them here according to the mouse pos..
3107 auto* peer
= getOwnerOfWindow (WindowFromPoint (currentMousePos
));
3109 if (peer
== nullptr)
3112 localPos
= peer
->globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (currentMousePos
), hwnd
).toFloat());
3116 static MouseInputSource::InputSourceType
getPointerType (WPARAM wParam
)
3118 if (getPointerTypeFunction
!= nullptr)
3120 POINTER_INPUT_TYPE pointerType
;
3122 if (getPointerTypeFunction (GET_POINTERID_WPARAM (wParam
), &pointerType
))
3124 if (pointerType
== 2)
3125 return MouseInputSource::InputSourceType::touch
;
3127 if (pointerType
== 3)
3128 return MouseInputSource::InputSourceType::pen
;
3132 return MouseInputSource::InputSourceType::mouse
;
3135 void doMouseWheel (const WPARAM wParam
, const bool isVertical
)
3137 updateKeyModifiers();
3138 const float amount
= jlimit (-1000.0f
, 1000.0f
, 0.5f
* (short) HIWORD (wParam
));
3140 MouseWheelDetails wheel
;
3141 wheel
.deltaX
= isVertical
? 0.0f
: amount
/ -256.0f
;
3142 wheel
.deltaY
= isVertical
? amount
/ 256.0f
: 0.0f
;
3143 wheel
.isReversed
= false;
3144 wheel
.isSmooth
= false;
3145 wheel
.isInertial
= false;
3147 Point
<float> localPos
;
3149 if (auto* peer
= findPeerUnderMouse (localPos
))
3150 peer
->handleMouseWheel (getPointerType (wParam
), localPos
, getMouseEventTime(), wheel
);
3153 bool doGestureEvent (LPARAM lParam
)
3157 gi
.cbSize
= sizeof (gi
);
3159 if (getGestureInfo
!= nullptr && getGestureInfo ((HGESTUREINFO
) lParam
, &gi
))
3161 updateKeyModifiers();
3162 Point
<float> localPos
;
3164 if (auto* peer
= findPeerUnderMouse (localPos
))
3168 case 3: /*GID_ZOOM*/
3169 if (gi
.dwFlags
!= 1 /*GF_BEGIN*/ && lastMagnifySize
> 0)
3170 peer
->handleMagnifyGesture (MouseInputSource::InputSourceType::touch
, localPos
, getMouseEventTime(),
3171 (float) ((double) gi
.ullArguments
/ (double) lastMagnifySize
));
3173 lastMagnifySize
= gi
.ullArguments
;
3177 case 5: /*GID_ROTATE*/
3178 case 6: /*GID_TWOFINGERTAP*/
3179 case 7: /*GID_PRESSANDTAP*/
3189 LRESULT
doTouchEvent (const int numInputs
, HTOUCHINPUT eventHandle
)
3191 if ((styleFlags
& windowIgnoresMouseClicks
) != 0)
3192 if (auto* parent
= getOwnerOfWindow (GetParent (hwnd
)))
3194 return parent
->doTouchEvent (numInputs
, eventHandle
);
3196 HeapBlock
<TOUCHINPUT
> inputInfo (numInputs
);
3198 if (getTouchInputInfo (eventHandle
, (UINT
) numInputs
, inputInfo
, sizeof (TOUCHINPUT
)))
3200 for (int i
= 0; i
< numInputs
; ++i
)
3202 auto flags
= inputInfo
[i
].dwFlags
;
3204 if ((flags
& (TOUCHEVENTF_DOWN
| TOUCHEVENTF_MOVE
| TOUCHEVENTF_UP
)) != 0)
3205 if (! handleTouchInput (inputInfo
[i
], (flags
& TOUCHEVENTF_DOWN
) != 0, (flags
& TOUCHEVENTF_UP
) != 0))
3206 return 0; // abandon method if this window was deleted by the callback
3210 closeTouchInputHandle (eventHandle
);
3214 bool handleTouchInput (const TOUCHINPUT
& touch
, const bool isDown
, const bool isUp
,
3215 const float touchPressure
= MouseInputSource::defaultPressure
,
3216 const float orientation
= 0.0f
)
3218 auto isCancel
= false;
3220 const auto touchIndex
= currentTouches
.getIndexOfTouch (this, touch
.dwID
);
3221 const auto time
= getMouseEventTime();
3222 const auto pos
= globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT ({ roundToInt (touch
.x
/ 100.0f
),
3223 roundToInt (touch
.y
/ 100.0f
) }), hwnd
).toFloat());
3224 const auto pressure
= touchPressure
;
3225 auto modsToSend
= ModifierKeys::currentModifiers
;
3229 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier
);
3230 modsToSend
= ModifierKeys::currentModifiers
;
3232 // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
3233 handleMouseEvent (MouseInputSource::InputSourceType::touch
, pos
, modsToSend
.withoutMouseButtons(),
3234 pressure
, orientation
, time
, {}, touchIndex
);
3236 if (! isValidPeer (this)) // (in case this component was deleted by the event)
3241 modsToSend
= modsToSend
.withoutMouseButtons();
3242 ModifierKeys::currentModifiers
= modsToSend
;
3243 currentTouches
.clearTouch (touchIndex
);
3245 if (! currentTouches
.areAnyTouchesActive())
3250 modsToSend
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier
);
3253 handleMouseEvent (MouseInputSource::InputSourceType::touch
, pos
, modsToSend
,
3254 pressure
, orientation
, time
, {}, touchIndex
);
3256 if (! isValidPeer (this))
3261 handleMouseEvent (MouseInputSource::InputSourceType::touch
, MouseInputSource::offscreenMousePos
, ModifierKeys::currentModifiers
.withoutMouseButtons(),
3262 pressure
, orientation
, time
, {}, touchIndex
);
3264 if (! isValidPeer (this))
3269 currentTouches
.clear();
3270 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons();
3277 bool handlePointerInput (WPARAM wParam
, LPARAM lParam
, const bool isDown
, const bool isUp
)
3279 if (! canUsePointerAPI
)
3282 auto pointerType
= getPointerType (wParam
);
3284 if (pointerType
== MouseInputSource::InputSourceType::touch
)
3286 POINTER_TOUCH_INFO touchInfo
;
3288 if (! getPointerTouchInfo (GET_POINTERID_WPARAM (wParam
), &touchInfo
))
3291 const auto pressure
= touchInfo
.touchMask
& TOUCH_MASK_PRESSURE
? static_cast<float> (touchInfo
.pressure
)
3292 : MouseInputSource::defaultPressure
;
3293 const auto orientation
= touchInfo
.touchMask
& TOUCH_MASK_ORIENTATION
? degreesToRadians (static_cast<float> (touchInfo
.orientation
))
3294 : MouseInputSource::defaultOrientation
;
3296 if (! handleTouchInput (emulateTouchEventFromPointer (touchInfo
.pointerInfo
.ptPixelLocationRaw
, wParam
),
3297 isDown
, isUp
, pressure
, orientation
))
3300 else if (pointerType
== MouseInputSource::InputSourceType::pen
)
3302 POINTER_PEN_INFO penInfo
;
3304 if (! getPointerPenInfo (GET_POINTERID_WPARAM (wParam
), &penInfo
))
3307 const auto pressure
= (penInfo
.penMask
& PEN_MASK_PRESSURE
) ? (float) penInfo
.pressure
/ 1024.0f
: MouseInputSource::defaultPressure
;
3309 if (! handlePenInput (penInfo
, globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam (lParam
)), hwnd
).toFloat()),
3310 pressure
, isDown
, isUp
))
3321 TOUCHINPUT
emulateTouchEventFromPointer (POINT p
, WPARAM wParam
)
3323 TOUCHINPUT touchInput
;
3325 touchInput
.dwID
= GET_POINTERID_WPARAM (wParam
);
3326 touchInput
.x
= p
.x
* 100;
3327 touchInput
.y
= p
.y
* 100;
3332 bool handlePenInput (POINTER_PEN_INFO penInfo
, Point
<float> pos
, const float pressure
, bool isDown
, bool isUp
)
3334 const auto time
= getMouseEventTime();
3335 ModifierKeys
modsToSend (ModifierKeys::currentModifiers
);
3336 PenDetails penDetails
;
3338 penDetails
.rotation
= (penInfo
.penMask
& PEN_MASK_ROTATION
) ? degreesToRadians (static_cast<float> (penInfo
.rotation
)) : MouseInputSource::defaultRotation
;
3339 penDetails
.tiltX
= (penInfo
.penMask
& PEN_MASK_TILT_X
) ? (float) penInfo
.tiltX
/ 90.0f
: MouseInputSource::defaultTiltX
;
3340 penDetails
.tiltY
= (penInfo
.penMask
& PEN_MASK_TILT_Y
) ? (float) penInfo
.tiltY
/ 90.0f
: MouseInputSource::defaultTiltY
;
3342 auto pInfoFlags
= penInfo
.pointerInfo
.pointerFlags
;
3344 if ((pInfoFlags
& POINTER_FLAG_FIRSTBUTTON
) != 0)
3345 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier
);
3346 else if ((pInfoFlags
& POINTER_FLAG_SECONDBUTTON
) != 0)
3347 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons().withFlags (ModifierKeys::rightButtonModifier
);
3351 modsToSend
= ModifierKeys::currentModifiers
;
3353 // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
3354 handleMouseEvent (MouseInputSource::InputSourceType::pen
, pos
, modsToSend
.withoutMouseButtons(),
3355 pressure
, MouseInputSource::defaultOrientation
, time
, penDetails
);
3357 if (! isValidPeer (this)) // (in case this component was deleted by the event)
3360 else if (isUp
|| ! (pInfoFlags
& POINTER_FLAG_INCONTACT
))
3362 modsToSend
= modsToSend
.withoutMouseButtons();
3363 ModifierKeys::currentModifiers
= ModifierKeys::currentModifiers
.withoutMouseButtons();
3366 handleMouseEvent (MouseInputSource::InputSourceType::pen
, pos
, modsToSend
, pressure
,
3367 MouseInputSource::defaultOrientation
, time
, penDetails
);
3369 if (! isValidPeer (this)) // (in case this component was deleted by the event)
3374 handleMouseEvent (MouseInputSource::InputSourceType::pen
, MouseInputSource::offscreenMousePos
, ModifierKeys::currentModifiers
,
3375 pressure
, MouseInputSource::defaultOrientation
, time
, penDetails
);
3377 if (! isValidPeer (this))
3384 //==============================================================================
3385 void sendModifierKeyChangeIfNeeded()
3387 if (modifiersAtLastCallback
!= ModifierKeys::currentModifiers
)
3389 modifiersAtLastCallback
= ModifierKeys::currentModifiers
;
3390 handleModifierKeysChange();
3394 bool doKeyUp (const WPARAM key
)
3396 updateKeyModifiers();
3415 sendModifierKeyChangeIfNeeded();
3418 return handleKeyUpOrDown (false)
3419 || Component::getCurrentlyModalComponent() != nullptr;
3422 bool doKeyDown (const WPARAM key
)
3424 updateKeyModifiers();
3444 used
= handleKeyUpOrDown (true);
3445 sendModifierKeyChangeIfNeeded();
3482 used
= handleKeyUpOrDown (true);
3483 used
= handleKeyPress (extendedKeyModifier
| (int) key
, 0) || used
;
3487 used
= handleKeyUpOrDown (true);
3491 if (! PeekMessage (&msg
, hwnd
, WM_CHAR
, WM_DEADCHAR
, PM_NOREMOVE
))
3493 // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
3494 // manually generate the key-press event that matches this key-down.
3495 const UINT keyChar
= MapVirtualKey ((UINT
) key
, 2);
3496 const UINT scanCode
= MapVirtualKey ((UINT
) key
, 0);
3498 ignoreUnused (GetKeyboardState (keyState
));
3500 WCHAR text
[16] = { 0 };
3501 if (ToUnicode ((UINT
) key
, scanCode
, keyState
, text
, 8, 0) != 1)
3504 used
= handleKeyPress ((int) LOWORD (keyChar
), (juce_wchar
) text
[0]) || used
;
3511 return used
|| (Component::getCurrentlyModalComponent() != nullptr);
3514 bool doKeyChar (int key
, const LPARAM flags
)
3516 updateKeyModifiers();
3518 auto textChar
= (juce_wchar
) key
;
3519 const int virtualScanCode
= (flags
>> 16) & 0xff;
3521 if (key
>= '0' && key
<= '9')
3523 switch (virtualScanCode
) // check for a numeric keypad scan-code
3535 key
= (key
- '0') + KeyPress::numberPad0
;
3543 // convert the scan code to an unmodified character code..
3544 const UINT virtualKey
= MapVirtualKey ((UINT
) virtualScanCode
, 1);
3545 UINT keyChar
= MapVirtualKey (virtualKey
, 2);
3547 keyChar
= LOWORD (keyChar
);
3550 key
= (int) keyChar
;
3552 // avoid sending junk text characters for some control-key combinations
3553 if (textChar
< ' ' && ModifierKeys::currentModifiers
.testFlags (ModifierKeys::ctrlModifier
| ModifierKeys::altModifier
))
3557 return handleKeyPress (key
, textChar
);
3560 void forwardMessageToParent (UINT message
, WPARAM wParam
, LPARAM lParam
) const
3562 if (HWND parentH
= GetParent (hwnd
))
3563 PostMessage (parentH
, message
, wParam
, lParam
);
3566 bool doAppCommand (const LPARAM lParam
)
3570 switch (GET_APPCOMMAND_LPARAM (lParam
))
3572 case APPCOMMAND_MEDIA_PLAY_PAUSE
: key
= KeyPress::playKey
; break;
3573 case APPCOMMAND_MEDIA_STOP
: key
= KeyPress::stopKey
; break;
3574 case APPCOMMAND_MEDIA_NEXTTRACK
: key
= KeyPress::fastForwardKey
; break;
3575 case APPCOMMAND_MEDIA_PREVIOUSTRACK
: key
= KeyPress::rewindKey
; break;
3581 updateKeyModifiers();
3583 if (hwnd
== GetActiveWindow())
3584 return handleKeyPress (key
, 0);
3590 bool isConstrainedNativeWindow() const
3592 return constrainer
!= nullptr
3593 && (styleFlags
& (windowHasTitleBar
| windowIsResizable
)) == (windowHasTitleBar
| windowIsResizable
)
3597 Rectangle
<int> getCurrentScaledBounds() const
3599 return ScalingHelpers::unscaledScreenPosToScaled (component
, windowBorder
.addedTo (ScalingHelpers::scaledScreenPosToUnscaled (component
, component
.getBounds())));
3602 LRESULT
handleSizeConstraining (RECT
& r
, const WPARAM wParam
)
3604 if (isConstrainedNativeWindow())
3606 const auto logicalBounds
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r
).toFloat(), hwnd
);
3607 auto pos
= ScalingHelpers::unscaledScreenPosToScaled (component
, logicalBounds
).toNearestInt();
3609 const auto original
= getCurrentScaledBounds();
3611 constrainer
->checkBounds (pos
, original
,
3612 Desktop::getInstance().getDisplays().getTotalBounds (true),
3613 wParam
== WMSZ_TOP
|| wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_TOPRIGHT
,
3614 wParam
== WMSZ_LEFT
|| wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_BOTTOMLEFT
,
3615 wParam
== WMSZ_BOTTOM
|| wParam
== WMSZ_BOTTOMLEFT
|| wParam
== WMSZ_BOTTOMRIGHT
,
3616 wParam
== WMSZ_RIGHT
|| wParam
== WMSZ_TOPRIGHT
|| wParam
== WMSZ_BOTTOMRIGHT
);
3618 r
= RECTFromRectangle (convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component
, pos
.toFloat()).toNearestInt(), hwnd
));
3624 LRESULT
handlePositionChanging (WINDOWPOS
& wp
)
3626 if (isConstrainedNativeWindow() && ! isFullScreen())
3628 if ((wp
.flags
& (SWP_NOMOVE
| SWP_NOSIZE
)) != (SWP_NOMOVE
| SWP_NOSIZE
)
3629 && (wp
.x
> -32000 && wp
.y
> -32000)
3630 && ! Component::isMouseButtonDownAnywhere())
3632 const auto logicalBounds
= convertPhysicalScreenRectangleToLogical (rectangleFromRECT ({ wp
.x
, wp
.y
, wp
.x
+ wp
.cx
, wp
.y
+ wp
.cy
}).toFloat(), hwnd
);
3633 auto pos
= ScalingHelpers::unscaledScreenPosToScaled (component
, logicalBounds
).toNearestInt();
3635 const auto original
= getCurrentScaledBounds();
3637 constrainer
->checkBounds (pos
, original
,
3638 Desktop::getInstance().getDisplays().getTotalBounds (true),
3639 pos
.getY() != original
.getY() && pos
.getBottom() == original
.getBottom(),
3640 pos
.getX() != original
.getX() && pos
.getRight() == original
.getRight(),
3641 pos
.getY() == original
.getY() && pos
.getBottom() != original
.getBottom(),
3642 pos
.getX() == original
.getX() && pos
.getRight() != original
.getRight());
3644 auto physicalBounds
= convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component
, pos
.toFloat()), hwnd
);
3646 auto getNewPositionIfNotRoundingError
= [] (int posIn
, float newPos
)
3648 return (std::abs ((float) posIn
- newPos
) >= 1.0f
) ? roundToInt (newPos
) : posIn
;
3651 wp
.x
= getNewPositionIfNotRoundingError (wp
.x
, physicalBounds
.getX());
3652 wp
.y
= getNewPositionIfNotRoundingError (wp
.y
, physicalBounds
.getY());
3653 wp
.cx
= getNewPositionIfNotRoundingError (wp
.cx
, physicalBounds
.getWidth());
3654 wp
.cy
= getNewPositionIfNotRoundingError (wp
.cy
, physicalBounds
.getHeight());
3658 if (((wp
.flags
& SWP_SHOWWINDOW
) != 0 && ! component
.isVisible()))
3659 component
.setVisible (true);
3660 else if (((wp
.flags
& SWP_HIDEWINDOW
) != 0 && component
.isVisible()))
3661 component
.setVisible (false);
3666 bool updateCurrentMonitor()
3668 auto monitor
= MonitorFromWindow (hwnd
, MONITOR_DEFAULTTONULL
);
3669 return std::exchange (currentMonitor
, monitor
) != monitor
;
3672 bool handlePositionChanged()
3674 auto pos
= getCurrentMousePos();
3676 if (contains (pos
.roundToInt(), false))
3678 const ScopedValueSetter
<bool> scope (inHandlePositionChanged
, true);
3680 if (! areOtherTouchSourcesActive())
3681 doMouseEvent (pos
, MouseInputSource::defaultPressure
);
3683 if (! isValidPeer (this))
3687 handleMovedOrResized();
3690 if (updateCurrentMonitor())
3691 VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor
);
3693 updateCurrentMonitor();
3696 return ! dontRepaint
; // to allow non-accelerated openGL windows to draw themselves correctly.
3699 //==============================================================================
3700 LRESULT
handleDPIChanging (int newDPI
, RECT newRect
)
3702 // Sometimes, windows that should not be automatically scaled (secondary windows in plugins)
3703 // are sent WM_DPICHANGED. The size suggested by the OS is incorrect for our unscaled
3704 // window, so we should ignore it.
3705 if (! isPerMonitorDPIAwareWindow (hwnd
))
3708 const auto newScale
= (double) newDPI
/ USER_DEFAULT_SCREEN_DPI
;
3710 if (approximatelyEqual (scaleFactor
, newScale
))
3713 scaleFactor
= newScale
;
3716 const ScopedValueSetter
<bool> setter (inDpiChange
, true);
3721 newRect
.right
- newRect
.left
,
3722 newRect
.bottom
- newRect
.top
,
3723 SWP_NOZORDER
| SWP_NOACTIVATE
);
3726 // This is to handle reentrancy. If responding to a DPI change triggers further DPI changes,
3727 // we should only notify listeners and resize windows once all of the DPI changes have
3731 // Danger! Re-entrant call to handleDPIChanging.
3732 // Please report this issue on the JUCE forum, along with instructions
3733 // so that a JUCE developer can reproduce the issue.
3739 InvalidateRect (hwnd
, nullptr, FALSE
);
3741 scaleFactorListeners
.call ([this] (ScaleFactorListener
& l
) { l
.nativeScaleFactorChanged (scaleFactor
); });
3746 //==============================================================================
3747 void handleAppActivation (const WPARAM wParam
)
3749 modifiersAtLastCallback
= -1;
3750 updateKeyModifiers();
3754 component
.repaint();
3755 handleMovedOrResized();
3757 if (! isValidPeer (this))
3761 auto* underMouse
= component
.getComponentAt (component
.getMouseXYRelative());
3763 if (underMouse
== nullptr)
3764 underMouse
= &component
;
3766 if (underMouse
->isCurrentlyBlockedByAnotherModalComponent())
3768 if (LOWORD (wParam
) == WA_CLICKACTIVE
)
3769 Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
3771 ModalComponentManager::getInstance()->bringModalComponentsToFront();
3775 handleBroughtToFront();
3779 void handlePowerBroadcast (WPARAM wParam
)
3781 if (auto* app
= JUCEApplicationBase::getInstance())
3785 case PBT_APMSUSPEND
: app
->suspended(); break;
3787 case PBT_APMQUERYSUSPENDFAILED
:
3788 case PBT_APMRESUMECRITICAL
:
3789 case PBT_APMRESUMESUSPEND
:
3790 case PBT_APMRESUMEAUTOMATIC
: app
->resumed(); break;
3797 void handleLeftClickInNCArea (WPARAM wParam
)
3799 if (! sendInputAttemptWhenModalMessage())
3812 if (isConstrainedNativeWindow())
3814 constrainerIsResizing
= true;
3815 constrainer
->resizeStart();
3825 void initialiseSysMenu (HMENU menu
) const
3827 if (! hasTitleBar())
3831 EnableMenuItem (menu
, SC_RESTORE
, MF_BYCOMMAND
| MF_ENABLED
);
3832 EnableMenuItem (menu
, SC_MOVE
, MF_BYCOMMAND
| MF_GRAYED
);
3834 else if (! isMinimised())
3836 EnableMenuItem (menu
, SC_MAXIMIZE
, MF_BYCOMMAND
| MF_GRAYED
);
3841 void doSettingChange()
3843 forceDisplayUpdate();
3845 if (fullScreen
&& ! isMinimised())
3846 setWindowPos (hwnd
, ScalingHelpers::scaledScreenPosToUnscaled (component
, Desktop::getInstance().getDisplays()
3847 .getDisplayForRect (component
.getScreenBounds())->userArea
),
3848 SWP_NOACTIVATE
| SWP_NOOWNERZORDER
| SWP_NOZORDER
| SWP_NOSENDCHANGING
);
3851 auto* dispatcher
= VBlankDispatcher::getInstance();
3852 dispatcher
->reconfigureDisplays();
3853 updateCurrentMonitor();
3854 dispatcher
->updateDisplay (*this, currentMonitor
);
3856 updateCurrentMonitor();
3860 //==============================================================================
3861 #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
3862 void setModifierKeyProvider (ModifierKeyProvider
* provider
) override
3864 modProvider
= provider
;
3867 void removeModifierKeyProvider() override
3869 modProvider
= nullptr;
3874 static LRESULT CALLBACK
windowProc (HWND h
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3876 // Ensure that non-client areas are scaled for per-monitor DPI awareness v1 - can't
3877 // do this in peerWindowProc as we have no window at this point
3878 if (message
== WM_NCCREATE
&& enableNonClientDPIScaling
!= nullptr)
3879 enableNonClientDPIScaling (h
);
3881 if (auto* peer
= getOwnerOfWindow (h
))
3883 jassert (isValidPeer (peer
));
3884 return peer
->peerWindowProc (h
, message
, wParam
, lParam
);
3887 return DefWindowProcW (h
, message
, wParam
, lParam
);
3891 static void* callFunctionIfNotLocked (MessageCallbackFunction
* callback
, void* userData
)
3893 auto& mm
= *MessageManager::getInstance();
3895 if (mm
.currentThreadHasLockedMessageManager())
3896 return callback (userData
);
3898 return mm
.callFunctionOnMessageThread (callback
, userData
);
3901 static POINT
getPOINTFromLParam (LPARAM lParam
) noexcept
3903 return { GET_X_LPARAM (lParam
), GET_Y_LPARAM (lParam
) };
3906 Point
<float> getPointFromLocalLParam (LPARAM lParam
) noexcept
3908 auto p
= pointFromPOINT (getPOINTFromLParam (lParam
));
3910 if (isPerMonitorDPIAwareWindow (hwnd
))
3912 // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the
3913 // physical screen position and then convert this to local logical coordinates
3914 auto r
= getWindowScreenRect (hwnd
);
3915 return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (pointFromPOINT ({ r
.left
+ p
.x
+ roundToInt (windowBorder
.getLeft() * scaleFactor
),
3916 r
.top
+ p
.y
+ roundToInt (windowBorder
.getTop() * scaleFactor
) })).toFloat());
3922 Point
<float> getCurrentMousePos() noexcept
3924 return globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam ((LPARAM
) GetMessagePos())), hwnd
).toFloat());
3927 LRESULT
peerWindowProc (HWND h
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3931 //==============================================================================
3933 if ((styleFlags
& windowIgnoresMouseClicks
) != 0)
3934 return HTTRANSPARENT
;
3936 if (! hasTitleBar())
3941 //==============================================================================
3943 handlePaintMessage();
3947 handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
3950 break; // let the DefWindowProc handle drawing the frame.
3961 //==============================================================================
3962 case WM_POINTERUPDATE
:
3963 if (handlePointerInput (wParam
, lParam
, false, false))
3967 case WM_POINTERDOWN
:
3968 if (handlePointerInput (wParam
, lParam
, true, false))
3973 if (handlePointerInput (wParam
, lParam
, false, true))
3977 //==============================================================================
3978 case WM_MOUSEMOVE
: doMouseMove (getPointFromLocalLParam (lParam
), false); return 0;
3980 case WM_POINTERLEAVE
:
3981 case WM_MOUSELEAVE
: doMouseExit(); return 0;
3983 case WM_LBUTTONDOWN
:
3984 case WM_MBUTTONDOWN
:
3985 case WM_RBUTTONDOWN
: doMouseDown (getPointFromLocalLParam (lParam
), wParam
); return 0;
3989 case WM_RBUTTONUP
: doMouseUp (getPointFromLocalLParam (lParam
), wParam
); return 0;
3991 case WM_POINTERWHEEL
:
3992 case 0x020A: /* WM_MOUSEWHEEL */ doMouseWheel (wParam
, true); return 0;
3994 case WM_POINTERHWHEEL
:
3995 case 0x020E: /* WM_MOUSEHWHEEL */ doMouseWheel (wParam
, false); return 0;
3997 case WM_CAPTURECHANGED
: doCaptureChanged(); return 0;
3999 case WM_NCPOINTERUPDATE
:
4000 case WM_NCMOUSEMOVE
:
4007 if (getTouchInputInfo
!= nullptr)
4008 return doTouchEvent ((int) wParam
, (HTOUCHINPUT
) lParam
);
4012 case 0x119: /* WM_GESTURE */
4013 if (doGestureEvent (lParam
))
4018 //==============================================================================
4019 case WM_SIZING
: return handleSizeConstraining (*(RECT
*) lParam
, wParam
);
4020 case WM_WINDOWPOSCHANGING
: return handlePositionChanging (*(WINDOWPOS
*) lParam
);
4021 case 0x2e0: /* WM_DPICHANGED */ return handleDPIChanging ((int) HIWORD (wParam
), *(RECT
*) lParam
);
4023 case WM_WINDOWPOSCHANGED
:
4025 const WINDOWPOS
& wPos
= *reinterpret_cast<WINDOWPOS
*> (lParam
);
4027 if ((wPos
.flags
& SWP_NOMOVE
) != 0 && (wPos
.flags
& SWP_NOSIZE
) != 0)
4030 if (handlePositionChanged())
4035 //==============================================================================
4038 if (doKeyDown (wParam
))
4041 forwardMessageToParent (message
, wParam
, lParam
);
4046 if (doKeyUp (wParam
))
4049 forwardMessageToParent (message
, wParam
, lParam
);
4053 if (doKeyChar ((int) wParam
, lParam
))
4056 forwardMessageToParent (message
, wParam
, lParam
);
4060 if (doAppCommand (lParam
))
4065 case WM_MENUCHAR
: // triggered when alt+something is pressed
4066 return MNC_CLOSE
<< 16; // (avoids making the default system beep)
4068 //==============================================================================
4070 updateKeyModifiers();
4075 if (hasCreatedCaret
)
4077 hasCreatedCaret
= false;
4083 if (auto* modal
= Component::getCurrentlyModalComponent())
4084 if (auto* peer
= modal
->getPeer())
4085 if ((peer
->getStyleFlags() & ComponentPeer::windowIsTemporary
) != 0)
4086 sendInputAttemptWhenModalMessage();
4090 case WM_ACTIVATEAPP
:
4091 // Windows does weird things to process priority when you swap apps,
4092 // so this forces an update when the app is brought to the front
4093 if (wParam
!= FALSE
)
4094 juce_repeatLastProcessPriority();
4096 Desktop::getInstance().setKioskModeComponent (nullptr); // turn kiosk mode off if we lose focus
4098 juce_checkCurrentlyFocusedTopLevelWindow();
4099 modifiersAtLastCallback
= -1;
4103 if (LOWORD (wParam
) == WA_ACTIVE
|| LOWORD (wParam
) == WA_CLICKACTIVE
)
4105 handleAppActivation (wParam
);
4112 // while a temporary window is being shown, prevent Windows from deactivating the
4113 // title bars of our main windows.
4114 if (wParam
== 0 && ! shouldDeactivateTitleBar
)
4115 wParam
= TRUE
; // change this and let it get passed to the DefWindowProc.
4119 case WM_POINTERACTIVATE
:
4120 case WM_MOUSEACTIVATE
:
4121 if (! component
.getMouseClickGrabsKeyboardFocus())
4122 return MA_NOACTIVATE
;
4129 component
.setVisible (true);
4130 handleBroughtToFront();
4136 if (! component
.isCurrentlyBlockedByAnotherModalComponent())
4137 handleUserClosingWindow();
4141 #if JUCE_REMOVE_COMPONENT_FROM_DESKTOP_ON_WM_DESTROY
4143 getComponent().removeFromDesktop();
4147 case WM_QUERYENDSESSION
:
4148 if (auto* app
= JUCEApplicationBase::getInstance())
4150 app
->systemRequestedQuit();
4151 return MessageManager::getInstance()->hasStopMessageBeenSent();
4155 case WM_POWERBROADCAST
:
4156 handlePowerBroadcast (wParam
);
4162 case WM_DISPLAYCHANGE
:
4163 InvalidateRect (h
, nullptr, 0);
4164 // intentional fall-through...
4166 case WM_SETTINGCHANGE
: // note the fall-through in the previous case!
4171 initialiseSysMenu ((HMENU
) wParam
);
4175 switch (wParam
& 0xfff0)
4178 if (sendInputAttemptWhenModalMessage())
4183 PostMessage (h
, WM_CLOSE
, 0, 0);
4189 #if ! JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU
4190 // This test prevents a press of the ALT key from triggering the ancient top-left window menu.
4191 // By default we suppress this behaviour because it's unlikely that more than a tiny subset of
4192 // our users will actually want it, and it causes problems if you're trying to use the ALT key
4193 // as a modifier for mouse actions. If you really need the old behaviour, then just define
4194 // JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU=1 in your app.
4195 if ((lParam
>> 16) <= 0) // Values above zero indicate that a mouse-click triggered the menu
4199 // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
4200 // situations that can arise if a modal loop is started from an alt-key keypress).
4201 if (hasTitleBar() && h
== GetCapture())
4207 if (! sendInputAttemptWhenModalMessage())
4208 setFullScreen (true);
4213 if (sendInputAttemptWhenModalMessage())
4216 if (! hasTitleBar())
4218 setMinimised (true);
4224 if (sendInputAttemptWhenModalMessage())
4231 setFullScreen (false);
4238 setMinimised (false);
4239 else if (isFullScreen())
4240 setFullScreen (false);
4249 case WM_NCPOINTERDOWN
:
4250 case WM_NCLBUTTONDOWN
:
4251 handleLeftClickInNCArea (wParam
);
4254 case WM_NCRBUTTONDOWN
:
4255 case WM_NCMBUTTONDOWN
:
4256 sendInputAttemptWhenModalMessage();
4259 case WM_IME_SETCONTEXT
:
4260 imeHandler
.handleSetContext (h
, wParam
== TRUE
);
4261 lParam
&= ~(LPARAM
) ISC_SHOWUICOMPOSITIONWINDOW
;
4264 case WM_IME_STARTCOMPOSITION
: imeHandler
.handleStartComposition (*this); return 0;
4265 case WM_IME_ENDCOMPOSITION
: imeHandler
.handleEndComposition (*this, h
); break;
4266 case WM_IME_COMPOSITION
: imeHandler
.handleComposition (*this, h
, lParam
); return 0;
4269 return DLGC_WANTALLKEYS
;
4274 if (static_cast<long> (lParam
) == WindowsAccessibility::getUiaRootObjectId())
4276 if (auto* handler
= component
.getAccessibilityHandler())
4280 if (WindowsAccessibility::handleWmGetObject (handler
, wParam
, lParam
, &res
))
4282 isAccessibilityActive
= true;
4295 return DefWindowProcW (h
, message
, wParam
, lParam
);
4298 bool sendInputAttemptWhenModalMessage()
4300 if (! component
.isCurrentlyBlockedByAnotherModalComponent())
4303 if (auto* current
= Component::getCurrentlyModalComponent())
4304 if (auto* owner
= getOwnerOfWindow ((HWND
) current
->getWindowHandle()))
4305 if (! owner
->shouldIgnoreModalDismiss
)
4306 current
->inputAttemptWhenModal();
4311 //==============================================================================
4319 void handleSetContext (HWND hWnd
, const bool windowIsActive
)
4321 if (compositionInProgress
&& ! windowIsActive
)
4323 if (HIMC hImc
= ImmGetContext (hWnd
))
4325 ImmNotifyIME (hImc
, NI_COMPOSITIONSTR
, CPS_COMPLETE
, 0);
4326 ImmReleaseContext (hWnd
, hImc
);
4329 // If the composition is still in progress, calling ImmNotifyIME may call back
4330 // into handleComposition to let us know that the composition has finished.
4331 // We need to set compositionInProgress *after* calling handleComposition, so that
4332 // the text replaces the current selection, rather than being inserted after the
4334 compositionInProgress
= false;
4338 void handleStartComposition (ComponentPeer
& owner
)
4342 if (auto* target
= owner
.findCurrentTextInputTarget())
4343 target
->insertTextAtCaret (String());
4346 void handleEndComposition (ComponentPeer
& owner
, HWND hWnd
)
4348 if (compositionInProgress
)
4350 // If this occurs, the user has cancelled the composition, so clear their changes..
4351 if (auto* target
= owner
.findCurrentTextInputTarget())
4353 target
->setHighlightedRegion (compositionRange
);
4354 target
->insertTextAtCaret (String());
4355 compositionRange
.setLength (0);
4357 target
->setHighlightedRegion (Range
<int>::emptyRange (compositionRange
.getEnd()));
4358 target
->setTemporaryUnderlining ({});
4361 if (auto hImc
= ImmGetContext (hWnd
))
4363 ImmNotifyIME (hImc
, NI_CLOSECANDIDATE
, 0, 0);
4364 ImmReleaseContext (hWnd
, hImc
);
4371 void handleComposition (ComponentPeer
& owner
, HWND hWnd
, const LPARAM lParam
)
4373 if (auto* target
= owner
.findCurrentTextInputTarget())
4375 if (auto hImc
= ImmGetContext (hWnd
))
4377 if (compositionRange
.getStart() < 0)
4378 compositionRange
= Range
<int>::emptyRange (target
->getHighlightedRegion().getStart());
4380 if ((lParam
& GCS_RESULTSTR
) != 0) // (composition has finished)
4382 replaceCurrentSelection (target
, getCompositionString (hImc
, GCS_RESULTSTR
),
4383 Range
<int>::emptyRange (-1));
4386 target
->setTemporaryUnderlining ({});
4388 else if ((lParam
& GCS_COMPSTR
) != 0) // (composition is still in-progress)
4390 replaceCurrentSelection (target
, getCompositionString (hImc
, GCS_COMPSTR
),
4391 getCompositionSelection (hImc
, lParam
));
4393 target
->setTemporaryUnderlining (getCompositionUnderlines (hImc
, lParam
));
4394 compositionInProgress
= true;
4397 moveCandidateWindowToLeftAlignWithSelection (hImc
, owner
, target
);
4398 ImmReleaseContext (hWnd
, hImc
);
4404 //==============================================================================
4405 Range
<int> compositionRange
; // The range being modified in the TextInputTarget
4406 bool compositionInProgress
;
4408 //==============================================================================
4411 compositionRange
= Range
<int>::emptyRange (-1);
4412 compositionInProgress
= false;
4415 String
getCompositionString (HIMC hImc
, const DWORD type
) const
4417 jassert (hImc
!= HIMC
{});
4419 const auto stringSizeBytes
= ImmGetCompositionString (hImc
, type
, nullptr, 0);
4421 if (stringSizeBytes
> 0)
4423 HeapBlock
<TCHAR
> buffer
;
4424 buffer
.calloc ((size_t) stringSizeBytes
/ sizeof (TCHAR
) + 1);
4425 ImmGetCompositionString (hImc
, type
, buffer
, (DWORD
) stringSizeBytes
);
4426 return String (buffer
.get());
4432 int getCompositionCaretPos (HIMC hImc
, LPARAM lParam
, const String
& currentIMEString
) const
4434 jassert (hImc
!= HIMC
{});
4436 if ((lParam
& CS_NOMOVECARET
) != 0)
4437 return compositionRange
.getStart();
4439 if ((lParam
& GCS_CURSORPOS
) != 0)
4441 const int localCaretPos
= ImmGetCompositionString (hImc
, GCS_CURSORPOS
, nullptr, 0);
4442 return compositionRange
.getStart() + jmax (0, localCaretPos
);
4445 return compositionRange
.getStart() + currentIMEString
.length();
4448 // Get selected/highlighted range while doing composition:
4449 // returned range is relative to beginning of TextInputTarget, not composition string
4450 Range
<int> getCompositionSelection (HIMC hImc
, LPARAM lParam
) const
4452 jassert (hImc
!= HIMC
{});
4453 int selectionStart
= 0;
4454 int selectionEnd
= 0;
4456 if ((lParam
& GCS_COMPATTR
) != 0)
4458 // Get size of attributes array:
4459 const int attributeSizeBytes
= ImmGetCompositionString (hImc
, GCS_COMPATTR
, nullptr, 0);
4461 if (attributeSizeBytes
> 0)
4463 // Get attributes (8 bit flag per character):
4464 HeapBlock
<char> attributes (attributeSizeBytes
);
4465 ImmGetCompositionString (hImc
, GCS_COMPATTR
, attributes
, (DWORD
) attributeSizeBytes
);
4469 for (selectionStart
= 0; selectionStart
< attributeSizeBytes
; ++selectionStart
)
4470 if (attributes
[selectionStart
] == ATTR_TARGET_CONVERTED
|| attributes
[selectionStart
] == ATTR_TARGET_NOTCONVERTED
)
4473 for (selectionEnd
= selectionStart
; selectionEnd
< attributeSizeBytes
; ++selectionEnd
)
4474 if (attributes
[selectionEnd
] != ATTR_TARGET_CONVERTED
&& attributes
[selectionEnd
] != ATTR_TARGET_NOTCONVERTED
)
4479 return Range
<int> (selectionStart
, selectionEnd
) + compositionRange
.getStart();
4482 void replaceCurrentSelection (TextInputTarget
* const target
, const String
& newContent
, Range
<int> newSelection
)
4484 if (compositionInProgress
)
4485 target
->setHighlightedRegion (compositionRange
);
4487 target
->insertTextAtCaret (newContent
);
4488 compositionRange
.setLength (newContent
.length());
4490 if (newSelection
.getStart() < 0)
4491 newSelection
= Range
<int>::emptyRange (compositionRange
.getEnd());
4493 target
->setHighlightedRegion (newSelection
);
4496 Array
<Range
<int>> getCompositionUnderlines (HIMC hImc
, LPARAM lParam
) const
4498 Array
<Range
<int>> result
;
4500 if (hImc
!= HIMC
{} && (lParam
& GCS_COMPCLAUSE
) != 0)
4502 auto clauseDataSizeBytes
= ImmGetCompositionString (hImc
, GCS_COMPCLAUSE
, nullptr, 0);
4504 if (clauseDataSizeBytes
> 0)
4506 const auto numItems
= (size_t) clauseDataSizeBytes
/ sizeof (uint32
);
4507 HeapBlock
<uint32
> clauseData (numItems
);
4509 if (ImmGetCompositionString (hImc
, GCS_COMPCLAUSE
, clauseData
, (DWORD
) clauseDataSizeBytes
) > 0)
4510 for (size_t i
= 0; i
+ 1 < numItems
; ++i
)
4511 result
.add (Range
<int> ((int) clauseData
[i
], (int) clauseData
[i
+ 1]) + compositionRange
.getStart());
4518 void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc
, ComponentPeer
& peer
, TextInputTarget
* target
) const
4520 if (auto* targetComp
= dynamic_cast<Component
*> (target
))
4522 auto area
= peer
.getComponent().getLocalArea (targetComp
, target
->getCaretRectangle());
4524 CANDIDATEFORM pos
= { 0, CFS_CANDIDATEPOS
, { area
.getX(), area
.getBottom() }, { 0, 0, 0, 0 } };
4525 ImmSetCandidateWindow (hImc
, &pos
);
4529 JUCE_DECLARE_NON_COPYABLE (IMEHandler
)
4532 void timerCallback() override
4534 handlePositionChanged();
4538 static bool isAncestor (HWND outer
, HWND inner
)
4540 if (outer
== nullptr || inner
== nullptr)
4546 return isAncestor (outer
, GetAncestor (inner
, GA_PARENT
));
4549 void windowShouldDismissModals (HWND originator
)
4551 if (shouldIgnoreModalDismiss
)
4554 if (isAncestor (originator
, hwnd
))
4555 sendInputAttemptWhenModalMessage();
4558 // Unfortunately SetWindowsHookEx only allows us to register a static function as a hook.
4559 // To get around this, we keep a static list of listeners which are interested in
4560 // top-level window events, and notify all of these listeners from the callback.
4561 class TopLevelModalDismissBroadcaster
4564 TopLevelModalDismissBroadcaster()
4565 : hook (SetWindowsHookEx (WH_CALLWNDPROC
,
4567 (HINSTANCE
) juce::Process::getCurrentModuleInstanceHandle(),
4568 GetCurrentThreadId()))
4571 ~TopLevelModalDismissBroadcaster() noexcept
4573 UnhookWindowsHookEx (hook
);
4577 static void processMessage (int nCode
, const CWPSTRUCT
* info
)
4579 if (nCode
< 0 || info
== nullptr)
4582 constexpr UINT events
[] { WM_MOVE
,
4584 WM_WINDOWPOSCHANGING
,
4590 if (std::find (std::begin (events
), std::end (events
), info
->message
) == std::end (events
))
4593 if (info
->message
== WM_WINDOWPOSCHANGING
)
4595 const auto* windowPos
= reinterpret_cast<const WINDOWPOS
*> (info
->lParam
);
4596 const auto windowPosFlags
= windowPos
->flags
;
4598 constexpr auto maskToCheck
= SWP_NOMOVE
| SWP_NOSIZE
;
4600 if ((windowPosFlags
& maskToCheck
) == maskToCheck
)
4604 // windowMayDismissModals could affect the number of active ComponentPeer instances
4605 for (auto i
= ComponentPeer::getNumPeers(); --i
>= 0;)
4606 if (i
< ComponentPeer::getNumPeers())
4607 if (auto* hwndPeer
= dynamic_cast<HWNDComponentPeer
*> (ComponentPeer::getPeer (i
)))
4608 hwndPeer
->windowShouldDismissModals (info
->hwnd
);
4611 static LRESULT CALLBACK
callWndProc (int nCode
, WPARAM wParam
, LPARAM lParam
)
4613 processMessage (nCode
, reinterpret_cast<CWPSTRUCT
*> (lParam
));
4614 return CallNextHookEx ({}, nCode
, wParam
, lParam
);
4620 SharedResourcePointer
<TopLevelModalDismissBroadcaster
> modalDismissBroadcaster
;
4621 IMEHandler imeHandler
;
4622 bool shouldIgnoreModalDismiss
= false;
4624 RectangleList
<int> deferredRepaints
;
4626 //==============================================================================
4627 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer
)
4630 MultiTouchMapper
<DWORD
> HWNDComponentPeer::currentTouches
;
4631 ModifierKeys
HWNDComponentPeer::modifiersAtLastCallback
;
4633 ComponentPeer
* Component::createNewPeer (int styleFlags
, void* parentHWND
)
4635 return new HWNDComponentPeer (*this, styleFlags
, (HWND
) parentHWND
, false);
4638 JUCE_API ComponentPeer
* createNonRepaintingEmbeddedWindowsPeer (Component
& component
, void* parentHWND
);
4639 JUCE_API ComponentPeer
* createNonRepaintingEmbeddedWindowsPeer (Component
& component
, void* parentHWND
)
4641 return new HWNDComponentPeer (component
, ComponentPeer::windowIgnoresMouseClicks
,
4642 (HWND
) parentHWND
, true);
4645 JUCE_IMPLEMENT_SINGLETON (HWNDComponentPeer::WindowClassHolder
)
4647 //==============================================================================
4648 bool KeyPress::isKeyCurrentlyDown (const int keyCode
)
4650 auto k
= (SHORT
) keyCode
;
4652 if ((keyCode
& extendedKeyModifier
) == 0)
4654 if (k
>= (SHORT
) 'a' && k
<= (SHORT
) 'z')
4655 k
+= (SHORT
) 'A' - (SHORT
) 'a';
4657 // Only translate if extendedKeyModifier flag is not set
4658 const SHORT translatedValues
[] = { (SHORT
) ',', VK_OEM_COMMA
,
4659 (SHORT
) '+', VK_OEM_PLUS
,
4660 (SHORT
) '-', VK_OEM_MINUS
,
4661 (SHORT
) '.', VK_OEM_PERIOD
,
4662 (SHORT
) ';', VK_OEM_1
,
4663 (SHORT
) ':', VK_OEM_1
,
4664 (SHORT
) '/', VK_OEM_2
,
4665 (SHORT
) '?', VK_OEM_2
,
4666 (SHORT
) '[', VK_OEM_4
,
4667 (SHORT
) ']', VK_OEM_6
};
4669 for (int i
= 0; i
< numElementsInArray (translatedValues
); i
+= 2)
4670 if (k
== translatedValues
[i
])
4671 k
= translatedValues
[i
+ 1];
4674 return HWNDComponentPeer::isKeyDown (k
);
4677 // (This internal function is used by the plugin client module)
4678 bool offerKeyMessageToJUCEWindow (MSG
& m
);
4679 bool offerKeyMessageToJUCEWindow (MSG
& m
) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m
); }
4681 //==============================================================================
4682 static DWORD
getProcess (HWND hwnd
)
4685 GetWindowThreadProcessId (hwnd
, &result
);
4689 /* Returns true if the viewComponent is embedded into a window
4690 owned by the foreground process.
4692 bool isEmbeddedInForegroundProcess (Component
* c
)
4697 auto* peer
= c
->getPeer();
4698 auto* hwnd
= peer
!= nullptr ? static_cast<HWND
> (peer
->getNativeHandle()) : nullptr;
4700 if (hwnd
== nullptr)
4703 const auto fgProcess
= getProcess (GetForegroundWindow());
4704 const auto ownerProcess
= getProcess (GetAncestor (hwnd
, GA_ROOTOWNER
));
4705 return fgProcess
== ownerProcess
;
4708 bool JUCE_CALLTYPE
Process::isForegroundProcess()
4710 if (auto fg
= GetForegroundWindow())
4711 return getProcess (fg
) == GetCurrentProcessId();
4716 // N/A on Windows as far as I know.
4717 void JUCE_CALLTYPE
Process::makeForegroundProcess() {}
4718 void JUCE_CALLTYPE
Process::hide() {}
4720 //==============================================================================
4721 static BOOL CALLBACK
enumAlwaysOnTopWindows (HWND hwnd
, LPARAM lParam
)
4723 if (IsWindowVisible (hwnd
))
4725 DWORD processID
= 0;
4726 GetWindowThreadProcessId (hwnd
, &processID
);
4728 if (processID
== GetCurrentProcessId())
4732 if (GetWindowInfo (hwnd
, &info
)
4733 && (info
.dwExStyle
& WS_EX_TOPMOST
) != 0)
4735 *reinterpret_cast<bool*> (lParam
) = true;
4744 bool juce_areThereAnyAlwaysOnTopWindows()
4746 bool anyAlwaysOnTopFound
= false;
4747 EnumWindows (&enumAlwaysOnTopWindows
, (LPARAM
) &anyAlwaysOnTopFound
);
4748 return anyAlwaysOnTopFound
;
4751 //==============================================================================
4753 // required to enable the newer dialog box on vista and above
4754 #pragma comment(linker, \
4755 "\"/MANIFESTDEPENDENCY:type='Win32' " \
4756 "name='Microsoft.Windows.Common-Controls' " \
4757 "version='6.0.0.0' " \
4758 "processorArchitecture='*' " \
4759 "publicKeyToken='6595b64144ccf1df' " \
4764 class WindowsMessageBoxBase
: private AsyncUpdater
4767 WindowsMessageBoxBase (Component
* comp
,
4768 std::unique_ptr
<ModalComponentManager::Callback
>&& cb
)
4769 : associatedComponent (comp
),
4770 callback (std::move (cb
))
4774 virtual int getResult() = 0;
4776 HWND
getParentHWND() const
4778 if (associatedComponent
!= nullptr)
4779 return (HWND
) associatedComponent
->getWindowHandle();
4784 using AsyncUpdater::triggerAsyncUpdate
;
4787 void handleAsyncUpdate() override
4789 const auto result
= getResult();
4791 if (callback
!= nullptr)
4792 callback
->modalStateFinished (result
);
4797 Component::SafePointer
<Component
> associatedComponent
;
4798 std::unique_ptr
<ModalComponentManager::Callback
> callback
;
4800 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsMessageBoxBase
)
4803 class PreVistaMessageBox
: public WindowsMessageBoxBase
4806 PreVistaMessageBox (const MessageBoxOptions
& opts
,
4808 std::unique_ptr
<ModalComponentManager::Callback
>&& cb
)
4809 : WindowsMessageBoxBase (opts
.getAssociatedComponent(), std::move (cb
)),
4810 flags (extraFlags
| getMessageBoxFlags (opts
.getIconType())),
4811 title (opts
.getTitle()), message (opts
.getMessage())
4815 int getResult() override
4817 const auto result
= MessageBox (getParentHWND(), message
.toWideCharPointer(), title
.toWideCharPointer(), flags
);
4819 if (result
== IDYES
|| result
== IDOK
) return 0;
4820 if (result
== IDNO
&& ((flags
& 1) != 0)) return 1;
4826 static UINT
getMessageBoxFlags (MessageBoxIconType iconType
) noexcept
4828 // this window can get lost behind JUCE windows which are set to be alwaysOnTop
4829 // so if there are any set it to be topmost
4830 const auto topmostFlag
= juce_areThereAnyAlwaysOnTopWindows() ? MB_TOPMOST
: 0;
4832 const auto iconFlags
= [&]() -> decltype (topmostFlag
)
4836 case MessageBoxIconType::QuestionIcon
: return MB_ICONQUESTION
;
4837 case MessageBoxIconType::WarningIcon
: return MB_ICONWARNING
;
4838 case MessageBoxIconType::InfoIcon
: return MB_ICONINFORMATION
;
4839 case MessageBoxIconType::NoIcon
: break;
4845 return static_cast<UINT
> (MB_TASKMODAL
| MB_SETFOREGROUND
| topmostFlag
| iconFlags
);
4849 const String title
, message
;
4851 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreVistaMessageBox
)
4854 using TaskDialogIndirectFunc
= HRESULT (WINAPI
*) (const TASKDIALOGCONFIG
*, INT
*, INT
*, BOOL
*);
4855 static TaskDialogIndirectFunc taskDialogIndirect
= nullptr;
4857 class WindowsTaskDialog
: public WindowsMessageBoxBase
4860 WindowsTaskDialog (const MessageBoxOptions
& opts
,
4861 std::unique_ptr
<ModalComponentManager::Callback
>&& cb
)
4862 : WindowsMessageBoxBase (opts
.getAssociatedComponent(), std::move (cb
)),
4863 iconType (opts
.getIconType()),
4864 title (opts
.getTitle()), message (opts
.getMessage()),
4865 button1 (opts
.getButtonText (0)), button2 (opts
.getButtonText (1)), button3 (opts
.getButtonText (2))
4869 int getResult() override
4871 TASKDIALOGCONFIG config
{};
4873 config
.cbSize
= sizeof (config
);
4874 config
.hwndParent
= getParentHWND();
4875 config
.pszWindowTitle
= title
.toWideCharPointer();
4876 config
.pszContent
= message
.toWideCharPointer();
4877 config
.hInstance
= (HINSTANCE
) Process::getCurrentModuleInstanceHandle();
4879 if (iconType
== MessageBoxIconType::QuestionIcon
)
4881 if (auto* questionIcon
= LoadIcon (nullptr, IDI_QUESTION
))
4883 config
.hMainIcon
= questionIcon
;
4884 config
.dwFlags
|= TDF_USE_HICON_MAIN
;
4889 auto icon
= [this]() -> LPWSTR
4893 case MessageBoxIconType::WarningIcon
: return TD_WARNING_ICON
;
4894 case MessageBoxIconType::InfoIcon
: return TD_INFORMATION_ICON
;
4896 case MessageBoxIconType::QuestionIcon
: JUCE_FALLTHROUGH
4897 case MessageBoxIconType::NoIcon
:
4904 if (icon
!= nullptr)
4905 config
.pszMainIcon
= icon
;
4908 std::vector
<TASKDIALOG_BUTTON
> buttons
;
4910 for (const auto* buttonText
: { &button1
, &button2
, &button3
})
4911 if (buttonText
->isNotEmpty())
4912 buttons
.push_back ({ (int) buttons
.size(), buttonText
->toWideCharPointer() });
4914 config
.pButtons
= buttons
.data();
4915 config
.cButtons
= (UINT
) buttons
.size();
4917 int buttonIndex
= 0;
4918 taskDialogIndirect (&config
, &buttonIndex
, nullptr, nullptr);
4923 static bool loadTaskDialog()
4925 static bool hasChecked
= false;
4931 const auto comctl
= "Comctl32.dll";
4932 LoadLibraryA (comctl
);
4933 const auto comctlModule
= GetModuleHandleA (comctl
);
4935 if (comctlModule
!= nullptr)
4936 taskDialogIndirect
= (TaskDialogIndirectFunc
) GetProcAddress (comctlModule
, "TaskDialogIndirect");
4939 return taskDialogIndirect
!= nullptr;
4943 MessageBoxIconType iconType
;
4944 String title
, message
, button1
, button2
, button3
;
4946 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTaskDialog
)
4949 static std::unique_ptr
<WindowsMessageBoxBase
> createMessageBox (const MessageBoxOptions
& options
,
4950 std::unique_ptr
<ModalComponentManager::Callback
> callback
)
4952 const auto useTaskDialog
=
4953 #if JUCE_MODAL_LOOPS_PERMITTED
4954 callback
!= nullptr &&
4956 SystemStats::getOperatingSystemType() >= SystemStats::WinVista
4957 && WindowsTaskDialog::loadTaskDialog();
4960 return std::make_unique
<WindowsTaskDialog
> (options
, std::move (callback
));
4962 const auto extraFlags
= [&options
]
4964 const auto numButtons
= options
.getNumButtons();
4966 if (numButtons
== 3)
4967 return MB_YESNOCANCEL
;
4969 if (numButtons
== 2)
4970 return options
.getButtonText (0) == "OK" ? MB_OKCANCEL
4976 return std::make_unique
<PreVistaMessageBox
> (options
, (UINT
) extraFlags
, std::move (callback
));
4979 static int showDialog (const MessageBoxOptions
& options
,
4980 ModalComponentManager::Callback
* callbackIn
,
4981 AlertWindowMappings::MapFn mapFn
)
4983 #if JUCE_MODAL_LOOPS_PERMITTED
4984 if (callbackIn
== nullptr)
4986 jassert (mapFn
!= nullptr);
4988 auto messageBox
= createMessageBox (options
, nullptr);
4989 return mapFn (messageBox
->getResult());
4993 auto messageBox
= createMessageBox (options
,
4994 AlertWindowMappings::getWrappedCallback (callbackIn
, mapFn
));
4996 messageBox
->triggerAsyncUpdate();
4997 messageBox
.release();
5002 #if JUCE_MODAL_LOOPS_PERMITTED
5003 void JUCE_CALLTYPE
NativeMessageBox::showMessageBox (MessageBoxIconType iconType
,
5004 const String
& title
, const String
& message
,
5005 Component
* associatedComponent
)
5007 showDialog (MessageBoxOptions()
5008 .withIconType (iconType
)
5010 .withMessage (message
)
5011 .withButton (TRANS("OK"))
5012 .withAssociatedComponent (associatedComponent
),
5013 nullptr, AlertWindowMappings::messageBox
);
5016 int JUCE_CALLTYPE
NativeMessageBox::show (const MessageBoxOptions
& options
)
5018 return showDialog (options
, nullptr, AlertWindowMappings::noMapping
);
5022 void JUCE_CALLTYPE
NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType
,
5023 const String
& title
, const String
& message
,
5024 Component
* associatedComponent
,
5025 ModalComponentManager::Callback
* callback
)
5027 showDialog (MessageBoxOptions()
5028 .withIconType (iconType
)
5030 .withMessage (message
)
5031 .withButton (TRANS("OK"))
5032 .withAssociatedComponent (associatedComponent
),
5033 callback
, AlertWindowMappings::messageBox
);
5036 bool JUCE_CALLTYPE
NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType
,
5037 const String
& title
, const String
& message
,
5038 Component
* associatedComponent
,
5039 ModalComponentManager::Callback
* callback
)
5041 return showDialog (MessageBoxOptions()
5042 .withIconType (iconType
)
5044 .withMessage (message
)
5045 .withButton (TRANS("OK"))
5046 .withButton (TRANS("Cancel"))
5047 .withAssociatedComponent (associatedComponent
),
5048 callback
, AlertWindowMappings::okCancel
) != 0;
5051 int JUCE_CALLTYPE
NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType
,
5052 const String
& title
, const String
& message
,
5053 Component
* associatedComponent
,
5054 ModalComponentManager::Callback
* callback
)
5056 return showDialog (MessageBoxOptions()
5057 .withIconType (iconType
)
5059 .withMessage (message
)
5060 .withButton (TRANS("Yes"))
5061 .withButton (TRANS("No"))
5062 .withButton (TRANS("Cancel"))
5063 .withAssociatedComponent (associatedComponent
),
5064 callback
, AlertWindowMappings::yesNoCancel
);
5067 int JUCE_CALLTYPE
NativeMessageBox::showYesNoBox (MessageBoxIconType iconType
,
5068 const String
& title
, const String
& message
,
5069 Component
* associatedComponent
,
5070 ModalComponentManager::Callback
* callback
)
5072 return showDialog (MessageBoxOptions()
5073 .withIconType (iconType
)
5075 .withMessage (message
)
5076 .withButton (TRANS("Yes"))
5077 .withButton (TRANS("No"))
5078 .withAssociatedComponent (associatedComponent
),
5079 callback
, AlertWindowMappings::okCancel
);
5082 void JUCE_CALLTYPE
NativeMessageBox::showAsync (const MessageBoxOptions
& options
,
5083 ModalComponentManager::Callback
* callback
)
5085 showDialog (options
, callback
, AlertWindowMappings::noMapping
);
5088 void JUCE_CALLTYPE
NativeMessageBox::showAsync (const MessageBoxOptions
& options
,
5089 std::function
<void (int)> callback
)
5091 showAsync (options
, ModalCallbackFunction::create (callback
));
5094 //==============================================================================
5095 bool MouseInputSource::SourceList::addSource()
5097 auto numSources
= sources
.size();
5099 if (numSources
== 0 || canUseMultiTouch())
5101 addSource (numSources
, numSources
== 0 ? MouseInputSource::InputSourceType::mouse
5102 : MouseInputSource::InputSourceType::touch
);
5109 bool MouseInputSource::SourceList::canUseTouch()
5111 return canUseMultiTouch();
5114 Point
<float> MouseInputSource::getCurrentRawMousePosition()
5117 GetCursorPos (&mousePos
);
5119 auto p
= pointFromPOINT (mousePos
);
5121 if (isPerMonitorDPIAwareThread())
5122 p
= Desktop::getInstance().getDisplays().physicalToLogical (p
);
5127 void MouseInputSource::setRawMousePosition (Point
<float> newPosition
)
5129 auto newPositionInt
= newPosition
.roundToInt();
5131 #if JUCE_WIN_PER_MONITOR_DPI_AWARE
5132 if (isPerMonitorDPIAwareThread())
5133 newPositionInt
= Desktop::getInstance().getDisplays().logicalToPhysical (newPositionInt
);
5136 auto point
= POINTFromPoint (newPositionInt
);
5137 SetCursorPos (point
.x
, point
.y
);
5140 //==============================================================================
5141 class ScreenSaverDefeater
: public Timer
5144 ScreenSaverDefeater()
5150 void timerCallback() override
5152 if (Process::isForegroundProcess())
5155 input
.type
= INPUT_MOUSE
;
5156 input
.mi
.mouseData
= MOUSEEVENTF_MOVE
;
5158 SendInput (1, &input
, sizeof (INPUT
));
5163 static std::unique_ptr
<ScreenSaverDefeater
> screenSaverDefeater
;
5165 void Desktop::setScreenSaverEnabled (const bool isEnabled
)
5168 screenSaverDefeater
= nullptr;
5169 else if (screenSaverDefeater
== nullptr)
5170 screenSaverDefeater
.reset (new ScreenSaverDefeater());
5173 bool Desktop::isScreenSaverEnabled()
5175 return screenSaverDefeater
== nullptr;
5178 //==============================================================================
5179 void LookAndFeel::playAlertSound()
5181 MessageBeep (MB_OK
);
5184 //==============================================================================
5185 void SystemClipboard::copyTextToClipboard (const String
& text
)
5187 if (OpenClipboard (nullptr) != 0)
5189 if (EmptyClipboard() != 0)
5191 auto bytesNeeded
= CharPointer_UTF16::getBytesRequiredFor (text
.getCharPointer()) + 4;
5193 if (bytesNeeded
> 0)
5195 if (auto bufH
= GlobalAlloc (GMEM_MOVEABLE
| GMEM_DDESHARE
| GMEM_ZEROINIT
, bytesNeeded
+ sizeof (WCHAR
)))
5197 if (auto* data
= static_cast<WCHAR
*> (GlobalLock (bufH
)))
5199 text
.copyToUTF16 (data
, bytesNeeded
);
5200 GlobalUnlock (bufH
);
5202 SetClipboardData (CF_UNICODETEXT
, bufH
);
5212 String
SystemClipboard::getTextFromClipboard()
5216 if (OpenClipboard (nullptr) != 0)
5218 if (auto bufH
= GetClipboardData (CF_UNICODETEXT
))
5220 if (auto* data
= (const WCHAR
*) GlobalLock (bufH
))
5222 result
= String (data
, (size_t) (GlobalSize (bufH
) / sizeof (WCHAR
)));
5223 GlobalUnlock (bufH
);
5233 //==============================================================================
5234 void Desktop::setKioskComponent (Component
* kioskModeComp
, bool enableOrDisable
, bool /*allowMenusAndBars*/)
5236 if (auto* tlw
= dynamic_cast<TopLevelWindow
*> (kioskModeComp
))
5237 tlw
->setUsingNativeTitleBar (! enableOrDisable
);
5239 if (kioskModeComp
!= nullptr && enableOrDisable
)
5240 kioskModeComp
->setBounds (getDisplays().getDisplayForRect (kioskModeComp
->getScreenBounds())->totalArea
);
5243 void Desktop::allowedOrientationsChanged() {}
5245 //==============================================================================
5246 static const Displays::Display
* getCurrentDisplayFromScaleFactor (HWND hwnd
)
5248 Array
<const Displays::Display
*> candidateDisplays
;
5250 const auto scaleToLookFor
= [&]
5252 if (auto* peer
= HWNDComponentPeer::getOwnerOfWindow (hwnd
))
5253 return peer
->getPlatformScaleFactor();
5255 return getScaleFactorForWindow (hwnd
);
5258 auto globalScale
= Desktop::getInstance().getGlobalScaleFactor();
5260 for (auto& d
: Desktop::getInstance().getDisplays().displays
)
5261 if (approximatelyEqual (d
.scale
/ globalScale
, scaleToLookFor
))
5262 candidateDisplays
.add (&d
);
5264 if (candidateDisplays
.size() > 0)
5266 if (candidateDisplays
.size() == 1)
5267 return candidateDisplays
[0];
5269 const auto bounds
= [&]
5271 if (auto* peer
= HWNDComponentPeer::getOwnerOfWindow (hwnd
))
5272 return peer
->getComponent().getTopLevelComponent()->getBounds();
5274 return Desktop::getInstance().getDisplays().physicalToLogical (rectangleFromRECT (getWindowScreenRect (hwnd
)));
5277 const Displays::Display
* retVal
= nullptr;
5280 for (auto* d
: candidateDisplays
)
5282 auto intersection
= d
->totalArea
.getIntersection (bounds
);
5283 auto area
= intersection
.getWidth() * intersection
.getHeight();
5292 if (retVal
!= nullptr)
5296 return Desktop::getInstance().getDisplays().getPrimaryDisplay();
5299 //==============================================================================
5302 MonitorInfo (bool main
, RECT totalArea
, RECT workArea
, double d
) noexcept
5304 totalAreaRect (totalArea
),
5305 workAreaRect (workArea
),
5311 RECT totalAreaRect
, workAreaRect
;
5315 static BOOL CALLBACK
enumMonitorsProc (HMONITOR hm
, HDC
, LPRECT
, LPARAM userInfo
)
5317 MONITORINFO info
= {};
5318 info
.cbSize
= sizeof (info
);
5319 GetMonitorInfo (hm
, &info
);
5321 auto isMain
= (info
.dwFlags
& 1 /* MONITORINFOF_PRIMARY */) != 0;
5324 if (getDPIForMonitor
!= nullptr)
5326 UINT dpiX
= 0, dpiY
= 0;
5328 if (SUCCEEDED (getDPIForMonitor (hm
, MDT_Default
, &dpiX
, &dpiY
)))
5329 dpi
= (dpiX
+ dpiY
) / 2.0;
5332 ((Array
<MonitorInfo
>*) userInfo
)->add ({ isMain
, info
.rcMonitor
, info
.rcWork
, dpi
});
5336 void Displays::findDisplays (float masterScale
)
5340 Array
<MonitorInfo
> monitors
;
5341 EnumDisplayMonitors (nullptr, nullptr, &enumMonitorsProc
, (LPARAM
) &monitors
);
5343 auto globalDPI
= getGlobalDPI();
5345 if (monitors
.size() == 0)
5347 auto windowRect
= getWindowScreenRect (GetDesktopWindow());
5348 monitors
.add ({ true, windowRect
, windowRect
, globalDPI
});
5351 // make sure the first in the list is the main monitor
5352 for (int i
= 1; i
< monitors
.size(); ++i
)
5353 if (monitors
.getReference (i
).isMain
)
5354 monitors
.swap (i
, 0);
5356 for (auto& monitor
: monitors
)
5360 d
.isMain
= monitor
.isMain
;
5361 d
.dpi
= monitor
.dpi
;
5366 d
.scale
= masterScale
;
5370 d
.scale
= (d
.dpi
/ USER_DEFAULT_SCREEN_DPI
) * (masterScale
/ Desktop::getDefaultMasterScale());
5373 d
.totalArea
= rectangleFromRECT (monitor
.totalAreaRect
);
5374 d
.userArea
= rectangleFromRECT (monitor
.workAreaRect
);
5379 #if JUCE_WIN_PER_MONITOR_DPI_AWARE
5380 if (isPerMonitorDPIAwareThread())
5385 for (auto& d
: displays
)
5387 d
.totalArea
/= masterScale
;
5388 d
.userArea
/= masterScale
;
5393 //==============================================================================
5394 static HICON
extractFileHICON (const File
& file
)
5397 WCHAR name
[MAX_PATH
* 2];
5398 file
.getFullPathName().copyToUTF16 (name
, sizeof (name
));
5400 return ExtractAssociatedIcon ((HINSTANCE
) Process::getCurrentModuleInstanceHandle(),
5404 Image
juce_createIconForFile (const File
& file
)
5408 if (auto icon
= extractFileHICON (file
))
5410 image
= IconConverters::createImageFromHICON (icon
);
5417 //==============================================================================
5418 class MouseCursor::PlatformSpecificHandle
5421 explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type
)
5422 : impl (makeHandle (type
)) {}
5424 explicit PlatformSpecificHandle (const CustomMouseCursorInfo
& info
)
5425 : impl (makeHandle (info
)) {}
5427 static void showInWindow (PlatformSpecificHandle
* handle
, ComponentPeer
* peer
)
5431 if (handle
!= nullptr && handle
->impl
!= nullptr && peer
!= nullptr)
5432 return handle
->impl
->getCursor (*peer
);
5434 return LoadCursor (nullptr, IDC_ARROW
);
5441 virtual ~Impl() = default;
5442 virtual HCURSOR
getCursor (ComponentPeer
&) = 0;
5445 class BuiltinImpl
: public Impl
5448 explicit BuiltinImpl (HCURSOR cursorIn
)
5449 : cursor (cursorIn
) {}
5451 HCURSOR
getCursor (ComponentPeer
&) override
{ return cursor
; }
5457 class ImageImpl
: public Impl
5460 explicit ImageImpl (const CustomMouseCursorInfo
& infoIn
) : info (infoIn
) {}
5462 ~ImageImpl() override
5464 for (auto& pair
: cursorsBySize
)
5465 DestroyCursor (pair
.second
);
5468 HCURSOR
getCursor (ComponentPeer
& peer
) override
5470 JUCE_ASSERT_MESSAGE_THREAD
;
5472 static auto getCursorSize
= getCursorSizeForPeerFunction();
5474 const auto size
= getCursorSize (peer
);
5475 const auto iter
= cursorsBySize
.find (size
);
5477 if (iter
!= cursorsBySize
.end())
5478 return iter
->second
;
5480 const auto logicalSize
= info
.image
.getScaledBounds();
5481 const auto scale
= (float) size
/ (float) unityCursorSize
;
5482 const auto physicalSize
= logicalSize
* scale
;
5484 const auto& image
= info
.image
.getImage();
5485 const auto rescaled
= image
.rescaled (roundToInt ((float) physicalSize
.getWidth()),
5486 roundToInt ((float) physicalSize
.getHeight()));
5488 const auto effectiveScale
= rescaled
.getWidth() / logicalSize
.getWidth();
5490 const auto hx
= jlimit (0, rescaled
.getWidth(), roundToInt ((float) info
.hotspot
.x
* effectiveScale
));
5491 const auto hy
= jlimit (0, rescaled
.getHeight(), roundToInt ((float) info
.hotspot
.y
* effectiveScale
));
5493 return cursorsBySize
.emplace (size
, IconConverters::createHICONFromImage (rescaled
, false, hx
, hy
)).first
->second
;
5497 const CustomMouseCursorInfo info
;
5498 std::map
<int, HCURSOR
> cursorsBySize
;
5501 static auto getCursorSizeForPeerFunction() -> int (*) (ComponentPeer
&)
5503 static const auto getDpiForMonitor
= []() -> GetDPIForMonitorFunc
5505 constexpr auto library
= "SHCore.dll";
5506 LoadLibraryA (library
);
5508 if (auto* handle
= GetModuleHandleA (library
))
5509 return (GetDPIForMonitorFunc
) GetProcAddress (handle
, "GetDpiForMonitor");
5514 static const auto getSystemMetricsForDpi
= []() -> GetSystemMetricsForDpiFunc
5516 constexpr auto library
= "User32.dll";
5517 LoadLibraryA (library
);
5519 if (auto* handle
= GetModuleHandleA (library
))
5520 return (GetSystemMetricsForDpiFunc
) GetProcAddress (handle
, "GetSystemMetricsForDpi");
5525 if (getDpiForMonitor
== nullptr || getSystemMetricsForDpi
== nullptr)
5526 return [] (ComponentPeer
&) { return unityCursorSize
; };
5528 return [] (ComponentPeer
& p
)
5530 const ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter
{ p
.getNativeHandle() };
5532 UINT dpiX
= 0, dpiY
= 0;
5534 if (auto* monitor
= MonitorFromWindow ((HWND
) p
.getNativeHandle(), MONITOR_DEFAULTTONULL
))
5535 if (SUCCEEDED (getDpiForMonitor (monitor
, MDT_Default
, &dpiX
, &dpiY
)))
5536 return getSystemMetricsForDpi (SM_CXCURSOR
, dpiX
);
5538 return unityCursorSize
;
5542 static constexpr auto unityCursorSize
= 32;
5544 static std::unique_ptr
<Impl
> makeHandle (const CustomMouseCursorInfo
& info
)
5546 return std::make_unique
<ImageImpl
> (info
);
5549 static std::unique_ptr
<Impl
> makeHandle (const MouseCursor::StandardCursorType type
)
5551 LPCTSTR cursorName
= IDC_ARROW
;
5556 case ParentCursor
: break;
5557 case NoCursor
: return std::make_unique
<BuiltinImpl
> (nullptr);
5558 case WaitCursor
: cursorName
= IDC_WAIT
; break;
5559 case IBeamCursor
: cursorName
= IDC_IBEAM
; break;
5560 case PointingHandCursor
: cursorName
= MAKEINTRESOURCE(32649); break;
5561 case CrosshairCursor
: cursorName
= IDC_CROSS
; break;
5563 case LeftRightResizeCursor
:
5564 case LeftEdgeResizeCursor
:
5565 case RightEdgeResizeCursor
: cursorName
= IDC_SIZEWE
; break;
5567 case UpDownResizeCursor
:
5568 case TopEdgeResizeCursor
:
5569 case BottomEdgeResizeCursor
: cursorName
= IDC_SIZENS
; break;
5571 case TopLeftCornerResizeCursor
:
5572 case BottomRightCornerResizeCursor
: cursorName
= IDC_SIZENWSE
; break;
5574 case TopRightCornerResizeCursor
:
5575 case BottomLeftCornerResizeCursor
: cursorName
= IDC_SIZENESW
; break;
5577 case UpDownLeftRightResizeCursor
: cursorName
= IDC_SIZEALL
; break;
5579 case DraggingHandCursor
:
5581 static const unsigned char dragHandData
[]
5582 { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
5583 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,
5584 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
5586 return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (dragHandData
, sizeof (dragHandData
))), { 8, 7 } });
5591 static const unsigned char copyCursorData
[]
5592 { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0,
5593 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,86,91,202,142,
5594 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83,
5595 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 };
5597 return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (copyCursorData
, sizeof (copyCursorData
))), { 1, 3 } });
5600 case NumStandardCursorTypes
: JUCE_FALLTHROUGH
5602 jassertfalse
; break;
5605 return std::make_unique
<BuiltinImpl
> ([&]
5607 if (auto* c
= LoadCursor (nullptr, cursorName
))
5610 return LoadCursor (nullptr, IDC_ARROW
);
5614 std::unique_ptr
<Impl
> impl
;
5617 //==============================================================================
5618 JUCE_END_IGNORE_WARNINGS_GCC_LIKE