summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/windows
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/windowing/windows
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/windowing/windows')
-rw-r--r--xbmc/windowing/windows/CMakeLists.txt14
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.cpp137
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.h37
-rw-r--r--xbmc/windowing/windows/Win32DPMSSupport.cpp52
-rw-r--r--xbmc/windowing/windows/Win32DPMSSupport.h20
-rw-r--r--xbmc/windowing/windows/WinEventsWin32.cpp1074
-rw-r--r--xbmc/windowing/windows/WinEventsWin32.h32
-rw-r--r--xbmc/windowing/windows/WinKeyMap.h181
-rw-r--r--xbmc/windowing/windows/WinSystemWin32.cpp1368
-rw-r--r--xbmc/windowing/windows/WinSystemWin32.h215
-rw-r--r--xbmc/windowing/windows/WinSystemWin32DX.cpp447
-rw-r--r--xbmc/windowing/windows/WinSystemWin32DX.h96
12 files changed, 3673 insertions, 0 deletions
diff --git a/xbmc/windowing/windows/CMakeLists.txt b/xbmc/windowing/windows/CMakeLists.txt
new file mode 100644
index 0000000..2b84cee
--- /dev/null
+++ b/xbmc/windowing/windows/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(SOURCES VideoSyncD3D.cpp
+ Win32DPMSSupport.cpp
+ WinEventsWin32.cpp
+ WinSystemWin32.cpp
+ WinSystemWin32DX.cpp)
+
+set(HEADERS VideoSyncD3D.h
+ Win32DPMSSupport.h
+ WinEventsWin32.h
+ WinSystemWin32.h
+ WinSystemWin32DX.h
+ WinKeyMap.h)
+
+core_add_library(windowing_windows)
diff --git a/xbmc/windowing/windows/VideoSyncD3D.cpp b/xbmc/windowing/windows/VideoSyncD3D.cpp
new file mode 100644
index 0000000..d95374f
--- /dev/null
+++ b/xbmc/windowing/windows/VideoSyncD3D.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoSyncD3D.h"
+
+#include "Utils/MathUtils.h"
+#include "Utils/TimeUtils.h"
+#include "rendering/dx/DeviceResources.h"
+#include "rendering/dx/RenderContext.h"
+#include "utils/StringUtils.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+void CVideoSyncD3D::OnLostDisplay()
+{
+ if (!m_displayLost)
+ {
+ m_displayLost = true;
+ m_lostEvent.Wait();
+ }
+}
+
+void CVideoSyncD3D::OnResetDisplay()
+{
+ m_displayReset = true;
+}
+
+void CVideoSyncD3D::RefreshChanged()
+{
+ m_displayReset = true;
+}
+
+bool CVideoSyncD3D::Setup(PUPDATECLOCK func)
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncD3D: Setting up Direct3d");
+ std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
+ DX::Windowing()->Register(this);
+ m_displayLost = false;
+ m_displayReset = false;
+ m_lostEvent.Reset();
+ UpdateClock = func;
+
+ // we need a high priority thread to get accurate timing
+ if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
+ CLog::Log(LOGDEBUG, "CVideoSyncD3D: SetThreadPriority failed");
+
+ return true;
+}
+
+void CVideoSyncD3D::Run(CEvent& stopEvent)
+{
+ int64_t Now;
+ int64_t LastVBlankTime;
+ int NrVBlanks;
+ double VBlankTime;
+ int64_t systemFrequency = CurrentHostFrequency();
+
+ // init the vblanktime
+ Now = CurrentHostCounter();
+ LastVBlankTime = Now;
+ m_lastUpdateTime = Now - systemFrequency;
+ while (!stopEvent.Signaled() && !m_displayLost && !m_displayReset)
+ {
+ // sleep until vblank
+ Microsoft::WRL::ComPtr<IDXGIOutput> pOutput;
+ DX::DeviceResources::Get()->GetOutput(&pOutput);
+ HRESULT hr = pOutput->WaitForVBlank();
+
+ // calculate how many vblanks happened
+ Now = CurrentHostCounter();
+ VBlankTime = (double)(Now - LastVBlankTime) / (double)systemFrequency;
+ NrVBlanks = MathUtils::round_int(VBlankTime * m_fps);
+
+ // update the vblank timestamp, update the clock and send a signal that we got a vblank
+ UpdateClock(NrVBlanks, Now, m_refClock);
+
+ // save the timestamp of this vblank so we can calculate how many vblanks happened next time
+ LastVBlankTime = Now;
+
+ if ((Now - m_lastUpdateTime) >= systemFrequency)
+ {
+ float fps = m_fps;
+ if (fps != GetFps())
+ break;
+ }
+
+ // because we had a vblank, sleep until half the refreshrate period because i think WaitForVBlank block any rendering stuf
+ // without sleeping we have freeze rendering
+ int SleepTime = (int)((LastVBlankTime + (systemFrequency / MathUtils::round_int(m_fps) / 2) - Now) * 1000 / systemFrequency);
+ if (SleepTime > 50)
+ SleepTime = 50; //failsafe
+ if (SleepTime > 0)
+ ::Sleep(SleepTime);
+ }
+
+ m_lostEvent.Set();
+ while (!stopEvent.Signaled() && m_displayLost && !m_displayReset)
+ {
+ KODI::TIME::Sleep(10ms);
+ }
+}
+
+void CVideoSyncD3D::Cleanup()
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncD3D: Cleaning up Direct3d");
+
+ m_lostEvent.Set();
+ DX::Windowing()->Unregister(this);
+}
+
+float CVideoSyncD3D::GetFps()
+{
+ DXGI_MODE_DESC DisplayMode = {};
+ DX::DeviceResources::Get()->GetDisplayMode(&DisplayMode);
+
+ m_fps = (DisplayMode.RefreshRate.Denominator != 0) ? (float)DisplayMode.RefreshRate.Numerator / (float)DisplayMode.RefreshRate.Denominator : 0.0f;
+
+ if (m_fps == 0.0)
+ m_fps = 60.0f;
+
+ if (DX::Windowing()->Interlaced())
+ {
+ m_fps *= 2;
+ }
+ return m_fps;
+}
+
diff --git a/xbmc/windowing/windows/VideoSyncD3D.h b/xbmc/windowing/windows/VideoSyncD3D.h
new file mode 100644
index 0000000..e241650
--- /dev/null
+++ b/xbmc/windowing/windows/VideoSyncD3D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "threads/Event.h"
+#include "windowing/VideoSync.h"
+
+class CVideoSyncD3D : public CVideoSync, IDispResource
+{
+public:
+ CVideoSyncD3D(void* clock)
+ : CVideoSync(clock), m_displayLost(false), m_displayReset(false), m_lastUpdateTime(0)
+ {
+ }
+ bool Setup(PUPDATECLOCK func) override;
+ void Run(CEvent& stopEvent) override;
+ void Cleanup() override;
+ float GetFps() override;
+ void RefreshChanged() override;
+ // IDispResource overrides
+ void OnLostDisplay() override;
+ void OnResetDisplay() override;
+
+private:
+ volatile bool m_displayLost;
+ volatile bool m_displayReset;
+ CEvent m_lostEvent;
+ int64_t m_lastUpdateTime;
+};
+
diff --git a/xbmc/windowing/windows/Win32DPMSSupport.cpp b/xbmc/windowing/windows/Win32DPMSSupport.cpp
new file mode 100644
index 0000000..99adc10
--- /dev/null
+++ b/xbmc/windowing/windows/Win32DPMSSupport.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "Win32DPMSSupport.h"
+
+#include "ServiceBroker.h"
+#include "rendering/dx/RenderContext.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/windows/WinSystemWin32.h"
+
+CWin32DPMSSupport::CWin32DPMSSupport()
+{
+ m_supportedModes.push_back(OFF);
+ m_supportedModes.push_back(STANDBY);
+}
+
+bool CWin32DPMSSupport::EnablePowerSaving(PowerSavingMode mode)
+{
+ auto winSystem = dynamic_cast<CWinSystemWin32*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return false;
+
+ if (!winSystem->GetGfxContext().IsFullScreenRoot())
+ {
+ CLog::Log(LOGDEBUG, "DPMS: not in fullscreen, power saving disabled");
+ return false;
+ }
+
+ switch (mode)
+ {
+ case OFF:
+ // Turn off display
+ return SendMessage(DX::Windowing()->GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, static_cast<LPARAM>(2)) == 0;
+ case STANDBY:
+ // Set display to low power
+ return SendMessage(DX::Windowing()->GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, static_cast<LPARAM>(1)) == 0;
+ default:
+ return true;
+ }
+}
+
+bool CWin32DPMSSupport::DisablePowerSaving()
+{
+ // Turn display on
+ return SendMessage(DX::Windowing()->GetHwnd(), WM_SYSCOMMAND, SC_MONITORPOWER, static_cast<LPARAM>(-1)) == 0;
+}
diff --git a/xbmc/windowing/windows/Win32DPMSSupport.h b/xbmc/windowing/windows/Win32DPMSSupport.h
new file mode 100644
index 0000000..75c192f
--- /dev/null
+++ b/xbmc/windowing/windows/Win32DPMSSupport.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "xbmc/powermanagement/DPMSSupport.h"
+
+class CWin32DPMSSupport : public CDPMSSupport
+{
+public:
+ CWin32DPMSSupport();
+ ~CWin32DPMSSupport() = default;
+
+protected:
+ bool EnablePowerSaving(PowerSavingMode mode) override;
+ bool DisablePowerSaving() override;
+};
diff --git a/xbmc/windowing/windows/WinEventsWin32.cpp b/xbmc/windowing/windows/WinEventsWin32.cpp
new file mode 100644
index 0000000..7b4cba3
--- /dev/null
+++ b/xbmc/windowing/windows/WinEventsWin32.cpp
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#ifndef _USE_MATH_DEFINES
+#define _USE_MATH_DEFINES
+#endif
+#include "WinEventsWin32.h"
+
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "WinKeyMap.h"
+#include "application/AppInboundProtocol.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIControl.h" // for EVENT_RESULT
+#include "guilib/GUIWindowManager.h"
+#include "input/InputManager.h"
+#include "input/mouse/MouseStat.h"
+#include "input/touch/generic/GenericTouchActionHandler.h"
+#include "input/touch/generic/GenericTouchSwipeDetector.h"
+#include "messaging/ApplicationMessenger.h"
+#include "network/Zeroconf.h"
+#include "network/ZeroconfBrowser.h"
+#include "peripherals/Peripherals.h"
+#include "rendering/dx/RenderContext.h"
+#include "storage/MediaManager.h"
+#include "utils/JobManager.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include "platform/win32/CharsetConverter.h"
+#include "platform/win32/WIN32Util.h"
+#include "platform/win32/powermanagement/Win32PowerSyscall.h"
+#include "platform/win32/storage/Win32StorageProvider.h"
+
+#include <array>
+#include <math.h>
+
+#include <Shlobj.h>
+#include <dbt.h>
+
+HWND g_hWnd = nullptr;
+
+#ifndef LODWORD
+#define LODWORD(longval) ((DWORD)((DWORDLONG)(longval)))
+#endif
+
+#define ROTATE_ANGLE_DEGREE(arg) GID_ROTATE_ANGLE_FROM_ARGUMENT(LODWORD(arg)) * 180 / M_PI
+
+#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
+#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
+
+/* Masks for processing the windows KEYDOWN and KEYUP messages */
+#define REPEATED_KEYMASK (1<<30)
+#define EXTENDED_KEYMASK (1<<24)
+#define EXTKEYPAD(keypad) ((scancode & 0x100)?(mvke):(keypad))
+
+static GUID USB_HID_GUID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
+
+uint32_t g_uQueryCancelAutoPlay = 0;
+bool g_sizeMoveSizing = false;
+bool g_sizeMoveMoving = false;
+int g_sizeMoveWidth = 0;
+int g_sizeMoveHight = 0;
+int g_sizeMoveX = -10000;
+int g_sizeMoveY = -10000;
+
+int XBMC_TranslateUNICODE = 1;
+
+int CWinEventsWin32::m_originalZoomDistance = 0;
+Pointer CWinEventsWin32::m_touchPointer;
+CGenericTouchSwipeDetector* CWinEventsWin32::m_touchSwipeDetector = nullptr;
+
+// register to receive SD card events (insert/remove)
+// seen at http://www.codeproject.com/Messages/2897423/Re-No-message-triggered-on-SD-card-insertion-remov.aspx
+#define WM_MEDIA_CHANGE (WM_USER + 666)
+SHChangeNotifyEntry shcne;
+
+static int XBMC_MapVirtualKey(int scancode, WPARAM vkey)
+{
+ int mvke = MapVirtualKeyEx(scancode & 0xFF, 1, nullptr);
+
+ switch (vkey)
+ { /* These are always correct */
+ case VK_DIVIDE:
+ case VK_MULTIPLY:
+ case VK_SUBTRACT:
+ case VK_ADD:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_APPS:
+ /* These are already handled */
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_SNAPSHOT:
+ case VK_PAUSE:
+ /* Multimedia keys are already handled */
+ case VK_BROWSER_BACK:
+ case VK_BROWSER_FORWARD:
+ case VK_BROWSER_REFRESH:
+ case VK_BROWSER_STOP:
+ case VK_BROWSER_SEARCH:
+ case VK_BROWSER_FAVORITES:
+ case VK_BROWSER_HOME:
+ case VK_VOLUME_MUTE:
+ case VK_VOLUME_DOWN:
+ case VK_VOLUME_UP:
+ case VK_MEDIA_NEXT_TRACK:
+ case VK_MEDIA_PREV_TRACK:
+ case VK_MEDIA_STOP:
+ case VK_MEDIA_PLAY_PAUSE:
+ case VK_LAUNCH_MAIL:
+ case VK_LAUNCH_MEDIA_SELECT:
+ case VK_LAUNCH_APP1:
+ case VK_LAUNCH_APP2:
+ return static_cast<int>(vkey);
+ default:;
+ }
+ switch (mvke)
+ { /* Distinguish between keypad and extended keys */
+ case VK_INSERT: return EXTKEYPAD(VK_NUMPAD0);
+ case VK_DELETE: return EXTKEYPAD(VK_DECIMAL);
+ case VK_END: return EXTKEYPAD(VK_NUMPAD1);
+ case VK_DOWN: return EXTKEYPAD(VK_NUMPAD2);
+ case VK_NEXT: return EXTKEYPAD(VK_NUMPAD3);
+ case VK_LEFT: return EXTKEYPAD(VK_NUMPAD4);
+ case VK_CLEAR: return EXTKEYPAD(VK_NUMPAD5);
+ case VK_RIGHT: return EXTKEYPAD(VK_NUMPAD6);
+ case VK_HOME: return EXTKEYPAD(VK_NUMPAD7);
+ case VK_UP: return EXTKEYPAD(VK_NUMPAD8);
+ case VK_PRIOR: return EXTKEYPAD(VK_NUMPAD9);
+ default:;
+ }
+ return mvke ? mvke : static_cast<int>(vkey);
+}
+
+
+static XBMC_keysym *TranslateKey(WPARAM vkey, UINT scancode, XBMC_keysym *keysym, int pressed)
+{
+ using namespace KODI::WINDOWING::WINDOWS;
+
+ uint8_t keystate[256];
+
+ /* Set the keysym information */
+ keysym->scancode = static_cast<unsigned char>(scancode);
+ keysym->unicode = 0;
+
+ if ((vkey == VK_RETURN) && (scancode & 0x100))
+ {
+ /* No VK_ code for the keypad enter key */
+ keysym->sym = XBMCK_KP_ENTER;
+ }
+ else
+ {
+ keysym->sym = VK_keymap[XBMC_MapVirtualKey(scancode, vkey)];
+ }
+
+ // Attempt to convert the keypress to a UNICODE character
+ GetKeyboardState(keystate);
+
+ if (pressed && XBMC_TranslateUNICODE)
+ {
+ std::array<uint16_t, 2> wchars;
+
+ /* Numlock isn't taken into account in ToUnicode,
+ * so we handle it as a special case here */
+ if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9)
+ {
+ keysym->unicode = static_cast<uint16_t>(vkey - VK_NUMPAD0 + '0');
+ }
+ else if (ToUnicode(static_cast<UINT>(vkey), scancode, keystate,
+ reinterpret_cast<LPWSTR>(wchars.data()), wchars.size(), 0) > 0)
+ {
+ keysym->unicode = wchars[0];
+ }
+ }
+
+ // Set the modifier bitmap
+
+ uint16_t mod = static_cast<uint16_t>(XBMCKMOD_NONE);
+
+ // If left control and right alt are down this usually means that
+ // AltGr is down
+ if ((keystate[VK_LCONTROL] & 0x80) && (keystate[VK_RMENU] & 0x80))
+ {
+ mod |= XBMCKMOD_MODE;
+ }
+ else
+ {
+ if (keystate[VK_LCONTROL] & 0x80) mod |= XBMCKMOD_LCTRL;
+ if (keystate[VK_RMENU] & 0x80) mod |= XBMCKMOD_RALT;
+ }
+
+ // Check the remaining modifiers
+ if (keystate[VK_LSHIFT] & 0x80) mod |= XBMCKMOD_LSHIFT;
+ if (keystate[VK_RSHIFT] & 0x80) mod |= XBMCKMOD_RSHIFT;
+ if (keystate[VK_RCONTROL] & 0x80) mod |= XBMCKMOD_RCTRL;
+ if (keystate[VK_LMENU] & 0x80) mod |= XBMCKMOD_LALT;
+ if (keystate[VK_LWIN] & 0x80) mod |= XBMCKMOD_LSUPER;
+ if (keystate[VK_RWIN] & 0x80) mod |= XBMCKMOD_LSUPER;
+ keysym->mod = static_cast<XBMCMod>(mod);
+
+ // Return the updated keysym
+ return(keysym);
+}
+
+
+bool CWinEventsWin32::MessagePump()
+{
+ MSG msg;
+ while( PeekMessage( &msg, nullptr, 0U, 0U, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+ return true;
+}
+
+LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ using KODI::PLATFORM::WINDOWS::FromW;
+
+ XBMC_Event newEvent = {};
+ static HDEVNOTIFY hDeviceNotify;
+
+#if 0
+ if (uMsg == WM_NCCREATE)
+ {
+ // if available, enable DPI scaling of non-client portion of window (title bar, etc.)
+ if (g_Windowing.PtrEnableNonClientDpiScaling != NULL)
+ {
+ g_Windowing.PtrEnableNonClientDpiScaling(hWnd);
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+#endif
+
+ if (uMsg == WM_CREATE)
+ {
+ g_hWnd = hWnd;
+ // need to set windows handle before WM_SIZE processing
+ DX::Windowing()->SetWindow(hWnd);
+
+ KODI::WINDOWING::WINDOWS::DIB_InitOSKeymap();
+
+ g_uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
+ shcne.pidl = nullptr;
+ shcne.fRecursive = TRUE;
+ long fEvents = SHCNE_DRIVEADD | SHCNE_DRIVEREMOVED | SHCNE_MEDIAREMOVED | SHCNE_MEDIAINSERTED;
+ SHChangeNotifyRegister(hWnd, SHCNRF_ShellLevel | SHCNRF_NewDelivery, fEvents, WM_MEDIA_CHANGE, 1, &shcne);
+ RegisterDeviceInterfaceToHwnd(USB_HID_GUID, hWnd, &hDeviceNotify);
+ return 0;
+ }
+
+ if (uMsg == WM_DESTROY)
+ g_hWnd = nullptr;
+
+ if(g_uQueryCancelAutoPlay != 0 && uMsg == g_uQueryCancelAutoPlay)
+ return S_FALSE;
+
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+
+ switch (uMsg)
+ {
+ case WM_CLOSE:
+ case WM_QUIT:
+ case WM_DESTROY:
+ if (hDeviceNotify)
+ {
+ if (UnregisterDeviceNotification(hDeviceNotify))
+ hDeviceNotify = nullptr;
+ else
+ CLog::LogF(LOGINFO, "UnregisterDeviceNotification failed ({})", GetLastError());
+ }
+ newEvent.type = XBMC_QUIT;
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ break;
+ case WM_SHOWWINDOW:
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ bool active = appPower->GetRenderGUI();
+ if (appPort)
+ appPort->SetRenderGUI(wParam != 0);
+ if (appPower->GetRenderGUI() != active)
+ DX::Windowing()->NotifyAppActiveChange(appPower->GetRenderGUI());
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "WM_SHOWWINDOW -> window is {}",
+ wParam != 0 ? "shown" : "hidden");
+ }
+ break;
+ case WM_ACTIVATE:
+ {
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "WM_ACTIVATE -> window is {}",
+ LOWORD(wParam) != WA_INACTIVE ? "active" : "inactive");
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ bool active = appPower->GetRenderGUI();
+ if (HIWORD(wParam))
+ {
+ if (appPort)
+ appPort->SetRenderGUI(false);
+ }
+ else
+ {
+ WINDOWPLACEMENT lpwndpl;
+ lpwndpl.length = sizeof(lpwndpl);
+ if (LOWORD(wParam) != WA_INACTIVE)
+ {
+ if (GetWindowPlacement(hWnd, &lpwndpl))
+ {
+ if (appPort)
+ appPort->SetRenderGUI(lpwndpl.showCmd != SW_HIDE);
+ }
+ }
+ else
+ {
+
+ }
+ }
+ if (appPower->GetRenderGUI() != active)
+ DX::Windowing()->NotifyAppActiveChange(appPower->GetRenderGUI());
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window is {}",
+ appPower->GetRenderGUI() ? "active" : "inactive");
+ }
+ break;
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ g_application.m_AppFocused = uMsg == WM_SETFOCUS;
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window focus {}",
+ g_application.m_AppFocused ? "set" : "lost");
+
+ DX::Windowing()->NotifyAppFocusChange(g_application.m_AppFocused);
+ if (uMsg == WM_KILLFOCUS)
+ {
+ std::string procfile;
+ if (CWIN32Util::GetFocussedProcess(procfile))
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "Focus switched to process {}", procfile);
+ }
+ break;
+ /* needs to be reviewed after frodo. we reset the system idle timer
+ and the display timer directly now (see m_screenSaverTimer).
+ case WM_SYSCOMMAND:
+ switch( wParam&0xFFF0 )
+ {
+ case SC_MONITORPOWER:
+ if (g_application.GetAppPlayer().IsPlaying() || g_application.GetAppPlayer().IsPausedPlayback())
+ return 0;
+ else if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) == 0)
+ return 0;
+ break;
+ case SC_SCREENSAVE:
+ return 0;
+ }
+ break;*/
+ case WM_SYSKEYDOWN:
+ switch (wParam)
+ {
+ case VK_F4: //alt-f4, default event quit.
+ return(DefWindowProc(hWnd, uMsg, wParam, lParam));
+ case VK_RETURN: //alt-return
+ if ((lParam & REPEATED_KEYMASK) == 0)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+ return 0;
+ default:;
+ }
+ //deliberate fallthrough
+ case WM_KEYDOWN:
+ {
+ switch (wParam)
+ {
+ case VK_CONTROL:
+ if ( lParam & EXTENDED_KEYMASK )
+ wParam = VK_RCONTROL;
+ else
+ wParam = VK_LCONTROL;
+ break;
+ case VK_SHIFT:
+ /* EXTENDED trick doesn't work here */
+ if (GetKeyState(VK_LSHIFT) & 0x8000)
+ wParam = VK_LSHIFT;
+ else if (GetKeyState(VK_RSHIFT) & 0x8000)
+ wParam = VK_RSHIFT;
+ break;
+ case VK_MENU:
+ if ( lParam & EXTENDED_KEYMASK )
+ wParam = VK_RMENU;
+ else
+ wParam = VK_LMENU;
+ break;
+ default:;
+ }
+ XBMC_keysym keysym;
+ TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
+
+ newEvent.type = XBMC_KEYDOWN;
+ newEvent.key.keysym = keysym;
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ return(0);
+
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ {
+ switch (wParam)
+ {
+ case VK_CONTROL:
+ if ( lParam&EXTENDED_KEYMASK )
+ wParam = VK_RCONTROL;
+ else
+ wParam = VK_LCONTROL;
+ break;
+ case VK_SHIFT:
+ {
+ uint32_t scanCodeL = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
+ uint32_t scanCodeR = MapVirtualKey(VK_RSHIFT, MAPVK_VK_TO_VSC);
+ uint32_t keyCode = static_cast<uint32_t>((lParam & 0xFF0000) >> 16);
+ if (keyCode == scanCodeL)
+ wParam = VK_LSHIFT;
+ else if (keyCode == scanCodeR)
+ wParam = VK_RSHIFT;
+ }
+ break;
+ case VK_MENU:
+ if ( lParam&EXTENDED_KEYMASK )
+ wParam = VK_RMENU;
+ else
+ wParam = VK_LMENU;
+ break;
+ default:;
+ }
+ XBMC_keysym keysym;
+ TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
+
+ if (wParam == VK_SNAPSHOT)
+ newEvent.type = XBMC_KEYDOWN;
+ else
+ newEvent.type = XBMC_KEYUP;
+ newEvent.key.keysym = keysym;
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ return(0);
+ case WM_APPCOMMAND: // MULTIMEDIA keys are mapped to APPCOMMANDS
+ {
+ const unsigned int appcmd = GET_APPCOMMAND_LPARAM(lParam);
+
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "APPCOMMAND {}", appcmd);
+
+ // Reset the screen saver
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->ResetScreenSaver();
+
+ // If we were currently in the screen saver wake up and don't process the
+ // appcommand
+ if (appPower->WakeUpScreenSaverAndDPMS())
+ return true;
+
+ // Retrieve the action associated with this appcommand from the mapping table
+ CKey key(appcmd | KEY_APPCOMMAND, 0U);
+ int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
+
+ CAction appcmdaction = CServiceBroker::GetInputManager().GetAction(iWin, key);
+ if (appcmdaction.GetID())
+ {
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "appcommand {}, action {}", appcmd,
+ appcmdaction.GetName());
+ CServiceBroker::GetInputManager().QueueAction(appcmdaction);
+ return true;
+ }
+
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "unknown appcommand {}", appcmd);
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+ case WM_GESTURENOTIFY:
+ {
+ OnGestureNotify(hWnd, lParam);
+ return DefWindowProc(hWnd, WM_GESTURENOTIFY, wParam, lParam);
+ }
+ case WM_GESTURE:
+ {
+ OnGesture(hWnd, lParam);
+ return 0;
+ }
+ case WM_SYSCHAR:
+ if (wParam == VK_RETURN) //stop system beep on alt-return
+ return 0;
+ break;
+ case WM_SETCURSOR:
+ if (HTCLIENT != LOWORD(lParam))
+ DX::Windowing()->ShowOSMouse(true);
+ break;
+ case WM_MOUSEMOVE:
+ newEvent.type = XBMC_MOUSEMOTION;
+ newEvent.motion.x = GET_X_LPARAM(lParam);
+ newEvent.motion.y = GET_Y_LPARAM(lParam);
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ return(0);
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ newEvent.type = XBMC_MOUSEBUTTONDOWN;
+ newEvent.button.x = GET_X_LPARAM(lParam);
+ newEvent.button.y = GET_Y_LPARAM(lParam);
+ newEvent.button.button = 0;
+ if (uMsg == WM_LBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_LEFT;
+ else if (uMsg == WM_MBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_MIDDLE;
+ else if (uMsg == WM_RBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_RIGHT;
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ return(0);
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ newEvent.type = XBMC_MOUSEBUTTONUP;
+ newEvent.button.x = GET_X_LPARAM(lParam);
+ newEvent.button.y = GET_Y_LPARAM(lParam);
+ newEvent.button.button = 0;
+ if (uMsg == WM_LBUTTONUP) newEvent.button.button = XBMC_BUTTON_LEFT;
+ else if (uMsg == WM_MBUTTONUP) newEvent.button.button = XBMC_BUTTON_MIDDLE;
+ else if (uMsg == WM_RBUTTONUP) newEvent.button.button = XBMC_BUTTON_RIGHT;
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ return(0);
+ case WM_MOUSEWHEEL:
+ {
+ // SDL, which our events system is based off, sends a MOUSEBUTTONDOWN message
+ // followed by a MOUSEBUTTONUP message. As this is a momentary event, we just
+ // react on the MOUSEBUTTONUP message, resetting the state after processing.
+ newEvent.type = XBMC_MOUSEBUTTONDOWN;
+ // the coordinates in WM_MOUSEWHEEL are screen, not client coordinates
+ POINT point;
+ point.x = GET_X_LPARAM(lParam);
+ point.y = GET_Y_LPARAM(lParam);
+ WindowFromScreenCoords(hWnd, &point);
+ newEvent.button.x = static_cast<uint16_t>(point.x);
+ newEvent.button.y = static_cast<uint16_t>(point.y);
+ newEvent.button.button = GET_Y_LPARAM(wParam) > 0 ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN;
+ if (appPort)
+ {
+ appPort->OnEvent(newEvent);
+ newEvent.type = XBMC_MOUSEBUTTONUP;
+ appPort->OnEvent(newEvent);
+ }
+ }
+ return(0);
+ case WM_DPICHANGED:
+ // This message tells the program that most of its window is on a
+ // monitor with a new DPI. The wParam contains the new DPI, and the
+ // lParam contains a rect which defines the window rectangle scaled
+ // the new DPI.
+ {
+ // get the suggested size of the window on the new display with a different DPI
+ uint16_t dpi = HIWORD(wParam);
+ RECT rc = *reinterpret_cast<RECT*>(lParam);
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "dpi changed event -> {} ({}, {}, {}, {})", dpi, rc.left,
+ rc.top, rc.right, rc.bottom);
+ DX::Windowing()->DPIChanged(dpi, rc);
+ return(0);
+ }
+ case WM_DISPLAYCHANGE:
+ {
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "display change event");
+ if (DX::Windowing()->IsTogglingHDR() || DX::Windowing()->IsAlteringWindow())
+ return (0);
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI() && GET_X_LPARAM(lParam) > 0 && GET_Y_LPARAM(lParam) > 0)
+ {
+ DX::Windowing()->UpdateResolutions();
+ }
+ return(0);
+ }
+ case WM_ENTERSIZEMOVE:
+ {
+ DX::Windowing()->SetSizeMoveMode(true);
+ }
+ return(0);
+ case WM_EXITSIZEMOVE:
+ {
+ DX::Windowing()->SetSizeMoveMode(false);
+ if (g_sizeMoveMoving)
+ {
+ g_sizeMoveMoving = false;
+ newEvent.type = XBMC_VIDEOMOVE;
+ newEvent.move.x = g_sizeMoveX;
+ newEvent.move.y = g_sizeMoveY;
+
+ // tell the device about new position
+ DX::Windowing()->OnMove(newEvent.move.x, newEvent.move.y);
+ // tell the application about new position
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow())
+ {
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ }
+ if (g_sizeMoveSizing)
+ {
+ g_sizeMoveSizing = false;
+ newEvent.type = XBMC_VIDEORESIZE;
+ newEvent.resize.w = g_sizeMoveWidth;
+ newEvent.resize.h = g_sizeMoveHight;
+
+ // tell the device about new size
+ DX::Windowing()->OnResize(newEvent.resize.w, newEvent.resize.h);
+ // tell the application about new size
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
+ newEvent.resize.w > 0 && newEvent.resize.h > 0)
+ {
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ }
+ }
+ return(0);
+ case WM_SIZE:
+ if (wParam == SIZE_MINIMIZED)
+ {
+ if (!DX::Windowing()->IsMinimized())
+ {
+ DX::Windowing()->SetMinimized(true);
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI())
+ {
+ if (appPort)
+ appPort->SetRenderGUI(false);
+ }
+ }
+ }
+ else if (DX::Windowing()->IsMinimized())
+ {
+ DX::Windowing()->SetMinimized(false);
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (!appPower->GetRenderGUI())
+ {
+ if (appPort)
+ appPort->SetRenderGUI(true);
+ }
+ }
+ else
+ {
+ g_sizeMoveWidth = GET_X_LPARAM(lParam);
+ g_sizeMoveHight = GET_Y_LPARAM(lParam);
+ if (DX::Windowing()->IsInSizeMoveMode())
+ {
+ // If an user is dragging the resize bars, we don't resize
+ // the buffers and don't rise XBMC_VIDEORESIZE here because
+ // as the user continuously resize the window, a lot of WM_SIZE
+ // messages are sent to the proc, and it'd be pointless (and slow)
+ // to resize for each WM_SIZE message received from dragging.
+ // So instead, we reset after the user is done resizing the
+ // window and releases the resize bars, which ends with WM_EXITSIZEMOVE.
+ g_sizeMoveSizing = true;
+ }
+ else
+ {
+ // API call such as SetWindowPos or SwapChain->SetFullscreenState
+ newEvent.type = XBMC_VIDEORESIZE;
+ newEvent.resize.w = g_sizeMoveWidth;
+ newEvent.resize.h = g_sizeMoveHight;
+
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window resize event {} x {}", newEvent.resize.w,
+ newEvent.resize.h);
+ // tell device about new size
+ DX::Windowing()->OnResize(newEvent.resize.w, newEvent.resize.h);
+ // tell application about size changes
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
+ newEvent.resize.w > 0 && newEvent.resize.h > 0)
+ {
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ }
+ }
+ return(0);
+ case WM_MOVE:
+ {
+ g_sizeMoveX = GET_X_LPARAM(lParam);
+ g_sizeMoveY = GET_Y_LPARAM(lParam);
+ if (DX::Windowing()->IsInSizeMoveMode())
+ {
+ // the same as WM_SIZE
+ g_sizeMoveMoving = true;
+ }
+ else
+ {
+ newEvent.type = XBMC_VIDEOMOVE;
+ newEvent.move.x = g_sizeMoveX;
+ newEvent.move.y = g_sizeMoveY;
+
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window move event");
+
+ // tell the device about new position
+ DX::Windowing()->OnMove(newEvent.move.x, newEvent.move.y);
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow())
+ {
+ if (appPort)
+ appPort->OnEvent(newEvent);
+ }
+ }
+ }
+ return(0);
+ case WM_MEDIA_CHANGE:
+ {
+ // This event detects media changes of usb, sd card and optical media.
+ // It only works if the explorer.exe process is started. Because this
+ // isn't the case for all setups we use WM_DEVICECHANGE for usb and
+ // optical media because this event is also triggered without the
+ // explorer process. Since WM_DEVICECHANGE doesn't detect sd card changes
+ // we still use this event only for sd.
+ long lEvent;
+ PIDLIST_ABSOLUTE *ppidl;
+ HANDLE hLock = SHChangeNotification_Lock(reinterpret_cast<HANDLE>(wParam), static_cast<DWORD>(lParam), &ppidl, &lEvent);
+
+ if (hLock)
+ {
+ wchar_t drivePath[MAX_PATH+1];
+ if (!SHGetPathFromIDList(ppidl[0], drivePath))
+ break;
+
+ switch(lEvent)
+ {
+ case SHCNE_DRIVEADD:
+ case SHCNE_MEDIAINSERTED:
+ if (GetDriveType(drivePath) != DRIVE_CDROM)
+ {
+ CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", FromW(drivePath));
+ CWin32StorageProvider::SetEvent();
+ }
+ break;
+
+ case SHCNE_DRIVEREMOVED:
+ case SHCNE_MEDIAREMOVED:
+ if (GetDriveType(drivePath) != DRIVE_CDROM)
+ {
+ CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", FromW(drivePath));
+ CWin32StorageProvider::SetEvent();
+ }
+ break;
+ default:;
+ }
+ SHChangeNotification_Unlock(hLock);
+ }
+ break;
+ }
+ case WM_POWERBROADCAST:
+ if (wParam==PBT_APMSUSPEND)
+ {
+ CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMSUSPEND event was sent");
+ CWin32PowerSyscall::SetOnSuspend();
+ }
+ else if(wParam==PBT_APMRESUMEAUTOMATIC)
+ {
+ CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMRESUMEAUTOMATIC event was sent");
+ CWin32PowerSyscall::SetOnResume();
+ }
+ break;
+ case WM_DEVICECHANGE:
+ {
+ switch(wParam)
+ {
+ case DBT_DEVNODES_CHANGED:
+ CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
+ break;
+ case DBT_DEVICEARRIVAL:
+ case DBT_DEVICEREMOVECOMPLETE:
+ if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
+ }
+ // check if an usb or optical media was inserted or removed
+ if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)
+ {
+ PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)((_DEV_BROADCAST_HEADER*) lParam);
+ // optical medium
+ if (lpdbv -> dbcv_flags & DBTF_MEDIA)
+ {
+ std::string strdrive = StringUtils::Format(
+ "{}:", CWIN32Util::FirstDriveFromMask(lpdbv->dbcv_unitmask));
+ if(wParam == DBT_DEVICEARRIVAL)
+ {
+ CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", strdrive);
+ CServiceBroker::GetJobManager()->AddJob(new CDetectDisc(strdrive, true), nullptr);
+ }
+ else
+ {
+ CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", strdrive);
+ CMediaSource share;
+ share.strPath = strdrive;
+ share.strName = share.strPath;
+ CServiceBroker::GetMediaManager().RemoveAutoSource(share);
+ }
+ }
+ else
+ CWin32StorageProvider::SetEvent();
+ }
+ default:;
+ }
+ break;
+ }
+ case WM_PAINT:
+ {
+ //some other app has painted over our window, mark everything as dirty
+ CGUIComponent* component = CServiceBroker::GetGUI();
+ if (component)
+ component->GetWindowManager().MarkDirty();
+ break;
+ }
+ case BONJOUR_EVENT:
+ CZeroconf::GetInstance()->ProcessResults();
+ break;
+ case BONJOUR_BROWSER_EVENT:
+ CZeroconfBrowser::GetInstance()->ProcessResults();
+ break;
+ case TRAY_ICON_NOTIFY:
+ {
+ switch (LOWORD(lParam))
+ {
+ case WM_LBUTTONDBLCLK:
+ {
+ DX::Windowing()->SetMinimized(false);
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (!appPower->GetRenderGUI())
+ {
+ if (appPort)
+ appPort->SetRenderGUI(true);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case WM_TIMER:
+ {
+ if (wParam == ID_TIMER_HDR)
+ {
+ CLog::LogFC(LOGDEBUG, LOGWINDOWING, "finish toggling HDR event");
+ DX::Windowing()->SetTogglingHDR(false);
+ KillTimer(hWnd, wParam);
+ }
+ break;
+ }
+ case WM_INITMENU:
+ {
+ HMENU hm = GetSystemMenu(hWnd, FALSE);
+ if (hm)
+ {
+ if (DX::Windowing()->IsFullScreen())
+ EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ else
+ EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_ENABLED);
+ }
+ break;
+ }
+ default:;
+ }
+ return(DefWindowProc(hWnd, uMsg, wParam, lParam));
+}
+
+void CWinEventsWin32::RegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND hWnd, HDEVNOTIFY *hDeviceNotify)
+{
+ DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {};
+
+ NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ NotificationFilter.dbcc_classguid = InterfaceClassGuid;
+
+ *hDeviceNotify = RegisterDeviceNotification(
+ hWnd, // events recipient
+ &NotificationFilter, // type of device
+ DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
+ );
+}
+
+void CWinEventsWin32::WindowFromScreenCoords(HWND hWnd, POINT *point)
+{
+ if (!point) return;
+ RECT clientRect;
+ GetClientRect(hWnd, &clientRect);
+ POINT windowPos;
+ windowPos.x = clientRect.left;
+ windowPos.y = clientRect.top;
+ ClientToScreen(hWnd, &windowPos);
+ point->x -= windowPos.x;
+ point->y -= windowPos.y;
+}
+
+void CWinEventsWin32::OnGestureNotify(HWND hWnd, LPARAM lParam)
+{
+ // convert to window coordinates
+ PGESTURENOTIFYSTRUCT gn = reinterpret_cast<PGESTURENOTIFYSTRUCT>(lParam);
+ POINT point = { gn->ptsLocation.x, gn->ptsLocation.y };
+ WindowFromScreenCoords(hWnd, &point);
+
+ // by default we only want twofingertap and pressandtap gestures
+ // the other gestures are enabled best on supported gestures
+ GESTURECONFIG gc[] = {{ GID_ZOOM, 0, GC_ZOOM},
+ { GID_ROTATE, 0, GC_ROTATE},
+ { GID_PAN, 0, GC_PAN},
+ { GID_TWOFINGERTAP, GC_TWOFINGERTAP, GC_TWOFINGERTAP },
+ { GID_PRESSANDTAP, GC_PRESSANDTAP, GC_PRESSANDTAP }};
+
+ // send a message to see if a control wants any
+ int gestures;
+ if ((gestures = CGenericTouchActionHandler::GetInstance().QuerySupportedGestures(static_cast<float>(point.x), static_cast<float>(point.y))) != EVENT_RESULT_UNHANDLED)
+ {
+ if (gestures & EVENT_RESULT_ZOOM)
+ gc[0].dwWant |= GC_ZOOM;
+ if (gestures & EVENT_RESULT_ROTATE)
+ gc[1].dwWant |= GC_ROTATE;
+ if (gestures & EVENT_RESULT_PAN_VERTICAL)
+ gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
+ if (gestures & EVENT_RESULT_PAN_VERTICAL_WITHOUT_INERTIA)
+ gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
+ if (gestures & EVENT_RESULT_PAN_HORIZONTAL)
+ gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
+ if (gestures & EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA)
+ gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
+ if (gestures & EVENT_RESULT_SWIPE)
+ {
+ gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER;
+
+ // create a new touch swipe detector
+ m_touchSwipeDetector = new CGenericTouchSwipeDetector(&CGenericTouchActionHandler::GetInstance(), 160.0f);
+ }
+
+ gc[0].dwBlock = gc[0].dwWant ^ 0x01;
+ gc[1].dwBlock = gc[1].dwWant ^ 0x01;
+ gc[2].dwBlock = gc[2].dwWant ^ 0x1F;
+ }
+ if (DX::Windowing()->PtrSetGestureConfig)
+ DX::Windowing()->PtrSetGestureConfig(hWnd, 0, 5, gc, sizeof(GESTURECONFIG));
+}
+
+void CWinEventsWin32::OnGesture(HWND hWnd, LPARAM lParam)
+{
+ if (!DX::Windowing()->PtrGetGestureInfo)
+ return;
+
+ GESTUREINFO gi = {};
+ gi.cbSize = sizeof(gi);
+ DX::Windowing()->PtrGetGestureInfo(reinterpret_cast<HGESTUREINFO>(lParam), &gi);
+
+ // convert to window coordinates
+ POINT point = { gi.ptsLocation.x, gi.ptsLocation.y };
+ WindowFromScreenCoords(hWnd, &point);
+
+ if (gi.dwID == GID_BEGIN)
+ m_touchPointer.reset();
+
+ // if there's a "current" touch from a previous event, copy it to "last"
+ if (m_touchPointer.current.valid())
+ m_touchPointer.last = m_touchPointer.current;
+
+ // set the "current" touch
+ m_touchPointer.current.x = static_cast<float>(point.x);
+ m_touchPointer.current.y = static_cast<float>(point.y);
+ m_touchPointer.current.time = time(nullptr);
+
+ switch (gi.dwID)
+ {
+ case GID_BEGIN:
+ {
+ // set the "down" touch
+ m_touchPointer.down = m_touchPointer.current;
+ m_originalZoomDistance = 0;
+
+ CGenericTouchActionHandler::GetInstance().OnTouchGestureStart(static_cast<float>(point.x), static_cast<float>(point.y));
+ }
+ break;
+
+ case GID_END:
+ CGenericTouchActionHandler::GetInstance().OnTouchGestureEnd(static_cast<float>(point.x), static_cast<float>(point.y), 0.0f, 0.0f, 0.0f, 0.0f);
+ break;
+
+ case GID_PAN:
+ {
+ if (!m_touchPointer.moving)
+ m_touchPointer.moving = true;
+
+ // calculate the velocity of the pan gesture
+ float velocityX, velocityY;
+ m_touchPointer.velocity(velocityX, velocityY);
+
+ CGenericTouchActionHandler::GetInstance().OnTouchGesturePan(m_touchPointer.current.x, m_touchPointer.current.y,
+ m_touchPointer.current.x - m_touchPointer.last.x, m_touchPointer.current.y - m_touchPointer.last.y,
+ velocityX, velocityY);
+
+ if (m_touchSwipeDetector != nullptr)
+ {
+ if (gi.dwFlags & GF_BEGIN)
+ {
+ m_touchPointer.down = m_touchPointer.current;
+ m_touchSwipeDetector->OnTouchDown(0, m_touchPointer);
+ }
+ else if (gi.dwFlags & GF_END)
+ {
+ m_touchSwipeDetector->OnTouchUp(0, m_touchPointer);
+
+ delete m_touchSwipeDetector;
+ m_touchSwipeDetector = nullptr;
+ }
+ else
+ m_touchSwipeDetector->OnTouchMove(0, m_touchPointer);
+ }
+ }
+ break;
+
+ case GID_ROTATE:
+ {
+ if (gi.dwFlags == GF_BEGIN)
+ break;
+
+ CGenericTouchActionHandler::GetInstance().OnRotate(static_cast<float>(point.x), static_cast<float>(point.y),
+ -static_cast<float>(ROTATE_ANGLE_DEGREE(gi.ullArguments)));
+ }
+ break;
+
+ case GID_ZOOM:
+ {
+ if (gi.dwFlags == GF_BEGIN)
+ {
+ m_originalZoomDistance = static_cast<int>(LODWORD(gi.ullArguments));
+ break;
+ }
+
+ // avoid division by 0
+ if (m_originalZoomDistance == 0)
+ break;
+
+ CGenericTouchActionHandler::GetInstance().OnZoomPinch(static_cast<float>(point.x), static_cast<float>(point.y),
+ static_cast<float>(LODWORD(gi.ullArguments)) / static_cast<float>(m_originalZoomDistance));
+ }
+ break;
+
+ case GID_TWOFINGERTAP:
+ CGenericTouchActionHandler::GetInstance().OnTap(static_cast<float>(point.x), static_cast<float>(point.y), 2);
+ break;
+
+ case GID_PRESSANDTAP:
+ default:
+ // You have encountered an unknown gesture
+ break;
+ }
+ if(DX::Windowing()->PtrCloseGestureInfoHandle)
+ DX::Windowing()->PtrCloseGestureInfoHandle(reinterpret_cast<HGESTUREINFO>(lParam));
+}
diff --git a/xbmc/windowing/windows/WinEventsWin32.h b/xbmc/windowing/windows/WinEventsWin32.h
new file mode 100644
index 0000000..466c755
--- /dev/null
+++ b/xbmc/windowing/windows/WinEventsWin32.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "input/touch/TouchTypes.h"
+#include "windowing/WinEvents.h"
+
+class CGenericTouchSwipeDetector;
+
+class CWinEventsWin32 : public IWinEvents
+{
+public:
+ bool MessagePump() override;
+ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+ static void RegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND hWnd, HDEVNOTIFY *hDeviceNotify);
+ static void WindowFromScreenCoords(HWND hWnd, POINT *point);
+ static void OnGestureNotify(HWND hWnd, LPARAM lParam);
+ static void OnGesture(HWND hWnd, LPARAM lParam);
+
+ static int m_originalZoomDistance;
+ static Pointer m_touchPointer;
+ static CGenericTouchSwipeDetector *m_touchSwipeDetector;
+};
+
diff --git a/xbmc/windowing/windows/WinKeyMap.h b/xbmc/windowing/windows/WinKeyMap.h
new file mode 100644
index 0000000..c163747
--- /dev/null
+++ b/xbmc/windowing/windows/WinKeyMap.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "input/XBMC_keysym.h"
+#include "input/XBMC_vkeys.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+
+#include <array>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace WINDOWS
+{
+
+static std::array<XBMCKey, XBMCK_LAST> VK_keymap;
+
+static void DIB_InitOSKeymap()
+{
+ /* Map the VK keysyms */
+ VK_keymap.fill(XBMCK_UNKNOWN);
+
+ VK_keymap[VK_BACK] = XBMCK_BACKSPACE;
+ VK_keymap[VK_TAB] = XBMCK_TAB;
+ VK_keymap[VK_CLEAR] = XBMCK_CLEAR;
+ VK_keymap[VK_RETURN] = XBMCK_RETURN;
+ VK_keymap[VK_PAUSE] = XBMCK_PAUSE;
+ VK_keymap[VK_ESCAPE] = XBMCK_ESCAPE;
+ VK_keymap[VK_SPACE] = XBMCK_SPACE;
+ VK_keymap[VK_APOSTROPHE] = XBMCK_QUOTE;
+ VK_keymap[VK_COMMA] = XBMCK_COMMA;
+ VK_keymap[VK_MINUS] = XBMCK_MINUS;
+ VK_keymap[VK_PERIOD] = XBMCK_PERIOD;
+ VK_keymap[VK_SLASH] = XBMCK_SLASH;
+ VK_keymap[VK_0] = XBMCK_0;
+ VK_keymap[VK_1] = XBMCK_1;
+ VK_keymap[VK_2] = XBMCK_2;
+ VK_keymap[VK_3] = XBMCK_3;
+ VK_keymap[VK_4] = XBMCK_4;
+ VK_keymap[VK_5] = XBMCK_5;
+ VK_keymap[VK_6] = XBMCK_6;
+ VK_keymap[VK_7] = XBMCK_7;
+ VK_keymap[VK_8] = XBMCK_8;
+ VK_keymap[VK_9] = XBMCK_9;
+ VK_keymap[VK_SEMICOLON] = XBMCK_SEMICOLON;
+ VK_keymap[VK_EQUALS] = XBMCK_EQUALS;
+ VK_keymap[VK_LBRACKET] = XBMCK_LEFTBRACKET;
+ VK_keymap[VK_BACKSLASH] = XBMCK_BACKSLASH;
+ VK_keymap[VK_OEM_102] = XBMCK_BACKSLASH;
+ VK_keymap[VK_RBRACKET] = XBMCK_RIGHTBRACKET;
+ VK_keymap[VK_GRAVE] = XBMCK_BACKQUOTE;
+ VK_keymap[VK_BACKTICK] = XBMCK_BACKQUOTE;
+ VK_keymap[VK_A] = XBMCK_a;
+ VK_keymap[VK_B] = XBMCK_b;
+ VK_keymap[VK_C] = XBMCK_c;
+ VK_keymap[VK_D] = XBMCK_d;
+ VK_keymap[VK_E] = XBMCK_e;
+ VK_keymap[VK_F] = XBMCK_f;
+ VK_keymap[VK_G] = XBMCK_g;
+ VK_keymap[VK_H] = XBMCK_h;
+ VK_keymap[VK_I] = XBMCK_i;
+ VK_keymap[VK_J] = XBMCK_j;
+ VK_keymap[VK_K] = XBMCK_k;
+ VK_keymap[VK_L] = XBMCK_l;
+ VK_keymap[VK_M] = XBMCK_m;
+ VK_keymap[VK_N] = XBMCK_n;
+ VK_keymap[VK_O] = XBMCK_o;
+ VK_keymap[VK_P] = XBMCK_p;
+ VK_keymap[VK_Q] = XBMCK_q;
+ VK_keymap[VK_R] = XBMCK_r;
+ VK_keymap[VK_S] = XBMCK_s;
+ VK_keymap[VK_T] = XBMCK_t;
+ VK_keymap[VK_U] = XBMCK_u;
+ VK_keymap[VK_V] = XBMCK_v;
+ VK_keymap[VK_W] = XBMCK_w;
+ VK_keymap[VK_X] = XBMCK_x;
+ VK_keymap[VK_Y] = XBMCK_y;
+ VK_keymap[VK_Z] = XBMCK_z;
+ VK_keymap[VK_DELETE] = XBMCK_DELETE;
+
+ VK_keymap[VK_NUMPAD0] = XBMCK_KP0;
+ VK_keymap[VK_NUMPAD1] = XBMCK_KP1;
+ VK_keymap[VK_NUMPAD2] = XBMCK_KP2;
+ VK_keymap[VK_NUMPAD3] = XBMCK_KP3;
+ VK_keymap[VK_NUMPAD4] = XBMCK_KP4;
+ VK_keymap[VK_NUMPAD5] = XBMCK_KP5;
+ VK_keymap[VK_NUMPAD6] = XBMCK_KP6;
+ VK_keymap[VK_NUMPAD7] = XBMCK_KP7;
+ VK_keymap[VK_NUMPAD8] = XBMCK_KP8;
+ VK_keymap[VK_NUMPAD9] = XBMCK_KP9;
+ VK_keymap[VK_DECIMAL] = XBMCK_KP_PERIOD;
+ VK_keymap[VK_DIVIDE] = XBMCK_KP_DIVIDE;
+ VK_keymap[VK_MULTIPLY] = XBMCK_KP_MULTIPLY;
+ VK_keymap[VK_SUBTRACT] = XBMCK_KP_MINUS;
+ VK_keymap[VK_ADD] = XBMCK_KP_PLUS;
+
+ VK_keymap[VK_UP] = XBMCK_UP;
+ VK_keymap[VK_DOWN] = XBMCK_DOWN;
+ VK_keymap[VK_RIGHT] = XBMCK_RIGHT;
+ VK_keymap[VK_LEFT] = XBMCK_LEFT;
+ VK_keymap[VK_INSERT] = XBMCK_INSERT;
+ VK_keymap[VK_HOME] = XBMCK_HOME;
+ VK_keymap[VK_END] = XBMCK_END;
+ VK_keymap[VK_PRIOR] = XBMCK_PAGEUP;
+ VK_keymap[VK_NEXT] = XBMCK_PAGEDOWN;
+
+ VK_keymap[VK_F1] = XBMCK_F1;
+ VK_keymap[VK_F2] = XBMCK_F2;
+ VK_keymap[VK_F3] = XBMCK_F3;
+ VK_keymap[VK_F4] = XBMCK_F4;
+ VK_keymap[VK_F5] = XBMCK_F5;
+ VK_keymap[VK_F6] = XBMCK_F6;
+ VK_keymap[VK_F7] = XBMCK_F7;
+ VK_keymap[VK_F8] = XBMCK_F8;
+ VK_keymap[VK_F9] = XBMCK_F9;
+ VK_keymap[VK_F10] = XBMCK_F10;
+ VK_keymap[VK_F11] = XBMCK_F11;
+ VK_keymap[VK_F12] = XBMCK_F12;
+ VK_keymap[VK_F13] = XBMCK_F13;
+ VK_keymap[VK_F14] = XBMCK_F14;
+ VK_keymap[VK_F15] = XBMCK_F15;
+
+ VK_keymap[VK_NUMLOCK] = XBMCK_NUMLOCK;
+ VK_keymap[VK_CAPITAL] = XBMCK_CAPSLOCK;
+ VK_keymap[VK_SCROLL] = XBMCK_SCROLLOCK;
+ VK_keymap[VK_RSHIFT] = XBMCK_RSHIFT;
+ VK_keymap[VK_LSHIFT] = XBMCK_LSHIFT;
+ VK_keymap[VK_RCONTROL] = XBMCK_RCTRL;
+ VK_keymap[VK_LCONTROL] = XBMCK_LCTRL;
+ VK_keymap[VK_RMENU] = XBMCK_RALT;
+ VK_keymap[VK_LMENU] = XBMCK_LALT;
+ VK_keymap[VK_RWIN] = XBMCK_RSUPER;
+ VK_keymap[VK_LWIN] = XBMCK_LSUPER;
+
+ VK_keymap[VK_HELP] = XBMCK_HELP;
+#ifdef VK_PRINT
+ VK_keymap[VK_PRINT] = XBMCK_PRINT;
+#endif
+ VK_keymap[VK_SNAPSHOT] = XBMCK_PRINT;
+ VK_keymap[VK_CANCEL] = XBMCK_BREAK;
+ VK_keymap[VK_APPS] = XBMCK_MENU;
+
+ // Only include the multimedia keys if they have been enabled in the
+ // advanced settings
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_enableMultimediaKeys)
+ {
+ VK_keymap[VK_BROWSER_BACK] = XBMCK_BROWSER_BACK;
+ VK_keymap[VK_BROWSER_FORWARD] = XBMCK_BROWSER_FORWARD;
+ VK_keymap[VK_BROWSER_REFRESH] = XBMCK_BROWSER_REFRESH;
+ VK_keymap[VK_BROWSER_STOP] = XBMCK_BROWSER_STOP;
+ VK_keymap[VK_BROWSER_SEARCH] = XBMCK_BROWSER_SEARCH;
+ VK_keymap[VK_BROWSER_FAVORITES] = XBMCK_BROWSER_FAVORITES;
+ VK_keymap[VK_BROWSER_HOME] = XBMCK_BROWSER_HOME;
+ VK_keymap[VK_VOLUME_MUTE] = XBMCK_VOLUME_MUTE;
+ VK_keymap[VK_VOLUME_DOWN] = XBMCK_VOLUME_DOWN;
+ VK_keymap[VK_VOLUME_UP] = XBMCK_VOLUME_UP;
+ VK_keymap[VK_MEDIA_NEXT_TRACK] = XBMCK_MEDIA_NEXT_TRACK;
+ VK_keymap[VK_MEDIA_PREV_TRACK] = XBMCK_MEDIA_PREV_TRACK;
+ VK_keymap[VK_MEDIA_STOP] = XBMCK_MEDIA_STOP;
+ VK_keymap[VK_MEDIA_PLAY_PAUSE] = XBMCK_MEDIA_PLAY_PAUSE;
+ VK_keymap[VK_LAUNCH_MAIL] = XBMCK_LAUNCH_MAIL;
+ VK_keymap[VK_LAUNCH_MEDIA_SELECT] = XBMCK_LAUNCH_MEDIA_SELECT;
+ VK_keymap[VK_LAUNCH_APP1] = XBMCK_LAUNCH_APP1;
+ VK_keymap[VK_LAUNCH_APP2] = XBMCK_LAUNCH_APP2;
+ }
+}
+
+} // namespace WINDOWS
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/windows/WinSystemWin32.cpp b/xbmc/windowing/windows/WinSystemWin32.cpp
new file mode 100644
index 0000000..9394b4b
--- /dev/null
+++ b/xbmc/windowing/windows/WinSystemWin32.cpp
@@ -0,0 +1,1368 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemWin32.h"
+
+#include "ServiceBroker.h"
+#include "VideoSyncD3D.h"
+#include "WIN32Util.h"
+#include "WinEventsWin32.h"
+#include "application/Application.h"
+#include "cores/AudioEngine/AESinkFactory.h"
+#include "cores/AudioEngine/Sinks/AESinkDirectSound.h"
+#include "cores/AudioEngine/Sinks/AESinkWASAPI.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "messaging/ApplicationMessenger.h"
+#include "platform/Environment.h"
+#include "rendering/dx/ScreenshotSurfaceWindows.h"
+#include "resource.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/windows/Win32DPMSSupport.h"
+
+#include "platform/win32/CharsetConverter.h"
+#include "platform/win32/input/IRServerSuite.h"
+
+#include <algorithm>
+#include <mutex>
+
+#include <tpcshrd.h>
+
+using namespace std::chrono_literals;
+
+const char* CWinSystemWin32::SETTING_WINDOW_TOP = "window.top";
+const char* CWinSystemWin32::SETTING_WINDOW_LEFT = "window.left";
+
+CWinSystemWin32::CWinSystemWin32()
+ : CWinSystemBase()
+ , PtrGetGestureInfo(nullptr)
+ , PtrSetGestureConfig(nullptr)
+ , PtrCloseGestureInfoHandle(nullptr)
+ , PtrEnableNonClientDpiScaling(nullptr)
+ , m_hWnd(nullptr)
+ , m_hMonitor(nullptr)
+ , m_hInstance(nullptr)
+ , m_hIcon(nullptr)
+ , m_ValidWindowedPosition(false)
+ , m_IsAlteringWindow(false)
+ , m_delayDispReset(false)
+ , m_state(WINDOW_STATE_WINDOWED)
+ , m_fullscreenState(WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW)
+ , m_windowState(WINDOW_WINDOW_STATE_WINDOWED)
+ , m_windowStyle(WINDOWED_STYLE)
+ , m_windowExStyle(WINDOWED_EX_STYLE)
+ , m_inFocus(false)
+ , m_bMinimized(false)
+{
+ std::string cacert = CEnvironment::getenv("SSL_CERT_FILE");
+ if (cacert.empty() || !XFILE::CFile::Exists(cacert))
+ {
+ cacert = CSpecialProtocol::TranslatePath("special://xbmc/system/certs/cacert.pem");
+ if (XFILE::CFile::Exists(cacert))
+ CEnvironment::setenv("SSL_CERT_FILE", cacert.c_str(), 1);
+ }
+
+ m_winEvents.reset(new CWinEventsWin32());
+ AE::CAESinkFactory::ClearSinks();
+ CAESinkDirectSound::Register();
+ CAESinkWASAPI::Register();
+ CScreenshotSurfaceWindows::Register();
+
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bScanIRServer)
+ {
+ m_irss.reset(new CIRServerSuite());
+ m_irss->Initialize();
+ }
+ m_dpms = std::make_shared<CWin32DPMSSupport>();
+}
+
+CWinSystemWin32::~CWinSystemWin32()
+{
+ if (m_hIcon)
+ {
+ DestroyIcon(m_hIcon);
+ m_hIcon = nullptr;
+ }
+};
+
+bool CWinSystemWin32::InitWindowSystem()
+{
+ if(!CWinSystemBase::InitWindowSystem())
+ return false;
+
+ return true;
+}
+
+bool CWinSystemWin32::DestroyWindowSystem()
+{
+ if (m_hMonitor)
+ {
+ MONITOR_DETAILS* details = GetDisplayDetails(m_hMonitor);
+ if (details)
+ RestoreDesktopResolution(details);
+ }
+ return true;
+}
+
+bool CWinSystemWin32::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ using KODI::PLATFORM::WINDOWS::ToW;
+ auto nameW = ToW(name);
+
+ m_hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
+ if(m_hInstance == nullptr)
+ CLog::LogF(LOGDEBUG, " GetModuleHandle failed with {}", GetLastError());
+
+ // Load Win32 procs if available
+ HMODULE hUser32 = GetModuleHandle(L"user32");
+ if (hUser32)
+ {
+ PtrGetGestureInfo = reinterpret_cast<pGetGestureInfo>(GetProcAddress(hUser32, "GetGestureInfo"));
+ PtrSetGestureConfig = reinterpret_cast<pSetGestureConfig>(GetProcAddress(hUser32, "SetGestureConfig"));
+ PtrCloseGestureInfoHandle = reinterpret_cast<pCloseGestureInfoHandle>(GetProcAddress(hUser32, "CloseGestureInfoHandle"));
+ // if available, enable automatic DPI scaling of the non-client area portions of the window.
+ PtrEnableNonClientDpiScaling = reinterpret_cast<pEnableNonClientDpiScaling>(GetProcAddress(hUser32, "EnableNonClientDpiScaling"));
+ }
+
+ UpdateStates(fullScreen);
+ // initialize the state
+ WINDOW_STATE state = GetState(fullScreen);
+
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_bFullScreen = fullScreen;
+ m_fRefreshRate = res.fRefreshRate;
+
+ m_hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
+
+ // Register the windows class
+ WNDCLASSEX wndClass = {};
+ wndClass.cbSize = sizeof(wndClass);
+ wndClass.style = CS_HREDRAW | CS_VREDRAW;
+ wndClass.lpfnWndProc = CWinEventsWin32::WndProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = m_hInstance;
+ wndClass.hIcon = m_hIcon;
+ wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW );
+ wndClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
+ wndClass.lpszMenuName = nullptr;
+ wndClass.lpszClassName = nameW.c_str();
+
+ if( !RegisterClassExW( &wndClass ) )
+ {
+ CLog::LogF(LOGERROR, " RegisterClassExW failed with {}", GetLastError());
+ return false;
+ }
+
+ // put the window at desired display
+ RECT screenRect = ScreenRect(m_hMonitor);
+ m_nLeft = screenRect.left;
+ m_nTop = screenRect.top;
+
+ if (state == WINDOW_STATE_WINDOWED)
+ {
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const int top = settings->GetInt(SETTING_WINDOW_TOP);
+ const int left = settings->GetInt(SETTING_WINDOW_LEFT);
+ const RECT vsRect = GetVirtualScreenRect();
+
+ // we check that window is inside of virtual screen rect (sum of all monitors)
+ // top 0 left 0 is a special position that centers the window on the screen
+ if ((top != 0 || left != 0) && top >= vsRect.top && top + m_nHeight <= vsRect.bottom &&
+ left >= vsRect.left && left + m_nWidth <= vsRect.right)
+ {
+ // restore previous window position
+ m_nLeft = left;
+ m_nTop = top;
+ }
+ else
+ {
+ // Windowed mode: position and size in settings and most places in Kodi
+ // are for the client part of the window.
+ RECT rcWorkArea = GetScreenWorkArea(m_hMonitor);
+
+ int workAreaWidth = rcWorkArea.right - rcWorkArea.left;
+ int workAreaHeight = rcWorkArea.bottom - rcWorkArea.top;
+
+ RECT rcNcArea = GetNcAreaOffsets(m_windowStyle, false, m_windowExStyle);
+ int maxClientWidth = (rcWorkArea.right - rcNcArea.right) - (rcWorkArea.left - rcNcArea.left);
+ int maxClientHeight = (rcWorkArea.bottom - rcNcArea.bottom) - (rcWorkArea.top - rcNcArea.top);
+
+ m_nWidth = std::min(m_nWidth, maxClientWidth);
+ m_nHeight = std::min(m_nHeight, maxClientHeight);
+ CWinSystemBase::SetWindowResolution(m_nWidth, m_nHeight);
+
+ // center window on desktop
+ m_nLeft = rcWorkArea.left - rcNcArea.left + (maxClientWidth - m_nWidth) / 2;
+ m_nTop = rcWorkArea.top - rcNcArea.top + (maxClientHeight - m_nHeight) / 2;
+ }
+ m_ValidWindowedPosition = true;
+ }
+
+ HWND hWnd = CreateWindowExW(
+ m_windowExStyle,
+ nameW.c_str(),
+ nameW.c_str(),
+ m_windowStyle,
+ m_nLeft,
+ m_nTop,
+ m_nWidth,
+ m_nHeight,
+ nullptr,
+ nullptr,
+ m_hInstance,
+ nullptr
+ );
+
+ if( hWnd == nullptr )
+ {
+ CLog::LogF(LOGERROR, " CreateWindow failed with {}", GetLastError());
+ return false;
+ }
+
+ m_inFocus = true;
+
+ DWORD dwHwndTabletProperty =
+ TABLET_DISABLE_PENBARRELFEEDBACK | // disables UI feedback on pen button down (circle)
+ TABLET_DISABLE_FLICKS; // disables pen flicks (back, forward, drag down, drag up)
+
+ SetProp(hWnd, MICROSOFT_TABLETPENSERVICE_PROPERTY, &dwHwndTabletProperty);
+
+ m_hWnd = hWnd;
+ m_bWindowCreated = true;
+
+ CreateBlankWindows();
+
+ m_state = state;
+ AdjustWindow(true);
+
+ // Show the window
+ ShowWindow( m_hWnd, SW_SHOWDEFAULT );
+ UpdateWindow( m_hWnd );
+
+ // Configure the tray icon.
+ m_trayIcon.cbSize = sizeof(m_trayIcon);
+ m_trayIcon.hWnd = m_hWnd;
+ m_trayIcon.hIcon = m_hIcon;
+ wcsncpy(m_trayIcon.szTip, nameW.c_str(), sizeof(m_trayIcon.szTip) / sizeof(WCHAR));
+ m_trayIcon.uCallbackMessage = TRAY_ICON_NOTIFY;
+ m_trayIcon.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
+
+ return true;
+}
+
+bool CWinSystemWin32::CreateBlankWindows()
+{
+ WNDCLASSEX wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style= CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc= DefWindowProc;
+ wcex.cbClsExtra= 0;
+ wcex.cbWndExtra= 0;
+ wcex.hInstance= nullptr;
+ wcex.hIcon= nullptr;
+ wcex.hCursor= nullptr;
+ wcex.hbrBackground= static_cast<HBRUSH>(CreateSolidBrush(RGB(0, 0, 0)));
+ wcex.lpszMenuName= nullptr;
+ wcex.lpszClassName= L"BlankWindowClass";
+ wcex.hIconSm= nullptr;
+
+ // Now we can go ahead and register our new window class
+ if(!RegisterClassEx(&wcex))
+ {
+ CLog::LogF(LOGERROR, "RegisterClass failed with {}", GetLastError());
+ return false;
+ }
+
+ // We need as many blank windows as there are screens (minus 1)
+ for (size_t i = 0; i < m_displays.size() - 1; i++)
+ {
+ HWND hBlankWindow = CreateWindowEx(WS_EX_TOPMOST, L"BlankWindowClass", L"", WS_POPUP | WS_DISABLED,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, nullptr, nullptr);
+
+ if (hBlankWindow == nullptr)
+ {
+ CLog::LogF(LOGERROR, "CreateWindowEx failed with {}", GetLastError());
+ return false;
+ }
+
+ m_hBlankWindows.push_back(hBlankWindow);
+ }
+
+ return true;
+}
+
+bool CWinSystemWin32::BlankNonActiveMonitors(bool bBlank)
+{
+ if (m_hBlankWindows.empty())
+ return false;
+
+ if (bBlank == false)
+ {
+ for (unsigned int i=0; i < m_hBlankWindows.size(); i++)
+ ShowWindow(m_hBlankWindows[i], SW_HIDE);
+ return true;
+ }
+
+ // Move a blank window in front of every display, except the current display.
+ for (size_t i = 0, j = 0; i < m_displays.size(); ++i)
+ {
+ MONITOR_DETAILS& details = m_displays[i];
+ if (details.hMonitor == m_hMonitor)
+ continue;
+
+ RECT rBounds = ScreenRect(details.hMonitor);
+ // move and resize the window
+ SetWindowPos(m_hBlankWindows[j], nullptr, rBounds.left, rBounds.top,
+ rBounds.right - rBounds.left, rBounds.bottom - rBounds.top,
+ SWP_NOACTIVATE);
+
+ ShowWindow(m_hBlankWindows[j], SW_SHOW | SW_SHOWNOACTIVATE);
+ j++;
+ }
+
+ if (m_hWnd)
+ SetForegroundWindow(m_hWnd);
+
+ return true;
+}
+
+bool CWinSystemWin32::CenterWindow()
+{
+ RESOLUTION_INFO DesktopRes = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP);
+
+ m_nLeft = (DesktopRes.iWidth / 2) - (m_nWidth / 2);
+ m_nTop = (DesktopRes.iHeight / 2) - (m_nHeight / 2);
+
+ RECT rc;
+ rc.left = m_nLeft;
+ rc.top = m_nTop;
+ rc.right = rc.left + m_nWidth;
+ rc.bottom = rc.top + m_nHeight;
+ AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, false );
+
+ SetWindowPos(m_hWnd, nullptr, rc.left, rc.top, 0, 0, SWP_NOSIZE);
+
+ return true;
+}
+
+bool CWinSystemWin32::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ m_nWidth = newWidth;
+ m_nHeight = newHeight;
+
+ if (newLeft > 0)
+ m_nLeft = newLeft;
+
+ if (newTop > 0)
+ m_nTop = newTop;
+
+ AdjustWindow();
+
+ return true;
+}
+
+void CWinSystemWin32::FinishWindowResize(int newWidth, int newHeight)
+{
+ m_nWidth = newWidth;
+ m_nHeight = newHeight;
+}
+
+void CWinSystemWin32::AdjustWindow(bool forceResize)
+{
+ CLog::LogF(LOGDEBUG, "adjusting window if required.");
+
+ HWND windowAfter;
+ RECT rc;
+
+ if (m_state == WINDOW_STATE_FULLSCREEN_WINDOW || m_state == WINDOW_STATE_FULLSCREEN)
+ {
+ windowAfter = HWND_TOP;
+ rc = ScreenRect(m_hMonitor);
+ }
+ else // m_state == WINDOW_STATE_WINDOWED
+ {
+ windowAfter = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST;
+
+ if (!m_ValidWindowedPosition)
+ {
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const int top = settings->GetInt(SETTING_WINDOW_TOP);
+ const int left = settings->GetInt(SETTING_WINDOW_LEFT);
+ const RECT vsRect = GetVirtualScreenRect();
+
+ // we check that window is inside of virtual screen rect (sum of all monitors)
+ // top 0 left 0 is a special position that centers the window on the screen
+ if ((top != 0 || left != 0) && top >= vsRect.top && top + m_nHeight <= vsRect.bottom &&
+ left >= vsRect.left && left + m_nWidth <= vsRect.right)
+ {
+ // restore previous window position
+ m_nTop = top;
+ m_nLeft = left;
+ }
+ else
+ {
+ // Windowed mode: position and size in settings and most places in Kodi
+ // are for the client part of the window.
+ RECT rcWorkArea = GetScreenWorkArea(m_hMonitor);
+
+ int workAreaWidth = rcWorkArea.right - rcWorkArea.left;
+ int workAreaHeight = rcWorkArea.bottom - rcWorkArea.top;
+
+ RECT rcNcArea = GetNcAreaOffsets(m_windowStyle, false, m_windowExStyle);
+ int maxClientWidth =
+ (rcWorkArea.right - rcNcArea.right) - (rcWorkArea.left - rcNcArea.left);
+ int maxClientHeight =
+ (rcWorkArea.bottom - rcNcArea.bottom) - (rcWorkArea.top - rcNcArea.top);
+
+ m_nWidth = std::min(m_nWidth, maxClientWidth);
+ m_nHeight = std::min(m_nHeight, maxClientHeight);
+ CWinSystemBase::SetWindowResolution(m_nWidth, m_nHeight);
+
+ // center window on desktop
+ m_nLeft = rcWorkArea.left - rcNcArea.left + (maxClientWidth - m_nWidth) / 2;
+ m_nTop = rcWorkArea.top - rcNcArea.top + (maxClientHeight - m_nHeight) / 2;
+ }
+ m_ValidWindowedPosition = true;
+ }
+
+ rc.left = m_nLeft;
+ rc.right = m_nLeft + m_nWidth;
+ rc.top = m_nTop;
+ rc.bottom = m_nTop + m_nHeight;
+
+ HMONITOR hMon = MonitorFromRect(&rc, MONITOR_DEFAULTTONULL);
+ HMONITOR hMon2 = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
+
+ if (!m_ValidWindowedPosition || hMon == nullptr || hMon != hMon2)
+ {
+ // centering window at desktop
+ RECT newScreenRect = ScreenRect(hMon2);
+ rc.left = m_nLeft = newScreenRect.left + ((newScreenRect.right - newScreenRect.left) / 2) - (m_nWidth / 2);
+ rc.top = m_nTop = newScreenRect.top + ((newScreenRect.bottom - newScreenRect.top) / 2) - (m_nHeight / 2);
+ rc.right = m_nLeft + m_nWidth;
+ rc.bottom = m_nTop + m_nHeight;
+ m_ValidWindowedPosition = true;
+ }
+ AdjustWindowRectEx(&rc, m_windowStyle, false, m_windowExStyle);
+ }
+
+ WINDOWINFO wi;
+ wi.cbSize = sizeof(WINDOWINFO);
+ if (!GetWindowInfo(m_hWnd, &wi))
+ {
+ CLog::LogF(LOGERROR, "GetWindowInfo failed with {}", GetLastError());
+ return;
+ }
+ RECT wr = wi.rcWindow;
+
+ if ( wr.bottom - wr.top == rc.bottom - rc.top
+ && wr.right - wr.left == rc.right - rc.left
+ && (wi.dwStyle & WS_CAPTION) == (m_windowStyle & WS_CAPTION)
+ && !forceResize)
+ {
+ return;
+ }
+
+ //Sets the window style
+ SetLastError(0);
+ SetWindowLongPtr( m_hWnd, GWL_STYLE, m_windowStyle );
+
+ //Sets the window ex style
+ SetLastError(0);
+ SetWindowLongPtr( m_hWnd, GWL_EXSTYLE, m_windowExStyle );
+
+ // resize window
+ CLog::LogF(LOGDEBUG, "resizing due to size change ({},{},{},{}{})->({},{},{},{}{})", wr.left,
+ wr.top, wr.right, wr.bottom, (wi.dwStyle & WS_CAPTION) ? "" : " fullscreen", rc.left,
+ rc.top, rc.right, rc.bottom, (m_windowStyle & WS_CAPTION) ? "" : " fullscreen");
+ SetWindowPos(
+ m_hWnd,
+ windowAfter,
+ rc.left,
+ rc.top,
+ rc.right - rc.left,
+ rc.bottom - rc.top,
+ SWP_SHOWWINDOW | SWP_DRAWFRAME
+ );
+}
+
+void CWinSystemWin32::CenterCursor() const
+{
+ RECT rect;
+ POINT point = {};
+
+ //Gets the client rect, then translates it to screen coordinates
+ //so that SetCursorPos isn't called with relative x and y values
+ GetClientRect(m_hWnd, &rect);
+ ClientToScreen(m_hWnd, &point);
+
+ rect.left += point.x;
+ rect.right += point.x;
+ rect.top += point.y;
+ rect.bottom += point.y;
+
+ int x = rect.left + (rect.right - rect.left) / 2;
+ int y = rect.top + (rect.bottom - rect.top) / 2;
+
+ SetCursorPos(x, y);
+}
+
+bool CWinSystemWin32::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ CWinSystemWin32::UpdateStates(fullScreen);
+ WINDOW_STATE state = GetState(fullScreen);
+
+ CLog::LogF(LOGDEBUG, "({}) with size {}x{}, refresh {:f}{}", window_state_names[state],
+ res.iWidth, res.iHeight, res.fRefreshRate,
+ (res.dwFlags & D3DPRESENTFLAG_INTERLACED) ? "i" : "");
+
+ // oldMonitor may be NULL if it's powered off or not available due windows settings
+ MONITOR_DETAILS* oldMonitor = GetDisplayDetails(m_hMonitor);
+ MONITOR_DETAILS* newMonitor = GetDisplayDetails(res.strOutput);
+
+ bool forceChange = false; // resolution/display is changed but window state isn't changed
+ bool changeScreen = false; // display is changed
+ bool stereoChange = IsStereoEnabled() != (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED);
+
+ if (m_nWidth != res.iWidth || m_nHeight != res.iHeight || m_fRefreshRate != res.fRefreshRate ||
+ !oldMonitor || oldMonitor->hMonitor != newMonitor->hMonitor || stereoChange ||
+ m_bFirstResChange)
+ {
+ if (!oldMonitor || oldMonitor->hMonitor != newMonitor->hMonitor)
+ changeScreen = true;
+ forceChange = true;
+ }
+
+ if (state == m_state && !forceChange)
+ {
+ m_bBlankOtherDisplay = blankOtherDisplays;
+ BlankNonActiveMonitors(m_bBlankOtherDisplay);
+ return true;
+ }
+
+ // entering to stereo mode, limit resolution to 1080p@23.976
+ if (stereoChange && !IsStereoEnabled() && res.iWidth > 1280)
+ {
+ res = CDisplaySettings::GetInstance().GetResolutionInfo(
+ CResolutionUtils::ChooseBestResolution(24.f / 1.001f, 1920, 1080, true));
+ }
+
+ if (m_state == WINDOW_STATE_WINDOWED)
+ {
+ WINDOWINFO wi = {};
+ wi.cbSize = sizeof(WINDOWINFO);
+ if (GetWindowInfo(m_hWnd, &wi) && wi.rcClient.top > 0)
+ {
+ m_nLeft = wi.rcClient.left;
+ m_nTop = wi.rcClient.top;
+ m_ValidWindowedPosition = true;
+ }
+ }
+
+ m_IsAlteringWindow = true;
+ ReleaseBackBuffer();
+
+ if (changeScreen)
+ {
+ // before we changing display we have to leave exclusive mode on "old" display
+ if (m_state == WINDOW_STATE_FULLSCREEN)
+ SetDeviceFullScreen(false, res);
+
+ // restoring native resolution on "old" display
+ RestoreDesktopResolution(oldMonitor);
+
+ // notify about screen change (it may require recreate rendering device)
+ m_fRefreshRate = res.fRefreshRate; // use desired refresh for driver hook
+ OnScreenChange(newMonitor->hMonitor);
+ }
+
+ m_bFirstResChange = false;
+ m_bFullScreen = fullScreen;
+ m_hMonitor = newMonitor->hMonitor;
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_bBlankOtherDisplay = blankOtherDisplays;
+ m_fRefreshRate = res.fRefreshRate;
+
+ if (state == WINDOW_STATE_FULLSCREEN)
+ {
+ SetForegroundWindowInternal(m_hWnd);
+
+ m_state = state;
+ AdjustWindow(changeScreen);
+
+ // enter in exclusive mode, this will change resolution if we already in
+ SetDeviceFullScreen(true, res);
+ }
+ else if (m_state == WINDOW_STATE_FULLSCREEN || m_state == WINDOW_STATE_FULLSCREEN_WINDOW) // we're in fullscreen state now
+ {
+ // guess we are leaving exclusive mode, this will not an effect if we already not in
+ SetDeviceFullScreen(false, res);
+
+ if (state == WINDOW_STATE_WINDOWED) // go to a windowed state
+ {
+ // need to restore resolution if it was changed to not native
+ // because we do not support resolution change in windowed mode
+ RestoreDesktopResolution(newMonitor);
+ }
+ else if (state == WINDOW_STATE_FULLSCREEN_WINDOW) // enter fullscreen window instead
+ {
+ ChangeResolution(res, stereoChange);
+ }
+
+ m_state = state;
+ AdjustWindow(changeScreen);
+ }
+ else // we're in windowed state now
+ {
+ if (state == WINDOW_STATE_FULLSCREEN_WINDOW)
+ {
+ ChangeResolution(res, stereoChange);
+
+ m_state = state;
+ AdjustWindow(changeScreen);
+ }
+ }
+
+ if (changeScreen)
+ CenterCursor();
+
+ CreateBackBuffer();
+
+ BlankNonActiveMonitors(m_bBlankOtherDisplay);
+ m_IsAlteringWindow = false;
+
+ return true;
+}
+
+bool CWinSystemWin32::DPIChanged(WORD dpi, RECT windowRect) const
+{
+ (void)dpi;
+ RECT resizeRect = windowRect;
+ HMONITOR hMon = MonitorFromRect(&resizeRect, MONITOR_DEFAULTTONULL);
+ if (hMon == nullptr)
+ {
+ hMon = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
+ }
+
+ if (hMon)
+ {
+ MONITORINFOEX monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfoW(hMon, &monitorInfo);
+
+ if (m_state == WINDOW_STATE_FULLSCREEN_WINDOW ||
+ m_state == WINDOW_STATE_FULLSCREEN)
+ {
+ resizeRect = monitorInfo.rcMonitor; // the whole screen
+ }
+ else
+ {
+ RECT wr = monitorInfo.rcWork; // it excludes task bar
+ long wrWidth = wr.right - wr.left;
+ long wrHeight = wr.bottom - wr.top;
+ long resizeWidth = resizeRect.right - resizeRect.left;
+ long resizeHeight = resizeRect.bottom - resizeRect.top;
+
+ if (resizeWidth > wrWidth)
+ {
+ resizeRect.right = resizeRect.left + wrWidth;
+ }
+
+ // make sure suggested windows size is not taller or wider than working area of new monitor (considers the toolbar)
+ if (resizeHeight > wrHeight)
+ {
+ resizeRect.bottom = resizeRect.top + wrHeight;
+ }
+ }
+ }
+
+ // resize the window to the suggested size. Will generate a WM_SIZE event
+ SetWindowPos(m_hWnd,
+ nullptr,
+ resizeRect.left,
+ resizeRect.top,
+ resizeRect.right - resizeRect.left,
+ resizeRect.bottom - resizeRect.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return true;
+}
+
+void CWinSystemWin32::SetMinimized(bool minimized)
+{
+ const auto advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
+
+ if (advancedSettings->m_minimizeToTray)
+ {
+ if (minimized)
+ {
+ Shell_NotifyIcon(NIM_ADD, &m_trayIcon);
+ ShowWindow(m_hWnd, SW_HIDE);
+ }
+ else
+ {
+ Shell_NotifyIcon(NIM_DELETE, &m_trayIcon);
+ ShowWindow(m_hWnd, SW_RESTORE);
+ }
+ }
+
+ m_bMinimized = minimized;
+}
+
+std::vector<std::string> CWinSystemWin32::GetConnectedOutputs()
+{
+ std::vector<std::string> outputs;
+
+ for (auto& display : m_displays)
+ {
+ outputs.emplace_back(KODI::PLATFORM::WINDOWS::FromW(display.MonitorNameW));
+ }
+
+ return outputs;
+}
+
+void CWinSystemWin32::RestoreDesktopResolution(MONITOR_DETAILS* details)
+{
+ if (!details)
+ return;
+
+ RESOLUTION_INFO info;
+ info.iWidth = details->ScreenWidth;
+ info.iHeight = details->ScreenHeight;
+ if ((details->RefreshRate + 1) % 24 == 0 || (details->RefreshRate + 1) % 30 == 0)
+ info.fRefreshRate = static_cast<float>(details->RefreshRate + 1) / 1.001f;
+ else
+ info.fRefreshRate = static_cast<float>(details->RefreshRate);
+ info.strOutput = KODI::PLATFORM::WINDOWS::FromW(details->DeviceNameW);
+ info.dwFlags = details->Interlaced ? D3DPRESENTFLAG_INTERLACED : 0;
+
+ CLog::LogF(LOGDEBUG, "restoring desktop resolution for '{}'.", KODI::PLATFORM::WINDOWS::FromW(details->MonitorNameW));
+ ChangeResolution(info);
+}
+
+MONITOR_DETAILS* CWinSystemWin32::GetDisplayDetails(const std::string& name)
+{
+ using KODI::PLATFORM::WINDOWS::ToW;
+
+ if (!name.empty() && name != "Default")
+ {
+ std::wstring nameW = ToW(name);
+ auto it = std::find_if(m_displays.begin(), m_displays.end(), [&nameW](MONITOR_DETAILS& m)
+ {
+ if (nameW[0] == '\\') // name is device name
+ return m.DeviceNameW == nameW;
+ return m.MonitorNameW == nameW;
+ });
+ if (it != m_displays.end())
+ return &(*it);
+ }
+
+ // fallback to primary
+ auto it = std::find_if(m_displays.begin(), m_displays.end(), [](MONITOR_DETAILS& m)
+ {
+ return m.IsPrimary;
+ });
+ if (it != m_displays.end())
+ return &(*it);
+
+ // nothing found
+ return nullptr;
+}
+
+MONITOR_DETAILS* CWinSystemWin32::GetDisplayDetails(HMONITOR handle)
+{
+ auto it = std::find_if(m_displays.begin(), m_displays.end(), [&handle](MONITOR_DETAILS& m)
+ {
+ return m.hMonitor == handle;
+ });
+ if (it != m_displays.end())
+ return &(*it);
+
+ return nullptr;
+}
+
+RECT CWinSystemWin32::ScreenRect(HMONITOR handle)
+{
+ const MONITOR_DETAILS* details = GetDisplayDetails(handle);
+ if (!details)
+ {
+ CLog::LogF(LOGERROR, "no monitor found for handle");
+ return RECT();
+ }
+
+ DEVMODEW sDevMode = {};
+ sDevMode.dmSize = sizeof(sDevMode);
+ if(!EnumDisplaySettingsW(details->DeviceNameW.c_str(), ENUM_CURRENT_SETTINGS, &sDevMode))
+ CLog::LogF(LOGERROR, " EnumDisplaySettings failed with {}", GetLastError());
+
+ RECT rc;
+ rc.left = sDevMode.dmPosition.x;
+ rc.right = sDevMode.dmPosition.x + sDevMode.dmPelsWidth;
+ rc.top = sDevMode.dmPosition.y;
+ rc.bottom = sDevMode.dmPosition.y + sDevMode.dmPelsHeight;
+
+ return rc;
+}
+
+void CWinSystemWin32::GetConnectedDisplays(std::vector<MONITOR_DETAILS>& outputs)
+{
+ using KODI::PLATFORM::WINDOWS::FromW;
+
+ const POINT ptZero = { 0, 0 };
+ HMONITOR hmPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
+
+ DISPLAY_DEVICEW ddAdapter = {};
+ ddAdapter.cb = sizeof(ddAdapter);
+
+ for (DWORD adapter = 0; EnumDisplayDevicesW(nullptr, adapter, &ddAdapter, 0); ++adapter)
+ {
+ // Exclude displays that are not part of the windows desktop. Using them is too different: no windows,
+ // direct access with GDI CreateDC() or DirectDraw for example. So it may be possible to play video, but GUI?
+ if ((ddAdapter.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
+ || !(ddAdapter.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
+ continue;
+
+ DISPLAY_DEVICEW ddMon = {};
+ ddMon.cb = sizeof(ddMon);
+ bool foundScreen = false;
+
+ DWORD screen = 0;
+ // Just look for the first active output, we're actually only interested in the information at the adapter level.
+ for (; EnumDisplayDevicesW(ddAdapter.DeviceName, screen, &ddMon, 0); ++screen)
+ {
+ if (ddMon.StateFlags & (DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED))
+ {
+ foundScreen = true;
+ break;
+ }
+ }
+
+ // Remoting returns no screens. Handle with a dummy screen.
+ if (!foundScreen && screen == 0)
+ {
+ lstrcpyW(ddMon.DeviceString, L"Dummy Monitor"); // safe: large static array
+ foundScreen = true;
+ }
+
+ if (foundScreen)
+ {
+ // get information about the display's current position and display mode
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+ if (EnumDisplaySettingsExW(ddAdapter.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0) == FALSE)
+ EnumDisplaySettingsExW(ddAdapter.DeviceName, ENUM_REGISTRY_SETTINGS, &dm, 0);
+
+ POINT pt = { dm.dmPosition.x, dm.dmPosition.y };
+ HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
+
+ MONITOR_DETAILS md = {};
+ uint8_t num = 1;
+ do
+ {
+ // `Monitor #N`
+ md.MonitorNameW = std::wstring(ddMon.DeviceString) + L" #" + std::to_wstring(num++);
+ }
+ while(std::any_of(outputs.begin(), outputs.end(), [&](MONITOR_DETAILS& m) { return m.MonitorNameW == md.MonitorNameW; }));
+
+ md.CardNameW = ddAdapter.DeviceString;
+ md.DeviceNameW = ddAdapter.DeviceName;
+ md.ScreenWidth = dm.dmPelsWidth;
+ md.ScreenHeight = dm.dmPelsHeight;
+ md.hMonitor = hm;
+ md.RefreshRate = dm.dmDisplayFrequency;
+ md.Bpp = dm.dmBitsPerPel;
+ md.Interlaced = (dm.dmDisplayFlags & DM_INTERLACED) ? true : false;
+
+ MONITORINFO mi = {};
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfoW(hm, &mi) && (mi.dwFlags & MONITORINFOF_PRIMARY))
+ md.IsPrimary = true;
+
+ outputs.push_back(md);
+ }
+ }
+}
+
+bool CWinSystemWin32::ChangeResolution(const RESOLUTION_INFO& res, bool forceChange /*= false*/)
+{
+ using KODI::PLATFORM::WINDOWS::ToW;
+ std::wstring outputW = ToW(res.strOutput);
+
+ DEVMODEW sDevMode = {};
+ sDevMode.dmSize = sizeof(sDevMode);
+
+ // If we can't read the current resolution or any detail of the resolution is different than res
+ if (!EnumDisplaySettingsW(outputW.c_str(), ENUM_CURRENT_SETTINGS, &sDevMode) ||
+ sDevMode.dmPelsWidth != res.iWidth || sDevMode.dmPelsHeight != res.iHeight ||
+ sDevMode.dmDisplayFrequency != static_cast<int>(res.fRefreshRate) ||
+ ((sDevMode.dmDisplayFlags & DM_INTERLACED) && !(res.dwFlags & D3DPRESENTFLAG_INTERLACED)) ||
+ (!(sDevMode.dmDisplayFlags & DM_INTERLACED) && (res.dwFlags & D3DPRESENTFLAG_INTERLACED))
+ || forceChange)
+ {
+ ZeroMemory(&sDevMode, sizeof(sDevMode));
+ sDevMode.dmSize = sizeof(sDevMode);
+ sDevMode.dmDriverExtra = 0;
+ sDevMode.dmPelsWidth = res.iWidth;
+ sDevMode.dmPelsHeight = res.iHeight;
+ sDevMode.dmDisplayFrequency = static_cast<int>(res.fRefreshRate);
+ sDevMode.dmDisplayFlags = (res.dwFlags & D3DPRESENTFLAG_INTERLACED) ? DM_INTERLACED : 0;
+ sDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
+
+ LONG rc;
+ bool bResChanged = false;
+
+ // Windows 8 refresh rate workaround for 24.0, 48.0 and 60.0 Hz
+ if ( CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin8)
+ && (res.fRefreshRate == 24.0 || res.fRefreshRate == 48.0 || res.fRefreshRate == 60.0))
+ {
+ CLog::LogF(LOGDEBUG, "Using Windows 8+ workaround for refresh rate {} Hz",
+ static_cast<int>(res.fRefreshRate));
+
+ // Get current resolution stored in registry
+ DEVMODEW sDevModeRegistry = {};
+ sDevModeRegistry.dmSize = sizeof(sDevModeRegistry);
+ if (EnumDisplaySettingsW(outputW.c_str(), ENUM_REGISTRY_SETTINGS, &sDevModeRegistry))
+ {
+ // Set requested mode in registry without actually changing resolution
+ rc = ChangeDisplaySettingsExW(outputW.c_str(), &sDevMode, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
+ if (rc == DISP_CHANGE_SUCCESSFUL)
+ {
+ // Change resolution based on registry setting
+ rc = ChangeDisplaySettingsExW(outputW.c_str(), nullptr, nullptr, CDS_FULLSCREEN, nullptr);
+ if (rc == DISP_CHANGE_SUCCESSFUL)
+ bResChanged = true;
+ else
+ CLog::LogF(
+ LOGERROR,
+ "ChangeDisplaySettingsEx (W8+ change resolution) failed with {}, using fallback",
+ rc);
+
+ // Restore registry with original values
+ sDevModeRegistry.dmSize = sizeof(sDevModeRegistry);
+ sDevModeRegistry.dmDriverExtra = 0;
+ sDevModeRegistry.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
+ rc = ChangeDisplaySettingsExW(outputW.c_str(), &sDevModeRegistry, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
+ if (rc != DISP_CHANGE_SUCCESSFUL)
+ CLog::LogF(LOGERROR, "ChangeDisplaySettingsEx (W8+ restore registry) failed with {}",
+ rc);
+ }
+ else
+ CLog::LogF(LOGERROR,
+ "ChangeDisplaySettingsEx (W8+ set registry) failed with {}, using fallback",
+ rc);
+ }
+ else
+ CLog::LogF(LOGERROR, "Unable to retrieve registry settings for Windows 8+ workaround, using fallback");
+ }
+
+ // Standard resolution change/fallback for Windows 8+ workaround
+ if (!bResChanged)
+ {
+ // CDS_FULLSCREEN is for temporary fullscreen mode and prevents icons and windows from moving
+ // to fit within the new dimensions of the desktop
+ rc = ChangeDisplaySettingsExW(outputW.c_str(), &sDevMode, nullptr, CDS_FULLSCREEN, nullptr);
+ if (rc == DISP_CHANGE_SUCCESSFUL)
+ bResChanged = true;
+ else
+ CLog::LogF(LOGERROR, "ChangeDisplaySettingsEx failed with {}", rc);
+ }
+
+ if (bResChanged)
+ ResolutionChanged();
+
+ return bResChanged;
+ }
+
+ // nothing to do, return success
+ return true;
+}
+
+void CWinSystemWin32::UpdateResolutions()
+{
+ using KODI::PLATFORM::WINDOWS::FromW;
+
+ m_displays.clear();
+
+ CWinSystemBase::UpdateResolutions();
+ GetConnectedDisplays(m_displays);
+
+ MONITOR_DETAILS* details = GetDisplayDetails(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
+ if (!details)
+ return;
+
+ float refreshRate;
+ int w = details->ScreenWidth;
+ int h = details->ScreenHeight;
+ if ((details->RefreshRate + 1) % 24 == 0 || (details->RefreshRate + 1) % 30 == 0)
+ refreshRate = static_cast<float>(details->RefreshRate + 1) / 1.001f;
+ else
+ refreshRate = static_cast<float>(details->RefreshRate);
+
+ std::string strOutput = FromW(details->DeviceNameW);
+ std::string monitorName = FromW(details->MonitorNameW);
+
+ uint32_t dwFlags = details->Interlaced ? D3DPRESENTFLAG_INTERLACED : 0;
+
+ RESOLUTION_INFO& info = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP);
+ UpdateDesktopResolution(info, monitorName, w, h, refreshRate, dwFlags);
+ info.strOutput = strOutput;
+
+ CLog::Log(LOGINFO, "Primary mode: {}", info.strMode);
+
+ // erase previous stored modes
+ CDisplaySettings::GetInstance().ClearCustomResolutions();
+
+ for(int mode = 0;; mode++)
+ {
+ DEVMODEW devmode = {};
+ devmode.dmSize = sizeof(devmode);
+ if(EnumDisplaySettingsW(details->DeviceNameW.c_str(), mode, &devmode) == 0)
+ break;
+ if(devmode.dmBitsPerPel != 32)
+ continue;
+
+ float refresh;
+ if ((devmode.dmDisplayFrequency + 1) % 24 == 0 || (devmode.dmDisplayFrequency + 1) % 30 == 0)
+ refresh = static_cast<float>(devmode.dmDisplayFrequency + 1) / 1.001f;
+ else
+ refresh = static_cast<float>(devmode.dmDisplayFrequency);
+ dwFlags = (devmode.dmDisplayFlags & DM_INTERLACED) ? D3DPRESENTFLAG_INTERLACED : 0;
+
+ RESOLUTION_INFO res;
+ res.iWidth = devmode.dmPelsWidth;
+ res.iHeight = devmode.dmPelsHeight;
+ res.bFullScreen = true;
+ res.dwFlags = dwFlags;
+ res.fRefreshRate = refresh;
+ res.fPixelRatio = 1.0f;
+ res.iScreenWidth = res.iWidth;
+ res.iScreenHeight = res.iHeight;
+ res.iSubtitles = res.iHeight;
+ res.strMode = StringUtils::Format("{}: {}x{} @ {:.2f}Hz", monitorName, res.iWidth, res.iHeight,
+ res.fRefreshRate);
+ GetGfxContext().ResetOverscan(res);
+ res.strOutput = strOutput;
+
+ if (AddResolution(res))
+ CLog::Log(LOGINFO, "Additional mode: {}", res.strMode);
+ }
+
+ CDisplaySettings::GetInstance().ApplyCalibrations();
+}
+
+bool CWinSystemWin32::AddResolution(const RESOLUTION_INFO &res)
+{
+ for (unsigned int i = RES_CUSTOM; i < CDisplaySettings::GetInstance().ResolutionInfoSize(); i++)
+ {
+ RESOLUTION_INFO& info = CDisplaySettings::GetInstance().GetResolutionInfo(i);
+ if (info.iWidth == res.iWidth
+ && info.iHeight == res.iHeight
+ && info.iScreenWidth == res.iScreenWidth
+ && info.iScreenHeight == res.iScreenHeight
+ && info.fRefreshRate == res.fRefreshRate
+ && info.dwFlags == res.dwFlags)
+ return false; // already have this resolution
+ }
+
+ CDisplaySettings::GetInstance().AddResolutionInfo(res);
+ return true;
+}
+
+void CWinSystemWin32::ShowOSMouse(bool show)
+{
+ static int counter = 0;
+ if ((counter < 0 && show) || (counter >= 0 && !show))
+ counter = ShowCursor(show);
+}
+
+bool CWinSystemWin32::Minimize()
+{
+ ShowWindow(m_hWnd, SW_MINIMIZE);
+ return true;
+}
+bool CWinSystemWin32::Restore()
+{
+ ShowWindow(m_hWnd, SW_RESTORE);
+ return true;
+}
+bool CWinSystemWin32::Hide()
+{
+ ShowWindow(m_hWnd, SW_HIDE);
+ return true;
+}
+bool CWinSystemWin32::Show(bool raise)
+{
+ HWND windowAfter = HWND_BOTTOM;
+ if (raise)
+ {
+ if (m_bFullScreen)
+ windowAfter = HWND_TOP;
+ else
+ windowAfter = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST;
+ }
+
+ SetWindowPos(m_hWnd, windowAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS);
+ UpdateWindow(m_hWnd);
+
+ if (raise)
+ {
+ SetForegroundWindow(m_hWnd);
+ SetFocus(m_hWnd);
+ }
+ return true;
+}
+
+void CWinSystemWin32::Register(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void CWinSystemWin32::Unregister(IDispResource* resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ m_resources.erase(i);
+}
+
+void CWinSystemWin32::OnDisplayLost()
+{
+ CLog::LogF(LOGDEBUG, "notify display lost event");
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnLostDisplay();
+ }
+}
+
+void CWinSystemWin32::OnDisplayReset()
+{
+ if (!m_delayDispReset)
+ {
+ CLog::LogF(LOGDEBUG, "notify display reset event");
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
+ (*i)->OnResetDisplay();
+ }
+}
+
+void CWinSystemWin32::OnDisplayBack()
+{
+ auto delay =
+ std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ "videoscreen.delayrefreshchange") *
+ 100);
+ if (delay > 0ms)
+ {
+ m_delayDispReset = true;
+ m_dispResetTimer.Set(delay);
+ }
+ OnDisplayReset();
+}
+
+void CWinSystemWin32::ResolutionChanged()
+{
+ OnDisplayLost();
+ OnDisplayBack();
+}
+
+void CWinSystemWin32::SetForegroundWindowInternal(HWND hWnd)
+{
+ if (!IsWindow(hWnd)) return;
+
+ // if the window isn't focused, bring it to front or SetFullScreen will fail
+ BYTE keyState[256] = {};
+ // to unlock SetForegroundWindow we need to imitate Alt pressing
+ if (GetKeyboardState(reinterpret_cast<LPBYTE>(&keyState)) && !(keyState[VK_MENU] & 0x80))
+ keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+
+ BOOL res = SetForegroundWindow(hWnd);
+
+ if (GetKeyboardState(reinterpret_cast<LPBYTE>(&keyState)) && !(keyState[VK_MENU] & 0x80))
+ keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+
+ if (!res)
+ {
+ //relation time of SetForegroundWindow lock
+ DWORD lockTimeOut = 0;
+ HWND hCurrWnd = GetForegroundWindow();
+ DWORD dwThisTID = GetCurrentThreadId(),
+ dwCurrTID = GetWindowThreadProcessId(hCurrWnd, nullptr);
+
+ // we need to bypass some limitations from Microsoft
+ if (dwThisTID != dwCurrTID)
+ {
+ AttachThreadInput(dwThisTID, dwCurrTID, TRUE);
+ SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, 0);
+ SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nullptr, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
+ AllowSetForegroundWindow(ASFW_ANY);
+ }
+
+ SetForegroundWindow(hWnd);
+
+ if (dwThisTID != dwCurrTID)
+ {
+ SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
+ AttachThreadInput(dwThisTID, dwCurrTID, FALSE);
+ }
+ }
+}
+
+std::unique_ptr<CVideoSync> CWinSystemWin32::GetVideoSync(void *clock)
+{
+ std::unique_ptr<CVideoSync> pVSync(new CVideoSyncD3D(clock));
+ return pVSync;
+}
+
+std::string CWinSystemWin32::GetClipboardText()
+{
+ std::wstring unicode_text;
+ std::string utf8_text;
+
+ if (OpenClipboard(nullptr))
+ {
+ HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
+ if (hglb != nullptr)
+ {
+ LPWSTR lpwstr = static_cast<LPWSTR>(GlobalLock(hglb));
+ if (lpwstr != nullptr)
+ {
+ unicode_text = lpwstr;
+ GlobalUnlock(hglb);
+ }
+ }
+ CloseClipboard();
+ }
+
+ return KODI::PLATFORM::WINDOWS::FromW(unicode_text);
+}
+
+bool CWinSystemWin32::UseLimitedColor()
+{
+ return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE);
+}
+
+void CWinSystemWin32::NotifyAppFocusChange(bool bGaining)
+{
+ if (m_state == WINDOW_STATE_FULLSCREEN && !m_IsAlteringWindow)
+ {
+ m_IsAlteringWindow = true;
+ ReleaseBackBuffer();
+
+ if (bGaining)
+ SetWindowPos(m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW);
+
+ RESOLUTION_INFO res = {};
+ const RESOLUTION resolution = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
+ if (bGaining && resolution > RES_INVALID)
+ res = CDisplaySettings::GetInstance().GetResolutionInfo(resolution);
+
+ SetDeviceFullScreen(bGaining, res);
+
+ if (!bGaining)
+ SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW);
+
+ CreateBackBuffer();
+ m_IsAlteringWindow = false;
+ }
+ m_inFocus = bGaining;
+}
+
+void CWinSystemWin32::UpdateStates(bool fullScreen)
+{
+ m_fullscreenState = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN)
+ ? WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW
+ : WINDOW_FULLSCREEN_STATE_FULLSCREEN;
+ m_windowState = WINDOW_WINDOW_STATE_WINDOWED; // currently only this allowed
+
+ // set the appropriate window style
+ if (fullScreen)
+ {
+ m_windowStyle = FULLSCREEN_WINDOW_STYLE;
+ m_windowExStyle = FULLSCREEN_WINDOW_EX_STYLE;
+ }
+ else
+ {
+ m_windowStyle = WINDOWED_STYLE;
+ m_windowExStyle = WINDOWED_EX_STYLE;
+ }
+}
+
+WINDOW_STATE CWinSystemWin32::GetState(bool fullScreen) const
+{
+ return static_cast<WINDOW_STATE>(fullScreen ? m_fullscreenState : m_windowState);
+}
+
+bool CWinSystemWin32::MessagePump()
+{
+ return m_winEvents->MessagePump();
+}
+
+void CWinSystemWin32::SetTogglingHDR(bool toggling)
+{
+ if (toggling)
+ SetTimer(m_hWnd, ID_TIMER_HDR, 6000U, nullptr);
+
+ m_IsTogglingHDR = toggling;
+}
+
+RECT CWinSystemWin32::GetVirtualScreenRect()
+{
+ RECT rect = {};
+ rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ rect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + rect.left;
+ rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ rect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + rect.top;
+
+ return rect;
+}
+
+int CWinSystemWin32::GetGuiSdrPeakLuminance() const
+{
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ return settings->GetInt(CSettings::SETTING_VIDEOSCREEN_GUISDRPEAKLUMINANCE);
+}
+
+RECT CWinSystemWin32::GetScreenWorkArea(HMONITOR handle) const
+{
+ MONITORINFO monitorInfo{};
+ monitorInfo.cbSize = sizeof(MONITORINFO);
+ if (!GetMonitorInfoW(handle, &monitorInfo))
+ {
+ CLog::LogF(LOGERROR, "GetMonitorInfoW failed with {}", GetLastError());
+ return RECT();
+ }
+ return monitorInfo.rcWork;
+}
+
+RECT CWinSystemWin32::GetNcAreaOffsets(DWORD dwStyle, BOOL bMenu, DWORD dwExStyle) const
+{
+ RECT rcNcArea{};
+ SetRectEmpty(&rcNcArea);
+
+ if (!AdjustWindowRectEx(&rcNcArea, dwStyle, false, dwExStyle))
+ {
+ CLog::LogF(LOGERROR, "AdjustWindowRectEx failed with {}", GetLastError());
+ return RECT();
+ }
+ return rcNcArea;
+}
diff --git a/xbmc/windowing/windows/WinSystemWin32.h b/xbmc/windowing/windows/WinSystemWin32.h
new file mode 100644
index 0000000..0573295
--- /dev/null
+++ b/xbmc/windowing/windows/WinSystemWin32.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+#include "windowing/WinSystem.h"
+
+#include <shellapi.h>
+#include <vector>
+
+static const DWORD WINDOWED_STYLE = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
+static const DWORD WINDOWED_EX_STYLE = NULL;
+static const DWORD FULLSCREEN_WINDOW_STYLE = WS_POPUP | WS_SYSMENU | WS_CLIPCHILDREN;
+static const DWORD FULLSCREEN_WINDOW_EX_STYLE = WS_EX_APPWINDOW;
+static const UINT ID_TIMER_HDR = 34U;
+
+/* Controls the way the window appears and behaves. */
+enum WINDOW_STATE
+{
+ WINDOW_STATE_FULLSCREEN = 1, // Exclusive fullscreen
+ WINDOW_STATE_FULLSCREEN_WINDOW, // Non-exclusive fullscreen window
+ WINDOW_STATE_WINDOWED, //Movable window with border
+ WINDOW_STATE_BORDERLESS //Non-movable window with no border
+};
+
+static const char* window_state_names[] =
+{
+ "unknown",
+ "true fullscreen",
+ "windowed fullscreen",
+ "windowed",
+ "borderless"
+};
+
+/* WINDOW_STATE restricted to fullscreen modes. */
+enum WINDOW_FULLSCREEN_STATE
+{
+ WINDOW_FULLSCREEN_STATE_FULLSCREEN = WINDOW_STATE_FULLSCREEN,
+ WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW = WINDOW_STATE_FULLSCREEN_WINDOW
+};
+
+/* WINDOW_STATE restricted to windowed modes. */
+enum WINDOW_WINDOW_STATE
+{
+ WINDOW_WINDOW_STATE_WINDOWED = WINDOW_STATE_WINDOWED,
+ WINDOW_WINDOW_STATE_BORDERLESS = WINDOW_STATE_BORDERLESS
+};
+
+struct MONITOR_DETAILS
+{
+ int ScreenWidth;
+ int ScreenHeight;
+ int RefreshRate;
+ int Bpp;
+ int DisplayId;
+ bool Interlaced;
+ bool IsPrimary;
+
+ HMONITOR hMonitor;
+ std::wstring MonitorNameW;
+ std::wstring CardNameW;
+ std::wstring DeviceNameW;
+};
+
+class CIRServerSuite;
+
+class CWinSystemWin32 : public CWinSystemBase
+{
+public:
+ CWinSystemWin32();
+ virtual ~CWinSystemWin32();
+
+ // CWinSystemBase overrides
+ bool InitWindowSystem() override;
+ bool DestroyWindowSystem() override;
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ void FinishWindowResize(int newWidth, int newHeight) override;
+ void UpdateResolutions() override;
+ bool CenterWindow() override;
+ virtual void NotifyAppFocusChange(bool bGaining) override;
+ void ShowOSMouse(bool show) override;
+ bool HasInertialGestures() override { return true; }//if win32 has touchscreen - it uses the win32 gesture api for inertial scrolling
+ bool Minimize() override;
+ bool Restore() override;
+ bool Hide() override;
+ bool Show(bool raise = true) override;
+ std::string GetClipboardText() override;
+ bool UseLimitedColor() override;
+
+ // videosync
+ std::unique_ptr<CVideoSync> GetVideoSync(void *clock) override;
+
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+
+ std::vector<std::string> GetConnectedOutputs() override;
+
+ // CWinSystemWin32
+ HWND GetHwnd() const { return m_hWnd; }
+ bool IsAlteringWindow() const { return m_IsAlteringWindow; }
+ void SetAlteringWindow(bool altering) { m_IsAlteringWindow = altering; }
+ bool IsTogglingHDR() const { return m_IsTogglingHDR; }
+ void SetTogglingHDR(bool toggling);
+ virtual bool DPIChanged(WORD dpi, RECT windowRect) const;
+ bool IsMinimized() const { return m_bMinimized; }
+ void SetMinimized(bool minimized);
+ int GetGuiSdrPeakLuminance() const;
+
+ // touchscreen support
+ typedef BOOL(WINAPI *pGetGestureInfo)(HGESTUREINFO, PGESTUREINFO);
+ typedef BOOL(WINAPI *pSetGestureConfig)(HWND, DWORD, UINT, PGESTURECONFIG, UINT);
+ typedef BOOL(WINAPI *pCloseGestureInfoHandle)(HGESTUREINFO);
+ typedef BOOL(WINAPI *pEnableNonClientDpiScaling)(HWND);
+ pGetGestureInfo PtrGetGestureInfo;
+ pSetGestureConfig PtrSetGestureConfig;
+ pCloseGestureInfoHandle PtrCloseGestureInfoHandle;
+ pEnableNonClientDpiScaling PtrEnableNonClientDpiScaling;
+
+ void SetSizeMoveMode(bool mode) { m_bSizeMoveEnabled = mode; }
+ bool IsInSizeMoveMode() const { return m_bSizeMoveEnabled; }
+
+ // winevents override
+ bool MessagePump() override;
+
+protected:
+ bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override = 0;
+ virtual void UpdateStates(bool fullScreen);
+ WINDOW_STATE GetState(bool fullScreen) const;
+ virtual void SetDeviceFullScreen(bool fullScreen, RESOLUTION_INFO& res) = 0;
+ virtual void ReleaseBackBuffer() = 0;
+ virtual void CreateBackBuffer() = 0;
+ virtual void ResizeDeviceBuffers() = 0;
+ virtual bool IsStereoEnabled() = 0;
+ virtual void OnScreenChange(HMONITOR monitor) = 0;
+ virtual void AdjustWindow(bool forceResize = false);
+ void CenterCursor() const;
+
+ virtual void Register(IDispResource *resource);
+ virtual void Unregister(IDispResource *resource);
+
+ virtual bool ChangeResolution(const RESOLUTION_INFO& res, bool forceChange = false);
+ virtual bool CreateBlankWindows();
+ virtual bool BlankNonActiveMonitors(bool bBlank);
+ MONITOR_DETAILS* GetDisplayDetails(const std::string& name);
+ MONITOR_DETAILS* GetDisplayDetails(HMONITOR handle);
+ void RestoreDesktopResolution(MONITOR_DETAILS* details);
+ RECT ScreenRect(HMONITOR handle);
+ void GetConnectedDisplays(std::vector<MONITOR_DETAILS>& outputs);
+
+
+ /*!
+ \brief Adds a resolution to the list of resolutions if we don't already have it
+ \param res resolution to add.
+ */
+ static bool AddResolution(const RESOLUTION_INFO &res);
+
+ void OnDisplayLost();
+ void OnDisplayReset();
+ void OnDisplayBack();
+ void ResolutionChanged();
+ static void SetForegroundWindowInternal(HWND hWnd);
+ static RECT GetVirtualScreenRect();
+ /*!
+ * Retrieve the work area of the screen (exclude task bar and other occlusions)
+ */
+ RECT GetScreenWorkArea(HMONITOR handle) const;
+ /*!
+ * Retrieve size of the title bar and borders
+ * Add to coordinates to convert client coordinates to window coordinates
+ * Substract from coordinates to convert from window coordinates to client coordinates
+ */
+ RECT GetNcAreaOffsets(DWORD dwStyle, BOOL bMenu, DWORD dwExStyle) const;
+
+ HWND m_hWnd;
+ HMONITOR m_hMonitor;
+
+ std::vector<HWND> m_hBlankWindows;
+ HINSTANCE m_hInstance;
+ HICON m_hIcon;
+ bool m_ValidWindowedPosition;
+ bool m_IsAlteringWindow;
+ bool m_IsTogglingHDR{false};
+
+ CCriticalSection m_resourceSection;
+ std::vector<IDispResource*> m_resources;
+ bool m_delayDispReset;
+ XbmcThreads::EndTime<> m_dispResetTimer;
+
+ WINDOW_STATE m_state; // the state of the window
+ WINDOW_FULLSCREEN_STATE m_fullscreenState; // the state of the window when in fullscreen
+ WINDOW_WINDOW_STATE m_windowState; // the state of the window when in windowed
+ DWORD m_windowStyle; // the style of the window
+ DWORD m_windowExStyle; // the ex style of the window
+ bool m_inFocus;
+ bool m_bMinimized;
+ bool m_bSizeMoveEnabled = false;
+ bool m_bFirstResChange = true;
+ std::unique_ptr<CIRServerSuite> m_irss;
+ std::vector<MONITOR_DETAILS> m_displays;
+
+ NOTIFYICONDATA m_trayIcon = {};
+
+ static const char* SETTING_WINDOW_TOP;
+ static const char* SETTING_WINDOW_LEFT;
+};
+
+extern HWND g_hWnd;
+
diff --git a/xbmc/windowing/windows/WinSystemWin32DX.cpp b/xbmc/windowing/windows/WinSystemWin32DX.cpp
new file mode 100644
index 0000000..6d568fc
--- /dev/null
+++ b/xbmc/windowing/windows/WinSystemWin32DX.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemWin32DX.h"
+
+#include "commons/ilog.h"
+#include "rendering/dx/RenderContext.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/SystemInfo.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include "platform/win32/CharsetConverter.h"
+#include "platform/win32/WIN32Util.h"
+
+#ifndef _M_X64
+#include "utils/SystemInfo.h"
+#endif
+#if _DEBUG
+#pragma comment(lib, "detoursd.lib")
+#else
+#pragma comment(lib, "detours.lib")
+#endif
+#pragma comment(lib, "dxgi.lib")
+#include <windows.h>
+#include <winnt.h>
+#include <winternl.h>
+#pragma warning(disable: 4091)
+#include <d3d10umddi.h>
+#pragma warning(default: 4091)
+#include <detours.h>
+
+using KODI::PLATFORM::WINDOWS::FromW;
+
+using namespace std::chrono_literals;
+
+// User Mode Driver hooks definitions
+void APIENTRY HookCreateResource(D3D10DDI_HDEVICE hDevice, const D3D10DDIARG_CREATERESOURCE* pResource, D3D10DDI_HRESOURCE hResource, D3D10DDI_HRTRESOURCE hRtResource);
+HRESULT APIENTRY HookCreateDevice(D3D10DDI_HADAPTER hAdapter, D3D10DDIARG_CREATEDEVICE* pCreateData);
+HRESULT APIENTRY HookOpenAdapter10_2(D3D10DDIARG_OPENADAPTER *pOpenData);
+static PFND3D10DDI_OPENADAPTER s_fnOpenAdapter10_2{ nullptr };
+static PFND3D10DDI_CREATEDEVICE s_fnCreateDeviceOrig{ nullptr };
+static PFND3D10DDI_CREATERESOURCE s_fnCreateResourceOrig{ nullptr };
+
+void CWinSystemWin32DX::Register()
+{
+ KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemWin32DX::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemWin32DX>();
+}
+
+CWinSystemWin32DX::CWinSystemWin32DX() : CRenderSystemDX()
+ , m_hDriverModule(nullptr)
+{
+}
+
+CWinSystemWin32DX::~CWinSystemWin32DX()
+{
+}
+
+void CWinSystemWin32DX::PresentRenderImpl(bool rendered)
+{
+ if (rendered)
+ m_deviceResources->Present();
+
+ if (m_delayDispReset && m_dispResetTimer.IsTimePast())
+ {
+ m_delayDispReset = false;
+ OnDisplayReset();
+ }
+
+ if (!rendered)
+ KODI::TIME::Sleep(40ms);
+}
+
+bool CWinSystemWin32DX::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ const MONITOR_DETAILS* monitor = GetDisplayDetails(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
+ if (!monitor)
+ return false;
+
+ m_hMonitor = monitor->hMonitor;
+ m_deviceResources = DX::DeviceResources::Get();
+ // setting monitor before creating window for proper hooking into a driver
+ m_deviceResources->SetMonitor(m_hMonitor);
+
+ return CWinSystemWin32::CreateNewWindow(name, fullScreen, res) && m_deviceResources->HasValidDevice();
+}
+
+void CWinSystemWin32DX::SetWindow(HWND hWnd) const
+{
+ m_deviceResources->SetWindow(hWnd);
+}
+
+bool CWinSystemWin32DX::DestroyRenderSystem()
+{
+ CRenderSystemDX::DestroyRenderSystem();
+
+ m_deviceResources->Release();
+ m_deviceResources.reset();
+ return true;
+}
+
+void CWinSystemWin32DX::SetDeviceFullScreen(bool fullScreen, RESOLUTION_INFO& res)
+{
+ if (m_deviceResources->SetFullScreen(fullScreen, res))
+ {
+ ResolutionChanged();
+ }
+}
+
+bool CWinSystemWin32DX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ CWinSystemWin32::ResizeWindow(newWidth, newHeight, newLeft, newTop);
+ CRenderSystemDX::OnResize();
+
+ return true;
+}
+
+void CWinSystemWin32DX::OnMove(int x, int y)
+{
+ // do not handle moving at window creation because MonitorFromWindow
+ // returns default system monitor in case of m_hWnd is null
+ if (!m_hWnd)
+ return;
+
+ HMONITOR newMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
+ if (newMonitor != m_hMonitor)
+ {
+ MONITOR_DETAILS* details = GetDisplayDetails(newMonitor);
+
+ if (!details)
+ return;
+
+ CDisplaySettings::GetInstance().SetMonitor(KODI::PLATFORM::WINDOWS::FromW(details->MonitorNameW));
+ m_deviceResources->SetMonitor(newMonitor);
+ m_hMonitor = newMonitor;
+ }
+
+ // Save window position if not fullscreen
+ if (!IsFullScreen() && (m_nLeft != x || m_nTop != y))
+ {
+ m_nLeft = x;
+ m_nTop = y;
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ settings->SetInt(SETTING_WINDOW_LEFT, x);
+ settings->SetInt(SETTING_WINDOW_TOP, y);
+ settings->Save();
+ }
+}
+
+bool CWinSystemWin32DX::DPIChanged(WORD dpi, RECT windowRect) const
+{
+ // on Win10 FCU the OS keeps window size exactly the same size as it was
+ if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10_1709))
+ return true;
+
+ m_deviceResources->SetDpi(dpi);
+ if (!IsAlteringWindow())
+ return __super::DPIChanged(dpi, windowRect);
+
+ return true;
+}
+
+void CWinSystemWin32DX::ReleaseBackBuffer()
+{
+ m_deviceResources->ReleaseBackBuffer();
+}
+
+void CWinSystemWin32DX::CreateBackBuffer()
+{
+ m_deviceResources->CreateBackBuffer();
+}
+
+void CWinSystemWin32DX::ResizeDeviceBuffers()
+{
+ m_deviceResources->ResizeBuffers();
+}
+
+bool CWinSystemWin32DX::IsStereoEnabled()
+{
+ return m_deviceResources->IsStereoEnabled();
+}
+
+void CWinSystemWin32DX::OnScreenChange(HMONITOR monitor)
+{
+ m_deviceResources->SetMonitor(monitor);
+}
+
+bool CWinSystemWin32DX::ChangeResolution(const RESOLUTION_INFO &res, bool forceChange)
+{
+ bool changed = CWinSystemWin32::ChangeResolution(res, forceChange);
+ // this is a try to fix FCU issue after changing resolution
+ if (m_deviceResources && changed)
+ m_deviceResources->ResizeBuffers();
+ return changed;
+}
+
+void CWinSystemWin32DX::OnResize(int width, int height)
+{
+ if (!m_IsAlteringWindow)
+ ReleaseBackBuffer();
+
+ m_deviceResources->SetLogicalSize(static_cast<float>(width), static_cast<float>(height));
+
+ if (!m_IsAlteringWindow)
+ CreateBackBuffer();
+}
+
+bool CWinSystemWin32DX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ bool const result = CWinSystemWin32::SetFullScreen(fullScreen, res, blankOtherDisplays);
+ CRenderSystemDX::OnResize();
+ return result;
+}
+
+void CWinSystemWin32DX::UninitHooks()
+{
+ // uninstall
+ if (!s_fnOpenAdapter10_2)
+ return;
+
+ DetourTransactionBegin();
+ DetourUpdateThread(GetCurrentThread());
+ DetourDetach(reinterpret_cast<void**>(&s_fnOpenAdapter10_2), HookOpenAdapter10_2);
+ DetourTransactionCommit();
+ if (m_hDriverModule)
+ {
+ FreeLibrary(m_hDriverModule);
+ m_hDriverModule = nullptr;
+ }
+}
+
+void CWinSystemWin32DX::InitHooks(IDXGIOutput* pOutput)
+{
+ DXGI_OUTPUT_DESC outputDesc;
+ if (!pOutput || FAILED(pOutput->GetDesc(&outputDesc)))
+ return;
+
+ DISPLAY_DEVICEW displayDevice;
+ displayDevice.cb = sizeof(DISPLAY_DEVICEW);
+ DWORD adapter = 0;
+ bool deviceFound = false;
+
+ // delete exiting hooks.
+ if (s_fnOpenAdapter10_2)
+ UninitHooks();
+
+ // enum devices to find matched
+ while (EnumDisplayDevicesW(nullptr, adapter, &displayDevice, 0))
+ {
+ if (wcscmp(displayDevice.DeviceName, outputDesc.DeviceName) == 0)
+ {
+ deviceFound = true;
+ break;
+ }
+ adapter++;
+ }
+ if (!deviceFound)
+ return;
+
+ CLog::LogF(LOGDEBUG, "Hooking into UserModeDriver on device {}. ",
+ FromW(displayDevice.DeviceKey));
+ const wchar_t* keyName =
+#ifndef _M_X64
+ // on x64 system and x32 build use UserModeDriverNameWow key
+ CSysInfo::GetKernelBitness() == 64 ? keyName = L"UserModeDriverNameWow" :
+#endif // !_WIN64
+ L"UserModeDriverName";
+
+ DWORD dwType = REG_MULTI_SZ;
+ HKEY hKey = nullptr;
+ wchar_t value[1024];
+ DWORD valueLength = sizeof(value);
+ LSTATUS lstat;
+
+ // to void \Registry\Machine at the beginning, we use shifted pointer at 18
+ if (ERROR_SUCCESS == (lstat = RegOpenKeyExW(HKEY_LOCAL_MACHINE, displayDevice.DeviceKey + 18, 0, KEY_READ, &hKey))
+ && ERROR_SUCCESS == (lstat = RegQueryValueExW(hKey, keyName, nullptr, &dwType, (LPBYTE)&value, &valueLength)))
+ {
+ // 1. registry value has a list of drivers for each API with the following format: dx9\0dx10\0dx11\0dx12\0\0
+ // 2. we split the value by \0
+ std::vector<std::wstring> drivers;
+ const wchar_t* pValue = value;
+ while (*pValue)
+ {
+ drivers.push_back(std::wstring(pValue));
+ pValue += drivers.back().size() + 1;
+ }
+ // no entries in the registry
+ if (drivers.empty())
+ return;
+ // 3. we take only first three values (dx12 driver isn't needed if it exists ofc)
+ if (drivers.size() > 3)
+ drivers = std::vector<std::wstring>(drivers.begin(), drivers.begin() + 3);
+ // 4. and then iterate with reverse order to start iterate with the best candidate for d3d11 driver
+ for (auto it = drivers.rbegin(); it != drivers.rend(); ++it)
+ {
+ m_hDriverModule = LoadLibraryW(it->c_str());
+ if (m_hDriverModule != nullptr)
+ {
+ s_fnOpenAdapter10_2 = reinterpret_cast<PFND3D10DDI_OPENADAPTER>(GetProcAddress(m_hDriverModule, "OpenAdapter10_2"));
+ if (s_fnOpenAdapter10_2 != nullptr)
+ {
+ DetourTransactionBegin();
+ DetourUpdateThread(GetCurrentThread());
+ DetourAttach(reinterpret_cast<void**>(&s_fnOpenAdapter10_2), HookOpenAdapter10_2);
+ if (NO_ERROR == DetourTransactionCommit())
+ // install and activate hook into a driver
+ {
+ CLog::LogF(LOGDEBUG, "D3D11 hook installed and activated.");
+ break;
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Unable to install and activate D3D11 hook.");
+ s_fnOpenAdapter10_2 = nullptr;
+ FreeLibrary(m_hDriverModule);
+ m_hDriverModule = nullptr;
+ }
+ }
+ }
+ }
+ }
+
+ if (lstat != ERROR_SUCCESS)
+ CLog::LogF(LOGDEBUG, "error open registry key with error {}.", lstat);
+
+ if (hKey != nullptr)
+ RegCloseKey(hKey);
+}
+
+void CWinSystemWin32DX::FixRefreshRateIfNecessary(const D3D10DDIARG_CREATERESOURCE* pResource) const
+{
+ if (pResource && pResource->pPrimaryDesc)
+ {
+ float refreshRate = RATIONAL_TO_FLOAT(pResource->pPrimaryDesc->ModeDesc.RefreshRate);
+ if (refreshRate > 10.0f && refreshRate < 300.0f)
+ {
+ // interlaced
+ if (pResource->pPrimaryDesc->ModeDesc.ScanlineOrdering > DXGI_DDI_MODE_SCANLINE_ORDER_PROGRESSIVE)
+ refreshRate /= 2;
+
+ uint32_t refreshNum, refreshDen;
+ DX::GetRefreshRatio(static_cast<uint32_t>(floor(m_fRefreshRate)), &refreshNum, &refreshDen);
+ float diff = fabs(refreshRate - static_cast<float>(refreshNum) / static_cast<float>(refreshDen)) / refreshRate;
+ CLog::LogF(LOGDEBUG,
+ "refreshRate: {:0.4f}, desired: {:0.4f}, deviation: {:.5f}, fixRequired: {}, {}",
+ refreshRate, m_fRefreshRate, diff, (diff > 0.0005 && diff < 0.1) ? "yes" : "no",
+ pResource->pPrimaryDesc->Flags);
+ if (diff > 0.0005 && diff < 0.1)
+ {
+ pResource->pPrimaryDesc->ModeDesc.RefreshRate.Numerator = refreshNum;
+ pResource->pPrimaryDesc->ModeDesc.RefreshRate.Denominator = refreshDen;
+ if (pResource->pPrimaryDesc->ModeDesc.ScanlineOrdering > DXGI_DDI_MODE_SCANLINE_ORDER_PROGRESSIVE)
+ pResource->pPrimaryDesc->ModeDesc.RefreshRate.Numerator *= 2;
+ CLog::LogF(LOGDEBUG, "refreshRate fix applied -> {:0.3f}",
+ RATIONAL_TO_FLOAT(pResource->pPrimaryDesc->ModeDesc.RefreshRate));
+ }
+ }
+ }
+}
+
+void APIENTRY HookCreateResource(D3D10DDI_HDEVICE hDevice, const D3D10DDIARG_CREATERESOURCE* pResource, D3D10DDI_HRESOURCE hResource, D3D10DDI_HRTRESOURCE hRtResource)
+{
+ if (pResource && pResource->pPrimaryDesc)
+ {
+ DX::Windowing()->FixRefreshRateIfNecessary(pResource);
+ }
+ s_fnCreateResourceOrig(hDevice, pResource, hResource, hRtResource);
+}
+
+HRESULT APIENTRY HookCreateDevice(D3D10DDI_HADAPTER hAdapter, D3D10DDIARG_CREATEDEVICE* pCreateData)
+{
+ HRESULT hr = s_fnCreateDeviceOrig(hAdapter, pCreateData);
+ if (pCreateData->pDeviceFuncs->pfnCreateResource)
+ {
+ CLog::LogF(LOGDEBUG, "hook into pCreateData->pDeviceFuncs->pfnCreateResource");
+ s_fnCreateResourceOrig = pCreateData->pDeviceFuncs->pfnCreateResource;
+ pCreateData->pDeviceFuncs->pfnCreateResource = HookCreateResource;
+ }
+ return hr;
+}
+
+HRESULT APIENTRY HookOpenAdapter10_2(D3D10DDIARG_OPENADAPTER *pOpenData)
+{
+ HRESULT hr = s_fnOpenAdapter10_2(pOpenData);
+ if (pOpenData->pAdapterFuncs->pfnCreateDevice)
+ {
+ CLog::LogF(LOGDEBUG, "hook into pOpenData->pAdapterFuncs->pfnCreateDevice");
+ s_fnCreateDeviceOrig = pOpenData->pAdapterFuncs->pfnCreateDevice;
+ pOpenData->pAdapterFuncs->pfnCreateDevice = HookCreateDevice;
+ }
+ return hr;
+}
+
+bool CWinSystemWin32DX::IsHDRDisplay()
+{
+ return (CWIN32Util::GetWindowsHDRStatus() != HDR_STATUS::HDR_UNSUPPORTED);
+}
+
+HDR_STATUS CWinSystemWin32DX::GetOSHDRStatus()
+{
+ return CWIN32Util::GetWindowsHDRStatus();
+}
+
+HDR_STATUS CWinSystemWin32DX::ToggleHDR()
+{
+ return m_deviceResources->ToggleHDR();
+}
+
+bool CWinSystemWin32DX::IsHDROutput() const
+{
+ return m_deviceResources->IsHDROutput();
+}
+
+bool CWinSystemWin32DX::IsTransferPQ() const
+{
+ return m_deviceResources->IsTransferPQ();
+}
+
+void CWinSystemWin32DX::SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const
+{
+ m_deviceResources->SetHdrMetaData(hdr10);
+}
+
+void CWinSystemWin32DX::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace) const
+{
+ m_deviceResources->SetHdrColorSpace(colorSpace);
+}
+
+DEBUG_INFO_RENDER CWinSystemWin32DX::GetDebugInfo()
+{
+ return m_deviceResources->GetDebugInfo();
+}
diff --git a/xbmc/windowing/windows/WinSystemWin32DX.h b/xbmc/windowing/windows/WinSystemWin32DX.h
new file mode 100644
index 0000000..d3673de
--- /dev/null
+++ b/xbmc/windowing/windows/WinSystemWin32DX.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "HDRStatus.h"
+#include "rendering/dx/RenderSystemDX.h"
+#include "windowing/windows/WinSystemWin32.h"
+
+struct D3D10DDIARG_CREATERESOURCE;
+
+class CWinSystemWin32DX : public CWinSystemWin32, public CRenderSystemDX
+{
+ friend interface DX::IDeviceNotify;
+public:
+ CWinSystemWin32DX();
+ ~CWinSystemWin32DX();
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystemBase via CWinSystemWin32
+ CRenderSystemBase *GetRenderSystem() override { return this; }
+ bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ void PresentRenderImpl(bool rendered) override;
+ bool DPIChanged(WORD dpi, RECT windowRect) const override;
+ void SetWindow(HWND hWnd) const;
+ bool DestroyRenderSystem() override;
+ void* GetHWContext() override { return m_deviceResources->GetD3DContext(); }
+
+ void UninitHooks();
+ void InitHooks(IDXGIOutput* pOutput);
+
+ void OnMove(int x, int y) override;
+ void OnResize(int width, int height);
+
+ /*!
+ \brief Register as a dependent of the DirectX Render System
+ Resources should call this on construction if they're dependent on the Render System
+ for survival. Any resources that registers will get callbacks on loss and reset of
+ device. In addition, callbacks for destruction and creation of the device are also called,
+ where any resources dependent on the DirectX device should be destroyed and recreated.
+ \sa Unregister, ID3DResource
+ */
+ void Register(ID3DResource *resource) const
+ {
+ m_deviceResources->Register(resource);
+ };
+ /*!
+ \brief Unregister as a dependent of the DirectX Render System
+ Resources should call this on destruction if they're a dependent on the Render System
+ \sa Register, ID3DResource
+ */
+ void Unregister(ID3DResource *resource) const
+ {
+ m_deviceResources->Unregister(resource);
+ };
+
+ void Register(IDispResource* resource) override { CWinSystemWin32::Register(resource); }
+ void Unregister(IDispResource* resource) override { CWinSystemWin32::Unregister(resource); }
+
+ void FixRefreshRateIfNecessary(const D3D10DDIARG_CREATERESOURCE* pResource) const;
+
+ // HDR OS/display override
+ bool IsHDRDisplay() override;
+ HDR_STATUS ToggleHDR() override;
+ HDR_STATUS GetOSHDRStatus() override;
+
+ // HDR support
+ bool IsHDROutput() const;
+ bool IsTransferPQ() const;
+ void SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const;
+ void SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace) const;
+
+ // Get debug info from swapchain
+ DEBUG_INFO_RENDER GetDebugInfo() override;
+
+protected:
+ void SetDeviceFullScreen(bool fullScreen, RESOLUTION_INFO& res) override;
+ void ReleaseBackBuffer() override;
+ void CreateBackBuffer() override;
+ void ResizeDeviceBuffers() override;
+ bool IsStereoEnabled() override;
+ void OnScreenChange(HMONITOR monitor) override;
+ bool ChangeResolution(const RESOLUTION_INFO& res, bool forceChange = false) override;
+
+ HMODULE m_hDriverModule;
+};
+