2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "WinSystemWin32.h"
11 #include "ServiceBroker.h"
12 #include "VideoSyncD3D.h"
13 #include "WIN32Util.h"
14 #include "WinEventsWin32.h"
15 #include "application/Application.h"
16 #include "cores/AudioEngine/AESinkFactory.h"
17 #include "cores/AudioEngine/Sinks/AESinkDirectSound.h"
18 #include "cores/AudioEngine/Sinks/AESinkWASAPI.h"
19 #include "filesystem/File.h"
20 #include "filesystem/SpecialProtocol.h"
21 #include "messaging/ApplicationMessenger.h"
22 #include "platform/Environment.h"
23 #include "rendering/dx/ScreenshotSurfaceWindows.h"
25 #include "settings/AdvancedSettings.h"
26 #include "settings/DisplaySettings.h"
27 #include "settings/Settings.h"
28 #include "settings/SettingsComponent.h"
29 #include "utils/StringUtils.h"
30 #include "utils/SystemInfo.h"
31 #include "utils/log.h"
32 #include "windowing/GraphicContext.h"
33 #include "windowing/windows/Win32DPMSSupport.h"
35 #include "platform/win32/CharsetConverter.h"
36 #include "platform/win32/input/IRServerSuite.h"
44 using namespace std::chrono_literals
;
46 const char* CWinSystemWin32::SETTING_WINDOW_TOP
= "window.top";
47 const char* CWinSystemWin32::SETTING_WINDOW_LEFT
= "window.left";
49 CWinSystemWin32::CWinSystemWin32()
53 , m_hInstance(nullptr)
55 , m_ValidWindowedPosition(false)
56 , m_IsAlteringWindow(false)
57 , m_delayDispReset(false)
58 , m_state(WINDOW_STATE_WINDOWED
)
59 , m_fullscreenState(WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW
)
60 , m_windowState(WINDOW_WINDOW_STATE_WINDOWED
)
61 , m_windowStyle(WINDOWED_STYLE
)
62 , m_windowExStyle(WINDOWED_EX_STYLE
)
66 std::string cacert
= CEnvironment::getenv("SSL_CERT_FILE");
67 if (cacert
.empty() || !XFILE::CFile::Exists(cacert
))
69 cacert
= CSpecialProtocol::TranslatePath("special://xbmc/system/certs/cacert.pem");
70 if (XFILE::CFile::Exists(cacert
))
71 CEnvironment::setenv("SSL_CERT_FILE", cacert
.c_str(), 1);
74 m_winEvents
.reset(new CWinEventsWin32());
75 AE::CAESinkFactory::ClearSinks();
76 CAESinkDirectSound::Register();
77 CAESinkWASAPI::Register();
78 CScreenshotSurfaceWindows::Register();
80 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bScanIRServer
)
82 m_irss
.reset(new CIRServerSuite());
85 m_dpms
= std::make_shared
<CWin32DPMSSupport
>();
88 CWinSystemWin32::~CWinSystemWin32()
97 bool CWinSystemWin32::InitWindowSystem()
99 if(!CWinSystemBase::InitWindowSystem())
105 bool CWinSystemWin32::DestroyWindowSystem()
109 MONITOR_DETAILS
* details
= GetDisplayDetails(m_hMonitor
);
111 RestoreDesktopResolution(details
);
116 bool CWinSystemWin32::CreateNewWindow(const std::string
& name
, bool fullScreen
, RESOLUTION_INFO
& res
)
118 using KODI::PLATFORM::WINDOWS::ToW
;
119 auto nameW
= ToW(name
);
121 m_hInstance
= static_cast<HINSTANCE
>(GetModuleHandle(nullptr));
122 if(m_hInstance
== nullptr)
123 CLog::LogF(LOGDEBUG
, " GetModuleHandle failed with {}", GetLastError());
125 UpdateStates(fullScreen
);
126 // initialize the state
127 WINDOW_STATE state
= GetState(fullScreen
);
129 m_nWidth
= res
.iWidth
;
130 m_nHeight
= res
.iHeight
;
131 m_bFullScreen
= fullScreen
;
132 m_fRefreshRate
= res
.fRefreshRate
;
134 m_hIcon
= LoadIcon(m_hInstance
, MAKEINTRESOURCE(IDI_MAIN_ICON
));
136 // Register the windows class
137 WNDCLASSEX wndClass
= {};
138 wndClass
.cbSize
= sizeof(wndClass
);
139 wndClass
.style
= CS_HREDRAW
| CS_VREDRAW
;
140 wndClass
.lpfnWndProc
= CWinEventsWin32::WndProc
;
141 wndClass
.cbClsExtra
= 0;
142 wndClass
.cbWndExtra
= 0;
143 wndClass
.hInstance
= m_hInstance
;
144 wndClass
.hIcon
= m_hIcon
;
145 wndClass
.hCursor
= LoadCursor(nullptr, IDC_ARROW
);
146 wndClass
.hbrBackground
= static_cast<HBRUSH
>(GetStockObject(BLACK_BRUSH
));
147 wndClass
.lpszMenuName
= nullptr;
148 wndClass
.lpszClassName
= nameW
.c_str();
150 if( !RegisterClassExW( &wndClass
) )
152 CLog::LogF(LOGERROR
, " RegisterClassExW failed with {}", GetLastError());
156 // put the window at desired display
157 RECT screenRect
= ScreenRect(m_hMonitor
);
158 m_nLeft
= screenRect
.left
;
159 m_nTop
= screenRect
.top
;
161 if (state
== WINDOW_STATE_WINDOWED
)
163 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
164 const int top
= settings
->GetInt(SETTING_WINDOW_TOP
);
165 const int left
= settings
->GetInt(SETTING_WINDOW_LEFT
);
166 const RECT vsRect
= GetVirtualScreenRect();
168 // we check that window is inside of virtual screen rect (sum of all monitors)
169 // top 0 left 0 is a special position that centers the window on the screen
170 if ((top
!= 0 || left
!= 0) && top
>= vsRect
.top
&& top
+ m_nHeight
<= vsRect
.bottom
&&
171 left
>= vsRect
.left
&& left
+ m_nWidth
<= vsRect
.right
)
173 // restore previous window position
179 // Windowed mode: position and size in settings and most places in Kodi
180 // are for the client part of the window.
181 RECT rcWorkArea
= GetScreenWorkArea(m_hMonitor
);
183 RECT rcNcArea
= GetNcAreaOffsets(m_windowStyle
, false, m_windowExStyle
);
184 int maxClientWidth
= (rcWorkArea
.right
- rcNcArea
.right
) - (rcWorkArea
.left
- rcNcArea
.left
);
185 int maxClientHeight
= (rcWorkArea
.bottom
- rcNcArea
.bottom
) - (rcWorkArea
.top
- rcNcArea
.top
);
187 m_nWidth
= std::min(m_nWidth
, maxClientWidth
);
188 m_nHeight
= std::min(m_nHeight
, maxClientHeight
);
189 CWinSystemBase::SetWindowResolution(m_nWidth
, m_nHeight
);
191 // center window on desktop
192 m_nLeft
= rcWorkArea
.left
- rcNcArea
.left
+ (maxClientWidth
- m_nWidth
) / 2;
193 m_nTop
= rcWorkArea
.top
- rcNcArea
.top
+ (maxClientHeight
- m_nHeight
) / 2;
195 m_ValidWindowedPosition
= true;
198 HWND hWnd
= CreateWindowExW(
213 if( hWnd
== nullptr )
215 CLog::LogF(LOGERROR
, " CreateWindow failed with {}", GetLastError());
221 DWORD dwHwndTabletProperty
=
222 TABLET_DISABLE_PENBARRELFEEDBACK
| // disables UI feedback on pen button down (circle)
223 TABLET_DISABLE_FLICKS
; // disables pen flicks (back, forward, drag down, drag up)
225 SetProp(hWnd
, MICROSOFT_TABLETPENSERVICE_PROPERTY
, &dwHwndTabletProperty
);
228 m_bWindowCreated
= true;
230 CreateBlankWindows();
236 ShowWindow( m_hWnd
, SW_SHOWDEFAULT
);
237 UpdateWindow( m_hWnd
);
239 // Configure the tray icon.
240 m_trayIcon
.cbSize
= sizeof(m_trayIcon
);
241 m_trayIcon
.hWnd
= m_hWnd
;
242 m_trayIcon
.hIcon
= m_hIcon
;
243 wcsncpy(m_trayIcon
.szTip
, nameW
.c_str(), sizeof(m_trayIcon
.szTip
) / sizeof(WCHAR
));
244 m_trayIcon
.uCallbackMessage
= TRAY_ICON_NOTIFY
;
245 m_trayIcon
.uFlags
= NIF_ICON
| NIF_TIP
| NIF_MESSAGE
;
250 bool CWinSystemWin32::CreateBlankWindows()
254 wcex
.cbSize
= sizeof(WNDCLASSEX
);
255 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
256 wcex
.lpfnWndProc
= DefWindowProc
;
259 wcex
.hInstance
= nullptr;
261 wcex
.hCursor
= nullptr;
262 wcex
.hbrBackground
= static_cast<HBRUSH
>(CreateSolidBrush(RGB(0, 0, 0)));
263 wcex
.lpszMenuName
= nullptr;
264 wcex
.lpszClassName
= L
"BlankWindowClass";
265 wcex
.hIconSm
= nullptr;
267 // Now we can go ahead and register our new window class
268 if(!RegisterClassEx(&wcex
))
270 CLog::LogF(LOGERROR
, "RegisterClass failed with {}", GetLastError());
274 // We need as many blank windows as there are screens (minus 1)
275 for (size_t i
= 0; i
< m_displays
.size() - 1; i
++)
277 HWND hBlankWindow
= CreateWindowEx(WS_EX_TOPMOST
, L
"BlankWindowClass", L
"", WS_POPUP
| WS_DISABLED
,
278 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, nullptr, nullptr, nullptr, nullptr);
280 if (hBlankWindow
== nullptr)
282 CLog::LogF(LOGERROR
, "CreateWindowEx failed with {}", GetLastError());
286 m_hBlankWindows
.push_back(hBlankWindow
);
292 bool CWinSystemWin32::BlankNonActiveMonitors(bool bBlank
)
294 if (m_hBlankWindows
.empty())
299 for (unsigned int i
=0; i
< m_hBlankWindows
.size(); i
++)
300 ShowWindow(m_hBlankWindows
[i
], SW_HIDE
);
304 // Move a blank window in front of every display, except the current display.
305 for (size_t i
= 0, j
= 0; i
< m_displays
.size(); ++i
)
307 MONITOR_DETAILS
& details
= m_displays
[i
];
308 if (details
.hMonitor
== m_hMonitor
)
311 RECT rBounds
= ScreenRect(details
.hMonitor
);
312 // move and resize the window
313 SetWindowPos(m_hBlankWindows
[j
], nullptr, rBounds
.left
, rBounds
.top
,
314 rBounds
.right
- rBounds
.left
, rBounds
.bottom
- rBounds
.top
,
317 ShowWindow(m_hBlankWindows
[j
], SW_SHOW
| SW_SHOWNOACTIVATE
);
322 SetForegroundWindow(m_hWnd
);
327 bool CWinSystemWin32::CenterWindow()
329 RESOLUTION_INFO DesktopRes
= CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP
);
331 m_nLeft
= (DesktopRes
.iWidth
/ 2) - (m_nWidth
/ 2);
332 m_nTop
= (DesktopRes
.iHeight
/ 2) - (m_nHeight
/ 2);
337 rc
.right
= rc
.left
+ m_nWidth
;
338 rc
.bottom
= rc
.top
+ m_nHeight
;
339 AdjustWindowRect( &rc
, WS_OVERLAPPEDWINDOW
, false );
341 SetWindowPos(m_hWnd
, nullptr, rc
.left
, rc
.top
, 0, 0, SWP_NOSIZE
);
346 bool CWinSystemWin32::ResizeWindow(int newWidth
, int newHeight
, int newLeft
, int newTop
)
349 m_nHeight
= newHeight
;
362 void CWinSystemWin32::FinishWindowResize(int newWidth
, int newHeight
)
365 m_nHeight
= newHeight
;
368 void CWinSystemWin32::ForceFullScreen(const RESOLUTION_INFO
& resInfo
)
370 ResizeWindow(resInfo
.iScreenWidth
, resInfo
.iScreenHeight
, 0, 0);
373 void CWinSystemWin32::AdjustWindow(bool forceResize
)
375 CLog::LogF(LOGDEBUG
, "adjusting window if required.");
380 if (m_state
== WINDOW_STATE_FULLSCREEN_WINDOW
|| m_state
== WINDOW_STATE_FULLSCREEN
)
382 windowAfter
= HWND_TOP
;
383 rc
= ScreenRect(m_hMonitor
);
385 else // m_state == WINDOW_STATE_WINDOWED
387 windowAfter
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_alwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
;
389 if (!m_ValidWindowedPosition
)
391 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
392 const int top
= settings
->GetInt(SETTING_WINDOW_TOP
);
393 const int left
= settings
->GetInt(SETTING_WINDOW_LEFT
);
394 const RECT vsRect
= GetVirtualScreenRect();
396 // we check that window is inside of virtual screen rect (sum of all monitors)
397 // top 0 left 0 is a special position that centers the window on the screen
398 if ((top
!= 0 || left
!= 0) && top
>= vsRect
.top
&& top
+ m_nHeight
<= vsRect
.bottom
&&
399 left
>= vsRect
.left
&& left
+ m_nWidth
<= vsRect
.right
)
401 // restore previous window position
407 // Windowed mode: position and size in settings and most places in Kodi
408 // are for the client part of the window.
409 RECT rcWorkArea
= GetScreenWorkArea(m_hMonitor
);
411 RECT rcNcArea
= GetNcAreaOffsets(m_windowStyle
, false, m_windowExStyle
);
413 (rcWorkArea
.right
- rcNcArea
.right
) - (rcWorkArea
.left
- rcNcArea
.left
);
414 int maxClientHeight
=
415 (rcWorkArea
.bottom
- rcNcArea
.bottom
) - (rcWorkArea
.top
- rcNcArea
.top
);
417 m_nWidth
= std::min(m_nWidth
, maxClientWidth
);
418 m_nHeight
= std::min(m_nHeight
, maxClientHeight
);
419 CWinSystemBase::SetWindowResolution(m_nWidth
, m_nHeight
);
421 // center window on desktop
422 m_nLeft
= rcWorkArea
.left
- rcNcArea
.left
+ (maxClientWidth
- m_nWidth
) / 2;
423 m_nTop
= rcWorkArea
.top
- rcNcArea
.top
+ (maxClientHeight
- m_nHeight
) / 2;
425 m_ValidWindowedPosition
= true;
429 rc
.right
= m_nLeft
+ m_nWidth
;
431 rc
.bottom
= m_nTop
+ m_nHeight
;
433 HMONITOR hMon
= MonitorFromRect(&rc
, MONITOR_DEFAULTTONULL
);
434 HMONITOR hMon2
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTOPRIMARY
);
436 if (!m_ValidWindowedPosition
|| hMon
== nullptr || hMon
!= hMon2
)
438 // centering window at desktop
439 RECT newScreenRect
= ScreenRect(hMon2
);
440 rc
.left
= m_nLeft
= newScreenRect
.left
+ ((newScreenRect
.right
- newScreenRect
.left
) / 2) - (m_nWidth
/ 2);
441 rc
.top
= m_nTop
= newScreenRect
.top
+ ((newScreenRect
.bottom
- newScreenRect
.top
) / 2) - (m_nHeight
/ 2);
442 rc
.right
= m_nLeft
+ m_nWidth
;
443 rc
.bottom
= m_nTop
+ m_nHeight
;
444 m_ValidWindowedPosition
= true;
446 AdjustWindowRectEx(&rc
, m_windowStyle
, false, m_windowExStyle
);
450 wi
.cbSize
= sizeof(WINDOWINFO
);
451 if (!GetWindowInfo(m_hWnd
, &wi
))
453 CLog::LogF(LOGERROR
, "GetWindowInfo failed with {}", GetLastError());
456 RECT wr
= wi
.rcWindow
;
458 if ( wr
.bottom
- wr
.top
== rc
.bottom
- rc
.top
459 && wr
.right
- wr
.left
== rc
.right
- rc
.left
460 && (wi
.dwStyle
& WS_CAPTION
) == (m_windowStyle
& WS_CAPTION
)
466 //Sets the window style
468 SetWindowLongPtr( m_hWnd
, GWL_STYLE
, m_windowStyle
);
470 //Sets the window ex style
472 SetWindowLongPtr( m_hWnd
, GWL_EXSTYLE
, m_windowExStyle
);
475 CLog::LogF(LOGDEBUG
, "resizing due to size change ({},{},{},{}{})->({},{},{},{}{})", wr
.left
,
476 wr
.top
, wr
.right
, wr
.bottom
, (wi
.dwStyle
& WS_CAPTION
) ? "" : " fullscreen", rc
.left
,
477 rc
.top
, rc
.right
, rc
.bottom
, (m_windowStyle
& WS_CAPTION
) ? "" : " fullscreen");
485 SWP_SHOWWINDOW
| SWP_DRAWFRAME
489 void CWinSystemWin32::CenterCursor() const
494 //Gets the client rect, then translates it to screen coordinates
495 //so that SetCursorPos isn't called with relative x and y values
496 GetClientRect(m_hWnd
, &rect
);
497 ClientToScreen(m_hWnd
, &point
);
499 rect
.left
+= point
.x
;
500 rect
.right
+= point
.x
;
502 rect
.bottom
+= point
.y
;
504 int x
= rect
.left
+ (rect
.right
- rect
.left
) / 2;
505 int y
= rect
.top
+ (rect
.bottom
- rect
.top
) / 2;
510 bool CWinSystemWin32::SetFullScreen(bool fullScreen
, RESOLUTION_INFO
& res
, bool blankOtherDisplays
)
512 // Initialisation not finished, pretend the function succeeded
516 CWinSystemWin32::UpdateStates(fullScreen
);
517 WINDOW_STATE state
= GetState(fullScreen
);
519 CLog::LogF(LOGDEBUG
, "({}) with size {}x{}, refresh {:f}{}", window_state_names
[state
],
520 res
.iWidth
, res
.iHeight
, res
.fRefreshRate
,
521 (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
) ? "i" : "");
523 // oldMonitor may be NULL if it's powered off or not available due windows settings
524 MONITOR_DETAILS
* oldMonitor
= GetDisplayDetails(m_hMonitor
);
525 MONITOR_DETAILS
* newMonitor
= GetDisplayDetails(res
.strOutput
);
527 bool forceChange
= false; // resolution/display is changed but window state isn't changed
528 bool changeScreen
= false; // display is changed
529 bool stereoChange
= IsStereoEnabled() != (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED
);
531 if (m_nWidth
!= res
.iWidth
|| m_nHeight
!= res
.iHeight
|| m_fRefreshRate
!= res
.fRefreshRate
||
532 !oldMonitor
|| oldMonitor
->hMonitor
!= newMonitor
->hMonitor
|| stereoChange
||
535 if (!oldMonitor
|| oldMonitor
->hMonitor
!= newMonitor
->hMonitor
)
540 if (state
== m_state
&& !forceChange
)
542 m_bBlankOtherDisplay
= blankOtherDisplays
;
543 BlankNonActiveMonitors(m_bBlankOtherDisplay
);
547 // entering to stereo mode, limit resolution to 1080p@23.976
548 if (stereoChange
&& !IsStereoEnabled() && res
.iWidth
> 1280)
550 res
= CDisplaySettings::GetInstance().GetResolutionInfo(
551 CResolutionUtils::ChooseBestResolution(24.f
/ 1.001f
, 1920, 1080, true));
554 if (m_state
== WINDOW_STATE_WINDOWED
)
557 wi
.cbSize
= sizeof(WINDOWINFO
);
558 if (GetWindowInfo(m_hWnd
, &wi
) && wi
.rcClient
.top
> 0)
560 m_nLeft
= wi
.rcClient
.left
;
561 m_nTop
= wi
.rcClient
.top
;
562 m_ValidWindowedPosition
= true;
566 m_IsAlteringWindow
= true;
571 // before we changing display we have to leave exclusive mode on "old" display
572 if (m_state
== WINDOW_STATE_FULLSCREEN
)
573 SetDeviceFullScreen(false, res
);
575 // restoring native resolution on "old" display
576 RestoreDesktopResolution(oldMonitor
);
578 // notify about screen change (it may require recreate rendering device)
579 m_fRefreshRate
= res
.fRefreshRate
; // use desired refresh for driver hook
580 OnScreenChange(newMonitor
->hMonitor
);
583 m_bFirstResChange
= false;
584 m_bFullScreen
= fullScreen
;
585 m_hMonitor
= newMonitor
->hMonitor
;
586 m_nWidth
= res
.iWidth
;
587 m_nHeight
= res
.iHeight
;
588 m_bBlankOtherDisplay
= blankOtherDisplays
;
589 m_fRefreshRate
= res
.fRefreshRate
;
591 if (state
== WINDOW_STATE_FULLSCREEN
)
593 SetForegroundWindowInternal(m_hWnd
);
596 AdjustWindow(changeScreen
);
598 // enter in exclusive mode, this will change resolution if we already in
599 SetDeviceFullScreen(true, res
);
601 else if (m_state
== WINDOW_STATE_FULLSCREEN
|| m_state
== WINDOW_STATE_FULLSCREEN_WINDOW
) // we're in fullscreen state now
603 // guess we are leaving exclusive mode, this will not an effect if we already not in
604 SetDeviceFullScreen(false, res
);
606 if (state
== WINDOW_STATE_WINDOWED
) // go to a windowed state
608 // need to restore resolution if it was changed to not native
609 // because we do not support resolution change in windowed mode
610 RestoreDesktopResolution(newMonitor
);
612 else if (state
== WINDOW_STATE_FULLSCREEN_WINDOW
) // enter fullscreen window instead
614 ChangeResolution(res
, stereoChange
);
618 AdjustWindow(changeScreen
);
620 else // we're in windowed state now
622 if (state
== WINDOW_STATE_FULLSCREEN_WINDOW
)
624 ChangeResolution(res
, stereoChange
);
627 AdjustWindow(changeScreen
);
636 BlankNonActiveMonitors(m_bBlankOtherDisplay
);
637 m_IsAlteringWindow
= false;
642 bool CWinSystemWin32::DPIChanged(WORD dpi
, RECT windowRect
) const
645 RECT resizeRect
= windowRect
;
646 HMONITOR hMon
= MonitorFromRect(&resizeRect
, MONITOR_DEFAULTTONULL
);
649 hMon
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTOPRIMARY
);
654 MONITORINFOEX monitorInfo
;
655 monitorInfo
.cbSize
= sizeof(MONITORINFOEX
);
656 GetMonitorInfoW(hMon
, &monitorInfo
);
658 if (m_state
== WINDOW_STATE_FULLSCREEN_WINDOW
||
659 m_state
== WINDOW_STATE_FULLSCREEN
)
661 resizeRect
= monitorInfo
.rcMonitor
; // the whole screen
665 RECT wr
= monitorInfo
.rcWork
; // it excludes task bar
666 long wrWidth
= wr
.right
- wr
.left
;
667 long wrHeight
= wr
.bottom
- wr
.top
;
668 long resizeWidth
= resizeRect
.right
- resizeRect
.left
;
669 long resizeHeight
= resizeRect
.bottom
- resizeRect
.top
;
671 if (resizeWidth
> wrWidth
)
673 resizeRect
.right
= resizeRect
.left
+ wrWidth
;
676 // make sure suggested windows size is not taller or wider than working area of new monitor (considers the toolbar)
677 if (resizeHeight
> wrHeight
)
679 resizeRect
.bottom
= resizeRect
.top
+ wrHeight
;
684 // resize the window to the suggested size. Will generate a WM_SIZE event
689 resizeRect
.right
- resizeRect
.left
,
690 resizeRect
.bottom
- resizeRect
.top
,
691 SWP_NOZORDER
| SWP_NOACTIVATE
);
696 void CWinSystemWin32::SetMinimized(bool minimized
)
698 const auto advancedSettings
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
700 if (advancedSettings
->m_minimizeToTray
)
704 Shell_NotifyIcon(NIM_ADD
, &m_trayIcon
);
705 ShowWindow(m_hWnd
, SW_HIDE
);
709 Shell_NotifyIcon(NIM_DELETE
, &m_trayIcon
);
710 ShowWindow(m_hWnd
, SW_RESTORE
);
714 m_bMinimized
= minimized
;
717 std::vector
<std::string
> CWinSystemWin32::GetConnectedOutputs()
719 std::vector
<std::string
> outputs
;
721 for (auto& display
: m_displays
)
723 outputs
.emplace_back(KODI::PLATFORM::WINDOWS::FromW(display
.MonitorNameW
));
729 void CWinSystemWin32::RestoreDesktopResolution(MONITOR_DETAILS
* details
)
734 RESOLUTION_INFO info
;
735 info
.iWidth
= details
->ScreenWidth
;
736 info
.iHeight
= details
->ScreenHeight
;
737 if ((details
->RefreshRate
+ 1) % 24 == 0 || (details
->RefreshRate
+ 1) % 30 == 0)
738 info
.fRefreshRate
= static_cast<float>(details
->RefreshRate
+ 1) / 1.001f
;
740 info
.fRefreshRate
= static_cast<float>(details
->RefreshRate
);
741 info
.strOutput
= KODI::PLATFORM::WINDOWS::FromW(details
->DeviceNameW
);
742 info
.dwFlags
= details
->Interlaced
? D3DPRESENTFLAG_INTERLACED
: 0;
744 CLog::LogF(LOGDEBUG
, "restoring desktop resolution for '{}'.", KODI::PLATFORM::WINDOWS::FromW(details
->MonitorNameW
));
745 ChangeResolution(info
);
748 MONITOR_DETAILS
* CWinSystemWin32::GetDisplayDetails(const std::string
& name
)
750 using KODI::PLATFORM::WINDOWS::ToW
;
752 if (!name
.empty() && name
!= "Default")
754 std::wstring nameW
= ToW(name
);
755 auto it
= std::find_if(m_displays
.begin(), m_displays
.end(), [&nameW
](MONITOR_DETAILS
& m
)
757 if (nameW
[0] == '\\') // name is device name
758 return m
.DeviceNameW
== nameW
;
759 else if (m
.MonitorNameW
== nameW
)
761 else if (m
.DeviceStringW
== nameW
)
765 if (it
!= m_displays
.end())
769 // fallback to primary
770 auto it
= std::find_if(m_displays
.begin(), m_displays
.end(), [](MONITOR_DETAILS
& m
)
774 if (it
!= m_displays
.end())
781 MONITOR_DETAILS
* CWinSystemWin32::GetDisplayDetails(HMONITOR handle
)
783 auto it
= std::find_if(m_displays
.begin(), m_displays
.end(), [&handle
](MONITOR_DETAILS
& m
)
785 return m
.hMonitor
== handle
;
787 if (it
!= m_displays
.end())
793 RECT
CWinSystemWin32::ScreenRect(HMONITOR handle
)
795 const MONITOR_DETAILS
* details
= GetDisplayDetails(handle
);
798 CLog::LogF(LOGERROR
, "no monitor found for handle");
802 DEVMODEW sDevMode
= {};
803 sDevMode
.dmSize
= sizeof(sDevMode
);
804 if(!EnumDisplaySettingsW(details
->DeviceNameW
.c_str(), ENUM_CURRENT_SETTINGS
, &sDevMode
))
805 CLog::LogF(LOGERROR
, " EnumDisplaySettings failed with {}", GetLastError());
808 rc
.left
= sDevMode
.dmPosition
.x
;
809 rc
.right
= sDevMode
.dmPosition
.x
+ sDevMode
.dmPelsWidth
;
810 rc
.top
= sDevMode
.dmPosition
.y
;
811 rc
.bottom
= sDevMode
.dmPosition
.y
+ sDevMode
.dmPelsHeight
;
816 void CWinSystemWin32::GetConnectedDisplays(std::vector
<MONITOR_DETAILS
>& outputs
)
818 using KODI::PLATFORM::WINDOWS::FromW
;
820 DISPLAY_DEVICEW ddAdapter
= {};
821 ddAdapter
.cb
= sizeof(ddAdapter
);
823 for (DWORD adapter
= 0; EnumDisplayDevicesW(nullptr, adapter
, &ddAdapter
, 0); ++adapter
)
825 // Exclude displays that are not part of the windows desktop. Using them is too different: no windows,
826 // direct access with GDI CreateDC() or DirectDraw for example. So it may be possible to play video, but GUI?
827 if ((ddAdapter
.StateFlags
& DISPLAY_DEVICE_MIRRORING_DRIVER
)
828 || !(ddAdapter
.StateFlags
& DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
))
831 DISPLAY_DEVICEW ddMon
= {};
832 ddMon
.cb
= sizeof(ddMon
);
833 bool foundScreen
= false;
836 // Just look for the first active output, we're actually only interested in the information at the adapter level.
837 for (; EnumDisplayDevicesW(ddAdapter
.DeviceName
, screen
, &ddMon
, 0); ++screen
)
839 if (ddMon
.StateFlags
& (DISPLAY_DEVICE_ACTIVE
| DISPLAY_DEVICE_ATTACHED
))
846 // Remoting returns no screens. Handle with a dummy screen.
847 if (!foundScreen
&& screen
== 0)
849 lstrcpyW(ddMon
.DeviceString
, L
"Dummy Monitor"); // safe: large static array
855 // get information about the display's current position and display mode
857 dm
.dmSize
= sizeof(dm
);
858 if (EnumDisplaySettingsExW(ddAdapter
.DeviceName
, ENUM_CURRENT_SETTINGS
, &dm
, 0) == FALSE
)
859 EnumDisplaySettingsExW(ddAdapter
.DeviceName
, ENUM_REGISTRY_SETTINGS
, &dm
, 0);
861 POINT pt
= { dm
.dmPosition
.x
, dm
.dmPosition
.y
};
862 HMONITOR hm
= MonitorFromPoint(pt
, MONITOR_DEFAULTTONULL
);
864 MONITOR_DETAILS md
= {};
869 md
.DeviceStringW
= std::wstring(ddMon
.DeviceString
) + L
" #" + std::to_wstring(num
++);
870 } while (std::any_of(outputs
.begin(), outputs
.end(), [&](MONITOR_DETAILS
& m
) {
871 return m
.DeviceStringW
== md
.DeviceStringW
;
874 std::wstring displayName
= CWIN32Util::GetDisplayFriendlyName(ddAdapter
.DeviceName
);
875 if (displayName
.empty())
876 displayName
= std::wstring(ddMon
.DeviceString
);
880 // `Pretty Monitor Name #N`
881 md
.MonitorNameW
= displayName
+ L
" #" + std::to_wstring(num
++);
882 } while (std::any_of(outputs
.begin(), outputs
.end(),
883 [&](MONITOR_DETAILS
& m
) { return m
.MonitorNameW
== md
.MonitorNameW
; }));
885 md
.CardNameW
= ddAdapter
.DeviceString
;
886 md
.DeviceNameW
= ddAdapter
.DeviceName
;
887 md
.ScreenWidth
= dm
.dmPelsWidth
;
888 md
.ScreenHeight
= dm
.dmPelsHeight
;
890 md
.RefreshRate
= dm
.dmDisplayFrequency
;
891 md
.Bpp
= dm
.dmBitsPerPel
;
892 md
.Interlaced
= (dm
.dmDisplayFlags
& DM_INTERLACED
) ? true : false;
895 mi
.cbSize
= sizeof(mi
);
896 if (GetMonitorInfoW(hm
, &mi
) && (mi
.dwFlags
& MONITORINFOF_PRIMARY
))
899 outputs
.push_back(md
);
904 bool CWinSystemWin32::ChangeResolution(const RESOLUTION_INFO
& res
, bool forceChange
/*= false*/)
906 using KODI::PLATFORM::WINDOWS::ToW
;
907 std::wstring outputW
= ToW(res
.strOutput
);
909 DEVMODEW sDevMode
= {};
910 sDevMode
.dmSize
= sizeof(sDevMode
);
912 // If we can't read the current resolution or any detail of the resolution is different than res
913 if (!EnumDisplaySettingsW(outputW
.c_str(), ENUM_CURRENT_SETTINGS
, &sDevMode
) ||
914 sDevMode
.dmPelsWidth
!= res
.iWidth
|| sDevMode
.dmPelsHeight
!= res
.iHeight
||
915 sDevMode
.dmDisplayFrequency
!= static_cast<int>(res
.fRefreshRate
) ||
916 ((sDevMode
.dmDisplayFlags
& DM_INTERLACED
) && !(res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
)) ||
917 (!(sDevMode
.dmDisplayFlags
& DM_INTERLACED
) && (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
))
920 ZeroMemory(&sDevMode
, sizeof(sDevMode
));
921 sDevMode
.dmSize
= sizeof(sDevMode
);
922 sDevMode
.dmDriverExtra
= 0;
923 sDevMode
.dmPelsWidth
= res
.iWidth
;
924 sDevMode
.dmPelsHeight
= res
.iHeight
;
925 sDevMode
.dmDisplayFrequency
= static_cast<int>(res
.fRefreshRate
);
926 sDevMode
.dmDisplayFlags
= (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
) ? DM_INTERLACED
: 0;
927 sDevMode
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
| DM_DISPLAYFREQUENCY
| DM_DISPLAYFLAGS
;
930 bool bResChanged
= false;
932 // Windows 8 refresh rate workaround for 24.0, 48.0 and 60.0 Hz
933 if (res
.fRefreshRate
== 24.0 || res
.fRefreshRate
== 48.0 || res
.fRefreshRate
== 60.0)
935 CLog::LogF(LOGDEBUG
, "Using Windows 8+ workaround for refresh rate {} Hz",
936 static_cast<int>(res
.fRefreshRate
));
938 // Get current resolution stored in registry
939 DEVMODEW sDevModeRegistry
= {};
940 sDevModeRegistry
.dmSize
= sizeof(sDevModeRegistry
);
941 if (EnumDisplaySettingsW(outputW
.c_str(), ENUM_REGISTRY_SETTINGS
, &sDevModeRegistry
))
943 // Set requested mode in registry without actually changing resolution
944 rc
= ChangeDisplaySettingsExW(outputW
.c_str(), &sDevMode
, nullptr, CDS_UPDATEREGISTRY
| CDS_NORESET
, nullptr);
945 if (rc
== DISP_CHANGE_SUCCESSFUL
)
947 // Change resolution based on registry setting
948 rc
= ChangeDisplaySettingsExW(outputW
.c_str(), nullptr, nullptr, CDS_FULLSCREEN
, nullptr);
949 if (rc
== DISP_CHANGE_SUCCESSFUL
)
954 "ChangeDisplaySettingsEx (W8+ change resolution) failed with {}, using fallback",
957 // Restore registry with original values
958 sDevModeRegistry
.dmSize
= sizeof(sDevModeRegistry
);
959 sDevModeRegistry
.dmDriverExtra
= 0;
960 sDevModeRegistry
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
| DM_DISPLAYFREQUENCY
| DM_DISPLAYFLAGS
;
961 rc
= ChangeDisplaySettingsExW(outputW
.c_str(), &sDevModeRegistry
, nullptr, CDS_UPDATEREGISTRY
| CDS_NORESET
, nullptr);
962 if (rc
!= DISP_CHANGE_SUCCESSFUL
)
963 CLog::LogF(LOGERROR
, "ChangeDisplaySettingsEx (W8+ restore registry) failed with {}",
968 "ChangeDisplaySettingsEx (W8+ set registry) failed with {}, using fallback",
972 CLog::LogF(LOGERROR
, "Unable to retrieve registry settings for Windows 8+ workaround, using fallback");
975 // Standard resolution change/fallback for Windows 8+ workaround
978 // CDS_FULLSCREEN is for temporary fullscreen mode and prevents icons and windows from moving
979 // to fit within the new dimensions of the desktop
980 rc
= ChangeDisplaySettingsExW(outputW
.c_str(), &sDevMode
, nullptr, CDS_FULLSCREEN
, nullptr);
981 if (rc
== DISP_CHANGE_SUCCESSFUL
)
984 CLog::LogF(LOGERROR
, "ChangeDisplaySettingsEx failed with {}", rc
);
993 // nothing to do, return success
997 void CWinSystemWin32::UpdateResolutions()
999 using KODI::PLATFORM::WINDOWS::FromW
;
1003 CWinSystemBase::UpdateResolutions();
1004 GetConnectedDisplays(m_displays
);
1006 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
1007 std::string settingsMonitor
= settings
->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR
);
1008 MONITOR_DETAILS
* details
= GetDisplayDetails(settingsMonitor
);
1011 // Migrate the setting to EDID-based name for connected and recognized screens.
1012 // Other screens may be temporarily turned off, ignore so they're used once available again
1013 if (settingsMonitor
!= FromW(details
->MonitorNameW
) &&
1014 (settingsMonitor
== FromW(details
->DeviceStringW
) ||
1015 settingsMonitor
== FromW(details
->DeviceNameW
)))
1016 CDisplaySettings::GetInstance().SetMonitor(FromW(details
->MonitorNameW
));
1019 int w
= details
->ScreenWidth
;
1020 int h
= details
->ScreenHeight
;
1021 if ((details
->RefreshRate
+ 1) % 24 == 0 || (details
->RefreshRate
+ 1) % 30 == 0)
1022 refreshRate
= static_cast<float>(details
->RefreshRate
+ 1) / 1.001f
;
1024 refreshRate
= static_cast<float>(details
->RefreshRate
);
1026 std::string strOutput
= FromW(details
->DeviceNameW
);
1027 std::string monitorName
= FromW(details
->MonitorNameW
);
1029 uint32_t dwFlags
= details
->Interlaced
? D3DPRESENTFLAG_INTERLACED
: 0;
1031 RESOLUTION_INFO
& info
= CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP
);
1032 UpdateDesktopResolution(info
, monitorName
, w
, h
, refreshRate
, dwFlags
);
1033 info
.strOutput
= strOutput
;
1035 CLog::Log(LOGINFO
, "Primary mode: {}", info
.strMode
);
1037 // erase previous stored modes
1038 CDisplaySettings::GetInstance().ClearCustomResolutions();
1040 for(int mode
= 0;; mode
++)
1042 DEVMODEW devmode
= {};
1043 devmode
.dmSize
= sizeof(devmode
);
1044 if(EnumDisplaySettingsW(details
->DeviceNameW
.c_str(), mode
, &devmode
) == 0)
1046 if(devmode
.dmBitsPerPel
!= 32)
1050 if ((devmode
.dmDisplayFrequency
+ 1) % 24 == 0 || (devmode
.dmDisplayFrequency
+ 1) % 30 == 0)
1051 refresh
= static_cast<float>(devmode
.dmDisplayFrequency
+ 1) / 1.001f
;
1053 refresh
= static_cast<float>(devmode
.dmDisplayFrequency
);
1054 dwFlags
= (devmode
.dmDisplayFlags
& DM_INTERLACED
) ? D3DPRESENTFLAG_INTERLACED
: 0;
1056 RESOLUTION_INFO res
;
1057 res
.iWidth
= devmode
.dmPelsWidth
;
1058 res
.iHeight
= devmode
.dmPelsHeight
;
1059 res
.bFullScreen
= true;
1060 res
.dwFlags
= dwFlags
;
1061 res
.fRefreshRate
= refresh
;
1062 res
.fPixelRatio
= 1.0f
;
1063 res
.iScreenWidth
= res
.iWidth
;
1064 res
.iScreenHeight
= res
.iHeight
;
1065 res
.iSubtitles
= res
.iHeight
;
1066 res
.strMode
= StringUtils::Format("{}: {}x{} @ {:.2f}Hz", monitorName
, res
.iWidth
, res
.iHeight
,
1068 GetGfxContext().ResetOverscan(res
);
1069 res
.strOutput
= strOutput
;
1071 if (AddResolution(res
))
1072 CLog::Log(LOGINFO
, "Additional mode: {}", res
.strMode
);
1075 CDisplaySettings::GetInstance().ApplyCalibrations();
1078 bool CWinSystemWin32::AddResolution(const RESOLUTION_INFO
&res
)
1080 for (unsigned int i
= RES_CUSTOM
; i
< CDisplaySettings::GetInstance().ResolutionInfoSize(); i
++)
1082 RESOLUTION_INFO
& info
= CDisplaySettings::GetInstance().GetResolutionInfo(i
);
1083 if (info
.iWidth
== res
.iWidth
1084 && info
.iHeight
== res
.iHeight
1085 && info
.iScreenWidth
== res
.iScreenWidth
1086 && info
.iScreenHeight
== res
.iScreenHeight
1087 && info
.fRefreshRate
== res
.fRefreshRate
1088 && info
.dwFlags
== res
.dwFlags
)
1089 return false; // already have this resolution
1092 CDisplaySettings::GetInstance().AddResolutionInfo(res
);
1096 void CWinSystemWin32::ShowOSMouse(bool show
)
1098 static int counter
= 0;
1099 if ((counter
< 0 && show
) || (counter
>= 0 && !show
))
1100 counter
= ShowCursor(show
);
1103 bool CWinSystemWin32::Minimize()
1105 ShowWindow(m_hWnd
, SW_MINIMIZE
);
1108 bool CWinSystemWin32::Restore()
1110 ShowWindow(m_hWnd
, SW_RESTORE
);
1113 bool CWinSystemWin32::Hide()
1115 ShowWindow(m_hWnd
, SW_HIDE
);
1118 bool CWinSystemWin32::Show(bool raise
)
1120 HWND windowAfter
= HWND_BOTTOM
;
1124 windowAfter
= HWND_TOP
;
1126 windowAfter
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_alwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
;
1129 SetWindowPos(m_hWnd
, windowAfter
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
| SWP_ASYNCWINDOWPOS
);
1130 UpdateWindow(m_hWnd
);
1134 SetForegroundWindow(m_hWnd
);
1140 void CWinSystemWin32::Register(IDispResource
*resource
)
1142 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
1143 m_resources
.push_back(resource
);
1146 void CWinSystemWin32::Unregister(IDispResource
* resource
)
1148 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
1149 std::vector
<IDispResource
*>::iterator i
= find(m_resources
.begin(), m_resources
.end(), resource
);
1150 if (i
!= m_resources
.end())
1151 m_resources
.erase(i
);
1154 void CWinSystemWin32::OnDisplayLost()
1156 CLog::LogF(LOGDEBUG
, "notify display lost event");
1159 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
1160 for (std::vector
<IDispResource
*>::iterator i
= m_resources
.begin(); i
!= m_resources
.end(); ++i
)
1161 (*i
)->OnLostDisplay();
1165 void CWinSystemWin32::OnDisplayReset()
1167 if (!m_delayDispReset
)
1169 CLog::LogF(LOGDEBUG
, "notify display reset event");
1170 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
1171 for (std::vector
<IDispResource
*>::iterator i
= m_resources
.begin(); i
!= m_resources
.end(); ++i
)
1172 (*i
)->OnResetDisplay();
1176 void CWinSystemWin32::OnDisplayBack()
1179 std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
1180 "videoscreen.delayrefreshchange") *
1184 m_delayDispReset
= true;
1185 m_dispResetTimer
.Set(delay
);
1190 void CWinSystemWin32::ResolutionChanged()
1196 void CWinSystemWin32::SetForegroundWindowInternal(HWND hWnd
)
1198 if (!IsWindow(hWnd
)) return;
1200 // if the window isn't focused, bring it to front or SetFullScreen will fail
1201 BYTE keyState
[256] = {};
1202 // to unlock SetForegroundWindow we need to imitate Alt pressing
1203 if (GetKeyboardState(reinterpret_cast<LPBYTE
>(&keyState
)) && !(keyState
[VK_MENU
] & 0x80))
1204 keybd_event(VK_MENU
, 0, KEYEVENTF_EXTENDEDKEY
| 0, 0);
1206 BOOL res
= SetForegroundWindow(hWnd
);
1208 if (GetKeyboardState(reinterpret_cast<LPBYTE
>(&keyState
)) && !(keyState
[VK_MENU
] & 0x80))
1209 keybd_event(VK_MENU
, 0, KEYEVENTF_EXTENDEDKEY
| KEYEVENTF_KEYUP
, 0);
1213 //relation time of SetForegroundWindow lock
1214 DWORD lockTimeOut
= 0;
1215 HWND hCurrWnd
= GetForegroundWindow();
1216 DWORD dwThisTID
= GetCurrentThreadId(),
1217 dwCurrTID
= GetWindowThreadProcessId(hCurrWnd
, nullptr);
1219 // we need to bypass some limitations from Microsoft
1220 if (dwThisTID
!= dwCurrTID
)
1222 AttachThreadInput(dwThisTID
, dwCurrTID
, TRUE
);
1223 SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT
, 0, &lockTimeOut
, 0);
1224 SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT
, 0, nullptr, SPIF_SENDWININICHANGE
| SPIF_UPDATEINIFILE
);
1225 AllowSetForegroundWindow(ASFW_ANY
);
1228 SetForegroundWindow(hWnd
);
1230 if (dwThisTID
!= dwCurrTID
)
1232 SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT
, 0, &lockTimeOut
, SPIF_SENDWININICHANGE
| SPIF_UPDATEINIFILE
);
1233 AttachThreadInput(dwThisTID
, dwCurrTID
, FALSE
);
1238 std::unique_ptr
<CVideoSync
> CWinSystemWin32::GetVideoSync(CVideoReferenceClock
* clock
)
1240 std::unique_ptr
<CVideoSync
> pVSync(new CVideoSyncD3D(clock
));
1244 std::string
CWinSystemWin32::GetClipboardText()
1246 std::wstring unicode_text
;
1247 std::string utf8_text
;
1249 if (OpenClipboard(nullptr))
1251 HGLOBAL hglb
= GetClipboardData(CF_UNICODETEXT
);
1252 if (hglb
!= nullptr)
1254 LPWSTR lpwstr
= static_cast<LPWSTR
>(GlobalLock(hglb
));
1255 if (lpwstr
!= nullptr)
1257 unicode_text
= lpwstr
;
1264 return KODI::PLATFORM::WINDOWS::FromW(unicode_text
);
1267 bool CWinSystemWin32::UseLimitedColor()
1269 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE
);
1272 void CWinSystemWin32::NotifyAppFocusChange(bool bGaining
)
1274 if (m_state
== WINDOW_STATE_FULLSCREEN
&& !m_IsAlteringWindow
)
1276 m_IsAlteringWindow
= true;
1277 ReleaseBackBuffer();
1280 SetWindowPos(m_hWnd
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
1282 RESOLUTION_INFO res
= {};
1283 const RESOLUTION resolution
= CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
1284 if (bGaining
&& resolution
> RES_INVALID
)
1285 res
= CDisplaySettings::GetInstance().GetResolutionInfo(resolution
);
1287 SetDeviceFullScreen(bGaining
, res
);
1290 SetWindowPos(m_hWnd
, HWND_NOTOPMOST
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
1293 m_IsAlteringWindow
= false;
1295 m_inFocus
= bGaining
;
1298 void CWinSystemWin32::UpdateStates(bool fullScreen
)
1300 m_fullscreenState
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN
)
1301 ? WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW
1302 : WINDOW_FULLSCREEN_STATE_FULLSCREEN
;
1303 m_windowState
= WINDOW_WINDOW_STATE_WINDOWED
; // currently only this allowed
1305 // set the appropriate window style
1308 m_windowStyle
= FULLSCREEN_WINDOW_STYLE
;
1309 m_windowExStyle
= FULLSCREEN_WINDOW_EX_STYLE
;
1313 m_windowStyle
= WINDOWED_STYLE
;
1314 m_windowExStyle
= WINDOWED_EX_STYLE
;
1318 WINDOW_STATE
CWinSystemWin32::GetState(bool fullScreen
) const
1320 return static_cast<WINDOW_STATE
>(fullScreen
? m_fullscreenState
: m_windowState
);
1323 bool CWinSystemWin32::MessagePump()
1325 return m_winEvents
->MessagePump();
1328 void CWinSystemWin32::SetTogglingHDR(bool toggling
)
1331 SetTimer(m_hWnd
, ID_TIMER_HDR
, 6000U, nullptr);
1333 m_IsTogglingHDR
= toggling
;
1336 RECT
CWinSystemWin32::GetVirtualScreenRect()
1339 rect
.left
= GetSystemMetrics(SM_XVIRTUALSCREEN
);
1340 rect
.right
= GetSystemMetrics(SM_CXVIRTUALSCREEN
) + rect
.left
;
1341 rect
.top
= GetSystemMetrics(SM_YVIRTUALSCREEN
);
1342 rect
.bottom
= GetSystemMetrics(SM_CYVIRTUALSCREEN
) + rect
.top
;
1348 * \brief Max luminance for GUI SDR content in HDR mode.
1349 * \return Max luminance in nits, lower than 10000.
1351 float CWinSystemWin32::GetGuiSdrPeakLuminance() const
1353 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
1355 // use cached system value as this is called for each frame
1356 if (settings
->GetBool(CSettings::SETTING_VIDEOSCREEN_USESYSTEMSDRPEAKLUMINANCE
) &&
1357 m_validSystemSdrPeakLuminance
)
1358 return m_systemSdrPeakLuminance
;
1360 // Max nits for 100% UI setting = 1000 nits, < 10000 nits, min 80 nits for 0%
1361 const int guiSdrPeak
= settings
->GetInt(CSettings::SETTING_VIDEOSCREEN_GUISDRPEAKLUMINANCE
);
1362 return (80.0f
* std::pow(std::exp(1.0f
), 0.025257f
* guiSdrPeak
));
1366 * \brief Test support of the OS for a SDR max luminance in HDR mode setting
1367 * \return true when the OS supports that setting, false otherwise
1369 bool CWinSystemWin32::HasSystemSdrPeakLuminance()
1371 MONITOR_DETAILS
* md
= GetDisplayDetails(m_hMonitor
);
1373 return CWIN32Util::GetSystemSdrWhiteLevel(md
->DeviceNameW
, nullptr);
1379 * \brief Cache the system HDR/SDR balance for use during rendering, instead of querying the API
1382 void CWinSystemWin32::CacheSystemSdrPeakLuminance()
1384 m_validSystemSdrPeakLuminance
= false;
1386 MONITOR_DETAILS
* md
= GetDisplayDetails(m_hMonitor
);
1389 m_validSystemSdrPeakLuminance
=
1390 CWIN32Util::GetSystemSdrWhiteLevel(md
->DeviceNameW
, &m_systemSdrPeakLuminance
);
1394 RECT
CWinSystemWin32::GetScreenWorkArea(HMONITOR handle
) const
1396 MONITORINFO monitorInfo
{};
1397 monitorInfo
.cbSize
= sizeof(MONITORINFO
);
1398 if (!GetMonitorInfoW(handle
, &monitorInfo
))
1400 CLog::LogF(LOGERROR
, "GetMonitorInfoW failed with {}", GetLastError());
1403 return monitorInfo
.rcWork
;
1406 RECT
CWinSystemWin32::GetNcAreaOffsets(DWORD dwStyle
, BOOL bMenu
, DWORD dwExStyle
) const
1409 SetRectEmpty(&rcNcArea
);
1411 if (!AdjustWindowRectEx(&rcNcArea
, dwStyle
, false, dwExStyle
))
1413 CLog::LogF(LOGERROR
, "AdjustWindowRectEx failed with {}", GetLastError());